First Commit

For Azeroth!
This commit is contained in:
Yehonal
2016-06-26 10:39:44 +02:00
commit e8e94a0a66
3777 changed files with 1419268 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef _FOLLOWERREFMANAGER
#define _FOLLOWERREFMANAGER
#include "RefManager.h"
class Unit;
class TargetedMovementGeneratorBase;
class FollowerRefManager : public RefManager<Unit, TargetedMovementGeneratorBase>
{
};
#endif

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "Unit.h"
#include "TargetedMovementGenerator.h"
#include "FollowerReference.h"
void FollowerReference::targetObjectBuildLink()
{
getTarget()->addFollower(this);
}
void FollowerReference::targetObjectDestroyLink()
{
getTarget()->removeFollower(this);
}
void FollowerReference::sourceObjectDestroyLink()
{
GetSource()->stopFollowing();
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef _FOLLOWERREFERENCE_H
#define _FOLLOWERREFERENCE_H
#include "Reference.h"
class TargetedMovementGeneratorBase;
class Unit;
class FollowerReference : public Reference<Unit, TargetedMovementGeneratorBase>
{
protected:
void targetObjectBuildLink();
void targetObjectDestroyLink();
void sourceObjectDestroyLink();
};
#endif

View File

@@ -0,0 +1,827 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "MotionMaster.h"
#include "CreatureAISelector.h"
#include "Creature.h"
#include "ConfusedMovementGenerator.h"
#include "FleeingMovementGenerator.h"
#include "HomeMovementGenerator.h"
#include "IdleMovementGenerator.h"
#include "PointMovementGenerator.h"
#include "TargetedMovementGenerator.h"
#include "WaypointMovementGenerator.h"
#include "RandomMovementGenerator.h"
#include "EscortMovementGenerator.h"
#include "MoveSpline.h"
#include "MoveSplineInit.h"
#include <cassert>
inline bool isStatic(MovementGenerator *mv)
{
return (mv == &si_idleMovement);
}
void MotionMaster::Initialize()
{
// clear ALL movement generators (including default)
while (!empty())
{
MovementGenerator *curr = top();
pop();
if (curr) DirectDelete(curr);
}
InitDefault();
}
// set new default movement generator
void MotionMaster::InitDefault()
{
// Xinef: Do not allow to initialize any motion generator for dead creatures
if (_owner->GetTypeId() == TYPEID_UNIT && _owner->IsAlive())
{
MovementGenerator* movement = FactorySelector::selectMovementGenerator(_owner->ToCreature());
Mutate(movement == NULL ? &si_idleMovement : movement, MOTION_SLOT_IDLE);
}
else
{
Mutate(&si_idleMovement, MOTION_SLOT_IDLE);
}
}
MotionMaster::~MotionMaster()
{
// clear ALL movement generators (including default)
while (!empty())
{
MovementGenerator *curr = top();
pop();
if (curr && !isStatic(curr))
delete curr; // Skip finalizing on delete, it might launch new movement
}
}
void MotionMaster::UpdateMotion(uint32 diff)
{
if (!_owner)
return;
if (_owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED)) // what about UNIT_STATE_DISTRACTED? Why is this not included?
{
// pussywizard: the same as at the bottom of this function
if (_owner->GetTypeId() == TYPEID_PLAYER)
_owner->UpdateUnderwaterState(_owner->GetMap(), _owner->GetPositionX(), _owner->GetPositionY(), _owner->GetPositionZ());
else
_owner->UpdateEnvironmentIfNeeded(0);
return;
}
ASSERT(!empty());
_cleanFlag |= MMCF_INUSE;
_cleanFlag |= MMCF_UPDATE;
if (!top()->Update(_owner, diff))
{
_cleanFlag &= ~MMCF_UPDATE;
MovementExpired();
}
else
_cleanFlag &= ~MMCF_UPDATE;
if (_expList)
{
for (size_t i = 0; i < _expList->size(); ++i)
{
MovementGenerator* mg = (*_expList)[i];
DirectDelete(mg);
}
delete _expList;
_expList = NULL;
if (empty())
Initialize();
else if (needInitTop())
InitTop();
else if (_cleanFlag & MMCF_RESET)
top()->Reset(_owner);
_cleanFlag &= ~MMCF_RESET;
}
_cleanFlag &= ~MMCF_INUSE;
if (_owner->GetTypeId() == TYPEID_PLAYER)
_owner->UpdateUnderwaterState(_owner->GetMap(), _owner->GetPositionX(), _owner->GetPositionY(), _owner->GetPositionZ());
else
_owner->UpdateEnvironmentIfNeeded(0);
}
void MotionMaster::DirectClean(bool reset)
{
while (size() > 1)
{
MovementGenerator *curr = top();
pop();
if (curr) DirectDelete(curr);
}
if (empty())
return;
if (needInitTop())
InitTop();
else if (reset)
top()->Reset(_owner);
}
void MotionMaster::DelayedClean()
{
while (size() > 1)
{
MovementGenerator *curr = top();
pop();
if (curr) DelayedDelete(curr);
}
}
void MotionMaster::DirectExpire(bool reset)
{
if (size() > 1)
{
MovementGenerator *curr = top();
pop();
if (curr) DirectDelete(curr);
}
while (!empty() && !top())
--_top;
if (empty())
Initialize();
else if (needInitTop())
InitTop();
else if (reset)
top()->Reset(_owner);
}
void MotionMaster::DelayedExpire()
{
if (size() > 1)
{
MovementGenerator *curr = top();
pop();
if (curr) DelayedDelete(curr);
}
while (!empty() && !top())
--_top;
}
void MotionMaster::DirectExpireSlot(MovementSlot slot, bool reset)
{
if (size() > 1)
{
MovementGenerator *curr = Impl[slot];
// pussywizard: clear slot AND decrease top immediately to avoid crashes when referencing null top in DirectDelete
Impl[slot] = NULL;
while (!empty() && !top())
--_top;
if (curr) DirectDelete(curr);
}
while (!empty() && !top())
--_top;
if (empty())
Initialize();
else if (needInitTop())
InitTop();
else if (reset)
top()->Reset(_owner);
}
void MotionMaster::MoveIdle()
{
//! Should be preceded by MovementExpired or Clear if there's an overlying movementgenerator active
if (empty() || !isStatic(top()))
Mutate(&si_idleMovement, MOTION_SLOT_IDLE);
}
void MotionMaster::MoveRandom(float spawndist)
{
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
return;
if (_owner->GetTypeId() == TYPEID_UNIT)
{
;//sLog->outStaticDebug("Creature (GUID: %u) start moving random", _owner->GetGUIDLow());
Mutate(new RandomMovementGenerator<Creature>(spawndist), MOTION_SLOT_IDLE);
}
}
void MotionMaster::MoveTargetedHome()
{
Clear(false);
if (_owner->GetTypeId() == TYPEID_UNIT && !_owner->ToCreature()->GetCharmerOrOwnerGUID())
{
;//sLog->outStaticDebug("Creature (Entry: %u GUID: %u) targeted home", _owner->GetEntry(), _owner->GetGUIDLow());
Mutate(new HomeMovementGenerator<Creature>(), MOTION_SLOT_ACTIVE);
}
else if (_owner->GetTypeId() == TYPEID_UNIT && _owner->ToCreature()->GetCharmerOrOwnerGUID())
{
_owner->ClearUnitState(UNIT_STATE_EVADE);
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
return;
;//sLog->outStaticDebug("Pet or controlled creature (Entry: %u GUID: %u) targeting home", _owner->GetEntry(), _owner->GetGUIDLow());
Unit* target = _owner->ToCreature()->GetCharmerOrOwner();
if (target)
{
;//sLog->outStaticDebug("Following %s (GUID: %u)", target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature", target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : ((Creature*)target)->GetDBTableGUIDLow());
Mutate(new FollowMovementGenerator<Creature>(target, PET_FOLLOW_DIST, _owner->GetFollowAngle()), MOTION_SLOT_ACTIVE);
}
}
else
{
sLog->outError("Player (GUID: %u) attempt targeted home", _owner->GetGUIDLow());
}
}
void MotionMaster::MoveConfused()
{
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
return;
if (_owner->GetTypeId() == TYPEID_PLAYER)
{
;//sLog->outStaticDebug("Player (GUID: %u) move confused", _owner->GetGUIDLow());
Mutate(new ConfusedMovementGenerator<Player>(), MOTION_SLOT_CONTROLLED);
}
else
{
;//sLog->outStaticDebug("Creature (Entry: %u GUID: %u) move confused",
// _owner->GetEntry(), _owner->GetGUIDLow());
Mutate(new ConfusedMovementGenerator<Creature>(), MOTION_SLOT_CONTROLLED);
}
}
void MotionMaster::MoveChase(Unit* target, float dist, float angle)
{
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
// ignore movement request if target not exist
if (!target || target == _owner || _owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
return;
//_owner->ClearUnitState(UNIT_STATE_FOLLOW);
if (_owner->GetTypeId() == TYPEID_PLAYER)
{
;//sLog->outStaticDebug("Player (GUID: %u) chase to %s (GUID: %u)",
// _owner->GetGUIDLow(),
// target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
// target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
Mutate(new ChaseMovementGenerator<Player>(target, dist, angle), MOTION_SLOT_ACTIVE);
}
else
{
;//sLog->outStaticDebug("Creature (Entry: %u GUID: %u) chase to %s (GUID: %u)",
// _owner->GetEntry(), _owner->GetGUIDLow(),
// target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
// target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
Mutate(new ChaseMovementGenerator<Creature>(target, dist, angle), MOTION_SLOT_ACTIVE);
}
}
void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlot slot)
{
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
// ignore movement request if target not exist
if (!target || target == _owner || _owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
return;
//_owner->AddUnitState(UNIT_STATE_FOLLOW);
if (_owner->GetTypeId() == TYPEID_PLAYER)
{
;//sLog->outStaticDebug("Player (GUID: %u) follow to %s (GUID: %u)", _owner->GetGUIDLow(),
// target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
// target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
Mutate(new FollowMovementGenerator<Player>(target, dist, angle), slot);
}
else
{
;//sLog->outStaticDebug("Creature (Entry: %u GUID: %u) follow to %s (GUID: %u)",
// _owner->GetEntry(), _owner->GetGUIDLow(),
// target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
// target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
Mutate(new FollowMovementGenerator<Creature>(target, dist, angle), slot);
}
}
void MotionMaster::MovePoint(uint32 id, float x, float y, float z, bool generatePath, bool forceDestination, MovementSlot slot)
{
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
return;
if (_owner->GetTypeId() == TYPEID_PLAYER)
{
;//sLog->outStaticDebug("Player (GUID: %u) targeted point (Id: %u X: %f Y: %f Z: %f)", _owner->GetGUIDLow(), id, x, y, z);
Mutate(new PointMovementGenerator<Player>(id, x, y, z, 0.0f, NULL, generatePath, forceDestination), slot);
}
else
{
;//sLog->outStaticDebug("Creature (Entry: %u GUID: %u) targeted point (ID: %u X: %f Y: %f Z: %f)",
// _owner->GetEntry(), _owner->GetGUIDLow(), id, x, y, z);
Mutate(new PointMovementGenerator<Creature>(id, x, y, z, 0.0f, NULL, generatePath, forceDestination), slot);
}
}
void MotionMaster::MoveSplinePath(Movement::PointsArray* path)
{
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
return;
if (_owner->GetTypeId() == TYPEID_PLAYER)
{
;//sLog->outStaticDebug("Player (GUID: %u) targeted point (Id: %u X: %f Y: %f Z: %f)", _owner->GetGUIDLow(), id, x, y, z);
Mutate(new EscortMovementGenerator<Player>(path), MOTION_SLOT_ACTIVE);
}
else
{
;//sLog->outStaticDebug("Creature (Entry: %u GUID: %u) targeted point (ID: %u X: %f Y: %f Z: %f)",
// _owner->GetEntry(), _owner->GetGUIDLow(), id, x, y, z);
Mutate(new EscortMovementGenerator<Creature>(path), MOTION_SLOT_ACTIVE);
}
}
void MotionMaster::MoveLand(uint32 id, Position const& pos, float speed)
{
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
return;
float x, y, z;
pos.GetPosition(x, y, z);
;//sLog->outStaticDebug("Creature (Entry: %u) landing point (ID: %u X: %f Y: %f Z: %f)", _owner->GetEntry(), id, x, y, z);
Movement::MoveSplineInit init(_owner);
init.MoveTo(x, y, z);
init.SetVelocity(speed);
init.SetAnimation(Movement::ToGround);
init.Launch();
Mutate(new EffectMovementGenerator(id), MOTION_SLOT_ACTIVE);
}
void MotionMaster::MoveLand(uint32 id, float x, float y, float z, float speed)
{
Position pos = {x, y, z, 0.0f};
MoveLand(id, pos, speed);
}
void MotionMaster::MoveTakeoff(uint32 id, Position const& pos, float speed)
{
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
return;
float x, y, z;
pos.GetPosition(x, y, z);
;//sLog->outStaticDebug("Creature (Entry: %u) landing point (ID: %u X: %f Y: %f Z: %f)", _owner->GetEntry(), id, x, y, z);
Movement::MoveSplineInit init(_owner);
init.MoveTo(x, y, z);
init.SetVelocity(speed);
init.SetAnimation(Movement::ToFly);
init.Launch();
Mutate(new EffectMovementGenerator(id), MOTION_SLOT_ACTIVE);
}
void MotionMaster::MoveTakeoff(uint32 id, float x, float y, float z, float speed)
{
Position pos = {x, y, z, 0.0f};
MoveTakeoff(id, pos, speed);
}
void MotionMaster::MoveKnockbackFrom(float srcX, float srcY, float speedXY, float speedZ)
{
//this function may make players fall below map
if (_owner->GetTypeId() == TYPEID_PLAYER)
return;
if (speedXY <= 0.1f)
return;
float moveTimeHalf = speedZ / Movement::gravity;
float dist = 2 * moveTimeHalf * speedXY;
float max_height = -Movement::computeFallElevation(moveTimeHalf, false, -speedZ);
Position pos;
_owner->GetNearPoint(_owner, pos.m_positionX, pos.m_positionY, pos.m_positionZ, _owner->GetObjectSize(), dist, _owner->GetAngle(srcX, srcY) + M_PI);
// xinef: check LoS!
if (!_owner->IsWithinLOS(pos.m_positionX, pos.m_positionY, pos.m_positionZ))
{
_owner->GetPosition(&pos);
_owner->MovePositionToFirstCollision(pos, dist, _owner->GetAngle(srcX, srcY) + M_PI);
}
Movement::MoveSplineInit init(_owner);
init.MoveTo(pos.m_positionX, pos.m_positionY, pos.m_positionZ);
init.SetParabolic(max_height, 0);
init.SetOrientationFixed(true);
init.SetVelocity(speedXY);
init.Launch();
Mutate(new EffectMovementGenerator(0), MOTION_SLOT_CONTROLLED);
}
void MotionMaster::MoveJumpTo(float angle, float speedXY, float speedZ)
{
//this function may make players fall below map
if (_owner->GetTypeId() == TYPEID_PLAYER)
return;
float x, y, z;
float moveTimeHalf = speedZ / Movement::gravity;
float dist = 2 * moveTimeHalf * speedXY;
_owner->GetClosePoint(x, y, z, _owner->GetObjectSize(), dist, angle);
MoveJump(x, y, z, speedXY, speedZ);
}
void MotionMaster::MoveJump(float x, float y, float z, float speedXY, float speedZ, uint32 id, Unit const* target)
{
;//sLog->outStaticDebug("Unit (GUID: %u) jump to point (X: %f Y: %f Z: %f)", _owner->GetGUIDLow(), x, y, z);
if (speedXY <= 0.1f)
return;
float moveTimeHalf = speedZ / Movement::gravity;
float max_height = -Movement::computeFallElevation(moveTimeHalf, false, -speedZ);
Movement::MoveSplineInit init(_owner);
init.MoveTo(x, y, z);
init.SetParabolic(max_height, 0);
init.SetVelocity(speedXY);
if (target)
init.SetFacing(target);
init.Launch();
Mutate(new EffectMovementGenerator(id), MOTION_SLOT_CONTROLLED);
}
void MotionMaster::MoveFall(uint32 id /*=0*/, bool addFlagForNPC)
{
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
return;
// use larger distance for vmap height search than in most other cases
float tz = _owner->GetMap()->GetHeight(_owner->GetPhaseMask(), _owner->GetPositionX(), _owner->GetPositionY(), _owner->GetPositionZ(), true, MAX_FALL_DISTANCE);
if (tz <= INVALID_HEIGHT)
{
;//sLog->outStaticDebug("MotionMaster::MoveFall: unable retrive a proper height at map %u (x: %f, y: %f, z: %f).",
// _owner->GetMap()->GetId(), _owner->GetPositionX(), _owner->GetPositionX(), _owner->GetPositionZ());
return;
}
// Abort too if the ground is very near
if (fabs(_owner->GetPositionZ() - tz) < 0.1f)
return;
if (_owner->GetTypeId() == TYPEID_PLAYER)
{
_owner->AddUnitMovementFlag(MOVEMENTFLAG_FALLING);
_owner->m_movementInfo.SetFallTime(0);
_owner->ToPlayer()->SetFallInformation(time(NULL), _owner->GetPositionZ());
}
else if (_owner->GetTypeId() == TYPEID_UNIT && addFlagForNPC) // pussywizard
{
_owner->RemoveUnitMovementFlag(MOVEMENTFLAG_MASK_MOVING);
_owner->RemoveUnitMovementFlag(MOVEMENTFLAG_FLYING|MOVEMENTFLAG_CAN_FLY);
_owner->AddUnitMovementFlag(MOVEMENTFLAG_FALLING);
_owner->m_movementInfo.SetFallTime(0);
_owner->SendMovementFlagUpdate();
}
Movement::MoveSplineInit init(_owner);
init.MoveTo(_owner->GetPositionX(), _owner->GetPositionY(), tz);
init.SetFall();
init.Launch();
Mutate(new EffectMovementGenerator(id), MOTION_SLOT_CONTROLLED);
}
void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id, const Movement::PointsArray* path, bool generatePath)
{
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
return;
if (Impl[MOTION_SLOT_CONTROLLED] && Impl[MOTION_SLOT_CONTROLLED]->GetMovementGeneratorType() != DISTRACT_MOTION_TYPE)
return;
if (_owner->GetTypeId() == TYPEID_PLAYER)
{
;//sLog->outStaticDebug("Player (GUID: %u) charge point (X: %f Y: %f Z: %f)", _owner->GetGUIDLow(), x, y, z);
Mutate(new PointMovementGenerator<Player>(id, x, y, z, speed, path, generatePath, generatePath), MOTION_SLOT_CONTROLLED);
}
else
{
;//sLog->outStaticDebug("Creature (Entry: %u GUID: %u) charge point (X: %f Y: %f Z: %f)",
// _owner->GetEntry(), _owner->GetGUIDLow(), x, y, z);
Mutate(new PointMovementGenerator<Creature>(id, x, y, z, speed, path, generatePath, generatePath), MOTION_SLOT_CONTROLLED);
}
}
void MotionMaster::MoveSeekAssistance(float x, float y, float z)
{
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
return;
if (_owner->GetTypeId() == TYPEID_PLAYER)
{
sLog->outError("Player (GUID: %u) attempt to seek assistance", _owner->GetGUIDLow());
}
else
{
;//sLog->outStaticDebug("Creature (Entry: %u GUID: %u) seek assistance (X: %f Y: %f Z: %f)",
// _owner->GetEntry(), _owner->GetGUIDLow(), x, y, z);
_owner->AttackStop();
_owner->CastStop(0, false);
_owner->ToCreature()->SetReactState(REACT_PASSIVE);
Mutate(new AssistanceMovementGenerator(x, y, z), MOTION_SLOT_ACTIVE);
}
}
void MotionMaster::MoveSeekAssistanceDistract(uint32 time)
{
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
return;
if (_owner->GetTypeId() == TYPEID_PLAYER)
{
sLog->outError("Player (GUID: %u) attempt to call distract after assistance", _owner->GetGUIDLow());
}
else
{
;//sLog->outStaticDebug("Creature (Entry: %u GUID: %u) is distracted after assistance call (Time: %u)",
// _owner->GetEntry(), _owner->GetGUIDLow(), time);
Mutate(new AssistanceDistractMovementGenerator(time), MOTION_SLOT_ACTIVE);
}
}
void MotionMaster::MoveFleeing(Unit* enemy, uint32 time)
{
if (!enemy)
return;
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
return;
if (_owner->GetTypeId() == TYPEID_PLAYER)
{
;//sLog->outStaticDebug("Player (GUID: %u) flee from %s (GUID: %u)", _owner->GetGUIDLow(),
// enemy->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
// enemy->GetTypeId() == TYPEID_PLAYER ? enemy->GetGUIDLow() : enemy->ToCreature()->GetDBTableGUIDLow());
Mutate(new FleeingMovementGenerator<Player>(enemy->GetGUID()), MOTION_SLOT_CONTROLLED);
}
else
{
;//sLog->outStaticDebug("Creature (Entry: %u GUID: %u) flee from %s (GUID: %u)%s",
// _owner->GetEntry(), _owner->GetGUIDLow(),
// enemy->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
// enemy->GetTypeId() == TYPEID_PLAYER ? enemy->GetGUIDLow() : enemy->ToCreature()->GetDBTableGUIDLow(),
// time ? " for a limited time" : "");
if (time)
Mutate(new TimedFleeingMovementGenerator(enemy->GetGUID(), time), MOTION_SLOT_CONTROLLED);
else
Mutate(new FleeingMovementGenerator<Creature>(enemy->GetGUID()), MOTION_SLOT_CONTROLLED);
}
}
void MotionMaster::MoveTaxiFlight(uint32 path, uint32 pathnode)
{
if (_owner->GetTypeId() == TYPEID_PLAYER)
{
if (path < sTaxiPathNodesByPath.size())
{
;//sLog->outStaticDebug("%s taxi to (Path %u node %u)", _owner->GetName().c_str(), path, pathnode);
FlightPathMovementGenerator* mgen = new FlightPathMovementGenerator(pathnode);
mgen->LoadPath(_owner->ToPlayer());
Mutate(mgen, MOTION_SLOT_CONTROLLED);
}
else
{
sLog->outError("%s attempt taxi to (not existed Path %u node %u)",
_owner->GetName().c_str(), path, pathnode);
}
}
else
{
sLog->outError("Creature (Entry: %u GUID: %u) attempt taxi to (Path %u node %u)",
_owner->GetEntry(), _owner->GetGUIDLow(), path, pathnode);
}
}
void MotionMaster::MoveDistract(uint32 timer)
{
if (Impl[MOTION_SLOT_CONTROLLED])
return;
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
return;
/*if (_owner->GetTypeId() == TYPEID_PLAYER)
{
;//sLog->outStaticDebug("Player (GUID: %u) distracted (timer: %u)", _owner->GetGUIDLow(), timer);
}
else
{
;//sLog->outStaticDebug("Creature (Entry: %u GUID: %u) (timer: %u)",
_owner->GetEntry(), _owner->GetGUIDLow(), timer);
}*/
DistractMovementGenerator* mgen = new DistractMovementGenerator(timer);
Mutate(mgen, MOTION_SLOT_CONTROLLED);
}
void MotionMaster::Mutate(MovementGenerator *m, MovementSlot slot)
{
while (MovementGenerator *curr = Impl[slot])
{
bool delayed = (_top == slot && (_cleanFlag & MMCF_UPDATE));
// pussywizard: clear slot AND decrease top immediately to avoid crashes when referencing null top in DirectDelete
Impl[slot] = NULL;
while (!empty() && !top())
--_top;
if (delayed)
DelayedDelete(curr);
else
DirectDelete(curr);
}
if (_top < slot)
_top = slot;
Impl[slot] = m;
if (_top > slot)
_needInit[slot] = true;
else
{
_needInit[slot] = false;
m->Initialize(_owner);
}
}
void MotionMaster::MovePath(uint32 path_id, bool repeatable)
{
if (!path_id)
return;
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
if (_owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
return;
//We set waypoint movement as new default movement generator
// clear ALL movement generators (including default)
/*while (!empty())
{
MovementGenerator *curr = top();
curr->Finalize(*_owner);
pop();
if (!isStatic(curr))
delete curr;
}*/
//_owner->GetTypeId() == TYPEID_PLAYER ?
//Mutate(new WaypointMovementGenerator<Player>(path_id, repeatable)):
Mutate(new WaypointMovementGenerator<Creature>(path_id, repeatable), MOTION_SLOT_IDLE);
;//sLog->outStaticDebug("%s (GUID: %u) start moving over path(Id:%u, repeatable: %s)",
// _owner->GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature",
// _owner->GetGUIDLow(), path_id, repeatable ? "YES" : "NO");
}
void MotionMaster::MoveRotate(uint32 time, RotateDirection direction)
{
if (!time)
return;
Mutate(new RotateMovementGenerator(time, direction), MOTION_SLOT_ACTIVE);
}
void MotionMaster::propagateSpeedChange()
{
/*Impl::container_type::iterator it = Impl::c.begin();
for (; it != end(); ++it)
{
(*it)->unitSpeedChanged();
}*/
for (int i = 0; i <= _top; ++i)
{
if (Impl[i])
Impl[i]->unitSpeedChanged();
}
}
void MotionMaster::ReinitializeMovement()
{
for (int i = 0; i <= _top; ++i)
{
if (Impl[i])
Impl[i]->Reset(_owner);
}
}
MovementGeneratorType MotionMaster::GetCurrentMovementGeneratorType() const
{
if (empty())
return IDLE_MOTION_TYPE;
return top()->GetMovementGeneratorType();
}
MovementGeneratorType MotionMaster::GetMotionSlotType(int slot) const
{
if (!Impl[slot])
return NULL_MOTION_TYPE;
else
return Impl[slot]->GetMovementGeneratorType();
}
// Xinef: Escort system
uint32 MotionMaster::GetCurrentSplineId() const
{
if (empty())
return 0;
return top()->GetSplineId();
}
void MotionMaster::InitTop()
{
top()->Initialize(_owner);
_needInit[_top] = false;
}
void MotionMaster::DirectDelete(_Ty curr)
{
if (isStatic(curr))
return;
curr->Finalize(_owner);
delete curr;
}
void MotionMaster::DelayedDelete(_Ty curr)
{
sLog->outCrash("Unit (Entry %u) is trying to delete its updating MG (Type %u)!", _owner->GetEntry(), curr->GetMovementGeneratorType());
if (isStatic(curr))
return;
if (!_expList)
_expList = new ExpireList();
_expList->push_back(curr);
}
bool MotionMaster::GetDestination(float &x, float &y, float &z)
{
if (_owner->movespline->Finalized())
return false;
G3D::Vector3 const& dest = _owner->movespline->FinalDestination();
x = dest.x;
y = dest.y;
z = dest.z;
return true;
}

View File

@@ -0,0 +1,234 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_MOTIONMASTER_H
#define TRINITY_MOTIONMASTER_H
#include "Common.h"
#include <vector>
#include "SharedDefines.h"
#include "Object.h"
#include "Spline/MoveSpline.h"
class MovementGenerator;
class Unit;
// Creature Entry ID used for waypoints show, visible only for GMs
#define VISUAL_WAYPOINT 1
// values 0 ... MAX_DB_MOTION_TYPE-1 used in DB
enum MovementGeneratorType
{
IDLE_MOTION_TYPE = 0, // IdleMovementGenerator.h
RANDOM_MOTION_TYPE = 1, // RandomMovementGenerator.h
WAYPOINT_MOTION_TYPE = 2, // WaypointMovementGenerator.h
MAX_DB_MOTION_TYPE = 3, // *** this and below motion types can't be set in DB.
ANIMAL_RANDOM_MOTION_TYPE = MAX_DB_MOTION_TYPE, // AnimalRandomMovementGenerator.h
CONFUSED_MOTION_TYPE = 4, // ConfusedMovementGenerator.h
CHASE_MOTION_TYPE = 5, // TargetedMovementGenerator.h
HOME_MOTION_TYPE = 6, // HomeMovementGenerator.h
FLIGHT_MOTION_TYPE = 7, // WaypointMovementGenerator.h
POINT_MOTION_TYPE = 8, // PointMovementGenerator.h
FLEEING_MOTION_TYPE = 9, // FleeingMovementGenerator.h
DISTRACT_MOTION_TYPE = 10, // IdleMovementGenerator.h
ASSISTANCE_MOTION_TYPE= 11, // PointMovementGenerator.h (first part of flee for assistance)
ASSISTANCE_DISTRACT_MOTION_TYPE = 12, // IdleMovementGenerator.h (second part of flee for assistance)
TIMED_FLEEING_MOTION_TYPE = 13, // FleeingMovementGenerator.h (alt.second part of flee for assistance)
FOLLOW_MOTION_TYPE = 14,
ROTATE_MOTION_TYPE = 15,
EFFECT_MOTION_TYPE = 16,
ESCORT_MOTION_TYPE = 17, // xinef: EscortMovementGenerator.h
NULL_MOTION_TYPE = 18
};
enum MovementSlot
{
MOTION_SLOT_IDLE,
MOTION_SLOT_ACTIVE,
MOTION_SLOT_CONTROLLED,
MAX_MOTION_SLOT
};
enum MMCleanFlag
{
MMCF_NONE = 0x00,
MMCF_UPDATE = 0x01, // Clear or Expire called from update
MMCF_RESET = 0x02, // Flag if need top()->Reset()
MMCF_INUSE = 0x04, // pussywizard: Flag if in MotionMaster::UpdateMotion
};
enum RotateDirection
{
ROTATE_DIRECTION_LEFT,
ROTATE_DIRECTION_RIGHT
};
// assume it is 25 yard per 0.6 second
#define SPEED_CHARGE 42.0f
class MotionMaster //: private std::stack<MovementGenerator *>
{
private:
//typedef std::stack<MovementGenerator *> Impl;
typedef MovementGenerator* _Ty;
void pop()
{
if (empty())
return;
Impl[_top] = NULL;
while (!empty() && !top())
--_top;
}
bool needInitTop() const
{
if (empty())
return false;
return _needInit[_top];
}
void InitTop();
public:
explicit MotionMaster(Unit* unit) : _expList(NULL), _top(-1), _owner(unit), _cleanFlag(MMCF_NONE)
{
for (uint8 i = 0; i < MAX_MOTION_SLOT; ++i)
{
Impl[i] = NULL;
_needInit[i] = true;
}
}
~MotionMaster();
void Initialize();
void InitDefault();
bool empty() const { return (_top < 0); }
int size() const { return _top + 1; }
_Ty top() const
{
ASSERT(!empty());
return Impl[_top];
}
_Ty GetMotionSlot(int slot) const
{
ASSERT(slot >= 0);
return Impl[slot];
}
uint8 GetCleanFlags() const { return _cleanFlag; }
void DirectDelete(_Ty curr);
void DelayedDelete(_Ty curr);
void UpdateMotion(uint32 diff);
void Clear(bool reset = true)
{
if (_cleanFlag & MMCF_UPDATE)
{
if (reset)
_cleanFlag |= MMCF_RESET;
else
_cleanFlag &= ~MMCF_RESET;
DelayedClean();
}
else
DirectClean(reset);
}
void MovementExpired(bool reset = true)
{
if (_cleanFlag & MMCF_UPDATE)
{
if (reset)
_cleanFlag |= MMCF_RESET;
else
_cleanFlag &= ~MMCF_RESET;
DelayedExpire();
}
else
DirectExpire(reset);
}
void MovementExpiredOnSlot(MovementSlot slot, bool reset = true)
{
// xinef: cannot be used during motion update!
if (!(_cleanFlag & MMCF_UPDATE))
DirectExpireSlot(slot, reset);
}
void MoveIdle();
void MoveTargetedHome();
void MoveRandom(float spawndist = 0.0f);
void MoveFollow(Unit* target, float dist, float angle, MovementSlot slot = MOTION_SLOT_ACTIVE);
void MoveChase(Unit* target, float dist = 0.0f, float angle = 0.0f);
void MoveConfused();
void MoveFleeing(Unit* enemy, uint32 time = 0);
void MovePoint(uint32 id, const Position &pos, bool generatePath = true, bool forceDestination = true)
{ MovePoint(id, pos.m_positionX, pos.m_positionY, pos.m_positionZ, generatePath, forceDestination); }
void MovePoint(uint32 id, float x, float y, float z, bool generatePath = true, bool forceDestination = true, MovementSlot slot = MOTION_SLOT_ACTIVE);
void MoveSplinePath(Movement::PointsArray* path);
// These two movement types should only be used with creatures having landing/takeoff animations
void MoveLand(uint32 id, Position const& pos, float speed);
void MoveLand(uint32 id, float x, float y, float z, float speed); // pussywizard: added for easy calling by passing 3 floats x, y, z
void MoveTakeoff(uint32 id, Position const& pos, float speed);
void MoveTakeoff(uint32 id, float x, float y, float z, float speed); // pussywizard: added for easy calling by passing 3 floats x, y, z
void MoveCharge(float x, float y, float z, float speed = SPEED_CHARGE, uint32 id = EVENT_CHARGE, const Movement::PointsArray* path = NULL, bool generatePath = false);
void MoveKnockbackFrom(float srcX, float srcY, float speedXY, float speedZ);
void MoveJumpTo(float angle, float speedXY, float speedZ);
void MoveJump(Position const& pos, float speedXY, float speedZ, uint32 id = 0)
{ MoveJump(pos.m_positionX, pos.m_positionY, pos.m_positionZ, speedXY, speedZ, id); };
void MoveJump(float x, float y, float z, float speedXY, float speedZ, uint32 id = 0, Unit const* target = NULL);
void MoveFall(uint32 id = 0, bool addFlagForNPC = false);
void MoveSeekAssistance(float x, float y, float z);
void MoveSeekAssistanceDistract(uint32 timer);
void MoveTaxiFlight(uint32 path, uint32 pathnode);
void MoveDistract(uint32 time);
void MovePath(uint32 path_id, bool repeatable);
void MoveRotate(uint32 time, RotateDirection direction);
MovementGeneratorType GetCurrentMovementGeneratorType() const;
MovementGeneratorType GetMotionSlotType(int slot) const;
uint32 GetCurrentSplineId() const; // Xinef: Escort system
void propagateSpeedChange();
void ReinitializeMovement();
bool GetDestination(float &x, float &y, float &z);
private:
void Mutate(MovementGenerator *m, MovementSlot slot); // use Move* functions instead
void DirectClean(bool reset);
void DelayedClean();
void DirectExpire(bool reset);
void DirectExpireSlot(MovementSlot slot, bool reset);
void DelayedExpire();
typedef std::vector<_Ty> ExpireList;
ExpireList* _expList;
_Ty Impl[MAX_MOTION_SLOT];
int _top;
Unit* _owner;
bool _needInit[MAX_MOTION_SLOT];
uint8 _cleanFlag;
};
#endif

View File

@@ -0,0 +1,23 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "MovementGenerator.h"
MovementGenerator::~MovementGenerator()
{
}

View File

@@ -0,0 +1,98 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_MOVEMENTGENERATOR_H
#define TRINITY_MOVEMENTGENERATOR_H
#include "Define.h"
#include <ace/Singleton.h>
#include "ObjectRegistry.h"
#include "FactoryHolder.h"
#include "Common.h"
#include "MotionMaster.h"
class Unit;
class MovementGenerator
{
public:
virtual ~MovementGenerator();
virtual void Initialize(Unit*) = 0;
virtual void Finalize(Unit*) = 0;
virtual void Reset(Unit*) = 0;
virtual bool Update(Unit*, uint32 time_diff) = 0;
virtual MovementGeneratorType GetMovementGeneratorType() = 0;
virtual uint32 GetSplineId() const { return 0; } // Xinef: Escort system
virtual void unitSpeedChanged() { }
// used by Evade code for select point to evade with expected restart default movement
virtual bool GetResetPosition(float& /*x*/, float& /*y*/, float& /*z*/) { return false; }
};
template<class T, class D>
class MovementGeneratorMedium : public MovementGenerator
{
public:
void Initialize(Unit* u)
{
//u->AssertIsType<T>();
(static_cast<D*>(this))->DoInitialize(static_cast<T*>(u));
}
void Finalize(Unit* u)
{
//u->AssertIsType<T>();
(static_cast<D*>(this))->DoFinalize(static_cast<T*>(u));
}
void Reset(Unit* u)
{
//u->AssertIsType<T>();
(static_cast<D*>(this))->DoReset(static_cast<T*>(u));
}
bool Update(Unit* u, uint32 time_diff)
{
//u->AssertIsType<T>();
return (static_cast<D*>(this))->DoUpdate(static_cast<T*>(u), time_diff);
}
};
struct SelectableMovement : public FactoryHolder<MovementGenerator, MovementGeneratorType>
{
SelectableMovement(MovementGeneratorType mgt) : FactoryHolder<MovementGenerator, MovementGeneratorType>(mgt) {}
};
template<class REAL_MOVEMENT>
struct MovementGeneratorFactory : public SelectableMovement
{
MovementGeneratorFactory(MovementGeneratorType mgt) : SelectableMovement(mgt) {}
MovementGenerator* Create(void *) const;
};
typedef FactoryHolder<MovementGenerator, MovementGeneratorType> MovementGeneratorCreator;
typedef FactoryHolder<MovementGenerator, MovementGeneratorType>::FactoryHolderRegistry MovementGeneratorRegistry;
typedef FactoryHolder<MovementGenerator, MovementGeneratorType>::FactoryHolderRepository MovementGeneratorRepository;
#endif

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_MOVEMENTGENERATOR_IMPL_H
#define TRINITY_MOVEMENTGENERATOR_IMPL_H
#include "MovementGenerator.h"
template<class MOVEMENT_GEN>
inline MovementGenerator*
MovementGeneratorFactory<MOVEMENT_GEN>::Create(void * /*data*/) const
{
return (new MOVEMENT_GEN());
}
#endif

View File

@@ -0,0 +1,184 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "Creature.h"
#include "MapManager.h"
#include "ConfusedMovementGenerator.h"
#include "VMapFactory.h"
#include "MoveSplineInit.h"
#include "MoveSpline.h"
#include "Player.h"
#ifdef MAP_BASED_RAND_GEN
#define rand_norm() unit.rand_norm()
#define urand(a, b) unit.urand(a, b)
#endif
template<class T>
void ConfusedMovementGenerator<T>::DoInitialize(T* unit)
{
unit->StopMoving();
float const wander_distance = 4;
float x = unit->GetPositionX();
float y = unit->GetPositionY();
float z = unit->GetPositionZ();
Map const* map = unit->GetBaseMap();
bool is_water_ok, is_land_ok;
_InitSpecific(unit, is_water_ok, is_land_ok);
for (uint8 idx = 0; idx < MAX_CONF_WAYPOINTS + 1; ++idx)
{
float wanderX = x + (wander_distance * (float)rand_norm() - wander_distance/2);
float wanderY = y + (wander_distance * (float)rand_norm() - wander_distance/2);
// prevent invalid coordinates generation
Trinity::NormalizeMapCoord(wanderX);
Trinity::NormalizeMapCoord(wanderY);
float new_z = map->GetHeight(unit->GetPhaseMask(), wanderX, wanderY, z, true);
if (new_z <= INVALID_HEIGHT || fabs(z-new_z) > 3.0f) // pussywizard
{
i_waypoints[idx][0] = idx > 0 ? i_waypoints[idx-1][0] : x;
i_waypoints[idx][1] = idx > 0 ? i_waypoints[idx-1][1] : y;
i_waypoints[idx][2] = idx > 0 ? i_waypoints[idx-1][2] : z;
continue;
}
else if (unit->IsWithinLOS(wanderX, wanderY, z))
{
bool is_water = map->IsInWater(wanderX, wanderY, z);
if ((is_water && !is_water_ok) || (!is_water && !is_land_ok))
{
//! Cannot use coordinates outside our InhabitType. Use the current or previous position.
i_waypoints[idx][0] = idx > 0 ? i_waypoints[idx-1][0] : x;
i_waypoints[idx][1] = idx > 0 ? i_waypoints[idx-1][1] : y;
i_waypoints[idx][2] = idx > 0 ? i_waypoints[idx-1][2] : z;
continue;
}
}
else
{
//! Trying to access path outside line of sight. Skip this by using the current or previous position.
i_waypoints[idx][0] = idx > 0 ? i_waypoints[idx-1][0] : x;
i_waypoints[idx][1] = idx > 0 ? i_waypoints[idx-1][1] : y;
i_waypoints[idx][2] = idx > 0 ? i_waypoints[idx-1][2] : z;
continue;
}
//unit->UpdateAllowedPositionZ(wanderX, wanderY, z);
//! Positions are fine - apply them to this waypoint
i_waypoints[idx][0] = wanderX;
i_waypoints[idx][1] = wanderY;
i_waypoints[idx][2] = new_z;
}
// Xinef: Call movement immediately to broadcast movement packet
// Xinef: Initial timer is set to 1 so update with 1
i_nextMove = urand(1, MAX_CONF_WAYPOINTS);
DoUpdate(unit, 1);
unit->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
unit->AddUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
}
template<>
void ConfusedMovementGenerator<Creature>::_InitSpecific(Creature* creature, bool &is_water_ok, bool &is_land_ok)
{
is_water_ok = creature->CanSwim();
is_land_ok = creature->CanWalk();
}
template<>
void ConfusedMovementGenerator<Player>::_InitSpecific(Player* , bool &is_water_ok, bool &is_land_ok)
{
is_water_ok = true;
is_land_ok = true;
}
template<class T>
void ConfusedMovementGenerator<T>::DoReset(T* unit)
{
DoInitialize(unit);
}
template<class T>
bool ConfusedMovementGenerator<T>::DoUpdate(T* unit, uint32 diff)
{
if (unit->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
return true;
if (i_nextMoveTime.Passed())
{
// currently moving, update location
unit->AddUnitState(UNIT_STATE_CONFUSED_MOVE);
if (unit->movespline->Finalized())
{
i_nextMove = urand(1, MAX_CONF_WAYPOINTS);
i_nextMoveTime.Reset(urand(600, 1200)); // Guessed
}
}
else
{
// waiting for next move
i_nextMoveTime.Update(diff);
if (i_nextMoveTime.Passed())
{
// start moving
unit->AddUnitState(UNIT_STATE_CONFUSED_MOVE);
ASSERT(i_nextMove <= MAX_CONF_WAYPOINTS);
float x = i_waypoints[i_nextMove][0];
float y = i_waypoints[i_nextMove][1];
float z = i_waypoints[i_nextMove][2];
Movement::MoveSplineInit init(unit);
init.MoveTo(x, y, z);
init.Launch();
}
}
return true;
}
template<>
void ConfusedMovementGenerator<Player>::DoFinalize(Player* unit)
{
unit->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
unit->ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
unit->StopMoving();
}
template<>
void ConfusedMovementGenerator<Creature>::DoFinalize(Creature* unit)
{
unit->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
unit->ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
if (unit->GetVictim())
unit->SetTarget(unit->GetVictim()->GetGUID());
}
template void ConfusedMovementGenerator<Player>::DoInitialize(Player*);
template void ConfusedMovementGenerator<Creature>::DoInitialize(Creature*);
template void ConfusedMovementGenerator<Player>::DoReset(Player*);
template void ConfusedMovementGenerator<Creature>::DoReset(Creature*);
template bool ConfusedMovementGenerator<Player>::DoUpdate(Player*, uint32 diff);
template bool ConfusedMovementGenerator<Creature>::DoUpdate(Creature*, uint32 diff);

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_CONFUSEDGENERATOR_H
#define TRINITY_CONFUSEDGENERATOR_H
#include "MovementGenerator.h"
#include "Timer.h"
#define MAX_CONF_WAYPOINTS 24 //! Allows a twelve second confusion if i_nextMove always is the absolute minimum timer.
template<class T>
class ConfusedMovementGenerator : public MovementGeneratorMedium< T, ConfusedMovementGenerator<T> >
{
public:
explicit ConfusedMovementGenerator() : i_nextMoveTime(1) {}
void DoInitialize(T*);
void DoFinalize(T*);
void DoReset(T*);
bool DoUpdate(T*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return CONFUSED_MOTION_TYPE; }
private:
void _InitSpecific(T*, bool &, bool &);
TimeTracker i_nextMoveTime;
float i_waypoints[MAX_CONF_WAYPOINTS+1][3];
uint32 i_nextMove;
};
#endif

View File

@@ -0,0 +1,102 @@
/*
Written by Xinef
*/
#include "EscortMovementGenerator.h"
#include "Errors.h"
#include "Creature.h"
#include "CreatureAI.h"
#include "World.h"
#include "MoveSplineInit.h"
#include "MoveSpline.h"
#include "Player.h"
template<class T>
void EscortMovementGenerator<T>::DoInitialize(T* unit)
{
if (!unit->IsStopped())
unit->StopMoving();
unit->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
i_recalculateSpeed = false;
Movement::MoveSplineInit init(unit);
if (m_precomputedPath.size() == 2) // xinef: simple case, just call move to
init.MoveTo(m_precomputedPath[1].x, m_precomputedPath[1].y, m_precomputedPath[1].z);
else if (m_precomputedPath.size())
init.MovebyPath(m_precomputedPath);
init.Launch();
_splineId = unit->movespline->GetId();
}
template<class T>
bool EscortMovementGenerator<T>::DoUpdate(T* unit, uint32 diff)
{
if (!&unit)
return false;
if (unit->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
{
unit->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
return true;
}
unit->AddUnitState(UNIT_STATE_ROAMING_MOVE);
bool arrived = unit->movespline->Finalized();
if (i_recalculateSpeed && !arrived)
{
i_recalculateSpeed = false;
Movement::MoveSplineInit init(unit);
// xinef: speed changed during path execution, calculate remaining path and launch it once more
if (m_precomputedPath.size())
{
uint32 offset = std::min(uint32(unit->movespline->_currentSplineIdx()), uint32(m_precomputedPath.size()));
Movement::PointsArray::iterator offsetItr = m_precomputedPath.begin();
std::advance(offsetItr, offset);
m_precomputedPath.erase(m_precomputedPath.begin(), offsetItr);
// restore 0 element (current position)
m_precomputedPath.insert(m_precomputedPath.begin(), G3D::Vector3(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ()));
if (m_precomputedPath.size() > 2)
init.MovebyPath(m_precomputedPath);
else if (m_precomputedPath.size() == 2)
init.MoveTo(m_precomputedPath[1].x, m_precomputedPath[1].y, m_precomputedPath[1].z);
}
init.Launch();
// Xinef: Override spline Id on recalculate launch
_splineId = unit->movespline->GetId();
}
return !arrived;
}
template<class T>
void EscortMovementGenerator<T>::DoFinalize(T* unit)
{
unit->ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
}
template<class T>
void EscortMovementGenerator<T>::DoReset(T* unit)
{
if (!unit->IsStopped())
unit->StopMoving();
unit->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
}
template void EscortMovementGenerator<Player>::DoInitialize(Player*);
template void EscortMovementGenerator<Creature>::DoInitialize(Creature*);
template void EscortMovementGenerator<Player>::DoFinalize(Player*);
template void EscortMovementGenerator<Creature>::DoFinalize(Creature*);
template void EscortMovementGenerator<Player>::DoReset(Player*);
template void EscortMovementGenerator<Creature>::DoReset(Creature*);
template bool EscortMovementGenerator<Player>::DoUpdate(Player* unit, uint32 diff);
template bool EscortMovementGenerator<Creature>::DoUpdate(Creature* unit, uint32 diff);

View File

@@ -0,0 +1,39 @@
/*
Written by Xinef
*/
#ifndef TRINITY_ESCORTMOVEMENTGENERATOR_H
#define TRINITY_ESCORTMOVEMENTGENERATOR_H
#include "MovementGenerator.h"
template<class T>
class EscortMovementGenerator : public MovementGeneratorMedium< T, EscortMovementGenerator<T> >
{
public:
EscortMovementGenerator(Movement::PointsArray* _path = NULL) : i_recalculateSpeed(false)
{
if (_path)
m_precomputedPath = *_path;
}
void DoInitialize(T*);
void DoFinalize(T*);
void DoReset(T*);
bool DoUpdate(T*, uint32);
void unitSpeedChanged() { i_recalculateSpeed = true; }
MovementGeneratorType GetMovementGeneratorType() { return ESCORT_MOTION_TYPE; }
uint32 GetSplineId() const { return _splineId; }
private:
bool i_recalculateSpeed;
Movement::PointsArray m_precomputedPath;
uint32 _splineId;
};
#endif

View File

@@ -0,0 +1,414 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "Creature.h"
#include "CreatureAI.h"
#include "MapManager.h"
#include "FleeingMovementGenerator.h"
#include "ObjectAccessor.h"
#include "MoveSplineInit.h"
#include "MoveSpline.h"
#include "Player.h"
#define MIN_QUIET_DISTANCE 28.0f
#define MAX_QUIET_DISTANCE 43.0f
template<class T>
void FleeingMovementGenerator<T>::_setTargetLocation(T* owner)
{
if (!owner)
return;
if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
return;
if (!_setMoveData(owner))
return;
float x, y, z;
if (!_getPoint(owner, x, y, z))
return;
owner->AddUnitState(UNIT_STATE_FLEEING_MOVE);
Movement::MoveSplineInit init(owner);
init.MoveTo(x,y,z);
init.SetWalk(false);
init.Launch();
}
template<class T>
bool FleeingMovementGenerator<T>::_getPoint(T* owner, float &x, float &y, float &z)
{
if (!&owner)
return false;
x = owner->GetPositionX();
y = owner->GetPositionY();
z = owner->GetPositionZ();
float temp_x, temp_y, angle;
const Map* _map = owner->GetBaseMap();
// primitive path-finding
for (uint8 i = 0; i < 18; ++i)
{
if (i_only_forward && i > 2)
break;
float distance = 5.0f;
switch (i)
{
case 0:
angle = i_cur_angle;
break;
case 1:
angle = i_cur_angle;
distance /= 2;
break;
case 2:
angle = i_cur_angle;
distance /= 4;
break;
case 3:
angle = i_cur_angle + static_cast<float>(M_PI/4);
break;
case 4:
angle = i_cur_angle - static_cast<float>(M_PI/4);
break;
case 5:
angle = i_cur_angle + static_cast<float>(M_PI/4);
distance /= 2;
break;
case 6:
angle = i_cur_angle - static_cast<float>(M_PI/4);
distance /= 2;
break;
case 7:
angle = i_cur_angle + static_cast<float>(M_PI/2);
break;
case 8:
angle = i_cur_angle - static_cast<float>(M_PI/2);
break;
case 9:
angle = i_cur_angle + static_cast<float>(M_PI/2);
distance /= 2;
break;
case 10:
angle = i_cur_angle - static_cast<float>(M_PI/2);
distance /= 2;
break;
case 11:
angle = i_cur_angle + static_cast<float>(M_PI/4);
distance /= 4;
break;
case 12:
angle = i_cur_angle - static_cast<float>(M_PI/4);
distance /= 4;
break;
case 13:
angle = i_cur_angle + static_cast<float>(M_PI/2);
distance /= 4;
break;
case 14:
angle = i_cur_angle - static_cast<float>(M_PI/2);
distance /= 4;
break;
case 15:
angle = i_cur_angle + static_cast<float>(3*M_PI/4);
distance /= 2;
break;
case 16:
angle = i_cur_angle - static_cast<float>(3*M_PI/4);
distance /= 2;
break;
case 17:
angle = i_cur_angle + static_cast<float>(M_PI);
distance /= 2;
break;
default:
angle = 0.0f;
distance = 0.0f;
break;
}
temp_x = x + distance * cos(angle);
temp_y = y + distance * sin(angle);
Trinity::NormalizeMapCoord(temp_x);
Trinity::NormalizeMapCoord(temp_y);
if (owner->IsWithinLOS(temp_x, temp_y, z))
{
bool is_water_now = _map->IsInWater(x,y,z);
if (is_water_now && _map->IsInWater(temp_x,temp_y,z))
{
x = temp_x;
y = temp_y;
return true;
}
float new_z = _map->GetHeight(owner->GetPhaseMask(), temp_x, temp_y, z, true);
if (new_z <= INVALID_HEIGHT || fabs(z-new_z) > 3.0f)
continue;
bool is_water_next = _map->IsInWater(temp_x, temp_y, new_z);
if ((is_water_now && !is_water_next && !is_land_ok) || (!is_water_now && is_water_next && !is_water_ok))
continue;
if (!(new_z - z) || distance / fabs(new_z - z) > 1.0f)
{
float new_z_left = _map->GetHeight(owner->GetPhaseMask(), temp_x + 1.0f*cos(angle+static_cast<float>(M_PI/2)),temp_y + 1.0f*sin(angle+static_cast<float>(M_PI/2)),z,true);
float new_z_right = _map->GetHeight(owner->GetPhaseMask(), temp_x + 1.0f*cos(angle-static_cast<float>(M_PI/2)),temp_y + 1.0f*sin(angle-static_cast<float>(M_PI/2)),z,true);
if (fabs(new_z_left - new_z) < 1.2f && fabs(new_z_right - new_z) < 1.2f)
{
x = temp_x;
y = temp_y;
z = new_z;
return true;
}
}
}
}
i_to_distance_from_caster = 0.0f;
i_nextCheckTime.Reset(urand(500,1000));
return false;
}
template<class T>
bool FleeingMovementGenerator<T>::_setMoveData(T* owner)
{
float cur_dist_xyz = owner->GetDistance(i_caster_x, i_caster_y, i_caster_z);
if (i_to_distance_from_caster > 0.0f)
{
if ((i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz < i_to_distance_from_caster) ||
// if we reach lower distance
(i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz > i_last_distance_from_caster) ||
// if we can't be close
(i_last_distance_from_caster < i_to_distance_from_caster && cur_dist_xyz > i_to_distance_from_caster) ||
// if we reach bigger distance
(cur_dist_xyz > MAX_QUIET_DISTANCE) || // if we are too far
(i_last_distance_from_caster > MIN_QUIET_DISTANCE && cur_dist_xyz < MIN_QUIET_DISTANCE))
// if we leave 'quiet zone'
{
// we are very far or too close, stopping
i_to_distance_from_caster = 0.0f;
i_nextCheckTime.Reset(urand(500,1000));
return false;
}
else
{
// now we are running, continue
i_last_distance_from_caster = cur_dist_xyz;
return true;
}
}
float cur_dist;
float angle_to_caster;
if (Unit* fright = ObjectAccessor::GetUnit(*owner, i_frightGUID))
{
cur_dist = fright->GetDistance(owner);
if (cur_dist < cur_dist_xyz)
{
i_caster_x = fright->GetPositionX();
i_caster_y = fright->GetPositionY();
i_caster_z = fright->GetPositionZ();
angle_to_caster = fright->GetAngle(owner);
}
else
{
cur_dist = cur_dist_xyz;
angle_to_caster = owner->GetAngle(i_caster_x, i_caster_y) + static_cast<float>(M_PI);
}
}
else
{
cur_dist = cur_dist_xyz;
angle_to_caster = owner->GetAngle(i_caster_x, i_caster_y) + static_cast<float>(M_PI);
}
// if we too close may use 'path-finding' else just stop
i_only_forward = cur_dist >= MIN_QUIET_DISTANCE/3;
//get angle and 'distance from caster' to run
float angle;
if (i_cur_angle == 0.0f && i_last_distance_from_caster == 0.0f) //just started, first time
{
angle = (float)rand_norm()*(1.0f - cur_dist/MIN_QUIET_DISTANCE) * static_cast<float>(M_PI/3) + (float)rand_norm()*static_cast<float>(M_PI*2/3);
i_to_distance_from_caster = MIN_QUIET_DISTANCE;
i_only_forward = true;
}
else if (cur_dist < MIN_QUIET_DISTANCE)
{
angle = static_cast<float>(M_PI/6) + (float)rand_norm()*static_cast<float>(M_PI*2/3);
i_to_distance_from_caster = cur_dist*2/3 + (float)rand_norm()*(MIN_QUIET_DISTANCE - cur_dist*2/3);
}
else if (cur_dist > MAX_QUIET_DISTANCE)
{
angle = (float)rand_norm()*static_cast<float>(M_PI/3) + static_cast<float>(M_PI*2/3);
i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + (float)rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
}
else
{
angle = (float)rand_norm()*static_cast<float>(M_PI);
i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + (float)rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
}
int8 sign = (float)rand_norm() > 0.5f ? 1 : -1;
i_cur_angle = sign*angle + angle_to_caster;
// current distance
i_last_distance_from_caster = cur_dist;
return true;
}
template<class T>
void FleeingMovementGenerator<T>::DoInitialize(T* owner)
{
if (!owner)
return;
owner->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
owner->AddUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
_Init(owner);
if (Unit* fright = ObjectAccessor::GetUnit(*owner, i_frightGUID))
{
i_caster_x = fright->GetPositionX();
i_caster_y = fright->GetPositionY();
i_caster_z = fright->GetPositionZ();
}
else
{
i_caster_x = owner->GetPositionX();
i_caster_y = owner->GetPositionY();
i_caster_z = owner->GetPositionZ();
}
i_only_forward = true;
i_cur_angle = 0.0f;
i_last_distance_from_caster = 0.0f;
i_to_distance_from_caster = 0.0f;
_setTargetLocation(owner);
}
template<>
void FleeingMovementGenerator<Creature>::_Init(Creature* owner)
{
if (!&owner)
return;
//owner->SetTargetGuid(ObjectGuid());
is_water_ok = owner->CanSwim();
is_land_ok = owner->CanWalk();
}
template<>
void FleeingMovementGenerator<Player>::_Init(Player* )
{
is_water_ok = true;
is_land_ok = true;
}
template<>
void FleeingMovementGenerator<Player>::DoFinalize(Player* owner)
{
owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
owner->ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
}
template<>
void FleeingMovementGenerator<Creature>::DoFinalize(Creature* owner)
{
owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
owner->ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
if (owner->GetVictim())
owner->SetTarget(owner->GetVictim()->GetGUID());
}
template<class T>
void FleeingMovementGenerator<T>::DoReset(T* owner)
{
DoInitialize(owner);
}
template<class T>
bool FleeingMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
{
if (!owner || !owner->IsAlive())
return false;
if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
{
owner->ClearUnitState(UNIT_STATE_FLEEING_MOVE);
return true;
}
i_nextCheckTime.Update(time_diff);
if (i_nextCheckTime.Passed() && owner->movespline->Finalized())
_setTargetLocation(owner);
return true;
}
template void FleeingMovementGenerator<Player>::DoInitialize(Player*);
template void FleeingMovementGenerator<Creature>::DoInitialize(Creature*);
template bool FleeingMovementGenerator<Player>::_setMoveData(Player*);
template bool FleeingMovementGenerator<Creature>::_setMoveData(Creature*);
template bool FleeingMovementGenerator<Player>::_getPoint(Player*, float&, float&, float&);
template bool FleeingMovementGenerator<Creature>::_getPoint(Creature*, float&, float&, float&);
template void FleeingMovementGenerator<Player>::_setTargetLocation(Player*);
template void FleeingMovementGenerator<Creature>::_setTargetLocation(Creature*);
template void FleeingMovementGenerator<Player>::DoReset(Player*);
template void FleeingMovementGenerator<Creature>::DoReset(Creature*);
template bool FleeingMovementGenerator<Player>::DoUpdate(Player*, uint32);
template bool FleeingMovementGenerator<Creature>::DoUpdate(Creature*, uint32);
void TimedFleeingMovementGenerator::Finalize(Unit* owner)
{
owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
owner->ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
if (owner->GetVictim())
owner->SetTarget(owner->GetVictim()->GetGUID());
}
bool TimedFleeingMovementGenerator::Update(Unit* owner, uint32 time_diff)
{
if (!owner->IsAlive())
return false;
if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
{
owner->ClearUnitState(UNIT_STATE_FLEEING_MOVE);
return true;
}
i_totalFleeTime.Update(time_diff);
if (i_totalFleeTime.Passed())
return false;
// This calls grant-parent Update method hiden by FleeingMovementGenerator::Update(Creature &, uint32) version
// This is done instead of casting Unit& to Creature& and call parent method, then we can use Unit directly
return MovementGeneratorMedium< Creature, FleeingMovementGenerator<Creature> >::Update(owner, time_diff);
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_FLEEINGMOVEMENTGENERATOR_H
#define TRINITY_FLEEINGMOVEMENTGENERATOR_H
#include "MovementGenerator.h"
template<class T>
class FleeingMovementGenerator : public MovementGeneratorMedium< T, FleeingMovementGenerator<T> >
{
public:
FleeingMovementGenerator(uint64 fright) : i_frightGUID(fright), i_nextCheckTime(0) {}
void DoInitialize(T*);
void DoFinalize(T*);
void DoReset(T*);
bool DoUpdate(T*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return FLEEING_MOTION_TYPE; }
private:
void _setTargetLocation(T*);
bool _getPoint(T*, float &x, float &y, float &z);
bool _setMoveData(T* owner);
void _Init(T* );
bool is_water_ok :1;
bool is_land_ok :1;
bool i_only_forward:1;
float i_caster_x;
float i_caster_y;
float i_caster_z;
float i_last_distance_from_caster;
float i_to_distance_from_caster;
float i_cur_angle;
uint64 i_frightGUID;
TimeTracker i_nextCheckTime;
};
class TimedFleeingMovementGenerator : public FleeingMovementGenerator<Creature>
{
public:
TimedFleeingMovementGenerator(uint64 fright, uint32 time) :
FleeingMovementGenerator<Creature>(fright),
i_totalFleeTime(time) {}
MovementGeneratorType GetMovementGeneratorType() { return TIMED_FLEEING_MOTION_TYPE; }
bool Update(Unit*, uint32);
void Finalize(Unit*);
private:
TimeTracker i_totalFleeTime;
};
#endif

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "HomeMovementGenerator.h"
#include "Creature.h"
#include "CreatureAI.h"
#include "WorldPacket.h"
#include "MoveSplineInit.h"
#include "MoveSpline.h"
void HomeMovementGenerator<Creature>::DoInitialize(Creature* owner)
{
_setTargetLocation(owner);
}
void HomeMovementGenerator<Creature>::DoFinalize(Creature* owner)
{
owner->ClearUnitState(UNIT_STATE_EVADE);
if (arrived)
{
// Xinef: npc run by default
//owner->SetWalk(true);
owner->LoadCreaturesAddon(true);
owner->AI()->JustReachedHome();
}
owner->m_targetsNotAcceptable.clear();
owner->UpdateEnvironmentIfNeeded(2);
}
void HomeMovementGenerator<Creature>::DoReset(Creature*)
{
}
void HomeMovementGenerator<Creature>::_setTargetLocation(Creature* owner)
{
// Xinef: dont interrupt in any cast!
//if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
// return;
Movement::MoveSplineInit init(owner);
float x, y, z, o;
// Xinef: if there is motion generator on controlled slot, this one is not updated
// Xinef: always get reset pos from idle slot
MovementGenerator* gen = owner->GetMotionMaster()->GetMotionSlot(MOTION_SLOT_IDLE);
if (owner->GetMotionMaster()->empty() || !gen || !gen->GetResetPosition(x, y, z))
{
owner->GetHomePosition(x, y, z, o);
init.SetFacing(o);
}
init.MoveTo(x, y, z, MMAP::MMapFactory::IsPathfindingEnabled(owner->FindMap()), true);
init.SetWalk(false);
init.Launch();
arrived = false;
owner->ClearUnitState(uint32(UNIT_STATE_ALL_STATE & ~(UNIT_STATE_POSSESSED | UNIT_STATE_EVADE | UNIT_STATE_IGNORE_PATHFINDING | UNIT_STATE_NO_ENVIRONMENT_UPD)));
}
bool HomeMovementGenerator<Creature>::DoUpdate(Creature* owner, const uint32 /*time_diff*/)
{
arrived = owner->movespline->Finalized();
if (arrived)
return false;
if (i_recalculateTravel)
{
_setTargetLocation(owner);
i_recalculateTravel = false;
}
return true;
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_HOMEMOVEMENTGENERATOR_H
#define TRINITY_HOMEMOVEMENTGENERATOR_H
#include "MovementGenerator.h"
class Creature;
template < class T >
class HomeMovementGenerator;
template <>
class HomeMovementGenerator<Creature> : public MovementGeneratorMedium< Creature, HomeMovementGenerator<Creature> >
{
public:
HomeMovementGenerator() : arrived(false), i_recalculateTravel(false) {}
~HomeMovementGenerator() {}
void DoInitialize(Creature*);
void DoFinalize(Creature*);
void DoReset(Creature*);
bool DoUpdate(Creature*, const uint32);
MovementGeneratorType GetMovementGeneratorType() { return HOME_MOTION_TYPE; }
void unitSpeedChanged() { i_recalculateTravel = true; }
private:
void _setTargetLocation(Creature*);
bool arrived : 1;
bool i_recalculateTravel : 1;
};
#endif

View File

@@ -0,0 +1,105 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "IdleMovementGenerator.h"
#include "CreatureAI.h"
#include "Creature.h"
IdleMovementGenerator si_idleMovement;
// StopMoving is needed to make unit stop if its last movement generator expires
// But it should not be sent otherwise there are many redundent packets
void IdleMovementGenerator::Initialize(Unit* owner)
{
Reset(owner);
}
void IdleMovementGenerator::Reset(Unit* owner)
{
if (!owner->IsStopped())
owner->StopMoving();
}
void RotateMovementGenerator::Initialize(Unit* owner)
{
if (!owner->IsStopped())
owner->StopMoving();
if (owner->GetVictim())
owner->SetInFront(owner->GetVictim());
owner->AddUnitState(UNIT_STATE_ROTATING);
owner->AttackStop();
}
bool RotateMovementGenerator::Update(Unit* owner, uint32 diff)
{
float angle = owner->GetOrientation();
if (m_direction == ROTATE_DIRECTION_LEFT)
{
angle += (float)diff * static_cast<float>(M_PI * 2) / m_maxDuration;
while (angle >= static_cast<float>(M_PI * 2)) angle -= static_cast<float>(M_PI * 2);
}
else
{
angle -= (float)diff * static_cast<float>(M_PI * 2) / m_maxDuration;
while (angle < 0) angle += static_cast<float>(M_PI * 2);
}
owner->SetFacingTo(angle);
if (m_duration > diff)
m_duration -= diff;
else
return false;
return true;
}
void RotateMovementGenerator::Finalize(Unit* unit)
{
unit->ClearUnitState(UNIT_STATE_ROTATING);
if (unit->GetTypeId() == TYPEID_UNIT)
unit->ToCreature()->AI()->MovementInform(ROTATE_MOTION_TYPE, 0);
}
void DistractMovementGenerator::Initialize(Unit* owner)
{
owner->AddUnitState(UNIT_STATE_DISTRACTED);
}
void DistractMovementGenerator::Finalize(Unit* owner)
{
owner->ClearUnitState(UNIT_STATE_DISTRACTED);
}
bool DistractMovementGenerator::Update(Unit* owner, uint32 time_diff)
{
if (owner->IsInCombat() || time_diff > m_timer)
return false;
m_timer -= time_diff;
return true;
}
void AssistanceDistractMovementGenerator::Finalize(Unit* unit)
{
unit->ClearUnitState(UNIT_STATE_DISTRACTED);
unit->ToCreature()->SetReactState(REACT_AGGRESSIVE);
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_IDLEMOVEMENTGENERATOR_H
#define TRINITY_IDLEMOVEMENTGENERATOR_H
#include "MovementGenerator.h"
class IdleMovementGenerator : public MovementGenerator
{
public:
void Initialize(Unit*);
void Finalize(Unit*) { }
void Reset(Unit*);
bool Update(Unit*, uint32) { return true; }
MovementGeneratorType GetMovementGeneratorType() { return IDLE_MOTION_TYPE; }
};
extern IdleMovementGenerator si_idleMovement;
class RotateMovementGenerator : public MovementGenerator
{
public:
explicit RotateMovementGenerator(uint32 time, RotateDirection direction) : m_duration(time), m_maxDuration(time), m_direction(direction) {}
void Initialize(Unit*);
void Finalize(Unit*);
void Reset(Unit* owner) { Initialize(owner); }
bool Update(Unit*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return ROTATE_MOTION_TYPE; }
private:
uint32 m_duration, m_maxDuration;
RotateDirection m_direction;
};
class DistractMovementGenerator : public MovementGenerator
{
public:
explicit DistractMovementGenerator(uint32 timer) : m_timer(timer) {}
void Initialize(Unit*);
void Finalize(Unit*);
void Reset(Unit* owner) { Initialize(owner); }
bool Update(Unit*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return DISTRACT_MOTION_TYPE; }
private:
uint32 m_timer;
};
class AssistanceDistractMovementGenerator : public DistractMovementGenerator
{
public:
AssistanceDistractMovementGenerator(uint32 timer) :
DistractMovementGenerator(timer) {}
MovementGeneratorType GetMovementGeneratorType() { return ASSISTANCE_DISTRACT_MOTION_TYPE; }
void Finalize(Unit*);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,157 @@
/*
* Copyright (C)
*
* 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
*/
#ifndef _PATH_GENERATOR_H
#define _PATH_GENERATOR_H
#include "SharedDefines.h"
#include "DetourNavMesh.h"
#include "DetourNavMeshQuery.h"
#include "MoveSplineInitArgs.h"
#include "MMapFactory.h"
#include "MMapManager.h"
class Unit;
// 74*4.0f=296y number_of_points*interval = max_path_len
// this is way more than actual evade range
// I think we can safely cut those down even more
#define MAX_PATH_LENGTH 74
#define MAX_POINT_PATH_LENGTH 74
#define SMOOTH_PATH_STEP_SIZE 4.0f
#define SMOOTH_PATH_SLOP 0.3f
#define ALLOWED_DIST_FROM_POLY 2.5f
#define ADDED_Z_FOR_POLY_LOOKUP 0.3f
#define DISALLOW_TIME_AFTER_FAIL 3 // secs
#define MAX_FIXABLE_Z_ERROR 12.0f
#define VERTEX_SIZE 3
#define INVALID_POLYREF 0
enum PathType
{
PATHFIND_BLANK = 0x00, // path not built yet
PATHFIND_NORMAL = 0x01, // normal path
PATHFIND_SHORTCUT = 0x02, // travel through obstacles, terrain, air, etc (old behavior)
PATHFIND_INCOMPLETE = 0x04, // we have partial path to follow - getting closer to target
PATHFIND_NOPATH = 0x08, // no valid path at all or error in generating one
PATHFIND_NOT_USING_PATH = 0x10, // used when we are either flying/swiming or on map w/o mmaps
PATHFIND_SHORT = 0x20, // path is longer or equal to its limited path length
};
class PathGenerator
{
public:
explicit PathGenerator(Unit const* owner);
~PathGenerator();
// Calculate the path from owner to given destination
// return: true if new path was calculated, false otherwise (no change needed)
bool CalculatePath(float destX, float destY, float destZ, bool forceDest = false);
// option setters - use optional
void SetUseStraightPath(bool useStraightPath) { _useStraightPath = useStraightPath; }
void SetPathLengthLimit(float distance) { _pointPathLimit = std::min<uint32>(uint32(distance/SMOOTH_PATH_STEP_SIZE), MAX_POINT_PATH_LENGTH); }
// result getters
G3D::Vector3 const& GetStartPosition() const { return _startPosition; }
G3D::Vector3 const& GetEndPosition() const { return _endPosition; }
G3D::Vector3 const& GetActualEndPosition() const { return _actualEndPosition; }
Movement::PointsArray const& GetPath() const { return _pathPoints; }
PathType GetPathType() const { return _type; }
float getPathLength() const
{
float len = 0.0f;
float dx, dy, dz;
uint32 size = _pathPoints.size();
if (size)
{
dx = _pathPoints[0].x - _startPosition.x; dy = _pathPoints[0].y - _startPosition.y; dz = _pathPoints[0].z - _startPosition.z;
len += sqrt( dx*dx + dy*dy + dz*dz );
}
else
return len;
for (uint32 i=1; i<size; ++i)
{
dx = _pathPoints[i].x - _pathPoints[i-1].x; dy = _pathPoints[i].y - _pathPoints[i-1].y; dz = _pathPoints[i].z - _pathPoints[i-1].z;
len += sqrt( dx*dx + dy*dy + dz*dz );
}
return len;
}
private:
dtPolyRef _pathPolyRefs[MAX_PATH_LENGTH]; // array of detour polygon references
uint32 _polyLength; // number of polygons in the path
Movement::PointsArray _pathPoints; // our actual (x,y,z) path to the target
PathType _type; // tells what kind of path this is
bool _useStraightPath; // type of path will be generated
bool _forceDestination; // when set, we will always arrive at given point
uint32 _pointPathLimit; // limit point path size; min(this, MAX_POINT_PATH_LENGTH)
G3D::Vector3 _startPosition; // {x, y, z} of current location
G3D::Vector3 _endPosition; // {x, y, z} of the destination
G3D::Vector3 _actualEndPosition; // {x, y, z} of the closest possible point to given destination
Unit const* const _sourceUnit; // the unit that is moving
dtNavMesh const* _navMesh; // the nav mesh
dtNavMeshQuery const* _navMeshQuery; // the nav mesh query used to find the path
dtQueryFilter _filter; // use single filter for all movements, update it when needed
void SetStartPosition(G3D::Vector3 const& point) { _startPosition = point; }
void SetEndPosition(G3D::Vector3 const& point) { _actualEndPosition = point; _endPosition = point; }
void SetActualEndPosition(G3D::Vector3 const& point) { _actualEndPosition = point; }
void Clear()
{
_polyLength = 0;
_pathPoints.clear();
}
bool InRange(G3D::Vector3 const& p1, G3D::Vector3 const& p2, float r, float h) const;
float Dist3DSqr(G3D::Vector3 const& p1, G3D::Vector3 const& p2) const;
bool InRangeYZX(float const* v1, float const* v2, float r, float h) const;
dtPolyRef GetPathPolyByPosition(dtPolyRef const* polyPath, uint32 polyPathSize, float const* Point, float* Distance = NULL) const;
dtPolyRef GetPolyByLocation(float* Point, float* Distance) const;
bool HaveTile(G3D::Vector3 const& p) const;
void BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 const& endPos, ACE_RW_Thread_Mutex& lock);
void BuildPointPath(float const* startPoint, float const* endPoint);
void BuildShortcut();
NavTerrain GetNavTerrain(float x, float y, float z);
void CreateFilter();
void UpdateFilter();
// smooth path aux functions
uint32 FixupCorridor(dtPolyRef* path, uint32 npath, uint32 maxPath, dtPolyRef const* visited, uint32 nvisited);
bool GetSteerTarget(float const* startPos, float const* endPos, float minTargetDist, dtPolyRef const* path, uint32 pathSize, float* steerPos,
unsigned char& steerPosFlag, dtPolyRef& steerPosRef);
dtStatus FindSmoothPath(float const* startPos, float const* endPos,
dtPolyRef const* polyPath, uint32 polyPathSize,
float* smoothPath, int* smoothPathSize, uint32 smoothPathMaxSize);
};
#endif

View File

@@ -0,0 +1,192 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "PointMovementGenerator.h"
#include "Errors.h"
#include "Creature.h"
#include "CreatureAI.h"
#include "World.h"
#include "MoveSplineInit.h"
#include "MoveSpline.h"
#include "Player.h"
//----- Point Movement Generator
template<class T>
void PointMovementGenerator<T>::DoInitialize(T* unit)
{
if (!unit->IsStopped())
unit->StopMoving();
unit->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
i_recalculateSpeed = false;
Movement::MoveSplineInit init(unit);
if (m_precomputedPath.size() > 2) // pussywizard: for charge
init.MovebyPath(m_precomputedPath);
else if (_generatePath)
{
PathGenerator path(unit);
bool result = path.CalculatePath(i_x, i_y, i_z, _forceDestination);
if (result && !(path.GetPathType() & PATHFIND_NOPATH) && path.GetPath().size() > 2)
{
m_precomputedPath = path.GetPath();
init.MovebyPath(m_precomputedPath);
}
else
{
// Xinef: fix strange client visual bug, moving on z coordinate only switches orientation by 180 degrees (visual only)
if (G3D::fuzzyEq(unit->GetPositionX(), i_x) && G3D::fuzzyEq(unit->GetPositionY(), i_y))
{
i_x += 0.2f*cos(unit->GetOrientation());
i_y += 0.2f*sin(unit->GetOrientation());
}
init.MoveTo(i_x, i_y, i_z);
}
}
else
{
// Xinef: fix strange client visual bug, moving on z coordinate only switches orientation by 180 degrees (visual only)
if (G3D::fuzzyEq(unit->GetPositionX(), i_x) && G3D::fuzzyEq(unit->GetPositionY(), i_y))
{
i_x += 0.2f*cos(unit->GetOrientation());
i_y += 0.2f*sin(unit->GetOrientation());
}
init.MoveTo(i_x, i_y, i_z);
}
if (speed > 0.0f)
init.SetVelocity(speed);
init.Launch();
}
template<class T>
bool PointMovementGenerator<T>::DoUpdate(T* unit, uint32 /*diff*/)
{
if (!unit)
return false;
if (unit->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
{
unit->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
return true;
}
unit->AddUnitState(UNIT_STATE_ROAMING_MOVE);
if (i_recalculateSpeed && !unit->movespline->Finalized())
{
i_recalculateSpeed = false;
Movement::MoveSplineInit init(unit);
// xinef: speed changed during path execution, calculate remaining path and launch it once more
if (m_precomputedPath.size())
{
uint32 offset = std::min(uint32(unit->movespline->_currentSplineIdx()), uint32(m_precomputedPath.size()));
Movement::PointsArray::iterator offsetItr = m_precomputedPath.begin();
std::advance(offsetItr, offset);
m_precomputedPath.erase(m_precomputedPath.begin(), offsetItr);
// restore 0 element (current position)
m_precomputedPath.insert(m_precomputedPath.begin(), G3D::Vector3(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ()));
if (m_precomputedPath.size() > 2)
init.MovebyPath(m_precomputedPath);
else if (m_precomputedPath.size() == 2)
init.MoveTo(m_precomputedPath[1].x, m_precomputedPath[1].y, m_precomputedPath[1].z);
}
else
init.MoveTo(i_x, i_y, i_z);
if (speed > 0.0f) // Default value for point motion type is 0.0, if 0.0 spline will use GetSpeed on unit
init.SetVelocity(speed);
init.Launch();
}
return !unit->movespline->Finalized();
}
template<class T>
void PointMovementGenerator<T>::DoFinalize(T* unit)
{
unit->ClearUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE);
if (unit->movespline->Finalized())
MovementInform(unit);
}
template<class T>
void PointMovementGenerator<T>::DoReset(T* unit)
{
if (!unit->IsStopped())
unit->StopMoving();
unit->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
}
template<class T>
void PointMovementGenerator<T>::MovementInform(T* /*unit*/)
{
}
template <> void PointMovementGenerator<Creature>::MovementInform(Creature* unit)
{
if (unit->AI())
unit->AI()->MovementInform(POINT_MOTION_TYPE, id);
}
template void PointMovementGenerator<Player>::DoInitialize(Player*);
template void PointMovementGenerator<Creature>::DoInitialize(Creature*);
template void PointMovementGenerator<Player>::DoFinalize(Player*);
template void PointMovementGenerator<Creature>::DoFinalize(Creature*);
template void PointMovementGenerator<Player>::DoReset(Player*);
template void PointMovementGenerator<Creature>::DoReset(Creature*);
template bool PointMovementGenerator<Player>::DoUpdate(Player*, uint32);
template bool PointMovementGenerator<Creature>::DoUpdate(Creature*, uint32);
void AssistanceMovementGenerator::Finalize(Unit* unit)
{
unit->ToCreature()->SetNoCallAssistance(false);
unit->ToCreature()->CallAssistance();
if (unit->IsAlive())
unit->GetMotionMaster()->MoveSeekAssistanceDistract(sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY));
}
bool EffectMovementGenerator::Update(Unit* unit, uint32)
{
return !unit->movespline->Finalized();
}
void EffectMovementGenerator::Finalize(Unit* unit)
{
if (unit->GetTypeId() != TYPEID_UNIT)
return;
if (unit->GetTypeId() == TYPEID_UNIT && unit->HasUnitMovementFlag(MOVEMENTFLAG_FALLING) && unit->movespline->isFalling()) // pussywizard
unit->RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING);
// Need restore previous movement since we have no proper states system
//if (unit->IsAlive() && !unit->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_FLEEING))
//{
// if (Unit* victim = unit->GetVictim())
// unit->GetMotionMaster()->MoveChase(victim);
// else
// unit->GetMotionMaster()->Initialize();
//}
if (unit->ToCreature()->AI())
unit->ToCreature()->AI()->MovementInform(EFFECT_MOTION_TYPE, m_Id);
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_POINTMOVEMENTGENERATOR_H
#define TRINITY_POINTMOVEMENTGENERATOR_H
#include "MovementGenerator.h"
#include "FollowerReference.h"
template<class T>
class PointMovementGenerator : public MovementGeneratorMedium< T, PointMovementGenerator<T> >
{
public:
PointMovementGenerator(uint32 _id, float _x, float _y, float _z, float _speed = 0.0f, const Movement::PointsArray* _path = NULL, bool generatePath = false, bool forceDestination = false) : id(_id),
i_x(_x), i_y(_y), i_z(_z), speed(_speed), _generatePath(generatePath), _forceDestination(forceDestination)
{
if (_path)
m_precomputedPath = *_path;
}
void DoInitialize(T*);
void DoFinalize(T*);
void DoReset(T*);
bool DoUpdate(T*, uint32);
void MovementInform(T*);
void unitSpeedChanged() { i_recalculateSpeed = true; }
MovementGeneratorType GetMovementGeneratorType() { return POINT_MOTION_TYPE; }
bool GetDestination(float& x, float& y, float& z) const { x=i_x; y=i_y; z=i_z; return true; }
private:
uint32 id;
float i_x, i_y, i_z;
float speed;
bool i_recalculateSpeed;
Movement::PointsArray m_precomputedPath;
bool _generatePath;
bool _forceDestination;
};
class AssistanceMovementGenerator : public PointMovementGenerator<Creature>
{
public:
AssistanceMovementGenerator(float _x, float _y, float _z) :
PointMovementGenerator<Creature>(0, _x, _y, _z) {}
MovementGeneratorType GetMovementGeneratorType() { return ASSISTANCE_MOTION_TYPE; }
void Finalize(Unit*);
};
// Does almost nothing - just doesn't allows previous movegen interrupt current effect.
class EffectMovementGenerator : public MovementGenerator
{
public:
explicit EffectMovementGenerator(uint32 Id) : m_Id(Id) {}
void Initialize(Unit*) {}
void Finalize(Unit*);
void Reset(Unit*) {}
bool Update(Unit*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return EFFECT_MOTION_TYPE; }
private:
uint32 m_Id;
};
#endif

View File

@@ -0,0 +1,293 @@
/*
REWRITTEN BY XINEF
*/
#include "Creature.h"
#include "MapManager.h"
#include "RandomMovementGenerator.h"
#include "ObjectAccessor.h"
#include "Map.h"
#include "Util.h"
#include "CreatureGroups.h"
#include "MoveSplineInit.h"
#include "MoveSpline.h"
#include "Spell.h"
template<>
void RandomMovementGenerator<Creature>::_setRandomLocation(Creature* creature)
{
if (creature->_moveState != MAP_OBJECT_CELL_MOVE_NONE)
return;
if (_validPointsVector[_currentPoint].empty())
{
if (_currentPoint == RANDOM_POINTS_NUMBER) // cant go anywhere from initial position, lets stay
return;
// go back to initial position and will never return to this point
_currentPoint = RANDOM_POINTS_NUMBER;
_currDestPosition.Relocate(_initialPosition);
creature->AddUnitState(UNIT_STATE_ROAMING_MOVE);
Movement::MoveSplineInit init(creature);
init.MoveTo(_currDestPosition.GetPositionX(), _currDestPosition.GetPositionY(), _currDestPosition.GetPositionZ());
init.SetWalk(true);
init.Launch();
if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature)
creature->GetFormation()->LeaderMoveTo(_currDestPosition.GetPositionX(), _currDestPosition.GetPositionY(), _currDestPosition.GetPositionZ(), false);
return;
}
uint8 random = urand(0, _validPointsVector[_currentPoint].size()-1);
std::vector<uint8>::iterator randomIter = _validPointsVector[_currentPoint].begin() + random;
uint8 newPoint = *randomIter;
uint16 pathIdx = uint16(_currentPoint*RANDOM_POINTS_NUMBER + newPoint);
// cant go anywhere from new point, so dont go there to not be stuck
if (_validPointsVector[newPoint].empty())
{
_validPointsVector[_currentPoint].erase(randomIter);
return;
}
Movement::PointsArray& finalPath = _preComputedPaths[pathIdx];
if (finalPath.empty())
{
Map* map = creature->GetMap();
float x = _destinationPoints[newPoint].x, y = _destinationPoints[newPoint].y, z = _destinationPoints[newPoint].z;
// invalid coordinates
if (!Trinity::IsValidMapCoord(x, y))
{
_validPointsVector[_currentPoint].erase(randomIter);
_preComputedPaths.erase(pathIdx);
return;
}
float ground = INVALID_HEIGHT;
float levelZ = map->GetWaterOrGroundLevel(x, y, z+4.0f, &ground);
float newZ = INVALID_HEIGHT;
// flying creature
if (creature->CanFly())
newZ = std::max<float>(levelZ, z + rand_norm()*_wanderDistance/2.0f);
// point underwater
else if (ground < levelZ)
{
if (!creature->CanSwim())
{
if (ground < levelZ - 1.5f)
{
_validPointsVector[_currentPoint].erase(randomIter);
_preComputedPaths.erase(pathIdx);
return;
}
levelZ = ground;
}
else
{
if (levelZ > INVALID_HEIGHT)
newZ = std::min<float>(levelZ-2.0f, z + rand_norm()*_wanderDistance/2.0f);
newZ = std::max<float>(ground, newZ);
}
}
// point on ground
else
{
if (levelZ <= INVALID_HEIGHT || !creature->CanWalk())
{
_validPointsVector[_currentPoint].erase(randomIter);
_preComputedPaths.erase(pathIdx);
return;
}
}
if (newZ > INVALID_HEIGHT)
{
// flying / swiming creature - dest not in los
if (!creature->IsWithinLOS(x, y, newZ))
{
_validPointsVector[_currentPoint].erase(randomIter);
_preComputedPaths.erase(pathIdx);
return;
}
finalPath.push_back(G3D::Vector3(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ()));
finalPath.push_back(G3D::Vector3(x, y, newZ));
}
else // ground
{
bool result = _pathGenerator->CalculatePath(x, y, levelZ, false);
if (result && !(_pathGenerator->GetPathType() & PATHFIND_NOPATH))
{
// generated path is too long
float pathLen = _pathGenerator->getPathLength();
if (pathLen*pathLen > creature->GetExactDistSq(x, y, levelZ) * MAX_PATH_LENGHT_FACTOR*MAX_PATH_LENGHT_FACTOR)
{
_validPointsVector[_currentPoint].erase(randomIter);
_preComputedPaths.erase(pathIdx);
return;
}
finalPath = _pathGenerator->GetPath();
Movement::PointsArray::iterator itr = finalPath.begin();
Movement::PointsArray::iterator itrNext = finalPath.begin()+1;
float zDiff, distDiff;
for (; itrNext != finalPath.end(); ++itr, ++itrNext)
{
distDiff = sqrt(((*itr).x-(*itrNext).x)*((*itr).x-(*itrNext).x) + ((*itr).y-(*itrNext).y)*((*itr).y-(*itrNext).y));
zDiff = fabs((*itr).z - (*itrNext).z);
// Xinef: tree climbing, cut as much as we can
if (zDiff > 2.0f || G3D::fuzzyNe(zDiff, 0.0f) && distDiff / zDiff < 2.15f) // ~25°
{
_validPointsVector[_currentPoint].erase(randomIter);
_preComputedPaths.erase(pathIdx);
return;
}
if (!map->isInLineOfSight((*itr).x, (*itr).y, (*itr).z+2.f, (*itrNext).x, (*itrNext).y, (*itrNext).z+2.f, creature->GetPhaseMask()))
{
_validPointsVector[_currentPoint].erase(randomIter);
_preComputedPaths.erase(pathIdx);
return;
}
}
// no valid path
if (finalPath.size() < 2)
{
_validPointsVector[_currentPoint].erase(randomIter);
_preComputedPaths.erase(pathIdx);
return;
}
}
else
{
_validPointsVector[_currentPoint].erase(randomIter);
_preComputedPaths.erase(pathIdx);
return;
}
}
}
_currentPoint = newPoint;
G3D::Vector3& finalPoint = finalPath[finalPath.size()-1];
_currDestPosition.Relocate(finalPoint.x, finalPoint.y, finalPoint.z);
creature->AddUnitState(UNIT_STATE_ROAMING_MOVE);
++_moveCount;
if (roll_chance_i((int32)_moveCount * 25 + 10))
{
_moveCount = 0;
_nextMoveTime.Reset(urand(4000, 8000));
}
Movement::MoveSplineInit init(creature);
init.MovebyPath(finalPath);
init.SetWalk(true);
init.Launch();
if (sWorld->getBoolConfig(CONFIG_DONT_CACHE_RANDOM_MOVEMENT_PATHS))
_preComputedPaths.erase(pathIdx);
//Call for creature group update
if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature)
creature->GetFormation()->LeaderMoveTo(finalPoint.x, finalPoint.y, finalPoint.z, false);
}
template<>
void RandomMovementGenerator<Creature>::DoInitialize(Creature* creature)
{
if (!creature->IsAlive())
return;
if (!_wanderDistance)
_wanderDistance = creature->GetRespawnRadius();
_nextMoveTime.Reset(creature->GetDBTableGUIDLow() && creature->GetRespawnRadius() == _wanderDistance ? urand(1, 5000) : 0);
_wanderDistance = std::max((creature->GetRespawnRadius() == _wanderDistance && creature->GetInstanceId() == 0) ? (creature->CanFly() ? MIN_WANDER_DISTANCE_AIR : MIN_WANDER_DISTANCE_GROUND) : 0.0f, _wanderDistance);
if (G3D::fuzzyEq(_initialPosition.GetExactDist2d(0.0f, 0.0f), 0.0f))
{
_initialPosition.Relocate(creature);
_destinationPoints.clear();
for (uint8 i = 0; i < RANDOM_POINTS_NUMBER; ++i)
{
float angle = (M_PI*2.0f/(float)RANDOM_POINTS_NUMBER)*i;
float factor = 0.5f + rand_norm()*0.5f;
_destinationPoints.push_back(G3D::Vector3(_initialPosition.GetPositionX() + _wanderDistance*cos(angle)*factor, _initialPosition.GetPositionY() + _wanderDistance*sin(angle)*factor, _initialPosition.GetPositionZ()));
}
}
if (!_pathGenerator)
_pathGenerator = new PathGenerator(creature);
creature->AddUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE);
}
template<>
void RandomMovementGenerator<Creature>::DoReset(Creature* creature)
{
DoInitialize(creature);
}
template<>
void RandomMovementGenerator<Creature>::DoFinalize(Creature* creature)
{
creature->ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
creature->SetWalk(false);
}
template<>
bool RandomMovementGenerator<Creature>::DoUpdate(Creature* creature, const uint32 diff)
{
if (creature->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
{
_nextMoveTime.Reset(0); // Expire the timer
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
return true;
}
// xinef: if we got disable move flag, do not remove default generator - just prevent movement
if (creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
{
_nextMoveTime.Reset(0); // Expire the timer
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
return true;
}
// prevent movement while casting spells with cast time or channel time
if (creature->HasUnitState(UNIT_STATE_CASTING))
{
bool stop = true;
if (Spell* spell = creature->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
if (!(spell->GetSpellInfo()->ChannelInterruptFlags & (AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_TURNING)) && !(spell->GetSpellInfo()->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT))
stop = false;
if (stop)
{
if (!creature->IsStopped())
creature->StopMoving();
return true;
}
}
if (creature->movespline->Finalized())
{
_nextMoveTime.Update(diff);
if (_nextMoveTime.Passed())
_setRandomLocation(creature);
}
return true;
}
template<>
bool RandomMovementGenerator<Creature>::GetResetPosition(float& x, float& y, float& z)
{
if (_currentPoint < RANDOM_POINTS_NUMBER)
_currDestPosition.GetPosition(x, y, z);
else if (G3D::fuzzyNe(_initialPosition.GetExactDist2d(0.0f, 0.0f), 0.0f)) // if initial position is not 0.0f, 0.0f
_initialPosition.GetPosition(x, y, z);
else
return false;
return true;
}

View File

@@ -0,0 +1,57 @@
/*
REWRITTEN BY XINEF
*/
#ifndef TRINITY_RANDOMMOTIONGENERATOR_H
#define TRINITY_RANDOMMOTIONGENERATOR_H
#include "MovementGenerator.h"
#include "PathGenerator.h"
#define RANDOM_POINTS_NUMBER 12
#define RANDOM_LINKS_COUNT 7
#define MIN_WANDER_DISTANCE_GROUND 6.0f
#define MIN_WANDER_DISTANCE_AIR 10.0f
#define MAX_PATH_LENGHT_FACTOR 1.85f
template<class T>
class RandomMovementGenerator : public MovementGeneratorMedium< T, RandomMovementGenerator<T> >
{
public:
RandomMovementGenerator(float spawnDist = 0.0f) : _nextMoveTime(0), _moveCount(0), _wanderDistance(spawnDist), _pathGenerator(NULL), _currentPoint(RANDOM_POINTS_NUMBER)
{
_initialPosition.Relocate(0.0f, 0.0f, 0.0f, 0.0f);
_destinationPoints.reserve(RANDOM_POINTS_NUMBER);
for (uint8 i = 0; i < RANDOM_POINTS_NUMBER; ++i)
{
_validPointsVector[i].reserve(RANDOM_LINKS_COUNT);
for (uint8 j = 0; j < RANDOM_LINKS_COUNT; ++j)
_validPointsVector[i].push_back((i + j + RANDOM_POINTS_NUMBER/2 - RANDOM_LINKS_COUNT/2) % RANDOM_POINTS_NUMBER);
}
_validPointsVector[RANDOM_POINTS_NUMBER].reserve(RANDOM_POINTS_NUMBER);
for (uint8 i = 0; i < RANDOM_POINTS_NUMBER; ++i)
_validPointsVector[RANDOM_POINTS_NUMBER].push_back(i);
}
void _setRandomLocation(T*);
void DoInitialize(T*);
void DoFinalize(T*);
void DoReset(T*);
bool DoUpdate(T*, const uint32);
bool GetResetPosition(float& x, float& y, float& z);
MovementGeneratorType GetMovementGeneratorType() { return RANDOM_MOTION_TYPE; }
private:
TimeTrackerSmall _nextMoveTime;
uint8 _moveCount;
float _wanderDistance;
PathGenerator* _pathGenerator;
std::vector<G3D::Vector3> _destinationPoints;
std::vector<uint8> _validPointsVector[RANDOM_POINTS_NUMBER+1];
uint8 _currentPoint;
std::map<uint16, Movement::PointsArray> _preComputedPaths;
Position _initialPosition, _currDestPosition;
};
#endif

View File

@@ -0,0 +1,444 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "ByteBuffer.h"
#include "TargetedMovementGenerator.h"
#include "Errors.h"
#include "Creature.h"
#include "CreatureAI.h"
#include "World.h"
#include "MoveSplineInit.h"
#include "MoveSpline.h"
#include "Player.h"
#include "Spell.h"
#include "BattlegroundRV.h"
#include "VehicleDefines.h"
#include "Transport.h"
#include <cmath>
template<class T, typename D>
void TargetedMovementGeneratorMedium<T,D>::_setTargetLocation(T* owner, bool initial)
{
if (!i_target.isValid() || !i_target->IsInWorld() || !owner->IsInMap(i_target.getTarget()))
return;
if (owner->HasUnitState(UNIT_STATE_NOT_MOVE))
return;
float x, y, z;
bool isPlayerPet = owner->IsPet() && IS_PLAYER_GUID(owner->GetOwnerGUID());
bool sameTransport = owner->GetTransport() && owner->GetTransport() == i_target->GetTransport();
if (owner->GetMapId() == 631 && owner->GetTransport() && owner->GetTransport()->IsMotionTransport() && i_target->GetTransport() && i_target->GetTransport()->IsMotionTransport()) // for ICC, if both on a motion transport => don't use mmaps
sameTransport = owner->GetTypeId() == TYPEID_UNIT && i_target->isInAccessiblePlaceFor(owner->ToCreature());
bool useMMaps = MMAP::MMapFactory::IsPathfindingEnabled(owner->FindMap()) && !sameTransport;
bool forceDest = (owner->FindMap() && owner->FindMap()->IsDungeon() && !isPlayerPet) || // force in instances to prevent exploiting
(owner->GetTypeId() == TYPEID_UNIT && ((owner->IsPet() && owner->HasUnitState(UNIT_STATE_FOLLOW)) || // allow pets following their master to cheat while generating paths
((Creature*)owner)->isWorldBoss() || ((Creature*)owner)->IsDungeonBoss())) || // force for all bosses, even not in instances
(owner->GetMapId() == 572 && (owner->GetPositionX() < 1275.0f || i_target->GetPositionX() < 1275.0f)) || // pussywizard: Ruins of Lordaeron - special case (acid)
sameTransport || // nothing to comment, can't find path on transports so allow it
(i_target->GetTypeId() == TYPEID_PLAYER && i_target->ToPlayer()->IsGameMaster()); // for .npc follow
bool forcePoint = ((!isPlayerPet || owner->GetMapId() == 618) && (forceDest || !useMMaps)) || sameTransport;
lastOwnerXYZ.Relocate(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ());
lastTargetXYZ.Relocate(i_target->GetPositionX(), i_target->GetPositionY(), i_target->GetPositionZ());
if (!i_offset)
{
float allowedRange = MELEE_RANGE;
if ((!initial || (owner->movespline->Finalized() && this->GetMovementGeneratorType() == CHASE_MOTION_TYPE)) && i_target->IsWithinMeleeRange(owner, allowedRange) && i_target->IsWithinLOS(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ()))
return;
bool inRange = i_target->GetRandomContactPoint(owner, x, y, z, forcePoint);
if (useMMaps && !inRange && (!isPlayerPet || i_target->GetPositionZ()-z > 50.0f))
{
//useMMaps = false;
owner->m_targetsNotAcceptable[i_target->GetGUID()] = MMapTargetData(sWorld->GetGameTime()+DISALLOW_TIME_AFTER_FAIL, owner, i_target.getTarget());
return;
}
}
else
{
float dist;
float size;
// Pets need special handling.
// We need to subtract GetObjectSize() because it gets added back further down the chain
// and that makes pets too far away. Subtracting it allows pets to properly
// be (GetCombatReach() + i_offset) away.
// Only applies when i_target is pet's owner otherwise pets and mobs end up
// doing a "dance" while fighting
if (owner->IsPet() && i_target->GetTypeId() == TYPEID_PLAYER)
{
dist = i_target->GetCombatReach();
size = i_target->GetCombatReach() - i_target->GetObjectSize();
}
else
{
dist = i_offset;
size = owner->GetObjectSize();
}
if ((!initial || (owner->movespline->Finalized() && this->GetMovementGeneratorType() == CHASE_MOTION_TYPE)) && i_target->IsWithinDistInMap(owner, dist) && i_target->IsWithinLOS(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ()))
return;
// Xinef: Fix follow angle for hostile units
float angle = i_angle;
if (angle == 0.0f && owner->GetVictim() && owner->GetVictim()->GetGUID() == i_target->GetGUID())
angle = MapManager::NormalizeOrientation(i_target->GetAngle(owner)-i_target->GetOrientation());
// to at i_offset distance from target and i_angle from target facing
bool inRange = i_target->GetClosePoint(x, y, z, size, dist, angle, owner, forcePoint);
if (!inRange && (forceDest || !useMMaps) && owner->HasUnitState(UNIT_STATE_FOLLOW) && fabs(i_target->GetPositionZ() - z) > 25.0f)
{
x = i_target->GetPositionX();
y = i_target->GetPositionY();
z = i_target->GetPositionZ();
}
}
D::_addUnitStateMove(owner);
i_targetReached = false;
i_recalculateTravel = false;
Movement::MoveSplineInit init(owner);
if (useMMaps) // pussywizard
{
if (!i_path)
i_path = new PathGenerator(owner);
if (!forceDest)
{
if (owner->GetMapId() == 618) // pussywizard: 618 Ring of Valor
{
if (Map* map = owner->FindMap())
if (Battleground* bg = ((BattlegroundMap*)map)->GetBG())
{
Position dest = {x, y, z, 0.0f};
if (GameObject* pillar = ((BattlegroundRV*)bg)->GetPillarAtPosition(&dest))
{
if (pillar->GetGoState() == GO_STATE_READY && pillar->ToTransport()->GetPathProgress() == 0 || owner->GetPositionZ() > 31.0f || owner->GetTransGUID() == pillar->GetGUID())
{
if (pillar->GetGoState() == GO_STATE_READY && pillar->ToTransport()->GetPathProgress() == 0)
z = std::max(z, 28.28f);
else
z = i_target->GetPositionZ();
init.MoveTo(x,y,z);
if (i_angle == 0.f)
init.SetFacing(i_target.getTarget());
init.SetWalk(((D*)this)->EnableWalking());
init.Launch();
return;
}
if (pillar->GetGoState() == GO_STATE_ACTIVE || pillar->GetGoState() == GO_STATE_READY && pillar->ToTransport()->GetPathProgress() > 0)
{
Position pos;
owner->GetFirstCollisionPositionForTotem(pos, owner->GetExactDist2d(i_target.getTarget()), owner->GetAngle(i_target.getTarget())-owner->GetOrientation(), false);
x = pos.GetPositionX();
y = pos.GetPositionY();
z = 28.28f;
}
}
}
}
}
if (!forceDest && getMSTimeDiff(lastPathingFailMSTime, World::GetGameTimeMS()) < 1000)
{
lastOwnerXYZ.Relocate(-5000.0f, -5000.0f, -5000.0f);
return;
}
bool result = i_path->CalculatePath(x, y, z, forceDest);
if (result)
{
float maxDist = MELEE_RANGE + owner->GetMeleeReach() + i_target->GetMeleeReach();
if (!forceDest && (i_path->GetPathType() & PATHFIND_NOPATH || !i_offset && !isPlayerPet && i_target->GetExactDistSq(i_path->GetActualEndPosition().x, i_path->GetActualEndPosition().y, i_path->GetActualEndPosition().z) > maxDist*maxDist))
{
lastPathingFailMSTime = World::GetGameTimeMS();
owner->m_targetsNotAcceptable[i_target->GetGUID()] = MMapTargetData(sWorld->GetGameTime()+DISALLOW_TIME_AFTER_FAIL, owner, i_target.getTarget());
return;
}
else
{
owner->m_targetsNotAcceptable.erase(i_target->GetGUID());
init.MovebyPath(i_path->GetPath());
if (i_angle == 0.f)
init.SetFacing(i_target.getTarget());
init.SetWalk(((D*)this)->EnableWalking());
init.Launch();
return;
}
}
// if failed to generate, just use normal MoveTo
}
init.MoveTo(x,y,z);
// Using the same condition for facing target as the one that is used for SetInFront on movement end
// - applies to ChaseMovementGenerator mostly
if (i_angle == 0.f)
init.SetFacing(i_target.getTarget());
init.SetWalk(((D*)this)->EnableWalking());
init.Launch();
}
template<class T, typename D>
bool TargetedMovementGeneratorMedium<T,D>::DoUpdate(T* owner, uint32 time_diff)
{
if (!i_target.isValid() || !i_target->IsInWorld())
return false;
if (!owner || !owner->IsAlive())
return false;
if (owner->HasUnitState(UNIT_STATE_NOT_MOVE))
{
D::_clearUnitStateMove(owner);
return true;
}
// prevent movement while casting spells with cast time or channel time
if (owner->HasUnitState(UNIT_STATE_CASTING))
{
bool stop = true;
if (Spell* spell = owner->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
if (!(spell->GetSpellInfo()->ChannelInterruptFlags & (AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_TURNING)) && !(spell->GetSpellInfo()->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT))
stop = false;
if (stop)
{
// Xinef: delay distance recheck in case of succeeding casts
i_recheckDistance.Reset(300);
if (!owner->IsStopped())
owner->StopMoving();
return true;
}
}
// prevent crash after creature killed pet
if (static_cast<D*>(this)->_lostTarget(owner))
{
D::_clearUnitStateMove(owner);
return true;
}
i_recheckDistanceForced.Update(time_diff);
if (i_recheckDistanceForced.Passed())
{
i_recheckDistanceForced.Reset(2500);
lastOwnerXYZ.Relocate(-5000.0f, -5000.0f, -5000.0f);
}
i_recheckDistance.Update(time_diff);
if (i_recheckDistance.Passed())
{
i_recheckDistance.Reset(50);
//More distance let have better performance, less distance let have more sensitive reaction at target move.
float allowed_dist_sq = i_target->GetObjectSize() + owner->GetObjectSize() + MELEE_RANGE - 0.5f;
// xinef: if offset is negative (follow distance is smaller than just object sizes), reduce minimum allowed distance which is based purely on object sizes
if (i_offset < 0.0f)
{
allowed_dist_sq += i_offset;
allowed_dist_sq = std::max<float>(0.0f, allowed_dist_sq);
}
allowed_dist_sq *= allowed_dist_sq;
G3D::Vector3 dest = owner->movespline->FinalDestination();
if (owner->movespline->onTransport)
if (TransportBase* transport = owner->GetDirectTransport())
transport->CalculatePassengerPosition(dest.x, dest.y, dest.z);
float dist = (dest - G3D::Vector3(i_target->GetPositionX(),i_target->GetPositionY(),i_target->GetPositionZ())).squaredLength();
float targetMoveDistSq = i_target->GetExactDistSq(&lastTargetXYZ);
if (dist >= allowed_dist_sq || (!i_offset && targetMoveDistSq >= 1.5f*1.5f))
if (targetMoveDistSq >= 0.1f*0.1f || owner->GetExactDistSq(&lastOwnerXYZ) >= 0.1f*0.1f)
_setTargetLocation(owner, false);
}
if (owner->movespline->Finalized())
{
static_cast<D*>(this)->MovementInform(owner);
if (i_angle == 0.f && !owner->HasInArc(0.01f, i_target.getTarget()))
owner->SetInFront(i_target.getTarget());
if (!i_targetReached)
{
i_targetReached = true;
static_cast<D*>(this)->_reachTarget(owner);
}
}
else
{
if (i_recalculateTravel)
_setTargetLocation(owner, false);
}
return true;
}
//-----------------------------------------------//
template<class T>
void ChaseMovementGenerator<T>::_reachTarget(T* owner)
{
if (owner->IsWithinMeleeRange(this->i_target.getTarget()))
owner->Attack(this->i_target.getTarget(),true);
}
template<>
void ChaseMovementGenerator<Player>::DoInitialize(Player* owner)
{
owner->AddUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
_setTargetLocation(owner, true);
}
template<>
void ChaseMovementGenerator<Creature>::DoInitialize(Creature* owner)
{
owner->SetWalk(false);
owner->AddUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
_setTargetLocation(owner, true);
}
template<class T>
void ChaseMovementGenerator<T>::DoFinalize(T* owner)
{
owner->ClearUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
}
template<class T>
void ChaseMovementGenerator<T>::DoReset(T* owner)
{
DoInitialize(owner);
}
template<class T>
void ChaseMovementGenerator<T>::MovementInform(T* /*unit*/)
{
}
template<>
void ChaseMovementGenerator<Creature>::MovementInform(Creature* unit)
{
// Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
if (unit->AI())
unit->AI()->MovementInform(CHASE_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
}
//-----------------------------------------------//
template<>
bool FollowMovementGenerator<Creature>::EnableWalking() const
{
return i_target.isValid() && (i_target->IsWalking() || i_target->movespline->isWalking());
}
template<>
bool FollowMovementGenerator<Player>::EnableWalking() const
{
return false;
}
template<>
void FollowMovementGenerator<Player>::_updateSpeed(Player* /*owner*/)
{
// nothing to do for Player
}
template<>
void FollowMovementGenerator<Creature>::_updateSpeed(Creature* owner)
{
// pet only sync speed with owner
/// Make sure we are not in the process of a map change (IsInWorld)
if (!IS_PLAYER_GUID(owner->GetOwnerGUID()) || !owner->IsInWorld() || !i_target.isValid() || i_target->GetGUID() != owner->GetOwnerGUID())
return;
owner->UpdateSpeed(MOVE_RUN, true);
owner->UpdateSpeed(MOVE_WALK, true);
owner->UpdateSpeed(MOVE_SWIM, true);
}
template<>
void FollowMovementGenerator<Player>::DoInitialize(Player* owner)
{
owner->AddUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
_updateSpeed(owner);
_setTargetLocation(owner, true);
}
template<>
void FollowMovementGenerator<Creature>::DoInitialize(Creature* owner)
{
owner->AddUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
_updateSpeed(owner);
_setTargetLocation(owner, true);
}
template<class T>
void FollowMovementGenerator<T>::DoFinalize(T* owner)
{
owner->ClearUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
_updateSpeed(owner);
}
template<class T>
void FollowMovementGenerator<T>::DoReset(T* owner)
{
DoInitialize(owner);
}
template<class T>
void FollowMovementGenerator<T>::MovementInform(T* /*unit*/)
{
}
template<>
void FollowMovementGenerator<Creature>::MovementInform(Creature* unit)
{
// Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
if (unit->AI())
unit->AI()->MovementInform(FOLLOW_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
}
//-----------------------------------------------//
template void TargetedMovementGeneratorMedium<Player,ChaseMovementGenerator<Player> >::_setTargetLocation(Player*, bool initial);
template void TargetedMovementGeneratorMedium<Player,FollowMovementGenerator<Player> >::_setTargetLocation(Player*, bool initial);
template void TargetedMovementGeneratorMedium<Creature,ChaseMovementGenerator<Creature> >::_setTargetLocation(Creature*, bool initial);
template void TargetedMovementGeneratorMedium<Creature,FollowMovementGenerator<Creature> >::_setTargetLocation(Creature*, bool initial);
template bool TargetedMovementGeneratorMedium<Player,ChaseMovementGenerator<Player> >::DoUpdate(Player*, uint32);
template bool TargetedMovementGeneratorMedium<Player,FollowMovementGenerator<Player> >::DoUpdate(Player*, uint32);
template bool TargetedMovementGeneratorMedium<Creature,ChaseMovementGenerator<Creature> >::DoUpdate(Creature*, uint32);
template bool TargetedMovementGeneratorMedium<Creature,FollowMovementGenerator<Creature> >::DoUpdate(Creature*, uint32);
template void ChaseMovementGenerator<Player>::_reachTarget(Player*);
template void ChaseMovementGenerator<Creature>::_reachTarget(Creature*);
template void ChaseMovementGenerator<Player>::DoFinalize(Player*);
template void ChaseMovementGenerator<Creature>::DoFinalize(Creature*);
template void ChaseMovementGenerator<Player>::DoReset(Player*);
template void ChaseMovementGenerator<Creature>::DoReset(Creature*);
template void ChaseMovementGenerator<Player>::MovementInform(Player*);
template void FollowMovementGenerator<Player>::DoFinalize(Player*);
template void FollowMovementGenerator<Creature>::DoFinalize(Creature*);
template void FollowMovementGenerator<Player>::DoReset(Player*);
template void FollowMovementGenerator<Creature>::DoReset(Creature*);
template void FollowMovementGenerator<Player>::MovementInform(Player*);

View File

@@ -0,0 +1,121 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_TARGETEDMOVEMENTGENERATOR_H
#define TRINITY_TARGETEDMOVEMENTGENERATOR_H
#include "MovementGenerator.h"
#include "FollowerReference.h"
#include "Timer.h"
#include "Unit.h"
#include "PathGenerator.h"
class TargetedMovementGeneratorBase
{
public:
TargetedMovementGeneratorBase(Unit* target) { i_target.link(target, this); }
void stopFollowing() { }
protected:
FollowerReference i_target;
Position lastOwnerXYZ;
Position lastTargetXYZ;
};
template<class T, typename D>
class TargetedMovementGeneratorMedium : public MovementGeneratorMedium< T, D >, public TargetedMovementGeneratorBase
{
protected:
TargetedMovementGeneratorMedium(Unit* target, float offset, float angle) :
TargetedMovementGeneratorBase(target), i_path(NULL), lastPathingFailMSTime(0),
i_recheckDistance(0), i_recheckDistanceForced(2500), i_offset(offset), i_angle(angle),
i_recalculateTravel(false), i_targetReached(false)
{
}
~TargetedMovementGeneratorMedium() { delete i_path; }
public:
bool DoUpdate(T*, uint32);
Unit* GetTarget() const { return i_target.getTarget(); }
void unitSpeedChanged() { i_recalculateTravel = true; }
bool IsReachable() const { return (i_path) ? (i_path->GetPathType() & PATHFIND_NORMAL) : true; }
protected:
void _setTargetLocation(T* owner, bool initial);
TimeTrackerSmall i_recheckDistance;
TimeTrackerSmall i_recheckDistanceForced;
float i_offset;
float i_angle;
bool i_recalculateTravel : 1;
bool i_targetReached : 1;
PathGenerator* i_path;
uint32 lastPathingFailMSTime;
};
template<class T>
class ChaseMovementGenerator : public TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >
{
public:
ChaseMovementGenerator(Unit* target)
: TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >(target) {}
ChaseMovementGenerator(Unit* target, float offset, float angle)
: TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >(target, offset, angle) {}
~ChaseMovementGenerator() {}
MovementGeneratorType GetMovementGeneratorType() { return CHASE_MOTION_TYPE; }
void DoInitialize(T*);
void DoFinalize(T*);
void DoReset(T*);
void MovementInform(T*);
static void _clearUnitStateMove(T* u) { u->ClearUnitState(UNIT_STATE_CHASE_MOVE); }
static void _addUnitStateMove(T* u) { u->AddUnitState(UNIT_STATE_CHASE_MOVE); }
bool EnableWalking() const { return false;}
bool _lostTarget(T* u) const { return u->GetVictim() != this->GetTarget(); }
void _reachTarget(T*);
};
template<class T>
class FollowMovementGenerator : public TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >
{
public:
FollowMovementGenerator(Unit* target)
: TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >(target){}
FollowMovementGenerator(Unit* target, float offset, float angle)
: TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >(target, offset, angle) {}
~FollowMovementGenerator() {}
MovementGeneratorType GetMovementGeneratorType() { return FOLLOW_MOTION_TYPE; }
void DoInitialize(T*);
void DoFinalize(T*);
void DoReset(T*);
void MovementInform(T*);
static void _clearUnitStateMove(T* u) { u->ClearUnitState(UNIT_STATE_FOLLOW_MOVE); }
static void _addUnitStateMove(T* u) { u->AddUnitState(UNIT_STATE_FOLLOW_MOVE); }
bool EnableWalking() const;
bool _lostTarget(T*) const { return false; }
void _reachTarget(T*) {}
private:
void _updateSpeed(T* owner);
};
#endif

View File

@@ -0,0 +1,495 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
//Basic headers
#include "WaypointMovementGenerator.h"
//Extended headers
#include "ObjectMgr.h"
#include "World.h"
#include "Transport.h"
//Flightmaster grid preloading
#include "MapManager.h"
//Creature-specific headers
#include "Creature.h"
#include "CreatureAI.h"
#include "CreatureGroups.h"
//Player-specific
#include "Player.h"
#include "MoveSplineInit.h"
#include "MoveSpline.h"
#include "Spell.h"
void WaypointMovementGenerator<Creature>::LoadPath(Creature* creature)
{
if (!path_id)
path_id = creature->GetWaypointPath();
i_path = sWaypointMgr->GetPath(path_id);
if (!i_path)
{
// No movement found for entry
sLog->outErrorDb("WaypointMovementGenerator::LoadPath: creature %s (Entry: %u GUID: %u) doesn't have waypoint path id: %u", creature->GetName().c_str(), creature->GetEntry(), creature->GetGUIDLow(), path_id);
return;
}
StartMoveNow(creature);
}
void WaypointMovementGenerator<Creature>::DoInitialize(Creature* creature)
{
LoadPath(creature);
creature->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
}
void WaypointMovementGenerator<Creature>::DoFinalize(Creature* creature)
{
creature->ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
creature->SetWalk(false);
}
void WaypointMovementGenerator<Creature>::DoReset(Creature* creature)
{
creature->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
StartMoveNow(creature);
}
void WaypointMovementGenerator<Creature>::OnArrived(Creature* creature)
{
if (!i_path || i_path->empty())
return;
if (m_isArrivalDone)
return;
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
m_isArrivalDone = true;
if (i_path->at(i_currentNode)->event_id && urand(0, 99) < i_path->at(i_currentNode)->event_chance)
{
;//sLog->outDebug(LOG_FILTER_MAPSCRIPTS, "Creature movement start script %u at point %u for "UI64FMTD".", i_path->at(i_currentNode)->event_id, i_currentNode, creature->GetGUID());
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
creature->GetMap()->ScriptsStart(sWaypointScripts, i_path->at(i_currentNode)->event_id, creature, NULL);
}
// Inform script
MovementInform(creature);
creature->UpdateWaypointID(i_currentNode);
if (i_path->at(i_currentNode)->delay)
{
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
Stop(i_path->at(i_currentNode)->delay);
}
}
bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature)
{
if (!i_path || i_path->empty())
return false;
// Xinef: Dont allow dead creatures to move
if (!creature->IsAlive())
return false;
if (Stopped())
return true;
bool transportPath = creature->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && creature->GetTransGUID();
if (m_isArrivalDone)
{
// Xinef: not true... update this at every waypoint!
//if ((i_currentNode == i_path->size() - 1) && !repeating) // If that's our last waypoint
{
float x = i_path->at(i_currentNode)->x;
float y = i_path->at(i_currentNode)->y;
float z = i_path->at(i_currentNode)->z;
float o = creature->GetOrientation();
if (!transportPath)
creature->SetHomePosition(x, y, z, o);
else
{
if (Transport* trans = (creature->GetTransport() ? creature->GetTransport()->ToMotionTransport() : NULL))
{
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
}
}
// Xinef: moved the upper IF here
if ((i_currentNode == i_path->size() - 1) && !repeating) // If that's our last waypoint
{
creature->GetMotionMaster()->Initialize();
return false;
}
i_currentNode = (i_currentNode+1) % i_path->size();
}
// xinef: do not initialize motion if we got stunned in movementinform
if (creature->HasUnitState(UNIT_STATE_NOT_MOVE))
return true;
WaypointData const* node = i_path->at(i_currentNode);
m_isArrivalDone = false;
creature->AddUnitState(UNIT_STATE_ROAMING_MOVE);
Movement::Location formationDest(node->x, node->y, node->z, 0.0f);
Movement::MoveSplineInit init(creature);
//! 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);
}
//! Do not use formationDest here, MoveTo requires transport offsets due to DisableTransportPathTransformations() call
//! but formationDest contains global coordinates
init.MoveTo(node->x, node->y, node->z);
//! Accepts angles such as 0.00001 and -0.00001, 0 must be ignored, default value in waypoint table
if (node->orientation && node->delay)
init.SetFacing(node->orientation);
switch (node->move_type)
{
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;
}
init.Launch();
//Call for creature group update
if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature)
creature->GetFormation()->LeaderMoveTo(formationDest.x, formationDest.y, formationDest.z, node->move_type == WAYPOINT_MOVE_TYPE_RUN);
return true;
}
bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, uint32 diff)
{
// Waypoint movement can be switched on/off
// This is quite handy for escort quests and other stuff
if (creature->HasUnitState(UNIT_STATE_NOT_MOVE))
{
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
return true;
}
// prevent a crash at empty waypoint path.
if (!i_path || i_path->empty())
return false;
// Xinef: Dont allow dead creatures to move
if (!creature->IsAlive())
return false;
// prevent movement while casting spells with cast time or channel time
if (creature->HasUnitState(UNIT_STATE_CASTING))
{
bool stop = true;
if (Spell* spell = creature->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
if (!(spell->GetSpellInfo()->ChannelInterruptFlags & (AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_TURNING)) && !(spell->GetSpellInfo()->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT))
stop = false;
if (stop)
{
Stop(1000);
if (!creature->IsStopped())
creature->StopMoving();
return true;
}
}
if (Stopped())
{
if (CanMove(diff))
return StartMove(creature);
}
else
{
if (creature->IsStopped())
Stop(STOP_TIME_FOR_PLAYER);
else
{
// xinef: code to detect pre-empetively if we should start movement to next waypoint
// xinef: do not start pre-empetive movement if current node has delay or we are ending waypoint movement
bool finished = creature->movespline->Finalized();
if (!finished && !i_path->at(i_currentNode)->delay && ((i_currentNode != i_path->size() - 1) || repeating))
finished = (creature->movespline->_Spline().length(creature->movespline->_currentSplineIdx()+1) - creature->movespline->timePassed()) < 200;
if (finished)
{
OnArrived(creature);
return StartMove(creature);
}
}
}
return true;
}
void WaypointMovementGenerator<Creature>::MovementInform(Creature* creature)
{
if (creature->AI())
creature->AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode);
}
//----------------------------------------------------//
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::LoadPath(Player* player)
{
_pointsForPathSwitch.clear();
std::vector<uint32> const& taxi = player->m_taxi.GetPath();
for (uint32 src = player->m_taxi.GetTaxiSegment(), dst = player->m_taxi.GetTaxiSegment()+1; dst < taxi.size(); src = dst++)
{
uint32 path, cost;
sObjectMgr->GetTaxiPath(taxi[src], taxi[dst], path, cost);
if (path > sTaxiPathNodesByPath.size())
return;
TaxiPathNodeList const& nodes = sTaxiPathNodesByPath[path];
if (!nodes.empty())
{
TaxiPathNodeEntry const* start = nodes[0];
TaxiPathNodeEntry const* end = nodes[nodes.size() - 1];
for (uint32 i = 0; i < nodes.size(); ++i)
i_path.push_back(nodes[i]);
}
_pointsForPathSwitch.push_back(uint32(i_path.size() - 1));
}
}
void FlightPathMovementGenerator::DoInitialize(Player* player)
{
Reset(player);
InitEndGridInfo();
}
void FlightPathMovementGenerator::DoFinalize(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_STATE_IN_FLIGHT);
// xinef: this should be cleaned by CleanupAfterTaxiFlight(); function!
player->Dismount();
player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
if (player->m_taxi.empty())
{
player->getHostileRefManager().setOnlineOfflineState(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();
player->SetFallInformation(time(NULL), player->GetPositionZ());
}
player->RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_TAXI_BENCHMARK);
}
#define PLAYER_FLIGHT_SPEED 32.0f
void FlightPathMovementGenerator::DoReset(Player* player)
{
player->getHostileRefManager().setOnlineOfflineState(false);
player->AddUnitState(UNIT_STATE_IN_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::DoUpdate(Player* player, uint32 /*diff*/)
{
// xinef: map was switched
if (_mapSwitch)
{
DoInitialize(player);
_mapSwitch = false;
return true;
}
uint32 pointId = (uint32)player->movespline->currentPathIdx();
if (pointId > i_currentNode)
{
bool departureEvent = true;
do
{
if (i_currentNode >= i_path.size())
{
sLog->outMisc("TAXI NODE WAS GREATER THAN PATH SIZE, GUID: %u, POINTID: %u, NODESIZE: %u, CURRENT: %u", player->GetGUIDLow(), pointId, i_path.size(), i_currentNode);
player->CleanupAfterTaxiFlight();
return false;
}
if (i_path[i_currentNode]->mapid != player->GetMapId())
{
sLog->outMisc("Player on different map, curmap: %u, pointmap: %u, nodesize: %u, currentnode: %u", player->GetMapId(), i_path[i_currentNode]->mapid, i_path.size(), i_currentNode);
player->CleanupAfterTaxiFlight();
return false;
}
DoEventIfAny(player, i_path[i_currentNode], departureEvent);
// xinef: erase any previous points
uint32 curSize = _pointsForPathSwitch.size();
while (!_pointsForPathSwitch.empty() && _pointsForPathSwitch.front() <= i_currentNode)
_pointsForPathSwitch.pop_front();
// xinef: switch destination only once
if (curSize != _pointsForPathSwitch.size())
player->m_taxi.NextTaxiDestination();
if (pointId == i_currentNode)
break;
if (i_currentNode == _preloadTargetNode && player->GetMapId() == _endMapId)
PreloadEndGrid();
i_currentNode += (uint32)departureEvent;
departureEvent = !departureEvent;
// xinef: map should be switched, do not rely on client packets QQ
if (i_currentNode + 1 < i_path.size() && i_path[i_currentNode+1]->mapid != player->GetMapId())
{
++i_currentNode;
_mapSwitch = true;
player->TeleportTo(i_path[i_currentNode]->mapid, i_path[i_currentNode]->x, i_path[i_currentNode]->y, i_path[i_currentNode]->z, player->GetOrientation(), TELE_TO_NOT_LEAVE_TAXI);
return true;
}
// xinef: reached the end
if (i_currentNode >= i_path.size() - 1)
{
player->CleanupAfterTaxiFlight();
player->SetFallInformation(time(NULL), player->GetPositionZ());
if (player->pvpInfo.IsHostile)
player->CastSpell(player, 2479, true);
return false;
}
}
while (true);
}
return i_currentNode < (i_path.size() - 1);
}
void FlightPathMovementGenerator::SetCurrentNodeAfterTeleport()
{
if (i_path.empty() || i_currentNode >= i_path.size())
return;
uint32 map0 = i_path[i_currentNode]->mapid;
for (size_t i = i_currentNode + 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)
{
;//sLog->outDebug(LOG_FILTER_MAPSCRIPTS, "Taxi %s event %u of node %u of path %u for player %s", departure ? "departure" : "arrival", eventid, node.index, node.path, player.GetName().c_str());
player->GetMap()->ScriptsStart(sEventScripts, eventid, player, player);
}
}
void FlightPathMovementGenerator::InitEndGridInfo()
{
/*! Storage to preload flightmaster grid at end of flight. For multi-stop flights, this will
be reinitialized for each flightmaster at the end of each spline (or stop) in the flight. */
uint32 nodeCount = i_path.size(); //! Number of nodes in path.
_endMapId = i_path[nodeCount - 1]->mapid; //! MapId of last node
// pussywizard:
{
_preloadTargetNode = nodeCount - 1;
for (uint8 i = 3; i > 0; --i)
if (nodeCount >= i && _endMapId == i_path[nodeCount - i]->mapid)
{
_preloadTargetNode = nodeCount - i;
break;
}
//_preloadTargetNode = nodeCount - 3; // pussywizard: this can be on other map
}
_endGridX = i_path[nodeCount - 1]->x;
_endGridY = i_path[nodeCount - 1]->y;
}
void FlightPathMovementGenerator::PreloadEndGrid()
{
// used to preload the final grid where the flightmaster is
Map* endMap = sMapMgr->FindBaseNonInstanceMap(_endMapId);
// Load the grid
if (endMap)
{
;//sLog->outDetail("Preloading rid (%f, %f) for map %u at node index %u/%u", _endGridX, _endGridY, _endMapId, _preloadTargetNode, (uint32)(i_path->size()-1));
endMap->LoadGrid(_endGridX, _endGridY);
}
else
;//sLog->outDetail("Unable to determine map to preload flightmaster grid");
}

View File

@@ -0,0 +1,144 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_WAYPOINTMOVEMENTGENERATOR_H
#define TRINITY_WAYPOINTMOVEMENTGENERATOR_H
/** @page PathMovementGenerator is used to generate movements
* of waypoints and flight paths. Each serves the purpose
* of generate activities so that it generates updated
* packets for the players.
*/
#include "MovementGenerator.h"
#include "WaypointManager.h"
#include "Player.h"
#define FLIGHT_TRAVEL_UPDATE 100
#define STOP_TIME_FOR_PLAYER 2 * MINUTE * IN_MILLISECONDS // 3 Minutes
#define TIMEDIFF_NEXT_WP 250
template<class T, class P>
class PathMovementBase
{
public:
PathMovementBase() : i_path(), i_currentNode(0) {}
PathMovementBase(P path) : i_path(path), i_currentNode(0) {}
virtual ~PathMovementBase() {};
uint32 GetCurrentNode() const { return i_currentNode; }
protected:
P i_path;
uint32 i_currentNode;
};
template<class T>
class WaypointMovementGenerator;
template<>
class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium< Creature, WaypointMovementGenerator<Creature> >,
public PathMovementBase<Creature, WaypointPath const*>
{
public:
WaypointMovementGenerator(uint32 _path_id = 0, bool _repeating = true)
: PathMovementBase((WaypointPath const*)NULL), i_nextMoveTime(0), m_isArrivalDone(false), path_id(_path_id), repeating(_repeating) {}
~WaypointMovementGenerator() { i_path = NULL; }
void DoInitialize(Creature*);
void DoFinalize(Creature*);
void DoReset(Creature*);
bool DoUpdate(Creature*, uint32 diff);
void MovementInform(Creature*);
MovementGeneratorType GetMovementGeneratorType() { return WAYPOINT_MOTION_TYPE; }
// now path movement implmementation
void LoadPath(Creature*);
private:
void Stop(int32 time) { i_nextMoveTime.Reset(time);}
bool Stopped() { return !i_nextMoveTime.Passed();}
bool CanMove(int32 diff)
{
i_nextMoveTime.Update(diff);
return i_nextMoveTime.Passed();
}
void OnArrived(Creature*);
bool StartMove(Creature*);
void StartMoveNow(Creature* creature)
{
i_nextMoveTime.Reset(0);
StartMove(creature);
}
TimeTrackerSmall i_nextMoveTime;
bool m_isArrivalDone;
uint32 path_id;
bool repeating;
};
/** FlightPathMovementGenerator generates movement of the player for the paths
* and hence generates ground and activities for the player.
*/
class FlightPathMovementGenerator : public MovementGeneratorMedium< Player, FlightPathMovementGenerator >,
public PathMovementBase<Player, TaxiPathNodeList>
{
public:
explicit FlightPathMovementGenerator(uint32 startNode = 0)
{
i_currentNode = startNode;
_endGridX = 0.0f;
_endGridY = 0.0f;
_endMapId = 0;
_preloadTargetNode = 0;
_mapSwitch = false;
}
void LoadPath(Player* player);
void DoInitialize(Player*);
void DoReset(Player*);
void DoFinalize(Player*);
bool DoUpdate(Player*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return FLIGHT_MOTION_TYPE; }
TaxiPathNodeList const& GetPath() { return i_path; }
uint32 GetPathAtMapEnd() const;
bool HasArrived() const { return (i_currentNode >= i_path.size()); }
void SetCurrentNodeAfterTeleport();
void SkipCurrentNode() { ++i_currentNode; }
void DoEventIfAny(Player* player, TaxiPathNodeEntry const* node, bool departure);
void InitEndGridInfo();
void PreloadEndGrid();
private:
float _endGridX; //! X coord of last node location
float _endGridY; //! Y coord of last node location
uint32 _endMapId; //! map Id of last node location
uint32 _preloadTargetNode; //! node index where preloading starts
bool _mapSwitch;
std::deque<uint32> _pointsForPathSwitch; //! node indexes and costs where TaxiPath changes
};
#endif

View File

@@ -0,0 +1,318 @@
/*
* Copyright (C)
*
* 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
*/
#include "MoveSpline.h"
#include <sstream>
#include "Log.h"
#include "Creature.h"
namespace Movement{
Location MoveSpline::ComputePosition() const
{
ASSERT(Initialized());
float u = 1.f;
int32 seg_time = spline.length(point_Idx, point_Idx+1);
if (seg_time > 0)
u = (time_passed - spline.length(point_Idx)) / (float)seg_time;
Location c;
c.orientation = initialOrientation;
spline.evaluate_percent(point_Idx, u, c);
if (splineflags.animation)
;// MoveSplineFlag::Animation disables falling or parabolic movement
else if (splineflags.parabolic)
computeParabolicElevation(c.z);
else if (splineflags.falling)
computeFallElevation(c.z);
if (splineflags.done && splineflags.isFacing())
{
if (splineflags.final_angle)
c.orientation = facing.angle;
else if (splineflags.final_point)
c.orientation = atan2(facing.f.y - c.y, facing.f.x - c.x);
//nothing to do for MoveSplineFlag::Final_Target flag
}
else
{
if (!splineflags.hasFlag(MoveSplineFlag::OrientationFixed | MoveSplineFlag::Falling))
{
Vector3 hermite;
spline.evaluate_derivative(point_Idx, u, hermite);
c.orientation = atan2(hermite.y, hermite.x);
}
if (splineflags.orientationInversed)
c.orientation = -c.orientation;
}
return c;
}
void MoveSpline::computeParabolicElevation(float& el) const
{
if (time_passed > effect_start_time)
{
float t_passedf = MSToSec(time_passed - effect_start_time);
float t_durationf = MSToSec(Duration() - effect_start_time); //client use not modified duration here
// -a*x*x + bx + c:
//(dur * v3->z_acceleration * dt)/2 - (v3->z_acceleration * dt * dt)/2 + Z;
el += (t_durationf - t_passedf) * 0.5f * vertical_acceleration * t_passedf;
}
}
void MoveSpline::computeFallElevation(float& el) const
{
float z_now = spline.getPoint(spline.first(), false).z - Movement::computeFallElevation(MSToSec(time_passed), false);
float final_z = FinalDestination().z;
el = std::max(z_now, final_z);
}
inline uint32 computeDuration(float length, float velocity)
{
return SecToMS(length / velocity);
}
struct FallInitializer
{
FallInitializer(float _start_elevation) : start_elevation(_start_elevation) {}
float start_elevation;
inline int32 operator()(Spline<int32>& s, int32 i)
{
return Movement::computeFallTime(start_elevation - s.getPoint(i+1, false).z, false) * 1000.f;
}
};
enum{
minimal_duration = 1
};
struct CommonInitializer
{
CommonInitializer(float _velocity) : velocityInv(1000.f/_velocity), time(minimal_duration) {}
float velocityInv;
int32 time;
inline int32 operator()(Spline<int32>& s, int32 i)
{
time += (s.SegLength(i) * velocityInv);
return time;
}
};
void MoveSpline::init_spline(const MoveSplineInitArgs& args)
{
const SplineBase::EvaluationMode modes[2] = {SplineBase::ModeLinear, SplineBase::ModeCatmullrom};
if (args.flags.cyclic)
{
uint32 cyclic_point = 0;
// MoveSplineFlag::Enter_Cycle support dropped
//if (splineflags & SPLINEFLAG_ENTER_CYCLE)
//cyclic_point = 1; // shouldn't be modified, came from client
spline.init_cyclic_spline(&args.path[0], args.path.size(), modes[args.flags.isSmooth()], cyclic_point);
}
else
{
spline.init_spline(&args.path[0], args.path.size(), modes[args.flags.isSmooth()]);
}
// init spline timestamps
if (splineflags.falling)
{
FallInitializer init(spline.getPoint(spline.first(), false).z);
spline.initLengths(init);
}
else
{
CommonInitializer init(args.velocity);
spline.initLengths(init);
}
// TODO: what to do in such cases? problem is in input data (all points are at same coords)
if (spline.length() < minimal_duration)
{
//sLog->outError("MoveSpline::init_spline: zero length spline, wrong input data?"); // ZOMG! temp comment to avoid console spam from transports
spline.set_length(spline.last(), spline.isCyclic() ? 1000 : 1);
}
point_Idx = spline.first();
}
void MoveSpline::Initialize(MoveSplineInitArgs const& args)
{
splineflags = args.flags;
facing = args.facing;
m_Id = args.splineId;
point_Idx_offset = args.path_Idx_offset;
initialOrientation = args.initialOrientation;
time_passed = 0;
vertical_acceleration = 0.f;
effect_start_time = 0;
// Check if its a stop spline
if (args.flags.done)
{
spline.clear();
return;
}
init_spline(args);
// init parabolic / animation
// spline initialized, duration known and i able to compute parabolic acceleration
if (args.flags & (MoveSplineFlag::Parabolic | MoveSplineFlag::Animation))
{
effect_start_time = Duration() * args.time_perc;
if (args.flags.parabolic && effect_start_time < Duration())
{
float f_duration = MSToSec(Duration() - effect_start_time);
vertical_acceleration = args.parabolic_amplitude * 8.f / (f_duration * f_duration);
}
}
}
MoveSpline::MoveSpline() : m_Id(0), time_passed(0),
vertical_acceleration(0.f), initialOrientation(0.f), effect_start_time(0), point_Idx(0), point_Idx_offset(0),
onTransport(false)
{
splineflags.done = true;
}
/// ============================================================================================
bool MoveSplineInitArgs::Validate(Unit* unit) const
{
#define CHECK(exp) \
if (!(exp))\
{\
sLog->outError("MoveSplineInitArgs::Validate: expression '%s' failed for GUID: %u Entry: %u", #exp, unit->GetTypeId() == TYPEID_PLAYER ? unit->GetGUIDLow() : unit->ToCreature()->GetDBTableGUIDLow(), unit->GetEntry());\
return false;\
}
CHECK(path.size() > 1);
CHECK(velocity > 0.1f);
CHECK(time_perc >= 0.f && time_perc <= 1.f);
//CHECK(_checkPathBounds());
return true;
#undef CHECK
}
// MONSTER_MOVE packet format limitation for not CatmullRom movement:
// each vertex offset packed into 11 bytes
bool MoveSplineInitArgs::_checkPathBounds() const
{
if (!(flags & MoveSplineFlag::Mask_CatmullRom) && path.size() > 2)
{
enum{
MAX_OFFSET = (1 << 11) / 2
};
Vector3 middle = (path.front()+path.back()) / 2;
Vector3 offset;
for (uint32 i = 1; i < path.size()-1; ++i)
{
offset = path[i] - middle;
if (fabs(offset.x) >= MAX_OFFSET || fabs(offset.y) >= MAX_OFFSET || fabs(offset.z) >= MAX_OFFSET)
{
sLog->outError("MoveSplineInitArgs::_checkPathBounds check failed");
return false;
}
}
}
return true;
}
/// ============================================================================================
MoveSpline::UpdateResult MoveSpline::_updateState(int32& ms_time_diff)
{
if (Finalized())
{
ms_time_diff = 0;
return Result_Arrived;
}
UpdateResult result = Result_None;
int32 minimal_diff = std::min(ms_time_diff, segment_time_elapsed());
ASSERT(minimal_diff >= 0);
time_passed += minimal_diff;
ms_time_diff -= minimal_diff;
if (time_passed >= next_timestamp())
{
++point_Idx;
if (point_Idx < spline.last())
{
result = Result_NextSegment;
}
else
{
if (spline.isCyclic())
{
point_Idx = spline.first();
time_passed = time_passed % Duration();
result = Movement::MoveSpline::UpdateResult(Result_NextCycle | Result_JustArrived);
}
else
{
_Finalize();
ms_time_diff = 0;
result = Movement::MoveSpline::UpdateResult(Result_Arrived | Result_JustArrived);
}
}
}
return result;
}
std::string MoveSpline::ToString() const
{
std::stringstream str;
str << "MoveSpline" << std::endl;
str << "spline Id: " << GetId() << std::endl;
str << "flags: " << splineflags.ToString() << std::endl;
if (splineflags.final_angle)
str << "facing angle: " << facing.angle;
else if (splineflags.final_target)
str << "facing target: " << facing.target;
else if (splineflags.final_point)
str << "facing point: " << facing.f.x << " " << facing.f.y << " " << facing.f.z;
str << std::endl;
str << "time passed: " << time_passed << std::endl;
str << "total time: " << Duration() << std::endl;
str << "spline point Id: " << point_Idx << std::endl;
str << "path point Id: " << currentPathIdx() << std::endl;
str << spline.ToString();
return str.str();
}
void MoveSpline::_Finalize()
{
splineflags.done = true;
point_Idx = spline.last() - 1;
time_passed = Duration();
}
int32 MoveSpline::currentPathIdx() const
{
int32 point = point_Idx_offset + point_Idx - spline.first() + (int)Finalized();
if (isCyclic())
point = point % (spline.last()-spline.first());
return point;
}
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright (C)
*
* 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
*/
#ifndef TRINITYSERVER_MOVEPLINE_H
#define TRINITYSERVER_MOVEPLINE_H
#include "Spline.h"
#include "MoveSplineInitArgs.h"
namespace Movement
{
struct Location : public Vector3
{
Location() : orientation(0) {}
Location(float x, float y, float z, float o) : Vector3(x, y, z), orientation(o) {}
Location(const Vector3& v) : Vector3(v), orientation(0) {}
Location(const Vector3& v, float o) : Vector3(v), orientation(o) {}
float orientation;
};
// MoveSpline represents smooth catmullrom or linear curve and point that moves belong it
// curve can be cyclic - in this case movement will be cyclic
// point can have vertical acceleration motion componemt(used in fall, parabolic movement)
class MoveSpline
{
public:
typedef Spline<int32> MySpline;
enum UpdateResult
{
Result_None = 0x01,
Result_Arrived = 0x02,
Result_NextCycle = 0x04,
Result_NextSegment = 0x08,
Result_JustArrived = 0x10,
};
friend class PacketBuilder;
protected:
MySpline spline;
FacingInfo facing;
uint32 m_Id;
MoveSplineFlag splineflags;
int32 time_passed;
// currently duration mods are unused, but its _currently_
//float duration_mod;
//float duration_mod_next;
float vertical_acceleration;
float initialOrientation;
int32 effect_start_time;
int32 point_Idx;
int32 point_Idx_offset;
void init_spline(const MoveSplineInitArgs& args);
protected:
const MySpline::ControlArray& getPath(bool visual) const { return spline.getPoints(visual); }
void computeParabolicElevation(float& el) const;
void computeFallElevation(float& el) const;
UpdateResult _updateState(int32& ms_time_diff);
int32 next_timestamp() const { return spline.length(point_Idx + 1); }
int32 segment_time_elapsed() const { return next_timestamp() - time_passed; }
public:
int32 timeElapsed() const { return Duration() - time_passed; } // xinef: moved to public for waypoint movegen
int32 timePassed() const { return time_passed; } // xinef: moved to public for waypoint movegen
int32 Duration() const { return spline.length(); }
MySpline const& _Spline() const { return spline; }
int32 _currentSplineIdx() const { return point_Idx; }
void _Finalize();
void _Interrupt() { splineflags.done = true; }
public:
void Initialize(const MoveSplineInitArgs&);
bool Initialized() const { return !spline.empty(); }
MoveSpline();
template<class UpdateHandler>
void updateState(int32 difftime, UpdateHandler& handler)
{
ASSERT(Initialized());
do
handler(_updateState(difftime));
while (difftime > 0);
}
void updateState(int32 difftime)
{
ASSERT(Initialized());
do _updateState(difftime);
while (difftime > 0);
}
Location ComputePosition() const;
uint32 GetId() const { return m_Id; }
bool Finalized() const { return splineflags.done; }
bool isCyclic() const { return splineflags.cyclic; }
bool isFalling() const { return splineflags.falling; }
bool isWalking() const { return splineflags.walkmode; }
Vector3 FinalDestination() const { return Initialized() ? spline.getPoint(spline.last(), false) : Vector3(); }
Vector3 CurrentDestination() const { return Initialized() ? spline.getPoint(point_Idx + 1, false) : Vector3(); }
int32 currentPathIdx() const;
bool onTransport;
std::string ToString() const;
};
}
#endif // TRINITYSERVER_MOVEPLINE_H

View File

@@ -0,0 +1,146 @@
/*
* Copyright (C)
*
* 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
*/
#ifndef TRINITYSERVER_MOVESPLINEFLAG_H
#define TRINITYSERVER_MOVESPLINEFLAG_H
#include "MovementTypedefs.h"
#include <string>
namespace Movement
{
#if defined( __GNUC__ )
#pragma pack(1)
#else
#pragma pack(push, 1)
#endif
class MoveSplineFlag
{
public:
enum eFlags
{
None = 0x00000000,
// x00-xFF(first byte) used as animation Ids storage in pair with Animation flag
Done = 0x00000100,
Falling = 0x00000200, // Affects elevation computation, can't be combined with Parabolic flag
No_Spline = 0x00000400,
Parabolic = 0x00000800, // Affects elevation computation, can't be combined with Falling flag
Walkmode = 0x00001000,
Flying = 0x00002000, // Smooth movement(Catmullrom interpolation mode), flying animation
OrientationFixed = 0x00004000, // Model orientation fixed
Final_Point = 0x00008000,
Final_Target = 0x00010000,
Final_Angle = 0x00020000,
Catmullrom = 0x00040000, // Used Catmullrom interpolation mode
Cyclic = 0x00080000, // Movement by cycled spline
Enter_Cycle = 0x00100000, // Everytimes appears with cyclic flag in monster move packet, erases first spline vertex after first cycle done
Animation = 0x00200000, // Plays animation after some time passed
Frozen = 0x00400000, // Will never arrive
TransportEnter = 0x00800000,
TransportExit = 0x01000000,
Unknown7 = 0x02000000,
Unknown8 = 0x04000000,
OrientationInversed = 0x08000000,
Unknown10 = 0x10000000,
Unknown11 = 0x20000000,
Unknown12 = 0x40000000,
Unknown13 = 0x80000000,
// Masks
Mask_Final_Facing = Final_Point | Final_Target | Final_Angle,
// animation ids stored here, see AnimType enum, used with Animation flag
Mask_Animations = 0xFF,
// flags that shouldn't be appended into SMSG_MONSTER_MOVE\SMSG_MONSTER_MOVE_TRANSPORT packet, should be more probably
Mask_No_Monster_Move = Mask_Final_Facing | Mask_Animations | Done,
// CatmullRom interpolation mode used
Mask_CatmullRom = Flying | Catmullrom,
// Unused, not suported flags
Mask_Unused = No_Spline|Enter_Cycle|Frozen|Unknown7|Unknown8|Unknown10|Unknown11|Unknown12|Unknown13
};
inline uint32& raw() { return (uint32&)*this; }
inline const uint32& raw() const { return (const uint32&)*this; }
MoveSplineFlag() { raw() = 0; }
MoveSplineFlag(uint32 f) { raw() = f; }
MoveSplineFlag(const MoveSplineFlag& f) { raw() = f.raw(); }
// Constant interface
bool isSmooth() const { return raw() & Mask_CatmullRom; }
bool isLinear() const { return !isSmooth(); }
bool isFacing() const { return raw() & Mask_Final_Facing; }
uint8 getAnimationId() const { return animId; }
bool hasAllFlags(uint32 f) const { return (raw() & f) == f; }
bool hasFlag(uint32 f) const { return (raw() & f) != 0; }
uint32 operator & (uint32 f) const { return (raw() & f); }
uint32 operator | (uint32 f) const { return (raw() | f); }
std::string ToString() const;
// Not constant interface
void operator &= (uint32 f) { raw() &= f; }
void operator |= (uint32 f) { raw() |= f; }
void EnableAnimation(uint8 anim) { raw() = (raw() & ~(Mask_Animations | Falling | Parabolic)) | Animation | anim; }
void EnableParabolic() { raw() = (raw() & ~(Mask_Animations | Falling | Animation)) | Parabolic; }
void EnableFalling() { raw() = (raw() & ~(Mask_Animations | Parabolic | Flying | Animation)) | Falling; }
void EnableFlying() { raw() = (raw() & ~(Falling | Catmullrom)) | Flying; }
void EnableCatmullRom() { raw() = (raw() & ~Flying) | Catmullrom; }
void EnableFacingPoint() { raw() = (raw() & ~Mask_Final_Facing) | Final_Point; }
void EnableFacingAngle() { raw() = (raw() & ~Mask_Final_Facing) | Final_Angle; }
void EnableFacingTarget() { raw() = (raw() & ~Mask_Final_Facing) | Final_Target; }
void EnableTransportEnter() { raw() = (raw() & ~TransportExit) | TransportEnter; }
void EnableTransportExit() { raw() = (raw() & ~TransportEnter) | TransportExit; }
uint8 animId : 8;
bool done : 1;
bool falling : 1;
bool no_spline : 1;
bool parabolic : 1;
bool walkmode : 1;
bool flying : 1;
bool orientationFixed : 1;
bool final_point : 1;
bool final_target : 1;
bool final_angle : 1;
bool catmullrom : 1;
bool cyclic : 1;
bool enter_cycle : 1;
bool animation : 1;
bool frozen : 1;
bool transportEnter : 1;
bool transportExit : 1;
bool unknown7 : 1;
bool unknown8 : 1;
bool orientationInversed : 1;
bool unknown10 : 1;
bool unknown11 : 1;
bool unknown12 : 1;
bool unknown13 : 1;
};
#if defined( __GNUC__ )
#pragma pack()
#else
#pragma pack(pop)
#endif
}
#endif // TRINITYSERVER_MOVESPLINEFLAG_H

View File

@@ -0,0 +1,242 @@
/*
* Copyright (C)
*
* 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
*/
#include "MoveSplineInit.h"
#include "MoveSpline.h"
#include "MovementPacketBuilder.h"
#include "Unit.h"
#include "Transport.h"
#include "Vehicle.h"
#include "WorldPacket.h"
#include "Opcodes.h"
namespace Movement
{
UnitMoveType SelectSpeedType(uint32 moveFlags)
{
if (moveFlags & MOVEMENTFLAG_FLYING)
{
if (moveFlags & MOVEMENTFLAG_BACKWARD /*&& speed_obj.flight >= speed_obj.flight_back*/)
return MOVE_FLIGHT_BACK;
else
return MOVE_FLIGHT;
}
else if (moveFlags & MOVEMENTFLAG_SWIMMING)
{
if (moveFlags & MOVEMENTFLAG_BACKWARD /*&& speed_obj.swim >= speed_obj.swim_back*/)
return MOVE_SWIM_BACK;
else
return MOVE_SWIM;
}
else if (moveFlags & MOVEMENTFLAG_WALKING)
{
//if (speed_obj.run > speed_obj.walk)
return MOVE_WALK;
}
else if (moveFlags & MOVEMENTFLAG_BACKWARD /*&& speed_obj.run >= speed_obj.run_back*/)
return MOVE_RUN_BACK;
// Flying creatures use MOVEMENTFLAG_CAN_FLY or MOVEMENTFLAG_DISABLE_GRAVITY
// Run speed is their default flight speed.
return MOVE_RUN;
}
int32 MoveSplineInit::Launch()
{
MoveSpline& move_spline = *unit->movespline;
bool transport = unit->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && unit->GetTransGUID();
Location real_position;
// there is a big chance that current position is unknown if current state is not finalized, need compute it
// this also allows CalculatePath spline position and update map position in much greater intervals
// Don't compute for transport movement if the unit is in a motion between two transports
if (!move_spline.Finalized() && move_spline.onTransport == transport)
real_position = move_spline.ComputePosition();
else
{
Position const* pos;
if (!transport)
pos = unit;
else
pos = &unit->m_movementInfo.transport.pos;
real_position.x = pos->GetPositionX();
real_position.y = pos->GetPositionY();
real_position.z = pos->GetPositionZ();
real_position.orientation = unit->GetOrientation();
}
// should i do the things that user should do? - no.
if (args.path.empty())
return 0;
// corrent first vertex
args.path[0] = real_position;
args.initialOrientation = real_position.orientation;
move_spline.onTransport = transport;
uint32 moveFlags = unit->m_movementInfo.GetMovementFlags();
moveFlags |= (MOVEMENTFLAG_SPLINE_ENABLED|MOVEMENTFLAG_FORWARD);
if (moveFlags & MOVEMENTFLAG_ROOT)
moveFlags &= ~MOVEMENTFLAG_MASK_MOVING;
if (!args.HasVelocity)
{
// If spline is initialized with SetWalk method it only means we need to select
// walk move speed for it but not add walk flag to unit
uint32 moveFlagsForSpeed = moveFlags;
if (args.flags.walkmode)
moveFlagsForSpeed |= MOVEMENTFLAG_WALKING;
else
moveFlagsForSpeed &= ~MOVEMENTFLAG_WALKING;
args.velocity = unit->GetSpeed(SelectSpeedType(moveFlagsForSpeed));
}
if (!args.Validate(unit))
return 0;
unit->m_movementInfo.SetMovementFlags(moveFlags);
move_spline.Initialize(args);
WorldPacket data(SMSG_MONSTER_MOVE, 64);
data.append(unit->GetPackGUID());
if (transport)
{
data.SetOpcode(SMSG_MONSTER_MOVE_TRANSPORT);
data.appendPackGUID(unit->GetTransGUID());
data << int8(unit->GetTransSeat());
}
Movement::SplineBase::ControlArray* visualPoints = const_cast<Movement::SplineBase::ControlArray*>(move_spline._Spline().allocateVisualPoints());
visualPoints->resize(move_spline._Spline().getPointCount());
// Xinef: Apply hover in creature movement packet
if (unit->IsHovering())
std::transform(move_spline._Spline().getPoints(false).begin(), move_spline._Spline().getPoints(false).end(), visualPoints->begin(), HoverMovementTransform(unit->GetHoverHeight()));
else
std::copy(move_spline._Spline().getPoints(false).begin(), move_spline._Spline().getPoints(false).end(), visualPoints->begin());
PacketBuilder::WriteMonsterMove(move_spline, data);
unit->SendMessageToSet(&data,true);
return move_spline.Duration();
}
void MoveSplineInit::Stop()
{
MoveSpline& move_spline = *unit->movespline;
// No need to stop if we are not moving
if (move_spline.Finalized())
return;
bool transport = unit->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && unit->GetTransGUID();
Location loc;
if (move_spline.onTransport == transport)
loc = move_spline.ComputePosition();
else
{
Position const* pos;
if (!transport)
pos = unit;
else
pos = &unit->m_movementInfo.transport.pos;
loc.x = pos->GetPositionX();
loc.y = pos->GetPositionY();
loc.z = pos->GetPositionZ();
loc.orientation = unit->GetOrientation();
}
args.flags = MoveSplineFlag::Done;
unit->m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_SPLINE_ENABLED);
move_spline.onTransport = transport;
move_spline.Initialize(args);
WorldPacket data(SMSG_MONSTER_MOVE, 64);
data.append(unit->GetPackGUID());
if (transport)
{
data.SetOpcode(SMSG_MONSTER_MOVE_TRANSPORT);
data.appendPackGUID(unit->GetTransGUID());
data << int8(unit->GetTransSeat());
}
// Xinef: increase z position in packet
loc.z += unit->GetHoverHeight();
PacketBuilder::WriteStopMovement(loc, args.splineId, data);
unit->SendMessageToSet(&data, true);
}
MoveSplineInit::MoveSplineInit(Unit* m) : unit(m)
{
args.splineId = splineIdGen.NewId();
args.TransformForTransport = unit->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && unit->GetTransGUID();
// mix existing state into new
args.flags.walkmode = unit->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_WALKING);
args.flags.flying = unit->m_movementInfo.HasMovementFlag((MovementFlags)(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_DISABLE_GRAVITY));
}
void MoveSplineInit::SetFacing(const Unit* target)
{
args.flags.EnableFacingTarget();
args.facing.target = target->GetGUID();
}
void MoveSplineInit::SetFacing(float angle)
{
if (args.TransformForTransport)
{
if (Unit* vehicle = unit->GetVehicleBase())
angle -= vehicle->GetOrientation();
else if (Transport* transport = unit->GetTransport())
angle -= transport->GetOrientation();
}
args.facing.angle = G3D::wrap(angle, 0.f, (float)G3D::twoPi());
args.flags.EnableFacingAngle();
}
void MoveSplineInit::MoveTo(const Vector3& dest, bool generatePath, bool forceDestination)
{
if (generatePath)
{
PathGenerator path(unit);
bool result = path.CalculatePath(dest.x, dest.y, dest.z, forceDestination);
if (result && !(path.GetPathType() & PATHFIND_NOPATH))
{
MovebyPath(path.GetPath());
return;
}
}
args.path_Idx_offset = 0;
args.path.resize(2);
TransportPathTransform transform(unit, args.TransformForTransport);
args.path[1] = transform(dest);
}
Vector3 TransportPathTransform::operator()(Vector3 input)
{
if (_transformForTransport)
if (TransportBase* transport = _owner->GetDirectTransport())
transport->CalculatePassengerOffset(input.x, input.y, input.z);
return input;
}
}

View File

@@ -0,0 +1,214 @@
/*
* Copyright (C)
*
* 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
*/
#ifndef TRINITYSERVER_MOVESPLINEINIT_H
#define TRINITYSERVER_MOVESPLINEINIT_H
#include "MoveSplineInitArgs.h"
#include "PathGenerator.h"
class Unit;
namespace Movement
{
// xinef: moved declaration here so it can be accessed out of MoveSplineInit.cpp
UnitMoveType SelectSpeedType(uint32 moveFlags);
enum AnimType
{
ToGround = 0, // 460 = ToGround, index of AnimationData.dbc
FlyToFly = 1, // 461 = FlyToFly?
ToFly = 2, // 458 = ToFly
FlyToGround = 3 // 463 = FlyToGround
};
// Transforms coordinates from global to transport offsets
class TransportPathTransform
{
public:
TransportPathTransform(Unit* owner, bool transformForTransport)
: _owner(owner), _transformForTransport(transformForTransport) { }
Vector3 operator()(Vector3 input);
private:
Unit* _owner;
bool _transformForTransport;
};
// Xinef: transforms z coordinate with hover offset
class HoverMovementTransform
{
public:
HoverMovementTransform(float z_offset) : _offset(z_offset) { }
Vector3 operator()(Vector3 input)
{
input.z += _offset;
return input;
}
private:
float _offset;
};
/* Initializes and launches spline movement
*/
class MoveSplineInit
{
public:
explicit MoveSplineInit(Unit* m);
/* Final pass of initialization that launches spline movement.
*/
int32 Launch();
/* Final pass of initialization that stops movement.
*/
void Stop();
/* Adds movement by parabolic trajectory
* @param amplitude - the maximum height of parabola, value could be negative and positive
* @param start_time - delay between movement starting time and beginning to move by parabolic trajectory
* can't be combined with final animation
*/
void SetParabolic(float amplitude, float start_time);
/* Plays animation after movement done
* can't be combined with parabolic movement
*/
void SetAnimation(AnimType anim);
/* Adds final facing animation
* sets unit's facing to specified point/angle after all path done
* you can have only one final facing: previous will be overriden
*/
void SetFacing(float angle);
void SetFacing(Vector3 const& point);
void SetFacing(const Unit* target);
/* Initializes movement by path
* @param path - array of points, shouldn't be empty
* @param pointId - Id of fisrt point of the path. Example: when third path point will be done it will notify that pointId + 3 done
*/
void MovebyPath(const PointsArray& path, int32 pointId = 0);
/* Initializes simple A to B motion, A is current unit's position, B is destination
*/
void MoveTo(const Vector3& destination, bool generatePath = false, bool forceDestination = false);
void MoveTo(float x, float y, float z, bool generatePath = false, bool forceDestination = false);
/* Sets Id of fisrt point of the path. When N-th path point will be done ILisener will notify that pointId + N done
* Needed for waypoint movement where path splitten into parts
*/
void SetFirstPointId(int32 pointId) { args.path_Idx_offset = pointId; }
/* Enables CatmullRom spline interpolation mode(makes path smooth)
* if not enabled linear spline mode will be choosen. Disabled by default
*/
void SetSmooth();
/* Enables CatmullRom spline interpolation mode, enables flying animation. Disabled by default
*/
void SetFly();
/* Enables walk mode. Disabled by default
*/
void SetWalk(bool enable);
/* Makes movement cyclic. Disabled by default
*/
void SetCyclic();
/* Enables falling mode. Disabled by default
*/
void SetFall();
/* Enters transport. Disabled by default
*/
void SetTransportEnter();
/* Exits transport. Disabled by default
*/
void SetTransportExit();
/* Inverses unit model orientation. Disabled by default
*/
void SetOrientationInversed();
/* Fixes unit's model rotation. Disabled by default
*/
void SetOrientationFixed(bool enable);
/* Sets the velocity (in case you want to have custom movement velocity)
* if no set, speed will be selected based on unit's speeds and current movement mode
* Has no effect if falling mode enabled
* velocity shouldn't be negative
*/
void SetVelocity(float velocity);
PointsArray& Path() { return args.path; }
/* Disables transport coordinate transformations for cases where raw offsets are available
*/
void DisableTransportPathTransformations();
protected:
MoveSplineInitArgs args;
Unit* unit;
};
inline void MoveSplineInit::SetFly() { args.flags.EnableFlying(); }
inline void MoveSplineInit::SetWalk(bool enable) { args.flags.walkmode = enable; }
inline void MoveSplineInit::SetSmooth() { args.flags.EnableCatmullRom(); }
inline void MoveSplineInit::SetCyclic() { args.flags.cyclic = true; }
inline void MoveSplineInit::SetFall() { args.flags.EnableFalling(); }
inline void MoveSplineInit::SetVelocity(float vel) { args.velocity = vel; args.HasVelocity = true; }
inline void MoveSplineInit::SetOrientationInversed() { args.flags.orientationInversed = true;}
inline void MoveSplineInit::SetTransportEnter() { args.flags.EnableTransportEnter(); }
inline void MoveSplineInit::SetTransportExit() { args.flags.EnableTransportExit(); }
inline void MoveSplineInit::SetOrientationFixed(bool enable) { args.flags.orientationFixed = enable; }
inline void MoveSplineInit::MovebyPath(const PointsArray& controls, int32 path_offset)
{
args.path_Idx_offset = path_offset;
args.path.resize(controls.size());
std::transform(controls.begin(), controls.end(), args.path.begin(), TransportPathTransform(unit, args.TransformForTransport));
}
inline void MoveSplineInit::MoveTo(float x, float y, float z, bool generatePath, bool forceDestination)
{
MoveTo(G3D::Vector3(x, y, z), generatePath, forceDestination);
}
inline void MoveSplineInit::SetParabolic(float amplitude, float time_shift)
{
args.time_perc = time_shift;
args.parabolic_amplitude = amplitude;
args.flags.EnableParabolic();
}
inline void MoveSplineInit::SetAnimation(AnimType anim)
{
args.time_perc = 0.f;
args.flags.EnableAnimation((uint8)anim);
}
inline void MoveSplineInit::SetFacing(Vector3 const& spot)
{
TransportPathTransform transform(unit, args.TransformForTransport);
Vector3 finalSpot = transform(spot);
args.facing.f.x = finalSpot.x;
args.facing.f.y = finalSpot.y;
args.facing.f.z = finalSpot.z;
args.flags.EnableFacingPoint();
}
inline void MoveSplineInit::DisableTransportPathTransformations() { args.TransformForTransport = false; }
}
#endif // TRINITYSERVER_MOVESPLINEINIT_H

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C)
*
* 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
*/
#ifndef TRINITYSERVER_MOVESPLINEINIT_ARGS_H
#define TRINITYSERVER_MOVESPLINEINIT_ARGS_H
#include "MoveSplineFlag.h"
#include <G3D/Vector3.h>
class Unit;
namespace Movement
{
typedef std::vector<Vector3> PointsArray;
union FacingInfo
{
struct {
float x, y, z;
} f;
uint64 target;
float angle;
FacingInfo(float o) : angle(o) {}
FacingInfo(uint64 t) : target(t) {}
FacingInfo() {}
};
struct MoveSplineInitArgs
{
MoveSplineInitArgs(size_t path_capacity = 16) : path_Idx_offset(0), velocity(0.f),
parabolic_amplitude(0.f), time_perc(0.f), splineId(0), initialOrientation(0.f),
HasVelocity(false), TransformForTransport(true)
{
path.reserve(path_capacity);
}
PointsArray path;
FacingInfo facing;
MoveSplineFlag flags;
int32 path_Idx_offset;
float velocity;
float parabolic_amplitude;
float time_perc;
uint32 splineId;
float initialOrientation;
bool HasVelocity;
bool TransformForTransport;
/** Returns true to show that the arguments were configured correctly and MoveSpline initialization will succeed. */
bool Validate(Unit* unit) const;
private:
bool _checkPathBounds() const;
};
}
#endif // TRINITYSERVER_MOVESPLINEINIT_ARGS_H

View File

@@ -0,0 +1,201 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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
*/
#include "MovementPacketBuilder.h"
#include "MoveSpline.h"
#include "ByteBuffer.h"
namespace Movement
{
inline void operator << (ByteBuffer& b, const Vector3& v)
{
b << v.x << v.y << v.z;
}
inline void operator >> (ByteBuffer& b, Vector3& v)
{
b >> v.x >> v.y >> v.z;
}
enum MonsterMoveType
{
MonsterMoveNormal = 0,
MonsterMoveStop = 1,
MonsterMoveFacingSpot = 2,
MonsterMoveFacingTarget = 3,
MonsterMoveFacingAngle = 4
};
void PacketBuilder::WriteCommonMonsterMovePart(const MoveSpline& move_spline, ByteBuffer& data)
{
MoveSplineFlag splineflags = move_spline.splineflags;
data << uint8(0); // sets/unsets MOVEMENTFLAG2_UNK7 (0x40)
data << move_spline.spline.getPoint(move_spline.spline.first(), true);
data << move_spline.GetId();
switch (splineflags & MoveSplineFlag::Mask_Final_Facing)
{
case MoveSplineFlag::Final_Target:
data << uint8(MonsterMoveFacingTarget);
data << move_spline.facing.target;
break;
case MoveSplineFlag::Final_Angle:
data << uint8(MonsterMoveFacingAngle);
data << move_spline.facing.angle;
break;
case MoveSplineFlag::Final_Point:
data << uint8(MonsterMoveFacingSpot);
data << move_spline.facing.f.x << move_spline.facing.f.y << move_spline.facing.f.z;
break;
default:
data << uint8(MonsterMoveNormal);
break;
}
// add fake Enter_Cycle flag - needed for client-side cyclic movement (client will erase first spline vertex after first cycle done)
// Xinef: this flag breaks cycle for ground movement, client teleports npc between last and first point instead of using smooth movement
if (splineflags & MoveSplineFlag::Flying)
splineflags.enter_cycle = move_spline.isCyclic();
data << uint32(splineflags & uint32(~MoveSplineFlag::Mask_No_Monster_Move));
if (splineflags.animation)
{
data << splineflags.getAnimationId();
data << move_spline.effect_start_time;
}
data << move_spline.Duration();
if (splineflags.parabolic)
{
data << move_spline.vertical_acceleration;
data << move_spline.effect_start_time;
}
}
void PacketBuilder::WriteStopMovement(Vector3 const& pos, uint32 splineId, ByteBuffer& data)
{
data << uint8(0); // sets/unsets MOVEMENTFLAG2_UNK7 (0x40)
data << pos;
data << splineId;
data << uint8(MonsterMoveStop);
}
void WriteLinearPath(const Spline<int32>& spline, ByteBuffer& data)
{
uint32 last_idx = spline.getPointCount() - 3;
const Vector3 * real_path = &spline.getPoint(1, true);
data << last_idx;
data << real_path[last_idx]; // destination
if (last_idx > 1)
{
Vector3 middle = (real_path[0] + real_path[last_idx]) / 2.f;
Vector3 offset;
// first and last points already appended
for (uint32 i = 1; i < last_idx; ++i)
{
offset = middle - real_path[i];
data.appendPackXYZ(offset.x, offset.y, offset.z);
}
}
}
void WriteCatmullRomPath(const Spline<int32>& spline, ByteBuffer& data)
{
uint32 count = spline.getPointCount() - 3;
data << count;
data.append<Vector3>(&spline.getPoint(2, true), count);
}
void WriteCatmullRomCyclicPath(const Spline<int32>& spline, ByteBuffer& data, bool flying)
{
uint32 count = spline.getPointCount() - 3;
data << uint32(count+1);
if (flying)
{
data << spline.getPoint(1, true); // fake point, client will erase it from the spline after first cycle done
data.append<Vector3>(&spline.getPoint(2, true), count);
}
else
{
data.append<Vector3>(&spline.getPoint(2, true), count);
data << Vector3::zero(); //Xinef: fake point
}
}
void PacketBuilder::WriteMonsterMove(const MoveSpline& move_spline, ByteBuffer& data)
{
WriteCommonMonsterMovePart(move_spline, data);
const Spline<int32>& spline = move_spline.spline;
MoveSplineFlag splineflags = move_spline.splineflags;
if (splineflags & MoveSplineFlag::Mask_CatmullRom)
{
if (splineflags.cyclic)
WriteCatmullRomCyclicPath(spline, data, splineflags & MoveSplineFlag::Flying);
else
WriteCatmullRomPath(spline, data);
}
else
WriteLinearPath(spline, data);
}
void PacketBuilder::WriteCreate(const MoveSpline& move_spline, ByteBuffer& data)
{
//WriteClientStatus(mov, data);
//data.append<float>(&mov.m_float_values[SpeedWalk], SpeedMaxCount);
//if (mov.SplineEnabled())
{
MoveSplineFlag const& splineFlags = move_spline.splineflags;
data << splineFlags.raw();
if (splineFlags.final_angle)
{
data << move_spline.facing.angle;
}
else if (splineFlags.final_target)
{
data << move_spline.facing.target;
}
else if (splineFlags.final_point)
{
data << move_spline.facing.f.x << move_spline.facing.f.y << move_spline.facing.f.z;
}
data << move_spline.timePassed();
data << move_spline.Duration();
data << move_spline.GetId();
data << float(1.f); // splineInfo.duration_mod; added in 3.1
data << float(1.f); // splineInfo.duration_mod_next; added in 3.1
data << move_spline.vertical_acceleration; // added in 3.1
data << move_spline.effect_start_time; // added in 3.1
uint32 nodes = move_spline.getPath(true).size();
data << nodes;
data.append<Vector3>(&move_spline.getPath(true)[0], nodes);
data << uint8(move_spline.spline.mode()); // added in 3.1
data << (move_spline.isCyclic() ? Vector3::zero() : move_spline.FinalDestination());
}
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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
*/
#ifndef TRINITYSERVER_PACKET_BUILDER_H
#define TRINITYSERVER_PACKET_BUILDER_H
#include "Define.h"
class ByteBuffer;
namespace G3D
{
class Vector3;
}
namespace Movement
{
using G3D::Vector3;
class MoveSpline;
class PacketBuilder
{
static void WriteCommonMonsterMovePart(const MoveSpline& mov, ByteBuffer& data);
public:
static void WriteMonsterMove(const MoveSpline& mov, ByteBuffer& data);
static void WriteStopMovement(Vector3 const& loc, uint32 splineId, ByteBuffer& data);
static void WriteCreate(const MoveSpline& mov, ByteBuffer& data);
};
}
#endif // TRINITYSERVER_PACKET_BUILDER_H

View File

@@ -0,0 +1,84 @@
/*
* Copyright (C)
*
* 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
*/
#ifndef TRINITYSERVER_TYPEDEFS_H
#define TRINITYSERVER_TYPEDEFS_H
#include "Common.h"
namespace G3D
{
class Vector2;
class Vector3;
class Vector4;
}
namespace Movement
{
using G3D::Vector2;
using G3D::Vector3;
using G3D::Vector4;
inline uint32 SecToMS(float sec)
{
return static_cast<uint32>(sec * 1000.f);
}
inline float MSToSec(uint32 ms)
{
return ms / 1000.f;
}
float computeFallTime(float path_length, bool isSafeFall);
float computeFallElevation(float t_passed, bool isSafeFall, float start_velocity = 0.0f);
#ifndef static_assert
#define CONCAT(x, y) CONCAT1 (x, y)
#define CONCAT1(x, y) x##y
#define static_assert(expr, msg) typedef char CONCAT(static_assert_failed_at_line_, __LINE__) [(expr) ? 1 : -1]
#endif
template<class T, T limit>
class counter
{
public:
counter() { init(); }
void Increase()
{
if (m_counter == limit)
init();
else
++m_counter;
}
T NewId() { Increase(); return m_counter; }
T getCurrent() const { return m_counter; }
private:
void init() { m_counter = 0; }
T m_counter;
};
typedef counter<uint32, 0xFFFFFFFF> UInt32Counter;
extern double gravity;
extern UInt32Counter splineIdGen;
}
#endif // TRINITYSERVER_TYPEDEFS_H

View File

@@ -0,0 +1,195 @@
/*
* Copyright (C)
*
* 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
*/
#include "MoveSplineFlag.h"
#include <math.h>
#include <string>
namespace Movement
{
double gravity = 19.29110527038574;
UInt32Counter splineIdGen;
/// Velocity bounds that makes fall speed limited
float terminalVelocity = 60.148003f;
float terminalSafefallVelocity = 7.0f;
const float terminal_length = float(terminalVelocity * terminalVelocity) / (2.0f * gravity);
const float terminal_safeFall_length = (terminalSafefallVelocity * terminalSafefallVelocity) / (2.0f * gravity);
const float terminal_fallTime = float(terminalVelocity / gravity); // the time that needed to reach terminalVelocity
const float terminal_safeFall_fallTime = float(terminalSafefallVelocity / gravity); // the time that needed to reach terminalVelocity with safefall
float computeFallTime(float path_length, bool isSafeFall)
{
if (path_length < 0.0f)
return 0.0f;
float time;
if (isSafeFall)
{
if (path_length >= terminal_safeFall_length)
time = (path_length - terminal_safeFall_length) / terminalSafefallVelocity + terminal_safeFall_fallTime;
else
time = sqrtf(2.0f * path_length / gravity);
}
else
{
if (path_length >= terminal_length)
time = (path_length - terminal_length) / terminalVelocity + terminal_fallTime;
else
time = sqrtf(2.0f * path_length / gravity);
}
return time;
}
float computeFallElevation(float t_passed, bool isSafeFall, float start_velocity /*= 0.0f*/)
{
float termVel;
float result;
if (isSafeFall)
termVel = terminalSafefallVelocity;
else
termVel = terminalVelocity;
if (start_velocity > termVel)
start_velocity = termVel;
float terminal_time = (isSafeFall ? terminal_safeFall_fallTime : terminal_fallTime) - start_velocity / gravity; // the time that needed to reach terminalVelocity
if (t_passed > terminal_time)
{
result = termVel * (t_passed - terminal_time) +
start_velocity * terminal_time +
gravity * terminal_time * terminal_time*0.5f;
}
else
result = t_passed * (start_velocity + t_passed * gravity * 0.5f);
return result;
}
#define STR(x) #x
char const* g_MovementFlag_names[] =
{
STR(Forward ), // 0x00000001,
STR(Backward ), // 0x00000002,
STR(Strafe_Left ), // 0x00000004,
STR(Strafe_Right ), // 0x00000008,
STR(Turn_Left ), // 0x00000010,
STR(Turn_Right ), // 0x00000020,
STR(Pitch_Up ), // 0x00000040,
STR(Pitch_Down ), // 0x00000080,
STR(Walk ), // 0x00000100, // Walking
STR(Ontransport ), // 0x00000200,
STR(Levitation ), // 0x00000400,
STR(Root ), // 0x00000800,
STR(Falling ), // 0x00001000,
STR(Fallingfar ), // 0x00002000,
STR(Pendingstop ), // 0x00004000,
STR(PendingSTRafestop ), // 0x00008000,
STR(Pendingforward ), // 0x00010000,
STR(Pendingbackward ), // 0x00020000,
STR(PendingSTRafeleft ), // 0x00040000,
STR(PendingSTRaferight ), // 0x00080000,
STR(Pendingroot ), // 0x00100000,
STR(Swimming ), // 0x00200000, // Appears With Fly Flag Also
STR(Ascending ), // 0x00400000, // Swim Up Also
STR(Descending ), // 0x00800000, // Swim Down Also
STR(Can_Fly ), // 0x01000000, // Can Fly In 3.3?
STR(Flying ), // 0x02000000, // Actual Flying Mode
STR(Spline_Elevation ), // 0x04000000, // Used For Flight Paths
STR(Spline_Enabled ), // 0x08000000, // Used For Flight Paths
STR(Waterwalking ), // 0x10000000, // Prevent Unit From Falling Through Water
STR(Safe_Fall ), // 0x20000000, // Active Rogue Safe Fall Spell (Passive)
STR(Hover ), // 0x40000000
STR(Unknown13 ), // 0x80000000
STR(Unk1 ),
STR(Unk2 ),
STR(Unk3 ),
STR(Fullspeedturning ),
STR(Fullspeedpitching ),
STR(Allow_Pitching ),
STR(Unk4 ),
STR(Unk5 ),
STR(Unk6 ),
STR(Unk7 ),
STR(Interp_Move ),
STR(Interp_Turning ),
STR(Interp_Pitching ),
STR(Unk8 ),
STR(Unk9 ),
STR(Unk10 ),
};
char const* g_SplineFlag_names[32] =
{
STR(AnimBit1 ), // 0x00000001,
STR(AnimBit2 ), // 0x00000002,
STR(AnimBit3 ), // 0x00000004,
STR(AnimBit4 ), // 0x00000008,
STR(AnimBit5 ), // 0x00000010,
STR(AnimBit6 ), // 0x00000020,
STR(AnimBit7 ), // 0x00000040,
STR(AnimBit8 ), // 0x00000080,
STR(Done ), // 0x00000100,
STR(Falling ), // 0x00000200, // Not Compartible With Trajectory Movement
STR(No_Spline ), // 0x00000400,
STR(Trajectory ), // 0x00000800, // Not Compartible With Fall Movement
STR(Walkmode ), // 0x00001000,
STR(Flying ), // 0x00002000, // Smooth Movement(Catmullrom Interpolation Mode), Flying Animation
STR(Knockback ), // 0x00004000, // Model Orientation Fixed
STR(Final_Point ), // 0x00008000,
STR(Final_Target ), // 0x00010000,
STR(Final_Angle ), // 0x00020000,
STR(Catmullrom ), // 0x00040000, // Used Catmullrom Interpolation Mode
STR(Cyclic ), // 0x00080000, // Movement By Cycled Spline
STR(Enter_Cycle ), // 0x00100000, // Everytime Appears With Cyclic Flag In Monster Move Packet
STR(Animation ), // 0x00200000, // Animationid (0...3), Uint32 Time, Not Compartible With Trajectory And Fall Movement
STR(Unknown4 ), // 0x00400000, // Disables Movement By Path
STR(Unknown5 ), // 0x00800000,
STR(Unknown6 ), // 0x01000000,
STR(Unknown7 ), // 0x02000000,
STR(Unknown8 ), // 0x04000000,
STR(OrientationInversed ), // 0x08000000, // Appears With Runmode Flag, Nodes ), // 1, Handles Orientation
STR(Unknown10 ), // 0x10000000,
STR(Unknown11 ), // 0x20000000,
STR(Unknown12 ), // 0x40000000,
STR(Unknown13 ), // 0x80000000,
};
template<class Flags, int N>
void print_flags(Flags t, char const* (&names)[N], std::string& str)
{
for (int i = 0; i < N; ++i)
{
if ((t & Flags(1 << i)) && names[i] != NULL)
str.append(" ").append(names[i]);
}
}
std::string MoveSplineFlag::ToString() const
{
std::string str;
print_flags(raw(), g_SplineFlag_names, str);
return str;
}
}

View File

@@ -0,0 +1,306 @@
/*
* Copyright (C)
*
* 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
*/
#include "Spline.h"
#include <sstream>
#include <G3D/Matrix4.h>
namespace Movement{
SplineBase::EvaluationMethtod SplineBase::evaluators[SplineBase::ModesEnd] =
{
&SplineBase::EvaluateLinear,
&SplineBase::EvaluateCatmullRom,
&SplineBase::EvaluateBezier3,
(EvaluationMethtod)&SplineBase::UninitializedSpline,
};
SplineBase::EvaluationMethtod SplineBase::derivative_evaluators[SplineBase::ModesEnd] =
{
&SplineBase::EvaluateDerivativeLinear,
&SplineBase::EvaluateDerivativeCatmullRom,
&SplineBase::EvaluateDerivativeBezier3,
(EvaluationMethtod)&SplineBase::UninitializedSpline,
};
SplineBase::SegLenghtMethtod SplineBase::seglengths[SplineBase::ModesEnd] =
{
&SplineBase::SegLengthLinear,
&SplineBase::SegLengthCatmullRom,
&SplineBase::SegLengthBezier3,
(SegLenghtMethtod)&SplineBase::UninitializedSpline,
};
SplineBase::InitMethtod SplineBase::initializers[SplineBase::ModesEnd] =
{
//&SplineBase::InitLinear,
&SplineBase::InitCatmullRom, // we should use catmullrom initializer even for linear mode! (client's internal structure limitation)
&SplineBase::InitCatmullRom,
&SplineBase::InitBezier3,
(InitMethtod)&SplineBase::UninitializedSpline,
};
///////////
using G3D::Matrix4;
static const Matrix4 s_catmullRomCoeffs(
-0.5f, 1.5f, -1.5f, 0.5f,
1.f, -2.5f, 2.f, -0.5f,
-0.5f, 0.f, 0.5f, 0.f,
0.f, 1.f, 0.f, 0.f);
static const Matrix4 s_Bezier3Coeffs(
-1.f, 3.f, -3.f, 1.f,
3.f, -6.f, 3.f, 0.f,
-3.f, 3.f, 0.f, 0.f,
1.f, 0.f, 0.f, 0.f);
/* classic view:
inline void C_Evaluate(const Vector3 *vertice, float t, const float (&matrix)[4][4], Vector3 &position)
{
Vector3 tvec(t*t*t, t*t, t);
int i = 0;
double c;
double x = 0, y = 0, z = 0;
while ( i < 4 )
{
c = matrix[0][i]*tvec.x + matrix[1][i]*tvec.y + matrix[2][i]*tvec.z + matrix[3][i];
x += c * vertice->x;
y += c * vertice->y;
z += c * vertice->z;
++i;
++vertice;
}
position.x = x;
position.y = y;
position.z = z;
}*/
inline void C_Evaluate(const Vector3 *vertice, float t, const Matrix4& matr, Vector3 &result)
{
Vector4 tvec(t*t*t, t*t, t, 1.f);
Vector4 weights(tvec * matr);
result = vertice[0] * weights[0] + vertice[1] * weights[1]
+ vertice[2] * weights[2] + vertice[3] * weights[3];
}
inline void C_Evaluate_Derivative(const Vector3 *vertice, float t, const Matrix4& matr, Vector3 &result)
{
Vector4 tvec(3.f*t*t, 2.f*t, 1.f, 0.f);
Vector4 weights(tvec * matr);
result = vertice[0] * weights[0] + vertice[1] * weights[1]
+ vertice[2] * weights[2] + vertice[3] * weights[3];
}
void SplineBase::EvaluateLinear(index_type index, float u, Vector3& result) const
{
ASSERT(index >= index_lo && index < index_hi);
result = points[index] + (points[index+1] - points[index]) * u;
}
void SplineBase::EvaluateCatmullRom( index_type index, float t, Vector3& result) const
{
ASSERT(index >= index_lo && index < index_hi);
C_Evaluate(&points[index - 1], t, s_catmullRomCoeffs, result);
}
void SplineBase::EvaluateBezier3(index_type index, float t, Vector3& result) const
{
index *= 3u;
ASSERT(index >= index_lo && index < index_hi);
C_Evaluate(&points[index], t, s_Bezier3Coeffs, result);
}
void SplineBase::EvaluateDerivativeLinear(index_type index, float, Vector3& result) const
{
ASSERT(index >= index_lo && index < index_hi);
result = points[index+1] - points[index];
}
void SplineBase::EvaluateDerivativeCatmullRom(index_type index, float t, Vector3& result) const
{
ASSERT(index >= index_lo && index < index_hi);
C_Evaluate_Derivative(&points[index - 1], t, s_catmullRomCoeffs, result);
}
void SplineBase::EvaluateDerivativeBezier3(index_type index, float t, Vector3& result) const
{
index *= 3u;
ASSERT(index >= index_lo && index < index_hi);
C_Evaluate_Derivative(&points[index], t, s_Bezier3Coeffs, result);
}
float SplineBase::SegLengthLinear(index_type index) const
{
ASSERT(index >= index_lo && index < index_hi);
return (points[index] - points[index+1]).length();
}
float SplineBase::SegLengthCatmullRom(index_type index) const
{
ASSERT(index >= index_lo && index < index_hi);
Vector3 curPos, nextPos;
const Vector3 * p = &points[index - 1];
curPos = nextPos = p[1];
index_type i = 1;
double length = 0;
while (i <= STEPS_PER_SEGMENT)
{
C_Evaluate(p, float(i) / float(STEPS_PER_SEGMENT), s_catmullRomCoeffs, nextPos);
length += (nextPos - curPos).length();
curPos = nextPos;
++i;
}
return length;
}
float SplineBase::SegLengthBezier3(index_type index) const
{
index *= 3u;
ASSERT(index >= index_lo && index < index_hi);
Vector3 curPos, nextPos;
const Vector3 * p = &points[index];
C_Evaluate(p, 0.f, s_Bezier3Coeffs, nextPos);
curPos = nextPos;
index_type i = 1;
double length = 0;
while (i <= STEPS_PER_SEGMENT)
{
C_Evaluate(p, float(i) / float(STEPS_PER_SEGMENT), s_Bezier3Coeffs, nextPos);
length += (nextPos - curPos).length();
curPos = nextPos;
++i;
}
return length;
}
void SplineBase::init_spline(const Vector3 * controls, index_type count, EvaluationMode m)
{
m_mode = m;
cyclic = false;
(this->*initializers[m_mode])(controls, count, cyclic, 0);
}
void SplineBase::init_cyclic_spline(const Vector3 * controls, index_type count, EvaluationMode m, index_type cyclic_point)
{
m_mode = m;
cyclic = true;
(this->*initializers[m_mode])(controls, count, cyclic, cyclic_point);
}
void SplineBase::InitLinear(const Vector3* controls, index_type count, bool cyclic, index_type cyclic_point)
{
ASSERT(count >= 2);
const int real_size = count + 1;
points.resize(real_size);
memcpy(&points[0], controls, sizeof(Vector3) * count);
// first and last two indexes are space for special 'virtual points'
// these points are required for proper C_Evaluate and C_Evaluate_Derivative methtod work
if (cyclic)
points[count] = controls[cyclic_point];
else
points[count] = controls[count-1];
index_lo = 0;
index_hi = cyclic ? count : (count - 1);
}
void SplineBase::InitCatmullRom(const Vector3* controls, index_type count, bool cyclic, index_type cyclic_point)
{
const int real_size = count + (cyclic ? (1+2) : (1+1));
points.resize(real_size);
int lo_index = 1;
int high_index = lo_index + count - 1;
memcpy(&points[lo_index], controls, sizeof(Vector3) * count);
// first and last two indexes are space for special 'virtual points'
// these points are required for proper C_Evaluate and C_Evaluate_Derivative methtod work
if (cyclic)
{
if (cyclic_point == 0)
points[0] = controls[count-1];
else
points[0] = controls[0].lerp(controls[1], -1);
points[high_index+1] = controls[cyclic_point];
points[high_index+2] = controls[cyclic_point+1];
}
else
{
points[0] = controls[0].lerp(controls[1], -1);
points[high_index+1] = controls[count-1];
}
index_lo = lo_index;
index_hi = high_index + (cyclic ? 1 : 0);
}
void SplineBase::InitBezier3(const Vector3* controls, index_type count, bool /*cyclic*/, index_type /*cyclic_point*/)
{
index_type c = count / 3u * 3u;
index_type t = c / 3u;
points.resize(c);
memcpy(&points[0], controls, sizeof(Vector3) * c);
index_lo = 0;
index_hi = t-1;
//mov_assert(points.size() % 3 == 0);
}
void SplineBase::clear()
{
index_lo = 0;
index_hi = 0;
points.clear();
pointsVisual.clear();
}
std::string SplineBase::ToString() const
{
std::stringstream str;
const char * mode_str[ModesEnd] = {"Linear", "CatmullRom", "Bezier3", "Uninitialized"};
index_type count = this->points.size();
str << "mode: " << mode_str[mode()] << std::endl;
str << "points count: " << count << std::endl;
for (index_type i = 0; i < count; ++i)
str << "point " << i << " : " << points[i].toString() << std::endl;
return str.str();
}
}

View File

@@ -0,0 +1,215 @@
/*
* Copyright (C)
*
* 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
*/
#ifndef TRINITYSERVER_SPLINE_H
#define TRINITYSERVER_SPLINE_H
#include "MovementTypedefs.h"
#include <G3D/Vector3.h>
#include <limits>
namespace Movement {
class SplineBase
{
public:
typedef int index_type;
typedef std::vector<Vector3> ControlArray;
enum EvaluationMode
{
ModeLinear,
ModeCatmullrom,
ModeBezier3_Unused,
UninitializedMode,
ModesEnd
};
protected:
ControlArray points;
ControlArray pointsVisual;
index_type index_lo;
index_type index_hi;
uint8 m_mode;
bool cyclic;
enum{
// could be modified, affects segment length evaluation precision
// lesser value saves more performance in cost of lover precision
// minimal value is 1
// client's value is 20, blizzs use 2-3 steps to compute length
STEPS_PER_SEGMENT = 3
};
static_assert(STEPS_PER_SEGMENT > 0, "shouldn't be lesser than 1");
protected:
void EvaluateLinear(index_type, float, Vector3&) const;
void EvaluateCatmullRom(index_type, float, Vector3&) const;
void EvaluateBezier3(index_type, float, Vector3&) const;
typedef void (SplineBase::*EvaluationMethtod)(index_type, float, Vector3&) const;
static EvaluationMethtod evaluators[ModesEnd];
void EvaluateDerivativeLinear(index_type, float, Vector3&) const;
void EvaluateDerivativeCatmullRom(index_type, float, Vector3&) const;
void EvaluateDerivativeBezier3(index_type, float, Vector3&) const;
static EvaluationMethtod derivative_evaluators[ModesEnd];
float SegLengthLinear(index_type) const;
float SegLengthCatmullRom(index_type) const;
float SegLengthBezier3(index_type) const;
typedef float (SplineBase::*SegLenghtMethtod)(index_type) const;
static SegLenghtMethtod seglengths[ModesEnd];
void InitLinear(const Vector3*, index_type, bool, index_type);
void InitCatmullRom(const Vector3*, index_type, bool, index_type);
void InitBezier3(const Vector3*, index_type, bool, index_type);
typedef void (SplineBase::*InitMethtod)(const Vector3*, index_type, bool, index_type);
static InitMethtod initializers[ModesEnd];
void UninitializedSpline() const { ASSERT(false);}
public:
explicit SplineBase() : index_lo(0), index_hi(0), m_mode(UninitializedMode), cyclic(false) {}
/** Caclulates the position for given segment Idx, and percent of segment length t
@param t - percent of segment length, assumes that t in range [0, 1]
@param Idx - spline segment index, should be in range [first, last)
*/
void evaluate_percent(index_type Idx, float u, Vector3& c) const {(this->*evaluators[m_mode])(Idx, u,c);}
/** Caclulates derivation in index Idx, and percent of segment length t
@param Idx - spline segment index, should be in range [first, last)
@param t - percent of spline segment length, assumes that t in range [0, 1]
*/
void evaluate_derivative(index_type Idx, float u, Vector3& hermite) const {(this->*derivative_evaluators[m_mode])(Idx, u,hermite);}
/** Bounds for spline indexes. All indexes should be in range [first, last). */
index_type first() const { return index_lo;}
index_type last() const { return index_hi;}
bool empty() const { return index_lo == index_hi;}
EvaluationMode mode() const { return (EvaluationMode)m_mode;}
bool isCyclic() const { return cyclic;}
// Xinef: DO NOT USE EXCEPT FOR SPLINE INITIALIZATION!!!!!!
const ControlArray* allocateVisualPoints() const { return &pointsVisual; }
const ControlArray& getPoints(bool visual) const { return visual ? pointsVisual : points;}
index_type getPointCount() const { return points.size();}
const Vector3& getPoint(index_type i, bool visual) const { return visual ? pointsVisual[i] : points[i];}
/** Initializes spline. Don't call other methods while spline not initialized. */
void init_spline(const Vector3 * controls, index_type count, EvaluationMode m);
void init_cyclic_spline(const Vector3 * controls, index_type count, EvaluationMode m, index_type cyclic_point);
/** As i can see there are a lot of ways how spline can be initialized
would be no harm to have some custom initializers. */
template<class Init> inline void init_spline_custom(Init& initializer)
{
initializer(m_mode, cyclic, points, index_lo, index_hi);
}
void clear();
/** Calculates distance between [i; i+1] points, assumes that index i is in bounds. */
float SegLength(index_type i) const { return (this->*seglengths[m_mode])(i);}
std::string ToString() const;
};
template<typename length_type>
class Spline : public SplineBase
{
public:
typedef length_type LengthType;
typedef std::vector<length_type> LengthArray;
protected:
LengthArray lengths;
index_type computeIndexInBounds(length_type length) const;
public:
explicit Spline(){}
/** Calculates the position for given t
@param t - percent of spline's length, assumes that t in range [0, 1]. */
void evaluate_percent(float t, Vector3 & c) const;
/** Calculates derivation for given t
@param t - percent of spline's length, assumes that t in range [0, 1]. */
void evaluate_derivative(float t, Vector3& hermite) const;
/** Calculates the position for given segment Idx, and percent of segment length t
@param t = partial_segment_length / whole_segment_length
@param Idx - spline segment index, should be in range [first, last). */
void evaluate_percent(index_type Idx, float u, Vector3& c) const { SplineBase::evaluate_percent(Idx, u,c);}
/** Caclulates derivation for index Idx, and percent of segment length t
@param Idx - spline segment index, should be in range [first, last)
@param t - percent of spline segment length, assumes that t in range [0, 1]. */
void evaluate_derivative(index_type Idx, float u, Vector3& c) const { SplineBase::evaluate_derivative(Idx, u,c);}
// Assumes that t in range [0, 1]
index_type computeIndexInBounds(float t) const;
void computeIndex(float t, index_type& out_idx, float& out_u) const;
/** Initializes spline. Don't call other methods while spline not initialized. */
void init_spline(const Vector3 * controls, index_type count, EvaluationMode m) { SplineBase::init_spline(controls, count, m);}
void init_cyclic_spline(const Vector3 * controls, index_type count, EvaluationMode m, index_type cyclic_point) { SplineBase::init_cyclic_spline(controls, count, m,cyclic_point);}
/** Initializes lengths with SplineBase::SegLength method. */
void initLengths();
/** Initializes lengths in some custom way
Note that value returned by cacher must be greater or equal to previous value. */
template<class T> inline void initLengths(T& cacher)
{
index_type i = index_lo;
lengths.resize(index_hi+1);
length_type prev_length = 0, new_length = 0;
while (i < index_hi)
{
new_length = cacher(*this, i);
// length overflowed, assign to max positive value
if (new_length < 0)
new_length = std::numeric_limits<length_type>::max();
lengths[++i] = new_length;
ASSERT(prev_length <= new_length);
prev_length = new_length;
}
}
/** Returns length of the whole spline. */
length_type length() const { return lengths[index_hi];}
/** Returns length between given nodes. */
length_type length(index_type first, index_type last) const { return lengths[last]-lengths[first];}
length_type length(index_type Idx) const { return lengths[Idx];}
void set_length(index_type i, length_type length) { lengths[i] = length;}
void clear();
};
}
#include "SplineImpl.h"
#endif // TRINITYSERVER_SPLINE_H

View File

@@ -0,0 +1,97 @@
/*
* Copyright (C)
*
* 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
*/
namespace Movement
{
template<typename length_type> void Spline<length_type>::evaluate_percent( float t, Vector3 & c ) const
{
index_type Index;
float u;
computeIndex(t, Index, u);
evaluate_percent(Index, u, c);
}
template<typename length_type> void Spline<length_type>::evaluate_derivative(float t, Vector3& hermite) const
{
index_type Index;
float u;
computeIndex(t, Index, u);
evaluate_derivative(Index, u, hermite);
}
template<typename length_type> SplineBase::index_type Spline<length_type>::computeIndexInBounds(length_type length_) const
{
// Temporary disabled: causes infinite loop with t = 1.f
/*
index_type hi = index_hi;
index_type lo = index_lo;
index_type i = lo + (float)(hi - lo) * t;
while ((lengths[i] > length) || (lengths[i + 1] <= length))
{
if (lengths[i] > length)
hi = i - 1; // too big
else if (lengths[i + 1] <= length)
lo = i + 1; // too small
i = (hi + lo) / 2;
}*/
index_type i = index_lo;
index_type N = index_hi;
while (i+1 < N && lengths[i+1] < length_)
++i;
return i;
}
template<typename length_type> void Spline<length_type>::computeIndex(float t, index_type& index, float& u) const
{
ASSERT(t >= 0.f && t <= 1.f);
length_type length_ = t * length();
index = computeIndexInBounds(length_);
ASSERT(index < index_hi);
u = (length_ - length(index)) / (float)length(index, index+1);
}
template<typename length_type> SplineBase::index_type Spline<length_type>::computeIndexInBounds( float t ) const
{
ASSERT(t >= 0.f && t <= 1.f);
return computeIndexInBounds(t * length());
}
template<typename length_type> void Spline<length_type>::initLengths()
{
index_type i = index_lo;
length_type length = 0;
lengths.resize(index_hi+1);
while (i < index_hi)
{
length += SegLength(i);
lengths[++i] = length;
}
}
template<typename length_type> void Spline<length_type>::clear()
{
SplineBase::clear();
lengths.clear();
}
}

View File

@@ -0,0 +1,158 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "DatabaseEnv.h"
#include "GridDefines.h"
#include "WaypointManager.h"
#include "MapManager.h"
#include "Log.h"
WaypointMgr::WaypointMgr()
{
}
WaypointMgr::~WaypointMgr()
{
for (WaypointPathContainer::iterator itr = _waypointStore.begin(); itr != _waypointStore.end(); ++itr)
{
for (WaypointPath::const_iterator it = itr->second.begin(); it != itr->second.end(); ++it)
delete *it;
itr->second.clear();
}
_waypointStore.clear();
}
void WaypointMgr::Load()
{
uint32 oldMSTime = getMSTime();
// 0 1 2 3 4 5 6 7 8 9
QueryResult result = WorldDatabase.Query("SELECT id, point, position_x, position_y, position_z, orientation, move_type, delay, action, action_chance FROM waypoint_data ORDER BY id, point");
if (!result)
{
sLog->outErrorDb(">> Loaded 0 waypoints. DB table `waypoint_data` is empty!");
sLog->outString();
return;
}
uint32 count = 0;
do
{
Field* fields = result->Fetch();
WaypointData* wp = new WaypointData();
uint32 pathId = fields[0].GetUInt32();
WaypointPath& path = _waypointStore[pathId];
float x = fields[2].GetFloat();
float y = fields[3].GetFloat();
float z = fields[4].GetFloat();
float o = fields[5].GetFloat();
Trinity::NormalizeMapCoord(x);
Trinity::NormalizeMapCoord(y);
wp->id = fields[1].GetUInt32();
wp->x = x;
wp->y = y;
wp->z = z;
wp->orientation = o;
wp->move_type = fields[6].GetUInt32();
if (wp->move_type >= WAYPOINT_MOVE_TYPE_MAX)
{
//TC_LOG_ERROR("sql.sql", "Waypoint %u in waypoint_data has invalid move_type, ignoring", wp->id);
delete wp;
continue;
}
wp->delay = fields[7].GetUInt32();
wp->event_id = fields[8].GetUInt32();
wp->event_chance = fields[9].GetInt16();
path.push_back(wp);
++count;
}
while (result->NextRow());
sLog->outString(">> Loaded %u waypoints in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
void WaypointMgr::ReloadPath(uint32 id)
{
WaypointPathContainer::iterator itr = _waypointStore.find(id);
if (itr != _waypointStore.end())
{
for (WaypointPath::const_iterator it = itr->second.begin(); it != itr->second.end(); ++it)
delete *it;
_waypointStore.erase(itr);
}
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_BY_ID);
stmt->setUInt32(0, id);
PreparedQueryResult result = WorldDatabase.Query(stmt);
if (!result)
return;
WaypointPath& path = _waypointStore[id];
do
{
Field* fields = result->Fetch();
WaypointData* wp = new WaypointData();
float x = fields[1].GetFloat();
float y = fields[2].GetFloat();
float z = fields[3].GetFloat();
float o = fields[4].GetFloat();
Trinity::NormalizeMapCoord(x);
Trinity::NormalizeMapCoord(y);
wp->id = fields[0].GetUInt32();
wp->x = x;
wp->y = y;
wp->z = z;
wp->orientation = o;
wp->move_type = fields[5].GetUInt32();
if (wp->move_type >= WAYPOINT_MOVE_TYPE_MAX)
{
//TC_LOG_ERROR("sql.sql", "Waypoint %u in waypoint_data has invalid move_type, ignoring", wp->id);
delete wp;
continue;
}
wp->delay = fields[6].GetUInt32();
wp->event_id = fields[7].GetUInt32();
wp->event_chance = fields[8].GetUInt8();
path.push_back(wp);
}
while (result->NextRow());
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_WAYPOINTMANAGER_H
#define TRINITY_WAYPOINTMANAGER_H
#include <ace/Singleton.h>
#include <ace/Null_Mutex.h>
#include <vector>
enum WaypointMoveType
{
WAYPOINT_MOVE_TYPE_WALK,
WAYPOINT_MOVE_TYPE_RUN,
WAYPOINT_MOVE_TYPE_LAND,
WAYPOINT_MOVE_TYPE_TAKEOFF,
WAYPOINT_MOVE_TYPE_MAX
};
struct WaypointData
{
uint32 id;
float x, y, z, orientation;
uint32 delay;
uint32 event_id;
uint32 move_type;
uint8 event_chance;
};
typedef std::vector<WaypointData*> WaypointPath;
typedef UNORDERED_MAP<uint32, WaypointPath> WaypointPathContainer;
class WaypointMgr
{
friend class ACE_Singleton<WaypointMgr, ACE_Null_Mutex>;
public:
// Attempts to reload a single path from database
void ReloadPath(uint32 id);
// Loads all paths from database, should only run on startup
void Load();
// Returns the path from a given id
WaypointPath const* GetPath(uint32 id) const
{
WaypointPathContainer::const_iterator itr = _waypointStore.find(id);
if (itr != _waypointStore.end())
return &itr->second;
return NULL;
}
private:
// Only allow instantiation from ACE_Singleton
WaypointMgr();
~WaypointMgr();
WaypointPathContainer _waypointStore;
};
#define sWaypointMgr ACE_Singleton<WaypointMgr, ACE_Null_Mutex>::instance()
#endif