void operator()(uint8& mode, bool& cyclic, Movement::PointsArray& points, int& lo, int& hi) const { mode = Movement::SplineBase::ModeCatmullrom; cyclic = false; points.assign(_points.begin(), _points.end()); lo = 1; hi = points.size() - 2; }
void InitializeAI() { ScriptedAI::InitializeAI(); Movement::PointsArray path; path.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ())); for (uint8 i = 0; i < WAYPOINTS_COUNT; ++i) path.push_back(G3D::Vector3(startPath[i].GetPositionX(), startPath[i].GetPositionY(), startPath[i].GetPositionZ())); me->GetMotionMaster()->MoveSplinePath(&path); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_IMMUNE_TO_NPC); me->Mount(SKARLOC_MOUNT_MODEL); }
void MotionMaster::MoveSmoothPath(uint32 pointId, Position const* pathPoints, size_t pathSize, bool walk) { Movement::MoveSplineInit init(_owner); Movement::PointsArray path; path.reserve(pathSize); std::transform(pathPoints, pathPoints + pathSize, std::back_inserter(path), [](Position const& point) { return G3D::Vector3(point.GetPositionX(), point.GetPositionY(), point.GetPositionZ()); }); init.MovebyPath(path); init.SetSmooth(); init.SetWalk(walk); // This code is not correct // GenericMovementGenerator does not affect UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE // need to call PointMovementGenerator with various pointIds Mutate(new GenericMovementGenerator(std::move(init), EFFECT_MOTION_TYPE, pointId), MOTION_SLOT_ACTIVE); }
void FillPath(Position const& pos, Movement::PointsArray& path) { G3D::Vector3 point; point.x = pos.GetPositionX(); point.y = pos.GetPositionY(); point.z = pos.GetPositionZ(); point.x -= 1.0f; path.push_back(point); point.x += 1.0f; path.push_back(point); point.z += 25.0f; path.push_back(point); path.push_back(point); }
uint32 SplineChainMovementGenerator::SendPathSpline(Unit* me, Movement::PointsArray const& wp) const { uint32 numWp = wp.size(); ASSERT(numWp > 1 && "Every path must have source & destination"); Movement::MoveSplineInit init(me); if (numWp > 2) init.MovebyPath(wp); else init.MoveTo(wp[1], false, true); init.SetWalk(_walk); return init.Launch(); }
uint32 SplineChainMovementGenerator::SendPathSpline(Unit* owner, Movement::PointsArray const& path) const { uint32 nodeCount = path.size(); ASSERT(nodeCount > 1, "SplineChainMovementGenerator::SendPathSpline: Every path must have source & destination (size > 1)! (%s)", owner->GetGUID().ToString().c_str()); Movement::MoveSplineInit init(owner); if (nodeCount > 2) init.MovebyPath(path); else init.MoveTo(path[1], false, true); init.SetWalk(_walk); return init.Launch(); }
void TransportMgr::GeneratePath(GameObjectTemplate const* goInfo, TransportTemplate* transport) { uint32 pathId = goInfo->moTransport.taxiPathId; TaxiPathNodeList const& path = sTaxiPathNodesByPath[pathId]; std::vector<KeyFrame>& keyFrames = transport->keyFrames; Movement::PointsArray splinePath; bool mapChange = false; bool cyclic = true; for (size_t i = 0; i < path.size(); ++i) { if (!mapChange) { TaxiPathNodeEntry const& node_i = path[i]; if (i != path.size() - 1 && (node_i.actionFlag == 1 || node_i.mapid != path[i + 1].mapid)) { cyclic = false; keyFrames.back().Teleport = true; mapChange = true; } else { KeyFrame k(node_i); keyFrames.push_back(k); splinePath.push_back(G3D::Vector3(node_i.x, node_i.y, node_i.z)); transport->mapsUsed.insert(k.Node->mapid); } } else mapChange = false; } // Not sure if data8 means the transport can be stopped or that its path in dbc does not contain extra spline points if (!goInfo->moTransport.canBeStopped && splinePath.size() >= 2) { // Remove special catmull-rom spline points splinePath.erase(splinePath.begin()); keyFrames.erase(keyFrames.begin()); splinePath.pop_back(); keyFrames.pop_back(); // Cyclic spline has one more extra point if (cyclic && !splinePath.empty()) { splinePath.pop_back(); keyFrames.pop_back(); } } ASSERT(!keyFrames.empty()); if (transport->mapsUsed.size() > 1) { for (std::set<uint32>::const_iterator itr = transport->mapsUsed.begin(); itr != transport->mapsUsed.end(); ++itr) ASSERT(!sMapStore.LookupEntry(*itr)->Instanceable()); transport->inInstance = false; } else transport->inInstance = sMapStore.LookupEntry(*transport->mapsUsed.begin())->Instanceable(); // last to first is always "teleport", even for closed paths keyFrames.back().Teleport = true; const float speed = float(goInfo->moTransport.moveSpeed); const float accel = float(goInfo->moTransport.accelRate); const float accel_dist = 0.5f * speed * speed / accel; transport->accelTime = speed / accel; transport->accelDist = accel_dist; int32 firstStop = -1; int32 lastStop = -1; // first cell is arrived at by teleportation :S keyFrames[0].DistFromPrev = 0; keyFrames[0].Index = 1; if (keyFrames[0].IsStopFrame()) { firstStop = 0; lastStop = 0; } // find the rest of the distances between key points // Every path segment has its own spline if (cyclic) { TransportSpline* spline = new TransportSpline(); spline->init_cyclic_spline(&splinePath[0], splinePath.size(), Movement::SplineBase::ModeCatmullrom, 0); spline->initLengths(); keyFrames[0].DistFromPrev = spline->length(spline->last() - 2, spline->last() - 1); keyFrames[0].Spline = spline; for (size_t i = 0; i < keyFrames.size(); ++i) { keyFrames[i].Index = i + 1; keyFrames[i].DistFromPrev = spline->length(i, i + 1); if (i > 0) keyFrames[i - 1].NextDistFromPrev = keyFrames[i].DistFromPrev; keyFrames[i].Spline = spline; if (keyFrames[i].IsStopFrame()) { // remember first stop frame if (firstStop == -1) firstStop = i; lastStop = i; } } } else { size_t start = 0; for (size_t i = 1; i < keyFrames.size(); ++i) { if (keyFrames[i - 1].Teleport || i + 1 == keyFrames.size()) { size_t extra = !keyFrames[i - 1].Teleport ? 1 : 0; TransportSpline* spline = new TransportSpline(); spline->init_spline(&splinePath[start], i - start + extra, Movement::SplineBase::ModeCatmullrom); spline->initLengths(); for (size_t j = start; j < i + extra; ++j) { keyFrames[j].Index = j - start + 1; keyFrames[j].DistFromPrev = spline->length(j - start, j + 1 - start); if (j > 0) keyFrames[j - 1].NextDistFromPrev = keyFrames[j].DistFromPrev; keyFrames[j].Spline = spline; } if (keyFrames[i - 1].Teleport) { keyFrames[i].Index = i - start + 1; keyFrames[i].DistFromPrev = 0.0f; keyFrames[i - 1].NextDistFromPrev = 0.0f; keyFrames[i].Spline = spline; } start = i; } if (keyFrames[i].IsStopFrame()) { // remember first stop frame if (firstStop == -1) firstStop = i; lastStop = i; } } } keyFrames.back().NextDistFromPrev = keyFrames.front().DistFromPrev; if (firstStop == -1 || lastStop == -1) firstStop = lastStop = 0; // at stopping keyframes, we define distSinceStop == 0, // and distUntilStop is to the next stopping keyframe. // this is required to properly handle cases of two stopping frames in a row (yes they do exist) float tmpDist = 0.0f; for (size_t i = 0; i < keyFrames.size(); ++i) { int32 j = (i + lastStop) % keyFrames.size(); if (keyFrames[j].IsStopFrame() || j == lastStop) tmpDist = 0.0f; else tmpDist += keyFrames[j].DistFromPrev; keyFrames[j].DistSinceStop = tmpDist; } tmpDist = 0.0f; for (int32 i = int32(keyFrames.size()) - 1; i >= 0; i--) { int32 j = (i + firstStop) % keyFrames.size(); tmpDist += keyFrames[(j + 1) % keyFrames.size()].DistFromPrev; keyFrames[j].DistUntilStop = tmpDist; if (keyFrames[j].IsStopFrame() || j == firstStop) tmpDist = 0.0f; } for (size_t i = 0; i < keyFrames.size(); ++i) { float total_dist = keyFrames[i].DistSinceStop + keyFrames[i].DistUntilStop; if (total_dist < 2 * accel_dist) // won't reach full speed { if (keyFrames[i].DistSinceStop < keyFrames[i].DistUntilStop) // is still accelerating { // calculate accel+brake time for this short segment float segment_time = 2.0f * sqrt((keyFrames[i].DistUntilStop + keyFrames[i].DistSinceStop) / accel); // substract acceleration time keyFrames[i].TimeTo = segment_time - sqrt(2 * keyFrames[i].DistSinceStop / accel); } else // slowing down keyFrames[i].TimeTo = sqrt(2 * keyFrames[i].DistUntilStop / accel); } else if (keyFrames[i].DistSinceStop < accel_dist) // still accelerating (but will reach full speed) { // calculate accel + cruise + brake time for this long segment float segment_time = (keyFrames[i].DistUntilStop + keyFrames[i].DistSinceStop) / speed + (speed / accel); // substract acceleration time keyFrames[i].TimeTo = segment_time - sqrt(2 * keyFrames[i].DistSinceStop / accel); } else if (keyFrames[i].DistUntilStop < accel_dist) // already slowing down (but reached full speed) keyFrames[i].TimeTo = sqrt(2 * keyFrames[i].DistUntilStop / accel); else // at full speed keyFrames[i].TimeTo = (keyFrames[i].DistUntilStop / speed) + (0.5f * speed / accel); } // calculate tFrom times from tTo times float segmentTime = 0.0f; for (size_t i = 0; i < keyFrames.size(); ++i) { int32 j = (i + lastStop) % keyFrames.size(); if (keyFrames[j].IsStopFrame() || j == lastStop) segmentTime = keyFrames[j].TimeTo; keyFrames[j].TimeFrom = segmentTime - keyFrames[j].TimeTo; } // calculate path times keyFrames[0].ArriveTime = 0; float curPathTime = 0.0f; if (keyFrames[0].IsStopFrame()) { curPathTime = float(keyFrames[0].Node->delay); keyFrames[0].DepartureTime = uint32(curPathTime * IN_MILLISECONDS); } for (size_t i = 1; i < keyFrames.size(); ++i) { curPathTime += keyFrames[i - 1].TimeTo; if (keyFrames[i].IsStopFrame()) { keyFrames[i].ArriveTime = uint32(curPathTime * IN_MILLISECONDS); keyFrames[i - 1].NextArriveTime = keyFrames[i].ArriveTime; curPathTime += float(keyFrames[i].Node->delay); keyFrames[i].DepartureTime = uint32(curPathTime * IN_MILLISECONDS); } else { curPathTime -= keyFrames[i].TimeTo; keyFrames[i].ArriveTime = uint32(curPathTime * IN_MILLISECONDS); keyFrames[i - 1].NextArriveTime = keyFrames[i].ArriveTime; keyFrames[i].DepartureTime = keyFrames[i].ArriveTime; } } keyFrames.back().NextArriveTime = keyFrames.back().DepartureTime; transport->pathTime = keyFrames.back().DepartureTime; }
bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature) { if (!creature || !creature->IsAlive()) return false; if (!i_path || i_path->nodes.empty()) return false; bool transportPath = creature->GetTransport() != nullptr; if (IsArrivalDone) { if ((i_currentNode == i_path->nodes.size() - 1) && !repeating) // If that's our last waypoint { WaypointNode const &waypoint = i_path->nodes.at(i_currentNode); float x = waypoint.x; float y = waypoint.y; float z = waypoint.z; float o = creature->GetOrientation(); if (!transportPath) creature->SetHomePosition(x, y, z, o); else { if (Transport* trans = creature->GetTransport()) { o -= trans->GetOrientation(); creature->SetTransportHomePosition(x, y, z, o); trans->CalculatePassengerPosition(x, y, z, &o); creature->SetHomePosition(x, y, z, o); } else transportPath = false; // else if (vehicle) - this should never happen, vehicle offsets are const } return false; } i_currentNode = (i_currentNode + 1) % i_path->nodes.size(); } float finalOrient = 0.0f; uint8 finalMove = WAYPOINT_MOVE_TYPE_WALK; Movement::PointsArray pathing; pathing.reserve((i_path->nodes.size() - i_currentNode) + 1); pathing.push_back(G3D::Vector3(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ())); for (uint32 i = i_currentNode; i < i_path->nodes.size(); ++i) { WaypointNode const &waypoint = i_path->nodes.at(i); pathing.push_back(G3D::Vector3(waypoint.x, waypoint.y, waypoint.z)); finalOrient = waypoint.orientation; finalMove = waypoint.moveType; if (waypoint.delay) break; } // if we have only 1 point, only current position, we shall return if (pathing.size() < 2) return false; IsArrivalDone = false; i_recalculateSpeed = false; creature->AddUnitState(UNIT_STATE_ROAMING_MOVE); Movement::MoveSplineInit init(creature); Movement::Location formationDest(i_path->nodes.at(i_currentNode).x, i_path->nodes.at(i_currentNode).y, i_path->nodes.at(i_currentNode).z, 0.0f); //! If creature is on transport, we assume waypoints set in DB are already transport offsets if (transportPath) { init.DisableTransportPathTransformations(); if (TransportBase* trans = creature->GetDirectTransport()) trans->CalculatePassengerPosition(formationDest.x, formationDest.y, formationDest.z, &formationDest.orientation); } init.MovebyPath(pathing, i_currentNode); switch (finalMove) { case WAYPOINT_MOVE_TYPE_LAND: init.SetAnimation(Movement::ToGround); break; case WAYPOINT_MOVE_TYPE_TAKEOFF: init.SetAnimation(Movement::ToFly); break; case WAYPOINT_MOVE_TYPE_RUN: init.SetWalk(false); break; case WAYPOINT_MOVE_TYPE_WALK: init.SetWalk(true); break; } if (finalOrient != 0.0f) init.SetFacing(finalOrient); init.Launch(); //Call for creature group update if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature) creature->GetFormation()->LeaderMoveTo(formationDest.x, formationDest.y, formationDest.z); return true; }
void UpdateAI(uint32 diff) { if (startPath) { me->StopMoving(); startPath = false; if (WaypointPath const* i_path = sWaypointMgr->GetPath(me->GetWaypointPath())) { Movement::PointsArray pathPoints; pathPoints.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ())); for (uint8 i = 0; i < i_path->size(); ++i) { WaypointData const* node = i_path->at(i); pathPoints.push_back(G3D::Vector3(node->x, node->y, node->z)); } me->GetMotionMaster()->MoveSplinePath(&pathPoints); } } if (!UpdateVictim()) return; events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) return; switch (events.GetEvent()) { case 0: break; case EVENT_ENRAGE: me->CastSpell(me, SPELL_BERSERK, true); events.RepeatEvent(600000); break; case EVENT_COMMANDER_SAY_AGGRO: if (Creature* commander = ObjectAccessor::GetCreature(*me, CommanderGUID)) commander->AI()->Talk(SAY_COMMANDER_AGGRO); events.PopEvent(); break; case EVENT_EE_SAY_MOVE_OUT: for (uint8 i=0; i<3; ++i) if (Creature* c = ObjectAccessor::GetCreature(*me, ExpeditionEngineerGUIDs[i])) { if (!i) c->MonsterYell(TEXT_EE_MOVE_OUT, LANG_UNIVERSAL, 0); c->AI()->SetData(1, 0); // start repairing } events.PopEvent(); break; case EVENT_SPELL_FIREBALL: if( Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 200.0f, true) ) me->CastSpell(pTarget, SPELL_FIREBALL, false); events.RepeatEvent(4000); break; case EVENT_SPELL_DEVOURING_FLAME: if( Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 200.0f, true) ) me->CastSpell(pTarget, SPELL_DEVOURINGFLAME, false); events.RepeatEvent(13000); break; case EVENT_SUMMON_MOLE_MACHINES: { memset(cords, '\0', sizeof(cords)); uint8 num = RAID_MODE( urand(2,3), urand(2,4) ); for( int i=0; i<num; ++i ) { // X: (550, 625) Y: (-185, -230) cords[i][0] = urand(550, 625); cords[i][1] = -230 + rand()%45; if( GameObject* drill = me->SummonGameObject(GO_DRILL, cords[i][0], cords[i][1], 391.1f, M_PI/4, 0.0f, 0.0f, 0.0f, 0.0f, 8) ) { //drill->SetGoAnimProgress(0); //drill->SetLootState(GO_READY); //drill->UseDoorOrButton(8); //drill->SetGoState(GO_STATE_READY); drill->SetGoState(GO_STATE_ACTIVE); drill->SetGoAnimProgress(0); } } events.RepeatEvent(45000); events.RescheduleEvent(EVENT_SUMMON_ADDS, 4000); } break; case EVENT_SUMMON_ADDS: for( int i=0; i<4; ++i ) { if( !cords[i][0] ) break; uint8 opt; uint8 r = urand(1,100); if( r <= 30 ) opt = 1; else if( r <= 65 ) opt = 2; else opt = 3; for( int j=0; j<4; ++j ) { float x = cords[i][0] + 4.0f*cos(j*M_PI/2); float y = cords[i][1] + 4.0f*sin(j*M_PI/2); uint32 npc_entry = 0; switch( opt ) { case 1: if( j == 1 ) npc_entry = NPC_DARK_RUNE_SENTINEL; break; case 2: switch( j ) { case 1: npc_entry = NPC_DARK_RUNE_WATCHER; break; case 2: npc_entry = NPC_DARK_RUNE_GUARDIAN; break; } break; default: // case 3: switch( j ) { case 1: npc_entry = NPC_DARK_RUNE_WATCHER; break; case 2: npc_entry = NPC_DARK_RUNE_GUARDIAN; break; case 3: npc_entry = NPC_DARK_RUNE_GUARDIAN; break; } break; } if( npc_entry ) if (Creature* c = me->SummonCreature(npc_entry, x, y, 391.1f, j*M_PI/2, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000)) DoZoneInCombat(c); } } events.PopEvent(); break; case EVENT_WARN_DEEP_BREATH: me->MonsterTextEmote(TEXT_DEEP_BREATH, 0, true); me->RemoveAura(62794); events.PopEvent(); events.ScheduleEvent(EVENT_PHASE2_FLAME_BREATH, 2500); break; case EVENT_PHASE2_FLAME_BREATH: me->CastSpell(me, S_FLAMEBREATH, true); events.PopEvent(); events.ScheduleEvent(EVENT_FLY_UP, 2000); break; case EVENT_FLY_UP: me->SetInCombatWithZone(); // just in case if (pInstance) for( int i=0; i<4; ++i ) if( uint64 guid = pInstance->GetData64(DATA_HARPOON_FIRE_STATE_1 + i) ) if( Creature* hfs = ObjectAccessor::GetCreature(*me, guid) ) { me->SummonCreature(34188, hfs->GetPositionX(), hfs->GetPositionY(), hfs->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 22000); hfs->AI()->SetData(1, 0); } me->RemoveAura(SPELL_LAUNCH_CHAIN); me->RemoveAura(SPELL_CHAIN_1); me->RemoveAura(SPELL_CHAIN_3); if (RAID_MODE(0,1)) { me->RemoveAura(SPELL_CHAIN_2); me->RemoveAura(SPELL_CHAIN_4); } me->CastSpell(me, SPELL_WINGBUFFET, true); if( (me->GetHealth()*100) / me->GetMaxHealth() < 50 ) // start phase 3 { me->SetControlled(false, UNIT_STATE_ROOT); me->DisableRotate(false); DoResetThreat(); Unit* target = SelectTarget(SELECT_TARGET_NEAREST, 0, 0.0, true); if (!target) target = me->SelectNearestPlayer(200.0f); if (target) { AttackStart(target); me->GetMotionMaster()->MoveChase(target); } bGroundPhase = true; events.PopEvent(); events.CancelEvent(EVENT_SPELL_FIREBALL); events.CancelEvent(EVENT_SPELL_DEVOURING_FLAME); events.CancelEvent(EVENT_SUMMON_MOLE_MACHINES); events.ScheduleEvent(EVENT_SPELL_FLAME_BREATH, 20000); events.ScheduleEvent(EVENT_SPELL_DEVOURING_FLAME_GROUND, 5000); events.ScheduleEvent(EVENT_SPELL_FUSE_ARMOR, 10000); events.ScheduleEvent(EVENT_SPELL_FLAME_BUFFET, 3000); break; } else { ++flyTimes; me->SetControlled(false, UNIT_STATE_ROOT); me->DisableRotate(false); me->SendMeleeAttackStop(me->GetVictim()); me->GetMotionMaster()->MoveIdle(); me->StopMoving(); me->SetCanFly(true); me->SetDisableGravity(true); me->SetHover(true); me->SendMovementFlagUpdate(); me->GetMotionMaster()->MoveTakeoff(1, CORDS_AIR, 25.0f); events.ScheduleEvent(EVENT_RESUME_FIXING, 22000); } events.PopEvent(); break; case EVENT_RESUME_FIXING: for (uint8 i=0; i<3; ++i) if (Creature* c = ObjectAccessor::GetCreature(*me, ExpeditionEngineerGUIDs[i])) { if (!i) c->MonsterYell(TEXT_EE_FIRES_OUT, LANG_UNIVERSAL, 0); c->AI()->SetData(1, 0); // start repairing } events.PopEvent(); break; case EVENT_SPELL_FLAME_BREATH: me->CastSpell(me->GetVictim(), S_FLAMEBREATH, false); events.RepeatEvent(20000); break; case EVENT_SPELL_DEVOURING_FLAME_GROUND: me->CastSpell(me->GetVictim(), SPELL_DEVOURINGFLAME, false); events.RepeatEvent(13000); break; case EVENT_SPELL_FUSE_ARMOR: if (Unit* victim = me->GetVictim()) if (me->IsWithinMeleeRange(victim)) { me->CastSpell(victim, SPELL_FUSEARMOR, false); if (Aura* aur = victim->GetAura(SPELL_FUSEARMOR)) if (aur->GetStackAmount() == 5) victim->CastSpell(victim, 64774, true); events.RepeatEvent(10000); break; } events.RepeatEvent(2000); break; case EVENT_SPELL_FLAME_BUFFET: me->CastSpell(me->GetVictim(), S_FLAMEBUFFET, false); events.RepeatEvent(7000); break; } if (bGroundPhase) DoMeleeAttackIfReady(); }