/* * Copyright (C) 2005-2012 MaNGOS * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* creature_movement Table alter table creature_movement add `textid1` int(11) NOT NULL default '0'; alter table creature_movement add `textid2` int(11) NOT NULL default '0'; alter table creature_movement add `textid3` int(11) NOT NULL default '0'; alter table creature_movement add `textid4` int(11) NOT NULL default '0'; alter table creature_movement add `textid5` int(11) NOT NULL default '0'; alter table creature_movement add `emote` int(10) unsigned default '0'; alter table creature_movement add `spell` int(5) unsigned default '0'; alter table creature_movement add `wpguid` int(11) default '0'; */ #include #include "WaypointMovementGenerator.h" #include "ObjectMgr.h" #include "Creature.h" #include "CreatureAI.h" #include "WaypointManager.h" #include "WorldPacket.h" #include "ScriptMgr.h" #include "movement/MoveSplineInit.h" #include "movement/MoveSpline.h" #include //-----------------------------------------------// void WaypointMovementGenerator::LoadPath(Creature& creature) { DETAIL_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "LoadPath: loading waypoint path for %s", creature.GetGuidStr().c_str()); i_path = sWaypointMgr.GetPath(creature.GetGUIDLow()); // We may LoadPath() for several occasions: // 1: When creature.MovementType=2 // 1a) Path is selected by creature.guid == creature_movement.id // 1b) Path for 1a) does not exist and then use path from creature.GetEntry() == creature_movement_template.entry // 2: When creature_template.MovementType=2 // 2a) Creature is summoned and has creature_template.MovementType=2 // Creators need to be sure that creature_movement_template is always valid for summons. // Mob that can be summoned anywhere should not have creature_movement_template for example. // No movement found for guid if (!i_path) { i_path = sWaypointMgr.GetPathTemplate(creature.GetEntry()); // No movement found for entry if (!i_path) { sLog.outErrorDb("WaypointMovementGenerator::LoadPath: creature %s (Entry: %u GUID: %u) doesn't have waypoint path", creature.GetName(), creature.GetEntry(), creature.GetGUIDLow()); return; } } StartMoveNow(creature); } void WaypointMovementGenerator::Initialize(Creature& creature) { LoadPath(creature); creature.addUnitState(UNIT_STAT_ROAMING | UNIT_STAT_ROAMING_MOVE); } void WaypointMovementGenerator::Finalize(Creature& creature) { creature.clearUnitState(UNIT_STAT_ROAMING | UNIT_STAT_ROAMING_MOVE); creature.SetWalk(false); } void WaypointMovementGenerator::Interrupt(Creature& creature) { creature.clearUnitState(UNIT_STAT_ROAMING | UNIT_STAT_ROAMING_MOVE); creature.SetWalk(false); } void WaypointMovementGenerator::Reset(Creature& creature) { creature.addUnitState(UNIT_STAT_ROAMING | UNIT_STAT_ROAMING_MOVE); StartMoveNow(creature); } void WaypointMovementGenerator::OnArrived(Creature& creature) { if (!i_path || i_path->empty()) return; if (m_isArrivalDone) return; creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); m_isArrivalDone = true; 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 %s.", i_path->at(i_currentNode).script_id, i_currentNode, creature.GetGuidStr().c_str()); 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 (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.MonsterSay(behavior->textid[rand() % i], LANG_UNIVERSAL); } else creature.MonsterSay(behavior->textid[0], LANG_UNIVERSAL); } } // Inform script MovementInform(creature); Stop(i_path->at(i_currentNode).delay); } void WaypointMovementGenerator::StartMove(Creature& creature) { if (!i_path || i_path->empty()) return; if (Stopped()) return; if (WaypointBehavior* behavior = i_path->at(i_currentNode).behavior) { if (behavior->model2 != 0) creature.SetDisplayId(behavior->model2); creature.SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); } if (m_isArrivalDone) i_currentNode = (i_currentNode + 1) % i_path->size(); m_isArrivalDone = false; creature.addUnitState(UNIT_STAT_ROAMING_MOVE); const WaypointNode& node = i_path->at(i_currentNode); Movement::MoveSplineInit init(creature); init.MoveTo(node.x, node.y, node.z, true); if (node.orientation != 100 && node.delay != 0) init.SetFacing(node.orientation); init.SetWalk(!creature.IsLevitating()); init.Launch(); } bool WaypointMovementGenerator::Update(Creature& creature, const uint32& diff) { // 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 (Stopped()) { if (CanMove(diff)) StartMove(creature); } else { if (creature.IsStopped()) Stop(STOP_TIME_FOR_PLAYER); else if (creature.movespline->Finalized()) { OnArrived(creature); StartMove(creature); } } return true; } void WaypointMovementGenerator::MovementInform(Creature& creature) { if (creature.AI()) creature.AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode); } bool WaypointMovementGenerator::GetResetPosition(Creature&, float& x, float& y, float& z) { // prevent a crash at empty waypoint path. if (!i_path || i_path->empty()) return false; const WaypointNode& node = i_path->at(i_currentNode); x = node.x; y = node.y; z = node.z; return true; } //----------------------------------------------------// uint32 FlightPathMovementGenerator::GetPathAtMapEnd() const { if (i_currentNode >= i_path->size()) return i_path->size(); uint32 curMapId = (*i_path)[i_currentNode].mapid; for (uint32 i = i_currentNode; i < i_path->size(); ++i) { if ((*i_path)[i].mapid != curMapId) return i; } return i_path->size(); } void FlightPathMovementGenerator::Initialize(Player& player) { Reset(player); } void FlightPathMovementGenerator::Finalize(Player& player) { // remove flag to prevent send object build movement packets for flight state and crash (movement generator already not at top of stack) player.clearUnitState(UNIT_STAT_TAXI_FLIGHT); player.Unmount(); player.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT); if (player.m_taxi.empty()) { player.getHostileRefManager().setOnlineOfflineState(true); if (player.pvpInfo.inHostileArea) player.CastSpell(&player, 2479, true); // update z position to ground and orientation for landing point // this prevent cheating with landing point at lags // when client side flight end early in comparison server side player.StopMoving(); } } void FlightPathMovementGenerator::Interrupt(Player& player) { player.clearUnitState(UNIT_STAT_TAXI_FLIGHT); } #define PLAYER_FLIGHT_SPEED 32.0f void FlightPathMovementGenerator::Reset(Player& player) { player.getHostileRefManager().setOnlineOfflineState(false); player.addUnitState(UNIT_STAT_TAXI_FLIGHT); player.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT); Movement::MoveSplineInit init(player); uint32 end = GetPathAtMapEnd(); for (uint32 i = GetCurrentNode(); i != end; ++i) { G3D::Vector3 vertice((*i_path)[i].x, (*i_path)[i].y, (*i_path)[i].z); init.Path().push_back(vertice); } init.SetFirstPointId(GetCurrentNode()); init.SetFly(); init.SetVelocity(PLAYER_FLIGHT_SPEED); init.Launch(); } bool FlightPathMovementGenerator::Update(Player& player, const uint32& diff) { uint32 pointId = (uint32)player.movespline->currentPathIdx(); if (pointId > i_currentNode) { bool departureEvent = true; do { DoEventIfAny(player, (*i_path)[i_currentNode], departureEvent); if (pointId == i_currentNode) break; i_currentNode += (uint32)departureEvent; departureEvent = !departureEvent; } while (true); } return i_currentNode < (i_path->size() - 1); } void FlightPathMovementGenerator::SetCurrentNodeAfterTeleport() { if (i_path->empty()) return; uint32 map0 = (*i_path)[0].mapid; for (size_t i = 1; i < i_path->size(); ++i) { if ((*i_path)[i].mapid != map0) { i_currentNode = i; return; } } } void FlightPathMovementGenerator::DoEventIfAny(Player& player, TaxiPathNodeEntry const& node, bool departure) { if (uint32 eventid = departure ? node.departureEventID : node.arrivalEventID) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Taxi %s event %u of node %u of path %u for player %s", departure ? "departure" : "arrival", eventid, node.index, node.path, player.GetName()); if (!sScriptMgr.OnProcessEvent(eventid, &player, &player, departure)) player.GetMap()->ScriptsStart(sEventScripts, eventid, &player, &player); } } bool FlightPathMovementGenerator::GetResetPosition(Player&, float& x, float& y, float& z) { const TaxiPathNodeEntry& node = (*i_path)[i_currentNode]; x = node.x; y = node.y; z = node.z; return true; }