server/src/game/WaypointMovementGenerator.cpp
2012-07-20 17:38:23 +02:00

366 lines
11 KiB
C++

/*
* Copyright (C) 2005-2012 MaNGOS <http://getmangos.com/>
*
* 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 <ctime>
#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 <cassert>
//-----------------------------------------------//
void WaypointMovementGenerator<Creature>::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<Creature>::Initialize(Creature& creature)
{
LoadPath(creature);
creature.addUnitState(UNIT_STAT_ROAMING | UNIT_STAT_ROAMING_MOVE);
}
void WaypointMovementGenerator<Creature>::Finalize(Creature& creature)
{
creature.clearUnitState(UNIT_STAT_ROAMING | UNIT_STAT_ROAMING_MOVE);
creature.SetWalk(false);
}
void WaypointMovementGenerator<Creature>::Interrupt(Creature& creature)
{
creature.clearUnitState(UNIT_STAT_ROAMING | UNIT_STAT_ROAMING_MOVE);
creature.SetWalk(false);
}
void WaypointMovementGenerator<Creature>::Reset(Creature& creature)
{
creature.addUnitState(UNIT_STAT_ROAMING | UNIT_STAT_ROAMING_MOVE);
StartMoveNow(creature);
}
void WaypointMovementGenerator<Creature>::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<Creature>::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<Creature>::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<Creature>::MovementInform(Creature& creature)
{
if (creature.AI())
creature.AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode);
}
bool WaypointMovementGenerator<Creature>::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;
}