bool HomeMovementGenerator<Creature>::Update(Creature &owner, const uint32& time_diff) { CreatureTraveller traveller(owner); i_destinationHolder.UpdateTraveller(traveller, time_diff); if (time_diff > i_travel_timer) { owner.AddUnitMovementFlag(MOVEFLAG_WALK_MODE); // restore orientation of not moving creature at returning to home if (owner.GetDefaultMovementType() == IDLE_MOTION_TYPE) { owner.SetOrientation(ori); WorldPacket packet; owner.BuildHeartBeatMsg(&packet); owner.SendMessageToSet(&packet, false); } owner.AI()->JustReachedHome(); return false; } i_travel_timer -= time_diff; return true; }
bool HomeMovementGenerator<Creature>::Update(Creature &owner, const uint32& time_diff) { CreatureTraveller traveller( owner); if (i_destinationHolder.UpdateTraveller(traveller, time_diff, false)) { if (!IsActive(owner)) // force stop processing (movement can move out active zone with cleanup movegens list) return true; // not expire now, but already lost } if (time_diff > i_travel_timer) { owner.AddSplineFlag(SPLINEFLAG_WALKMODE); // restore orientation of not moving creature at returning to home if(owner.GetDefaultMovementType()==IDLE_MOTION_TYPE) { if(CreatureData const* data = sObjectMgr.GetCreatureData(owner.GetDBTableGUIDLow())) { owner.SetOrientation(data->orientation); WorldPacket packet; owner.BuildHeartBeatMsg(&packet); owner.SendMessageToSet(&packet, false); } } owner.LoadCreatureAddon(true); owner.AI()->JustReachedHome(); return false; } i_travel_timer -= time_diff; return true; }
bool HomeMovementGenerator<Creature>::Update(Creature &owner, const uint32 time_diff) { CreatureTraveller traveller(owner); i_destinationHolder.UpdateTraveller(traveller, time_diff); if (time_diff > i_travel_timer) { owner.AddUnitMovementFlag(MOVEMENTFLAG_WALKING); // restore orientation of not moving creature at returning to home if (owner.GetDefaultMovementType() == IDLE_MOTION_TYPE) { //sLog->outDebug("Entering HomeMovement::GetDestination(z, y, z)"); owner.SetOrientation(ori); WorldPacket packet; owner.BuildHeartBeatMsg(&packet); owner.SendMessageToSet(&packet, false); } owner.ClearUnitState(UNIT_STAT_EVADE); owner.LoadCreaturesAddon(true); owner.AI()->JustReachedHome(); return false; } i_travel_timer -= time_diff; return true; }
void SpellColdflameSpellScript::SelectTarget(std::list<WorldObject*>& targets) { targets.clear(); // select any unit but not the tank (by owners threatlist) Unit* target = GetCaster()->GetAI()->SelectTarget(SELECT_TARGET_RANDOM, 1, -GetCaster()->GetObjectSize(), true, -SPELL_IMPALED); if (!target) target = GetCaster()->GetAI()->SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true); // or the tank if its solo if (!target) return; Creature* dummy = GetCaster()->SummonCreature(NPC_COLDFLAME_TARGET_STALKER, *GetCaster(), TEMPSUMMON_TIMED_DESPAWN, 10000); dummy->SetOrientation(GetCaster()->GetAngle(target)); if (!GetCaster()->HasAura(SPELL_BONE_STORM)) { Position pos; dummy->GetNearPosition(pos, GetCaster()->GetObjectSize(), 0); dummy->NearTeleportTo(pos.GetPositionX(), pos.GetPositionY(), dummy->GetPositionZ(), dummy->GetOrientation()); } GetCaster()->GetAI()->SetGUID(dummy->GetGUID(), DATA_COLDFLAME_GUID); targets.push_back(target); }
bool HomeMovementGenerator<Creature>::Update(Creature &owner, const uint32& time_diff) { CreatureTraveller traveller( owner); i_destinationHolder.UpdateTraveller(traveller, time_diff, false); if (time_diff > i_travel_timer) { owner.AddSplineFlag(SPLINEFLAG_WALKMODE); // restore orientation of not moving creature at returning to home if(owner.GetDefaultMovementType()==IDLE_MOTION_TYPE) { if(CreatureData const* data = sObjectMgr.GetCreatureData(owner.GetDBTableGUIDLow())) { owner.SetOrientation(data->orientation); WorldPacket packet; owner.BuildHeartBeatMsg(&packet); owner.SendMessageToSet(&packet, false); } } owner.AI()->JustReachedHome(); return false; } i_travel_timer -= time_diff; return true; }
void DoAction(const int32 actionId) { switch (actionId) { case ACTION_OUTRO: { Position pos; if (Creature* pIck = GetIck()) { // TODO: tele on Ick then run some distance. pIck->GetNearPosition(pos, 5.0f, 3.14f); me->NearTeleportTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), 0.0f); } me->SetVisible(true); Creature* pJainaOrSylvanas = me->GetCreature(*me, pInstance->GetData64(DATA_JAINA_SYLVANAS_1)); if (pJainaOrSylvanas) { Position pos; me->GetNearPosition(pos, 5.0f, 0); pJainaOrSylvanas->NearTeleportTo( pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetAngle(me->GetPositionX(), me->GetPositionY())); } else { if (pInstance->GetData(DATA_TEAM_IN_INSTANCE) == TEAM_ALLIANCE) pJainaOrSylvanas = me->SummonCreature( NPC_SYLVANAS_PART1, *me, TEMPSUMMON_MANUAL_DESPAWN); else pJainaOrSylvanas = me->SummonCreature(NPC_JAINA_PART1, *me, TEMPSUMMON_MANUAL_DESPAWN); } if (pJainaOrSylvanas) { pJainaOrSylvanas->SetOrientation( pJainaOrSylvanas->GetAngle(me->GetPositionX(), me->GetPositionY())); me->SetOrientation( me->GetAngle(pJainaOrSylvanas->GetPositionX(), pJainaOrSylvanas->GetPositionY())); uiNpcOutroDialog = pJainaOrSylvanas->GetGUID(); } phase = PHASE_OUTRO; events.Reset(); events.ScheduleEvent(EVENT_OUTRO_1, 1000); break; } } }
void StartEvent() { Step = 1; EventStarted = true; Creature* Arcanagos = me->SummonCreature(MOB_ARCANAGOS,ArcanagosPos[0],ArcanagosPos[1],ArcanagosPos[2],0,TEMPSUMMON_CORPSE_TIMED_DESPAWN,20000); if (!Arcanagos) return; ArcanagosGUID = Arcanagos->GetGUID(); Arcanagos->AddUnitMovementFlag(MOVEFLAG_ONTRANSPORT | MOVEFLAG_LEVITATING); (*Arcanagos).GetMotionMaster()->MovePoint(0,ArcanagosPos[0],ArcanagosPos[1],ArcanagosPos[2]); Arcanagos->SetOrientation(ArcanagosPos[3]); me->SetOrientation(MedivPos[3]); YellTimer = 10000; }
void StartEvent() { Step = 1; EventStarted = true; Creature* Arcanagos = me->SummonCreature(MOB_ARCANAGOS, ArcanagosPos[0], ArcanagosPos[1], ArcanagosPos[2], 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); if (!Arcanagos) return; ArcanagosGUID = Arcanagos->GetGUID(); Arcanagos->SetDisableGravity(true); (*Arcanagos).GetMotionMaster()->MovePoint(0, ArcanagosPos[0], ArcanagosPos[1], ArcanagosPos[2]); Arcanagos->SetOrientation(ArcanagosPos[3]); me->SetOrientation(MedivPos[3]); YellTimer = 10000; }
void SpawnHighborneLamenter(Player* pThis, uint32 entry, float posX, float posY, float posZ, float posO) { CreatureProperties const* p = sMySQLStore.getCreatureProperties(entry); if (p == nullptr) return; Creature* creature = pThis->GetMapMgr()->CreateCreature(entry); creature->m_spawn = nullptr; creature->Load(p, posX, posY, posZ); creature->SetOrientation(posO); creature->GetAIInterface()->setCombatDisabled(true); creature->GetAIInterface()->setMeleeDisabled(true); creature->GetAIInterface()->setTargetingDisabled(true); creature->PushToWorld(pThis->GetMapMgr()); creature->Despawn(180000, 0); creature->setUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, 35); creature->setServersideFaction(); }
bool HomeMovementGenerator<Creature>::Update(Creature &owner, const uint32& time_diff) { CreatureTraveller traveller(owner); i_destinationHolder.UpdateTraveller(traveller, time_diff); if (time_diff > i_travel_timer) { float x, y, z; owner.GetRespawnCoord(x, y, z); float myx, myy, myz; owner.GetPosition(myx, myy, myz); if (x != myx || y != myy || z != myz) { Position travelto = owner.GetMap()->getNextPositionOnPathToLocation(myx,myy,myz,x,y,z); uint32 travel_time = i_destinationHolder.SetDestination(traveller, travelto.m_positionX, travelto.m_positionY, travelto.m_positionZ); modifyTravelTime(travel_time); return true; } owner.AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); // restore orientation of not moving creature at returning to home if (owner.GetDefaultMovementType() == IDLE_MOTION_TYPE) { //sLog.outDebug("Entering HomeMovement::GetDestination(z,y,z)"); owner.SetOrientation(ori); WorldPacket packet; owner.BuildHeartBeatMsg(&packet); owner.SendMessageToSet(&packet, false); } owner.clearUnitState(UNIT_STAT_EVADE); owner.AI()->JustReachedHome(); return false; } i_travel_timer -= time_diff; return true; }
// Emote Ardonis and Pathaleon void Turn_to_Pathaleons_Image() { Creature* ardonis = Unit::GetCreature(*me, ardonisGUID); Creature* pathaleon = Unit::GetCreature(*me, pathaleonGUID); Player* player = Unit::GetPlayer(*me, PlayerGUID); if (!ardonis || !pathaleon || !player) return; //Calculate the angle to Pathaleon angle_dawnforge = me->GetAngle(pathaleon->GetPositionX(), pathaleon->GetPositionY()); angle_ardonis = ardonis->GetAngle(pathaleon->GetPositionX(), pathaleon->GetPositionY()); //Turn Dawnforge and update me->SetOrientation(angle_dawnforge); me->SendUpdateToPlayer(player); //Turn Ardonis and update ardonis->SetOrientation(angle_ardonis); ardonis->SendUpdateToPlayer(player); //Set them to kneel me->SetStandState(UNIT_STAND_STATE_KNEEL); ardonis->SetStandState(UNIT_STAND_STATE_KNEEL); }
bool WaypointMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff) { if (!&creature) return true; // Waypoint movement can be switched on/off // This is quite handy for escort quests and other stuff if (creature.hasUnitState(UNIT_STAT_NOT_MOVE)) { creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); return true; } // prevent a crash at empty waypoint path. if (!i_path || i_path->empty()) { creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); return true; } if (i_currentNode >= i_path->size()) i_currentNode = 0; CreatureTraveller traveller(creature); i_nextMoveTime.Update(diff); if (i_destinationHolder.UpdateTraveller(traveller, diff, false, true)) { if (!IsActive(creature)) // force stop processing (movement can move out active zone with cleanup movegens list) return true; // not expire now, but already lost } // creature has been stopped in middle of the waypoint segment if (!i_destinationHolder.HasArrived() && creature.IsStopped()) { // Timer has elapsed, meaning this part controlled it if (i_nextMoveTime.Passed()) { SetStoppedByPlayer(false); creature.addUnitState(UNIT_STAT_ROAMING_MOVE); if (creature.canFly()) creature.AddSplineFlag(SPLINEFLAG_UNKNOWN7); // Now we re-set destination to same node and start travel const WaypointNode &node = i_path->at(i_currentNode); i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z); i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); } else // if( !i_nextMoveTime.Passed()) { // unexpected end of timer && creature stopped && not at end of segment if (!IsStoppedByPlayer()) { // Put 30 seconds delay i_destinationHolder.IncreaseTravelTime(STOP_TIME_FOR_PLAYER); i_nextMoveTime.Reset(STOP_TIME_FOR_PLAYER); SetStoppedByPlayer(true); // Mark we did it } } return true; // Abort here this update } if (creature.IsStopped() && !i_hasDone) // creature is waiting on a waypoint and hasn't done the behavior yet { uint32 idx = i_currentNode > 0 ? i_currentNode-1 : i_path->size()-1; if (i_path->at(idx).orientation != 100) creature.SetOrientation(i_path->at(idx).orientation); if (WaypointBehavior *behavior = i_path->at(idx).behavior) { if (behavior->emote != 0) creature.HandleEmote(behavior->emote); if (behavior->spell != 0) { creature.CastSpell(&creature, behavior->spell, false); if (!IsActive(creature)) // force stop processing (cast can change movegens list) return true; // not expire now, but already lost } if (behavior->model1 != 0) creature.SetDisplayId(behavior->model1); if (behavior->textid[0]) { // Not only one text is set if (behavior->textid[1]) { // Select one from max 5 texts (0 and 1 already checked) int i = 2; for(; i < MAX_WAYPOINT_TEXT; ++i) { if (!behavior->textid[i]) break; } creature.Say(behavior->textid[rand() % i], 0, 0); } else creature.Say(behavior->textid[0], 0, 0); } } // behavior done i_hasDone = true; MovementInform(creature); if (!IsActive(creature)) // force stop processing (movement can move out active zone with cleanup movegens list) return true; // not expire now, but already lost // prevent a crash at empty waypoint path. if (!i_path || i_path->empty() || i_currentNode >= i_path->size()) { creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); return true; } } // i_creature.IsStopped() // This is at the end of waypoint segment or has been stopped by player if (i_nextMoveTime.Passed()) { // If stopped then begin a new move segment if (creature.IsStopped()) { creature.addUnitState(UNIT_STAT_ROAMING_MOVE); if (creature.canFly()) creature.AddSplineFlag(SPLINEFLAG_UNKNOWN7); const WaypointNode &node = i_path->at(i_currentNode); i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z); i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); uint32 idx = i_currentNode > 0 ? i_currentNode-1 : i_path->size()-1; if (i_path->at(idx).orientation != 100) creature.SetOrientation(i_path->at(idx).orientation); if (WaypointBehavior *behavior = i_path->at(idx).behavior) { if (behavior->model2 != 0) creature.SetDisplayId(behavior->model2); creature.SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); } } else { // If not stopped then stop it and set the reset of TimeTracker to waittime creature.StopMoving(); SetStoppedByPlayer(false); i_nextMoveTime.Reset(i_path->at(i_currentNode).delay); ++i_currentNode; i_hasDone = false; if (i_currentNode >= i_path->size()) i_currentNode = 0; } } return true; }
bool WaypointMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff) { if(!&creature) return true; // Waypoint movement can be switched on/off // This is quite handy for escort quests and other stuff if(creature.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED)) return true; // prevent crash at empty waypoint path. if(i_path.Empty()) { return true; } CreatureTraveller traveller(creature); /* if( npcIsStopped[creature.GetGUID()] ) { i_nextMoveTime.Update(40000); i_destinationHolder.UpdateTraveller(traveller, ((diff)-40000), false); npcIsStopped[creature.GetGUID()] = false; return true; } */ i_nextMoveTime.Update(diff); i_destinationHolder.UpdateTraveller(traveller, diff, false); if( creature.IsStopped() ) { uint32 wpB = i_currentNode > 0 ? i_currentNode-1 : i_wpBehaviour.size()-1; if( i_wpBehaviour[wpB] != NULL ) { struct WaypointBehavior *tmpBehavior = i_wpBehaviour[wpB]; if (!tmpBehavior->HasDone) { if(tmpBehavior->emote != 0) { creature.SetUInt32Value(UNIT_NPC_EMOTESTATE,tmpBehavior->emote); } if(tmpBehavior->aiscript != "") { WPAIScript(creature, tmpBehavior->aiscript); } //sLog.outDebug("DEBUG: tmpBehavior->text[0] TEST"); if(tmpBehavior->text[0] != "") { //sLog.outDebug("DEBUG: tmpBehavior->text[0] != \"\""); // Only one text is set if( tmpBehavior->text[1] == "" ) { //sLog.outDebug("DEBUG: tmpBehavior->text[1] == NULL"); creature.Say(tmpBehavior->text[0].c_str(), 0, 0); } else { // Select one from max 5 texts for( int i = 0; i < 4; ++i ) { if( tmpBehavior->text[i] == "" ) { //sLog.outDebug("DEBUG: tmpBehavior->text[i] == \"\": %d", i); //sLog.outDebug("DEBUG: rand() % (i): %d", rand() % (i)); creature.Say(tmpBehavior->text[rand() % i].c_str(), 0, 0); break; } } } } if(tmpBehavior->spell != 0) { //sLog.outDebug("DEBUG: wpSys - spell"); creature.CastSpell(&creature,tmpBehavior->spell, false); } if (tmpBehavior->orientation !=100) { //sLog.outDebug("DEBUG: wpSys - orientation"); creature.SetOrientation(tmpBehavior->orientation); } if(tmpBehavior->model1 != 0) { //sLog.outDebug("DEBUG: wpSys - model1"); creature.SetUInt32Value(UNIT_FIELD_DISPLAYID, tmpBehavior->model1); } tmpBehavior->HasDone = true; } // HasDone == false } // wpBehaviour found } // i_creature.IsStopped() if( i_nextMoveTime.Passed() ) { if( creature.IsStopped() ) { assert( i_currentNode < i_path.Size() ); creature.addUnitState(UNIT_STAT_ROAMING); const Path::PathNode &node(i_path(i_currentNode)); i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z); i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); uint32 wpB = i_currentNode > 0 ? i_currentNode-1 : i_wpBehaviour.size()-1; if( i_wpBehaviour[wpB] != NULL ) { struct WaypointBehavior *tmpBehavior = i_wpBehaviour[wpB]; tmpBehavior->HasDone = false; if(tmpBehavior->model2 != 0) { creature.SetUInt32Value(UNIT_FIELD_DISPLAYID, tmpBehavior->model2); } if (tmpBehavior->orientation !=100) { creature.SetOrientation(tmpBehavior->orientation); } creature.SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); } } else { creature.StopMoving(); i_nextMoveTime.Reset(i_delays[i_currentNode]); ++i_currentNode; if( i_currentNode >= i_path.Size() ) i_currentNode = 0; } } return true; }
void RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature) { float X,Y,Z,z,nx,ny,nz,wander_distance,ori,dist; creature.GetRespawnCoord(X, Y, Z, &ori, &wander_distance); z = creature.GetPositionZ(); Map const* map = creature.GetBaseMap(); // For 2D/3D system selection //bool is_land_ok = creature.canWalk(); // not used? //bool is_water_ok = creature.canSwim(); // not used? bool is_air_ok = creature.canFly(); const float angle = rand_norm_f()*(M_PI_F*2.0f); const float range = rand_norm_f()*wander_distance; const float distanceX = range * cos(angle); const float distanceY = range * sin(angle); nx = X + distanceX; ny = Y + distanceY; // prevent invalid coordinates generation MaNGOS::NormalizeMapCoord(nx); MaNGOS::NormalizeMapCoord(ny); dist = distanceX*distanceX + distanceY*distanceY; if (is_air_ok) // 3D system above ground and above water (flying mode) { // Limit height change const float distanceZ = rand_norm_f() * sqrtf(dist)/2.0f; nz = Z + distanceZ; float tz = map->GetHeight(nx, ny, nz-2.0f, false); // Map check only, vmap needed here but need to alter vmaps checks for height. float wz = map->GetWaterLevel(nx, ny); // Problem here, we must fly above the ground and water, not under. Let's try on next tick if (tz >= nz || wz >= nz) return; } //else if (is_water_ok) // 3D system under water and above ground (swimming mode) else // 2D only { nz = Z; if (!map->IsNextZcoordOK(nx, ny, nz, dist)) return; // let's forget this bad coords where a z cannot be find and retry at next tick creature.UpdateGroundPositionZ(nx, ny, nz, dist); } Traveller<Creature> traveller(creature); creature.SetOrientation(creature.GetAngle(nx, ny)); i_destinationHolder.SetDestination(traveller, nx, ny, nz); creature.addUnitState(UNIT_STAT_ROAMING_MOVE); if (is_air_ok && !(creature.canWalk() && creature.IsAtGroundLevel(nx, ny, nz))) { i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); creature.AddSplineFlag(SPLINEFLAG_UNKNOWN7); } //else if (is_water_ok) // Swimming mode to be done with more than this check else { i_nextMoveTime.Reset(urand(500+i_destinationHolder.GetTotalTravelTime(), 10000+i_destinationHolder.GetTotalTravelTime())); creature.AddSplineFlag(SPLINEFLAG_WALKMODE); } }
bool WaypointMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff) { if(!&creature) return true; // Waypoint movement can be switched on/off // This is quite handy for escort quests and other stuff if(creature.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED)) return true; // prevent a crash at empty waypoint path. if(!i_path || i_path->empty()) return true; // i_path was modified by chat commands for example if(i_path->size() != i_hasDone.size()) i_hasDone.resize(i_path->size()); if(i_currentNode >= i_path->size()) i_currentNode = 0; CreatureTraveller traveller(creature); i_nextMoveTime.Update(diff); i_destinationHolder.UpdateTraveller(traveller, diff, false, true); // creature has been stopped in middle of the waypoint segment if (!i_destinationHolder.HasArrived() && creature.IsStopped()) { if( i_nextMoveTime.Passed()) // Timer has elapsed, meaning this part controlled it { SetStopedByPlayer(false); // Now we re-set destination to same node and start travel creature.addUnitState(UNIT_STAT_ROAMING); if (creature.canFly()) creature.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2); const WaypointNode &node = i_path->at(i_currentNode); i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z); i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); } else // if( !i_nextMoveTime.Passed()) { // unexpected end of timer && creature stopped && not at end of segment if (!IsStopedByPlayer()) { // Put 30 seconds delay i_destinationHolder.IncreaseTravelTime(STOP_TIME_FOR_PLAYER); i_nextMoveTime.Reset(STOP_TIME_FOR_PLAYER); SetStopedByPlayer(true); // Mark we did it } } return true; // Abort here this update } if( creature.IsStopped()) { uint32 idx = i_currentNode > 0 ? i_currentNode-1 : i_path->size()-1; if (!i_hasDone[idx]) { if (i_path->at(idx).orientation !=100) creature.SetOrientation(i_path->at(idx).orientation); if(WaypointBehavior *behavior = i_path->at(idx).behavior) { if(behavior->emote != 0) creature.SetUInt32Value(UNIT_NPC_EMOTESTATE,behavior->emote); if(behavior->spell != 0) creature.CastSpell(&creature,behavior->spell, false); if(behavior->model1 != 0) creature.SetDisplayId(behavior->model1); if(behavior->textid[0]) { // Not only one text is set if( behavior->textid[1] ) { // Select one from max 5 texts (0 and 1 laready checked) int i = 2; for( ; i < MAX_WAYPOINT_TEXT; ++i ) if( !behavior->textid[i] ) break; creature.Say(behavior->textid[rand() % i], 0, 0); } else creature.Say(behavior->textid[0], 0, 0); } i_hasDone[idx] = true; MovementInform(creature); } // wpBehaviour found } // HasDone == false } // i_creature.IsStopped() if( i_nextMoveTime.Passed() ) // This is at the end of waypoint segment or has been stopped by player { if( creature.IsStopped() ) // If stopped then begin a new move segment { creature.addUnitState(UNIT_STAT_ROAMING); if (creature.canFly()) creature.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2); const WaypointNode &node = i_path->at(i_currentNode); i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z); i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); uint32 idx = i_currentNode > 0 ? i_currentNode-1 : i_path->size()-1; if (i_path->at(idx).orientation !=100) creature.SetOrientation(i_path->at(idx).orientation); if(WaypointBehavior *behavior = i_path->at(idx).behavior ) { i_hasDone[idx] = false; if(behavior->model2 != 0) creature.SetDisplayId(behavior->model2); creature.SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); } } else // If not stopped then stop it and set the reset of TimeTracker to waittime { creature.StopMoving(); SetStopedByPlayer(false); i_nextMoveTime.Reset(i_path->at(i_currentNode).delay); ++i_currentNode; if( i_currentNode >= i_path->size() ) i_currentNode = 0; } } return true; }
bool WaypointMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff) { if (!&creature) return true; // Waypoint movement can be switched on/off // This is quite handy for escort quests and other stuff if (creature.hasUnitState(UNIT_STAT_NOT_MOVE)) { creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); return true; } // prevent a crash at empty waypoint path. if (!i_path || i_path->empty()) { creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); return true; } if (i_currentNode >= i_path->size()) { sLog.outError("WaypointMovement currentNode (%u) is equal or bigger than path size (creature entry %u)", i_currentNode, creature.GetEntry()); i_currentNode = 0; } CreatureTraveller traveller(creature); i_nextMoveTime.Update(diff); if (i_destinationHolder.UpdateTraveller(traveller, diff, false, true)) { if (!IsActive(creature)) // force stop processing (movement can move out active zone with cleanup movegens list) return true; // not expire now, but already lost } // creature has been stopped in middle of the waypoint segment if (!i_destinationHolder.HasArrived() && creature.IsStopped()) { // Timer has elapsed, meaning this part controlled it if (i_nextMoveTime.Passed()) { SetStoppedByPlayer(false); creature.addUnitState(UNIT_STAT_ROAMING_MOVE); if (creature.canFly()) creature.AddSplineFlag(SPLINEFLAG_UNKNOWN7); // Now we re-set destination to same node and start travel const WaypointNode &node = i_path->at(i_currentNode); i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z); i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); } else // if( !i_nextMoveTime.Passed()) { // unexpected end of timer && creature stopped && not at end of segment if (!IsStoppedByPlayer()) { // Put 30 seconds delay i_destinationHolder.IncreaseTravelTime(STOP_TIME_FOR_PLAYER); i_nextMoveTime.Reset(STOP_TIME_FOR_PLAYER); SetStoppedByPlayer(true); // Mark we did it } } return true; // Abort here this update } if (creature.IsStopped()) { if (!m_isArrivalDone) { if (i_path->at(i_currentNode).orientation != 100) creature.SetOrientation(i_path->at(i_currentNode).orientation); if (i_path->at(i_currentNode).script_id) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature movement start script %u at point %u for creature %u (entry %u).", i_path->at(i_currentNode).script_id, i_currentNode, creature.GetDBTableGUIDLow(), creature.GetEntry()); creature.GetMap()->ScriptsStart(sCreatureMovementScripts, i_path->at(i_currentNode).script_id, &creature, &creature); } // We have reached the destination and can process behavior if (WaypointBehavior *behavior = i_path->at(i_currentNode).behavior) { if (behavior->emote != 0) creature.HandleEmote(behavior->emote); if (behavior->spell != 0) { creature.CastSpell(&creature, behavior->spell, false); if (!IsActive(creature)) // force stop processing (cast can change movegens list) return true; // not expire now, but already lost } if (behavior->model1 != 0) creature.SetDisplayId(behavior->model1); if (behavior->textid[0]) { // Not only one text is set if (behavior->textid[1]) { // Select one from max 5 texts (0 and 1 already checked) int i = 2; for(; i < MAX_WAYPOINT_TEXT; ++i) { if (!behavior->textid[i]) break; } creature.Say(behavior->textid[rand() % i], 0, 0); } else creature.Say(behavior->textid[0], 0, 0); } } // wpBehaviour found // Can only do this once for the node m_isArrivalDone = true; // Inform script MovementInform(creature); if (!IsActive(creature)) // force stop processing (movement can move out active zone with cleanup movegens list) return true; // not expire now, but already lost // prevent a crash at empty waypoint path. if (!i_path || i_path->empty() || i_currentNode >= i_path->size()) { creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); return true; } } } // i_creature.IsStopped() // This is at the end of waypoint segment (incl. was previously stopped by player, extending the time) if (i_nextMoveTime.Passed()) { // If stopped then begin a new move segment if (creature.IsStopped()) { creature.addUnitState(UNIT_STAT_ROAMING_MOVE); if (creature.canFly()) creature.AddSplineFlag(SPLINEFLAG_UNKNOWN7); if (WaypointBehavior *behavior = i_path->at(i_currentNode).behavior) { if (behavior->model2 != 0) creature.SetDisplayId(behavior->model2); creature.SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); } // behavior for "departure" of the current node is done m_isArrivalDone = false; // Proceed with increment current node and then send to the next destination ++i_currentNode; // Oops, end of the line so need to start from the beginning if (i_currentNode >= i_path->size()) i_currentNode = 0; if (i_path->at(i_currentNode).orientation != 100) creature.SetOrientation(i_path->at(i_currentNode).orientation); const WaypointNode &node = i_path->at(i_currentNode); i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z); i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); } else { // If not stopped then stop it creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); SetStoppedByPlayer(false); // Set TimeTracker to waittime for the current node i_nextMoveTime.Reset(i_path->at(i_currentNode).delay); } } return true; }
void RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature) { float X, Y, Z, z, nx, ny, nz, ori, dist; creature.GetHomePosition(X, Y, Z, ori); z = creature.GetPositionZ(); Map const* map = creature.GetBaseMap(); // For 2D/3D system selection //bool is_land_ok = creature.canWalk(); //bool is_water_ok = creature.canSwim(); bool is_air_ok = creature.canFly(); for (uint32 i = 0; ; ++i) { const float angle = (float)rand_norm()*static_cast<float>(M_PI*2); const float range = (float)rand_norm()*wander_distance; const float distanceX = range * cos(angle); const float distanceY = range * sin(angle); nx = X + distanceX; ny = Y + distanceY; // prevent invalid coordinates generation Trillium::NormalizeMapCoord(nx); Trillium::NormalizeMapCoord(ny); dist = (nx - X)*(nx - X) + (ny - Y)*(ny - Y); if (i == 5) { nz = Z; break; } if (is_air_ok) // 3D system above ground and above water (flying mode) { const float distanceZ = (float)(rand_norm()) * sqrtf(dist)/2; // Limit height change nz = Z + distanceZ; float tz = map->GetHeight(nx, ny, nz-2.0f, false); // Map check only, vmap needed here but need to alter vmaps checks for height. float wz = map->GetWaterLevel(nx, ny); if (tz >= nz || wz >= nz) continue; // Problem here, we must fly above the ground and water, not under. Let's try on next tick } //else if (is_water_ok) // 3D system under water and above ground (swimming mode) else // 2D only { dist = dist >= 100.0f ? 10.0f : sqrtf(dist); // 10.0 is the max that vmap high can check (MAX_CAN_FALL_DISTANCE) // The fastest way to get an accurate result 90% of the time. // Better result can be obtained like 99% accuracy with a ray light, but the cost is too high and the code is too long. nz = map->GetHeight(nx, ny, Z+dist-2.0f, false); // Map check if (fabs(nz-Z)>dist) { nz = map->GetHeight(nx, ny, Z-2.0f, true); // Vmap Horizontal or above if (fabs(nz-Z)>dist) { nz = map->GetHeight(nx, ny, Z+dist-2.0f, true); // Vmap Higher if (fabs(nz-Z)>dist) continue; // let's forget this bad coords where a z cannot be find and retry at next tick } } } break; } Traveller<Creature> traveller(creature); creature.SetOrientation(creature.GetAngle(nx, ny)); i_destinationHolder.SetDestination(traveller, nx, ny, nz); creature.AddUnitState(UNIT_STAT_ROAMING); if (is_air_ok) { i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); } //else if (is_water_ok) // Swimming mode to be done with more than this check else { i_nextMoveTime.Reset(urand(500+i_destinationHolder.GetTotalTravelTime(), 5000+i_destinationHolder.GetTotalTravelTime())); creature.AddUnitMovementFlag(MOVEMENTFLAG_WALKING); } //Call for creature group update if (creature.GetFormation() && creature.GetFormation()->getLeader() == &creature) { creature.GetFormation()->LeaderMoveTo(nx, ny, nz); } }
void RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature) { float respX, respY, respZ, respO, currZ, destX, destY, destZ, wander_distance, travelDistZ; creature.GetRespawnCoord(respX, respY, respZ, &respO, &wander_distance); currZ = creature.GetPositionZ(); TerrainInfo const* map = creature.GetTerrain(); // For 2D/3D system selection //bool is_land_ok = creature.CanWalk(); // not used? //bool is_water_ok = creature.CanSwim(); // not used? bool is_air_ok = creature.CanFly(); const float angle = rand_norm_f() * (M_PI_F*2.0f); const float range = rand_norm_f() * wander_distance; const float distanceX = range * cos(angle); const float distanceY = range * sin(angle); destX = respX + distanceX; destY = respY + distanceY; // prevent invalid coordinates generation MaNGOS::NormalizeMapCoord(destX); MaNGOS::NormalizeMapCoord(destY); travelDistZ = distanceX*distanceX + distanceY*distanceY; if (is_air_ok) // 3D system above ground and above water (flying mode) { // Limit height change const float distanceZ = rand_norm_f() * sqrtf(travelDistZ)/2.0f; destZ = respZ + distanceZ; float levelZ = map->GetWaterOrGroundLevel(destX, destY, destZ-2.0f); // Problem here, we must fly above the ground and water, not under. Let's try on next tick if (levelZ >= destZ) return; } //else if (is_water_ok) // 3D system under water and above ground (swimming mode) else // 2D only { // 10.0 is the max that vmap high can check (MAX_CAN_FALL_DISTANCE) travelDistZ = travelDistZ >= 100.0f ? 10.0f : sqrtf(travelDistZ); // The fastest way to get an accurate result 90% of the time. // Better result can be obtained like 99% accuracy with a ray light, but the cost is too high and the code is too long. destZ = map->GetHeight(destX, destY, respZ+travelDistZ-2.0f, false); if (fabs(destZ - respZ) > travelDistZ) // Map check { // Vmap Horizontal or above destZ = map->GetHeight(destX, destY, respZ - 2.0f, true); if (fabs(destZ - respZ) > travelDistZ) { // Vmap Higher destZ = map->GetHeight(destX, destY, respZ+travelDistZ-2.0f, true); // let's forget this bad coords where a z cannot be find and retry at next tick if (fabs(destZ - respZ) > travelDistZ) return; } } } Traveller<Creature> traveller(creature); creature.SetOrientation(creature.GetAngle(destX, destY)); i_destinationHolder.SetDestination(traveller, destX, destY, destZ); creature.addUnitState(UNIT_STAT_ROAMING_MOVE); if (is_air_ok) { i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); creature.AddSplineFlag(SPLINEFLAG_FLYING); } //else if (is_water_ok) // Swimming mode to be done with more than this check else { i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime() + urand(500, 10000)); creature.AddSplineFlag(SPLINEFLAG_WALKMODE); } }
void RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature) { float X,Y,Z,z,nx,ny,nz,wander_distance,ori,dist; creature.GetRespawnCoord(X, Y, Z, &ori, &wander_distance); z = creature.GetPositionZ(); uint32 mapid=creature.GetMapId(); Map const* map = MapManager::Instance().GetBaseMap(mapid); // For 2D/3D system selection //bool is_land_ok = creature.canWalk(); // not used? //bool is_water_ok = creature.canSwim(); // not used? bool is_air_ok = creature.canFly(); const float angle = rand_norm()*(M_PI*2); const float range = rand_norm()*wander_distance; const float distanceX = range * cos(angle); const float distanceY = range * sin(angle); nx = X + distanceX; ny = Y + distanceY; // prevent invalid coordinates generation MaNGOS::NormalizeMapCoord(nx); MaNGOS::NormalizeMapCoord(ny); dist = distanceX*distanceX + distanceY*distanceY; if (is_air_ok) // 3D system above ground and above water (flying mode) { const float distanceZ = rand_norm() * sqrtf(dist)/2; // Limit height change nz = Z + distanceZ; float tz = map->GetHeight(nx, ny, nz-2.0f, false); // Map check only, vmap needed here but need to alter vmaps checks for height. float wz = map->GetWaterLevel(nx, ny); if (tz >= nz || wz >= nz) return; // Problem here, we must fly above the ground and water, not under. Let's try on next tick } //else if (is_water_ok) // 3D system under water and above ground (swimming mode) else // 2D only { dist = dist>=100.0f ? 10.0f : sqrtf(dist); // 10.0 is the max that vmap high can check (MAX_CAN_FALL_DISTANCE) // The fastest way to get an accurate result 90% of the time. // Better result can be obtained like 99% accuracy with a ray light, but the cost is too high and the code is too long. nz = map->GetHeight(nx,ny,Z+dist-2.0f,false); // Map check if (fabs(nz-Z)>dist) { nz = map->GetHeight(nx,ny,Z-2.0f,true); // Vmap Horizontal or above if (fabs(nz-Z)>dist) { nz = map->GetHeight(nx,ny,Z+dist-2.0f,true); // Vmap Higher if (fabs(nz-Z)>dist) return; // let's forget this bad coords where a z cannot be find and retry at next tick } } } Traveller<Creature> traveller(creature); creature.SetOrientation(creature.GetAngle(nx,ny)); i_destinationHolder.SetDestination(traveller, nx, ny, nz); creature.addUnitState(UNIT_STAT_ROAMING); if (is_air_ok) { i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); creature.AddUnitMovementFlag(MONSTER_MOVE_FLY); } //else if (is_water_ok) // Swimming mode to be done with more than this check else { i_nextMoveTime.Reset(urand(500+i_destinationHolder.GetTotalTravelTime(),5000+i_destinationHolder.GetTotalTravelTime())); creature.SetUnitMovementFlags(MONSTER_MOVE_WALK); } }
void UpdateAI(uint32 diff) override { ScriptedAI::UpdateAI(diff); DoMeleeAttackIfReady(); if (m_uiViciousSliceTimer <= diff) { DoCast(me->GetVictim(), SPELL_VICIOUS_SLICE); m_uiViciousSliceTimer = 10000; } else m_uiViciousSliceTimer -= diff; if (HealthBelowPct(50)) { if (!bSummoned) { DoCast(me->GetVictim(), SPELL_SUMMON_MINIONS); bSummoned = true; } if (!bSay) { Talk(1); bSay = true; } } if (HealthBelowPct(35)) { if (!bSay2) { Talk(4); bSay2 = true; } if (!bGo1) { me->AttackStop(); me->SetReactState(REACT_PASSIVE); me->GetMotionMaster()->MovePoint(0, -10142.081f, 671.773f, 36.014f); bGo1 = true; } } if (HealthBelowPct(10)) { if (!bSummoned3) { if (!bSay3) { Talk(5); bSay3 = true; } me->CombatStop(true); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); Creature* General = me->SummonCreature(46942, -10133.275f, 663.244f, 35.964616f, 2.45f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 180000); GeneralGUID = General->GetGUID(); Creature* Mage1 = me->SummonCreature(46941, -10129.976f, 667.982f, 35.67f, 2.85f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 180000); Mage1GUID = Mage1->GetGUID(); Creature* Mage2 = me->SummonCreature(46940, -10137.671f, 659.926f, 35.971f, 2.051f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 180000); Mage2GUID = Mage2->GetGUID(); Creature* Raga1 = me->SummonCreature(46943, -10133.339f, 660.087f, 35.971f, 2.26f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 180000); Raga1GUID = Raga1->GetGUID(); Creature* Raga2 = me->SummonCreature(46943, -10129.461f, 663.180f, 35.9491f, 2.37f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 180000); Raga2GUID = Raga2->GetGUID(); Summon = true; m_uiPhaseTimer = 1500; phase = 1; } if (Summon) { Creature* General = ObjectAccessor::GetCreature(*me, GeneralGUID); Creature* Raga2 = ObjectAccessor::GetCreature(*me, Raga2GUID); Creature* Raga1 = ObjectAccessor::GetCreature(*me, Raga1GUID); Creature* Mage2 = ObjectAccessor::GetCreature(*me, Mage2GUID); Creature* Mage1 = ObjectAccessor::GetCreature(*me, Mage1GUID); if (!bCasted) { General->CastSpell(General, 64446, true); Raga2->CastSpell(Raga2, 64446, true); Raga1->CastSpell(Raga1, 64446, true); Mage2->CastSpell(Raga2, 64446, true); Mage1->CastSpell(Raga1, 64446, true); bCasted = true; } bSummoned3 = true; me->SetSpeed(MOVE_RUN, 1.0f); } } if (bCasted) { if (m_uiPhaseTimer <= diff) { Creature* General = ObjectAccessor::GetCreature(*me, GeneralGUID); Creature* Raga2 = ObjectAccessor::GetCreature(*me, Raga2GUID); Creature* Raga1 = ObjectAccessor::GetCreature(*me, Raga1GUID); Creature* Mage2 = ObjectAccessor::GetCreature(*me, Mage2GUID); Creature* Mage1 = ObjectAccessor::GetCreature(*me, Mage1GUID); switch (phase) { case 1: me->GetMotionMaster()->MovePoint(1, -10141.054f, 670.719f, 35.9569f); m_uiPhaseTimer = 3000; phase = 2; break; case 2: General->AI()->Talk(0); m_uiPhaseTimer = 2500; phase = 3; break; case 3: Raga1->AI()->Talk(0); m_uiPhaseTimer = 1500; phase = 4; break; case 4: Raga2->AI()->Talk(1); m_uiPhaseTimer = 1500; phase = 5; break; case 5: { if (Creature* General = ObjectAccessor::GetCreature(*me, GeneralGUID)) { General->AI()->Talk(1); General->AddUnitMovementFlag(MOVEMENTFLAG_WALKING); General->GetMotionMaster()->MovePoint(0, -10137.162f, 667.919f, 35.937f); m_uiPhaseTimer = 3000; } phase = 6; break; } case 6: Talk(2); m_uiPhaseTimer = 4000; phase = 7; break; case 7: { General->HandleEmoteCommand(EMOTE_ONESHOT_POINT); m_uiPhaseTimer = 4500; phase = 8; break; } case 8: General->AI()->Talk(2); m_uiPhaseTimer = 4000; phase = 9; break; case 9: Talk(3); m_uiPhaseTimer = 4000; phase = 10; break; case 10: General->AI()->Talk(3); m_uiPhaseTimer = 4000; phase = 11; break; General->SetOrientation(6.08f); case 11: { General->CastSpell(General, 64446, true); Raga1->CastSpell(Raga1, 64446, true); Raga2->CastSpell(Raga2, 64446, true); Mage1->CastSpell(Mage1, 64446, true); Mage2->CastSpell(Mage2, 64446, true); me->CastSpell(me, 64446, true); General->DespawnOrUnsummon(500); Raga1->DespawnOrUnsummon(500); Raga2->DespawnOrUnsummon(500); Mage1->DespawnOrUnsummon(500); Mage2->DespawnOrUnsummon(500); me->DespawnOrUnsummon(500); std::list<Player*> players = me->FindNearestPlayers(35.0f); for (std::list<Player*>::const_iterator itr = players.begin(); itr != players.end(); ++itr) (*itr)->KilledMonsterCredit(448); phase = 0; break; } default: break; } } else m_uiPhaseTimer -= diff; } if (me->GetHealth() <= 1) { if (m_uiDespawnTimer <= diff) { me->CombatStop(true); me->AttackStop(); me->ClearAllReactives(); me->DeleteThreatList(); me->SetHealth(me->GetMaxHealth()); } else m_uiDespawnTimer -= diff; } }
void RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature) { float respX, respY, respZ, respO, currZ, destX, destY, destZ, wander_distance, travelDistZ; creature.GetRespawnCoord(respX, respY, respZ, &respO, &wander_distance); currZ = creature.GetPositionZ(); TerrainInfo const* map = creature.GetTerrain(); // For 2D/3D system selection //bool is_land_ok = creature.CanWalk(); // not used? //bool is_water_ok = creature.CanSwim(); // not used? bool is_air_ok = creature.CanFly(); const float angle = rand_norm_f() * (M_PI_F*2.0f); const float range = rand_norm_f() * wander_distance; const float distanceX = range * cos(angle); const float distanceY = range * sin(angle); destX = respX + distanceX; destY = respY + distanceY; // prevent invalid coordinates generation MaNGOS::NormalizeMapCoord(destX); MaNGOS::NormalizeMapCoord(destY); travelDistZ = distanceX*distanceX + distanceY*distanceY; if (is_air_ok) // 3D system above ground and above water (flying mode) { // Limit height change const float distanceZ = rand_norm_f() * sqrtf(travelDistZ)/2.0f; destZ = respZ + distanceZ; float levelZ = map->GetWaterOrGroundLevel(destX, destY, destZ-2.0f); // Problem here, we must fly above the ground and water, not under. Let's try on next tick if (levelZ >= destZ) return; } //else if (is_water_ok) // 3D system under water and above ground (swimming mode) else // 2D only { destZ = respZ; if(!map->IsNextZcoordOK(destX, destY, destZ, travelDistZ)) return; // let's forget this bad coords where a z cannot be find and retry at next tick creature.UpdateGroundPositionZ(destX, destY, destZ, travelDistZ); } Traveller<Creature> traveller(creature); creature.SetOrientation(creature.GetAngle(destX, destY)); i_destinationHolder.SetDestination(traveller, destX, destY, destZ); creature.addUnitState(UNIT_STAT_ROAMING_MOVE); if (is_air_ok) { i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); creature.AddSplineFlag(SPLINEFLAG_UNKNOWN7); } //else if (is_water_ok) // Swimming mode to be done with more than this check else { i_nextMoveTime.Reset(urand(500+i_destinationHolder.GetTotalTravelTime(), 10000+i_destinationHolder.GetTotalTravelTime())); creature.AddSplineFlag(SPLINEFLAG_WALKMODE); } }