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(MONSTER_MOVE_FLY); 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(MONSTER_MOVE_FLY); 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; }
void CreatureGroup::LeaderMoveTo(float x, float y, float z, bool run) { //! To do: This should probably get its own movement generator or use WaypointMovementGenerator. //! If the leader's path is known, member's path can be plotted as well using formation offsets. if (!m_leader) return; uint8 groupAI = sFormationMgr->CreatureGroupMap[m_leader->GetDBTableGUIDLow()]->groupAI; if (groupAI == 5) return; float pathDist = m_leader->GetExactDist(x, y, z); float pathAngle = m_leader->GetAngle(x, y); for (CreatureGroupMemberType::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) { Creature* member = itr->first; if (member == m_leader || !member->IsAlive() || member->GetVictim()) continue; // Xinef: If member is stunned / rooted etc don't allow to move him if (member->HasUnitState(UNIT_STATE_NOT_MOVE)) continue; // Xinef: this should be automatized, if turn angle is greater than PI/2 (90°) we should swap formation angle if (M_PI - fabs(fabs(m_leader->GetOrientation() - pathAngle) - M_PI) > M_PI*0.50f) { // pussywizard: in both cases should be 2*M_PI - follow_angle // pussywizard: also, GetCurrentWaypointID() returns 0..n-1, while point_1 must be > 0, so +1 // pussywizard: db table waypoint_data shouldn't have point id 0 and shouldn't have any gaps for this to work! // if (m_leader->GetCurrentWaypointID()+1 == itr->second->point_1 || m_leader->GetCurrentWaypointID()+1 == itr->second->point_2) itr->second->follow_angle = Position::NormalizeOrientation(itr->second->follow_angle + M_PI); //(2 * M_PI) - itr->second->follow_angle; } float followAngle = itr->second->follow_angle; float followDist = itr->second->follow_dist; float dx = x + cos(followAngle + pathAngle) * followDist; float dy = y + sin(followAngle + pathAngle) * followDist; float dz = z; Trinity::NormalizeMapCoord(dx); Trinity::NormalizeMapCoord(dy); member->UpdateGroundPositionZ(dx, dy, dz); member->SetUnitMovementFlags(m_leader->GetUnitMovementFlags()); // pussywizard: setting the same movementflags is not enough, spline decides whether leader walks/runs, so spline param is now passed as "run" parameter to this function if (run && member->IsWalking()) member->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); else if (!run && !member->IsWalking()) member->AddUnitMovementFlag(MOVEMENTFLAG_WALKING); // xinef: if we move members to position without taking care of sizes, we should compare distance without sizes // xinef: change members speed basing on distance - if too far speed up, if too close slow down UnitMoveType mtype = Movement::SelectSpeedType(member->GetUnitMovementFlags()); member->SetSpeedRate(mtype, m_leader->GetSpeedRate(mtype) * member->GetExactDist(dx, dy, dz) / pathDist); member->GetMotionMaster()->MovePoint(0, dx, dy, dz); member->SetHomePosition(dx, dy, dz, pathAngle); } }
void DoIntro() { Creature* Madrigosa = Unit::GetCreature(*me, pInstance ? pInstance->GetData64(DATA_MADRIGOSA) : 0); if (!Madrigosa) return; switch (IntroPhase) { case 0: DoScriptText(YELL_MADR_ICE_BARRIER, Madrigosa); IntroPhaseTimer = 7000; ++IntroPhase; break; case 1: me->SetInFront(Madrigosa); Madrigosa->SetInFront(me); DoScriptText(YELL_MADR_INTRO, Madrigosa, me); IntroPhaseTimer = 9000; ++IntroPhase; break; case 2: DoScriptText(YELL_INTRO, me, Madrigosa); IntroPhaseTimer = 13000; ++IntroPhase; break; case 3: DoCast(me, SPELL_INTRO_FROST_BLAST); Madrigosa->AddUnitMovementFlag(MOVEFLAG_ONTRANSPORT | MOVEFLAG_LEVITATING); me->AttackStop(); Madrigosa->AttackStop(); IntroFrostBoltTimer = 3000; IntroPhaseTimer = 28000; ++IntroPhase; break; case 4: DoScriptText(YELL_INTRO_BREAK_ICE, me); IntroPhaseTimer = 6000; ++IntroPhase; break; case 5: Madrigosa->CastSpell(me, SPELL_INTRO_ENCAPSULATE_CHANELLING, false); DoScriptText(YELL_MADR_TRAP, Madrigosa); DoCast(me, SPELL_INTRO_ENCAPSULATE); IntroPhaseTimer = 11000; ++IntroPhase; break; case 6: DoScriptText(YELL_INTRO_CHARGE, me); IntroPhaseTimer = 5000; ++IntroPhase; break; case 7: me->Kill(Madrigosa); DoScriptText(YELL_MADR_DEATH, Madrigosa); me->SetHealth(me->GetMaxHealth()); me->AttackStop(); IntroPhaseTimer = 4000; ++IntroPhase; break; case 8: DoScriptText(YELL_INTRO_KILL_MADRIGOSA, me); me->SetOrientation(0.14f); me->StopMoving(); Madrigosa->setDeathState(CORPSE); IntroPhaseTimer = 8000; ++IntroPhase; break; case 9: DoScriptText(YELL_INTRO_TAUNT, me); IntroPhaseTimer = 5000; ++IntroPhase; break; case 10: EndIntro(); break; } }
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(); 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 BlizzLike::NormalizeMapCoord(nx); BlizzLike::NormalizeMapCoord(ny); dist = (nx - X)*(nx - X) + (ny - Y)*(ny - Y); if (is_air_ok) // 3D system above ground and above water (flying mode) { // Limit height change const float distanceZ = rand_norm() * 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 { 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); if (fabs(nz-Z) > dist) // Map check { nz = map->GetHeight(nx, ny, Z-2.0f, true); // Vmap Horizontal or above if (fabs(nz-Z) > dist) { // Vmap Higher nz = map->GetHeight(nx, ny, Z+dist-2.0f, true); // let's forget this bad coords where a z cannot be find and retry at next tick if (fabs(nz-Z) > dist) return; } } } 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(MOVEFLAG_FLYING2); } //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.SetUnitMovementFlags(MOVEFLAG_WALK_MODE); } }