Core/Movement: Implement spline movement subsystem.

Spline movement controls movements of server-side controlled units (monster movement, taxi movement, etc).
Proper implementation of effects such as charge, jump, cyclic movement will rely on it.
However, need improve our states system before.

Technical changes:

* Added linear, catmullrom and bezier3 splines which based on client's algorthims. They can be reused for proper transport position interpolation.
* Precission increased. There are no more position desync issues since client's position calculation formulas used.
* Now possible to move by paths with multiple points, send whole path to client.

--
Original author of research and implementation: SilverIce. Massive kudos.
Original port for Trinity (ref #4629) Chaplain and Venugh
With the following incremental fixes during my review:

- Restore flightmaster end grid pre-loading
- Fix uninitialized Creature::m_path_id
- Add missing trinity_string entries for .movegens command
- Fix a bug in WaypointMovementGenerator that would trigger unexpected pausing at waypoints for various amounts of time

Known issues:
- Errors like WaypointMovementGenerator::LoadPath creature XXX (Entry: YYYYY GUID: ZZZZZZ) doesn't have waypoint path id: 0.
This is caused by bad DB data. This commit didn't "break" it.

Do not forget to re-run CMake before compiling.
This commit is contained in:
Machiavelli
2012-01-14 15:36:07 +01:00
parent 798677ca54
commit dbbac0bdaa
84 changed files with 3356 additions and 1943 deletions
+43 -29
View File
@@ -48,6 +48,8 @@
#include "Vehicle.h"
#include "SpellAuraEffects.h"
#include "Group.h"
#include "MoveSplineInit.h"
#include "MoveSpline.h"
// apply implementation of the singletons
TrainerSpell const* TrainerSpellData::Find(uint32 spell_id) const
@@ -143,7 +145,7 @@ m_PlayerDamageReq(0), m_lootMoney(0), m_lootRecipient(0), m_lootRecipientGroup(0
m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_reactState(REACT_AGGRESSIVE),
m_defaultMovementType(IDLE_MOTION_TYPE), m_DBTableGuid(0), m_equipmentId(0), m_AlreadyCallAssistance(false),
m_AlreadySearchedAssistance(false), m_regenHealth(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL),
m_creatureInfo(NULL), m_creatureData(NULL), m_formation(NULL)
m_creatureInfo(NULL), m_creatureData(NULL), m_formation(NULL), m_path_id(0)
{
m_regenTimer = CREATURE_REGEN_INTERVAL;
m_valuesCount = UNIT_END;
@@ -332,6 +334,7 @@ bool Creature::InitEntry(uint32 Entry, uint32 /*team*/, const CreatureData* data
SetSpeed(MOVE_FLIGHT, 1.0f); // using 1.0 rate
SetFloatValue(OBJECT_FIELD_SCALE_X, cinfo->scale);
SetLevitate(canFly());
// checked at loading
m_defaultMovementType = MovementGeneratorType(cinfo->MovementType);
@@ -435,6 +438,17 @@ void Creature::Update(uint32 diff)
m_vehicleKit->Reset();
}
if (IsInWater())
{
if (canSwim())
AddUnitMovementFlag(MOVEMENTFLAG_SWIMMING);
}
else
{
if (canWalk())
RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING);
}
switch (m_deathState)
{
case JUST_ALIVED:
@@ -472,9 +486,7 @@ void Creature::Update(uint32 diff)
}
case CORPSE:
{
m_Events.Update(diff);
_UpdateSpells(diff);
Unit::Update(diff);
// deathstate changed on spells update, prevent problems
if (m_deathState != CORPSE)
break;
@@ -565,9 +577,6 @@ void Creature::Update(uint32 diff)
m_regenTimer = CREATURE_REGEN_INTERVAL;
break;
}
case DEAD_FALLING:
GetMotionMaster()->UpdateMotion(diff);
break;
default:
break;
}
@@ -945,7 +954,8 @@ void Creature::AI_SendMoveToPacket(float x, float y, float z, uint32 time, uint3
m_startMove = getMSTime();
m_moveTime = time;*/
SendMonsterMove(x, y, z, time);
float speed = GetDistance(x, y, z) / ((float)time * 0.001f);
MonsterMoveWithSpeed(x, y, z, speed);
}
Player* Creature::GetLootRecipient() const
@@ -1507,8 +1517,8 @@ void Creature::setDeathState(DeathState s)
if (m_formation && m_formation->getLeader() == this)
m_formation->FormationReset(true);
if ((canFly() || IsFlying()) && FallGround())
return;
if ((canFly() || IsFlying()))
i_motionMaster.MoveFall();
Unit::setDeathState(CORPSE);
}
@@ -1520,7 +1530,7 @@ void Creature::setDeathState(DeathState s)
SetLootRecipient(NULL);
ResetPlayerDamageReq();
CreatureTemplate const* cinfo = GetCreatureInfo();
AddUnitMovementFlag(MOVEMENTFLAG_WALKING);
SetWalk(true);
if (GetCreatureInfo()->InhabitType & INHABIT_AIR)
AddUnitMovementFlag(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_FLYING);
if (GetCreatureInfo()->InhabitType & INHABIT_WATER)
@@ -1536,24 +1546,6 @@ void Creature::setDeathState(DeathState s)
}
}
bool Creature::FallGround()
{
// Let's abort after we called this function one time
if (getDeathState() == DEAD_FALLING)
return false;
float x, y, z;
GetPosition(x, y, z);
// use larger distance for vmap height search than in most other cases
float ground_Z = GetMap()->GetHeight(x, y, z, true, MAX_FALL_DISTANCE);
if (fabs(ground_Z - z) < 0.1f)
return false;
GetMotionMaster()->MoveFall(ground_Z, EVENT_FALL_GROUND);
Unit::setDeathState(DEAD_FALLING);
return true;
}
void Creature::Respawn(bool force)
{
DestroyForNearbyPlayers();
@@ -2415,3 +2407,25 @@ bool Creature::IsDungeonBoss() const
CreatureTemplate const* cinfo = sObjectMgr->GetCreatureTemplate(GetEntry());
return cinfo && (cinfo->flags_extra & CREATURE_FLAG_EXTRA_DUNGEON_BOSS);
}
void Creature::SetWalk(bool enable)
{
if (enable)
AddUnitMovementFlag(MOVEMENTFLAG_WALKING);
else
RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
WorldPacket data(enable ? SMSG_SPLINE_MOVE_SET_WALK_MODE : SMSG_SPLINE_MOVE_SET_RUN_MODE, 9);
data.append(GetPackGUID());
SendMessageToSet(&data, true);
}
void Creature::SetLevitate(bool enable)
{
if (enable)
AddUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
else
RemoveUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
WorldPacket data(enable ? SMSG_SPLINE_MOVE_GRAVITY_DISABLE : SMSG_SPLINE_MOVE_GRAVITY_ENABLE, 9);
data.append(GetPackGUID());
SendMessageToSet(&data, true);
}
+3 -1
View File
@@ -520,6 +520,9 @@ class Creature : public Unit, public GridObject<Creature>, public MapCreature
void AI_SendMoveToPacket(float x, float y, float z, uint32 time, uint32 MovementFlags, uint8 type);
CreatureAI* AI() const { return (CreatureAI*)i_AI; }
void SetWalk(bool enable);
void SetLevitate(bool enable);
uint32 GetShieldBlockValue() const //dunno mob block value
{
return (getLevel()/2 + uint32(GetStat(STAT_STRENGTH)/20));
@@ -573,7 +576,6 @@ class Creature : public Unit, public GridObject<Creature>, public MapCreature
const char* GetNameForLocaleIdx(LocaleConstant locale_idx) const;
void setDeathState(DeathState s); // override virtual Unit::setDeathState
bool FallGround();
bool LoadFromDB(uint32 guid, Map* map) { return LoadCreatureFromDB(guid, map, false); }
bool LoadCreatureFromDB(uint32 guid, Map* map, bool addToMap = true);
@@ -212,10 +212,12 @@ void CreatureGroup::FormationReset(bool dismiss)
void CreatureGroup::LeaderMoveTo(float x, float y, float z)
{
//! To do: This should probably get its own movement generator or use WaypointMovementGenerator.
//! If the leader's path is known, member's path can be plotted as well using formation offsets.
if (!m_leader)
return;
float pathangle = atan2(m_leader->GetPositionY() - y, m_leader->GetPositionX() - x);
float pathangle = atan2(m_leader->GetPositionY() - y, m_leader->GetPositionX() - x);
for (CreatureGroupMemberType::iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
{
@@ -1869,4 +1869,4 @@ void GameObject::SetLootState(LootState s)
{
m_lootState = s;
AI()->OnStateChanged(s);
}
}
+72 -67
View File
@@ -45,6 +45,7 @@
#include "TemporarySummon.h"
#include "Totem.h"
#include "OutdoorPvPMgr.h"
#include "MovementPacketBuilder.h"
uint32 GuidHigh2TypeId(uint32 guid_hi)
{
@@ -312,78 +313,17 @@ void Object::_BuildMovementUpdate(ByteBuffer * data, uint16 flags) const
*data << ((Unit*)this)->GetSpeed(MOVE_WALK);
*data << ((Unit*)this)->GetSpeed(MOVE_RUN);
*data << ((Unit*)this)->GetSpeed(MOVE_SWIM_BACK);
*data << ((Unit*)this)->GetSpeed(MOVE_SWIM);
*data << ((Unit*)this)->GetSpeed(MOVE_RUN_BACK);
*data << ((Unit*)this)->GetSpeed(MOVE_SWIM);
*data << ((Unit*)this)->GetSpeed(MOVE_SWIM_BACK);
*data << ((Unit*)this)->GetSpeed(MOVE_FLIGHT);
*data << ((Unit*)this)->GetSpeed(MOVE_FLIGHT_BACK);
*data << ((Unit*)this)->GetSpeed(MOVE_TURN_RATE);
*data << ((Unit*)this)->GetSpeed(MOVE_PITCH_RATE);
const Player* player = ToPlayer();
// 0x08000000
if (player && player->isInFlight())
{
uint32 flags3 = SPLINEFLAG_GLIDE;
*data << uint32(flags3); // splines flag?
if (flags3 & 0x20000) // may be orientation
{
*data << (float)0;
}
else
{
if (flags3 & 0x8000) // probably x, y, z coords there
{
*data << (float)0;
*data << (float)0;
*data << (float)0;
}
if (flags3 & 0x10000) // probably guid there
{
*data << uint64(0);
}
}
FlightPathMovementGenerator *fmg =
(FlightPathMovementGenerator*)(player->GetMotionMaster()->top());
TaxiPathNodeList const& path = fmg->GetPath();
float x, y, z;
player->GetPosition(x, y, z);
uint32 inflighttime = uint32(path.GetPassedLength(fmg->GetCurrentNode(), x, y, z) * 32);
uint32 traveltime = uint32(path.GetTotalLength() * 32);
*data << uint32(inflighttime); // passed move time?
*data << uint32(traveltime); // full move time?
*data << uint32(0); // ticks count?
*data << float(0); // added in 3.1
*data << float(0); // added in 3.1
*data << float(0); // added in 3.1
*data << uint32(0); // added in 3.1
uint32 poscount = uint32(path.size());
*data << uint32(poscount); // points count
for (uint32 i = 0; i < poscount; ++i)
{
*data << float(path[i].x);
*data << float(path[i].y);
*data << float(path[i].z);
}
*data << uint8(0); // added in 3.0.8
*data << float(path[poscount-1].x);
*data << float(path[poscount-1].y);
*data << float(path[poscount-1].z);
}
if (((Unit*)this)->m_movementInfo.GetMovementFlags() & MOVEMENTFLAG_SPLINE_ENABLED)
Movement::PacketBuilder::WriteCreate(*((Unit*)this)->movespline, *data);
}
else
{
@@ -487,7 +427,7 @@ void Object::_BuildMovementUpdate(ByteBuffer * data, uint16 flags) const
// 0x200
if (flags & UPDATEFLAG_ROTATION)
{
*data << uint64(((GameObject*)this)->GetRotation());
*data << int64(((GameObject*)this)->GetRotation());
}
}
@@ -1595,6 +1535,70 @@ void WorldObject::UpdateGroundPositionZ(float x, float y, float &z) const
z = new_z+ 0.05f; // just to be sure that we are not a few pixel under the surface
}
void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const
{
switch (GetTypeId())
{
case TYPEID_UNIT:
{
// non fly unit don't must be in air
// non swim unit must be at ground (mostly speedup, because it don't must be in water and water level check less fast
if (!((Creature const*)this)->canFly())
{
bool canSwim = ((Creature const*)this)->canSwim();
float ground_z = z;
float max_z = canSwim
? GetBaseMap()->GetWaterOrGroundLevel(x, y, z, &ground_z, !((Unit const*)this)->HasAuraType(SPELL_AURA_WATER_WALK))
: ((ground_z = GetBaseMap()->GetHeight(x, y, z, true)));
if (max_z > INVALID_HEIGHT)
{
if (z > max_z)
z = max_z;
else if (z < ground_z)
z = ground_z;
}
}
else
{
float ground_z = GetBaseMap()->GetHeight(x, y, z, true);
if (z < ground_z)
z = ground_z;
}
break;
}
case TYPEID_PLAYER:
{
// for server controlled moves playr work same as creature (but it can always swim)
if (!((Player const*)this)->canFly())
{
float ground_z = z;
float max_z = GetBaseMap()->GetWaterOrGroundLevel(x, y, z, &ground_z, !((Unit const*)this)->HasAuraType(SPELL_AURA_WATER_WALK));
if (max_z > INVALID_HEIGHT)
{
if (z > max_z)
z = max_z;
else if (z < ground_z)
z = ground_z;
}
}
else
{
float ground_z = GetBaseMap()->GetHeight(x, y, z, true);
if (z < ground_z)
z = ground_z;
}
break;
}
default:
{
float ground_z = GetBaseMap()->GetHeight(x, y, z, true);
if(ground_z > INVALID_HEIGHT)
z = ground_z;
break;
}
}
}
bool Position::IsPositionValid() const
{
return Trinity::IsValidMapCoord(m_positionX, m_positionY, m_positionZ, m_orientation);
@@ -2033,7 +2037,8 @@ void Unit::BuildHeartBeatMsg(WorldPacket* data) const
void WorldObject::SendMessageToSet(WorldPacket* data, bool self)
{
SendMessageToSetInRange(data, GetVisibilityRange(), self);
if (IsInWorld())
SendMessageToSetInRange(data, GetVisibilityRange(), self);
}
void WorldObject::SendMessageToSetInRange(WorldPacket* data, float dist, bool /*self*/)
+3
View File
@@ -497,7 +497,9 @@ struct MovementInfo
}
uint32 GetMovementFlags() { return flags; }
void SetMovementFlags(uint32 flag) { flags = flag; }
void AddMovementFlag(uint32 flag) { flags |= flag; }
void RemoveMovementFlag(uint32 flag) { flags &= ~flag; }
bool HasMovementFlag(uint32 flag) const { return flags & flag; }
uint16 GetExtraMovementFlags() { return flags2; }
@@ -615,6 +617,7 @@ class WorldObject : public Object, public WorldLocation
return (m_valuesCount > UNIT_FIELD_COMBATREACH) ? m_floatValues[UNIT_FIELD_COMBATREACH] : DEFAULT_WORLD_OBJECT_SIZE;
}
void UpdateGroundPositionZ(float x, float y, float &z) const;
void UpdateAllowedPositionZ(float x, float y, float &z) const;
void GetRandomPoint(const Position &srcPos, float distance, float &rand_x, float &rand_y, float &rand_z) const;
void GetRandomPoint(const Position &srcPos, float distance, Position &pos) const
+2 -36
View File
@@ -2101,6 +2101,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
// reset movement flags at teleport, because player will continue move with these flags after teleport
SetUnitMovementFlags(0);
DisableSpline();
if (m_transport)
{
@@ -5148,43 +5149,10 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness)
}
}
/**
* FallMode = 0 implies that the player is dying, or already dead, and the proper death state will be set.
* = 1 simply causes the player to plummet towards the ground, and not suffer any damage.
* = 2 causes the player to plummet towards the ground, and causes falling damage, regardless
* of any auras that might of prevented fall damage.
*/
bool Player::FallGround(uint8 FallMode)
{
// Let's abort after we called this function one time
if (getDeathState() == DEAD_FALLING && FallMode == 0)
return false;
float x, y, z;
GetPosition(x, y, z);
float ground_Z = GetMap()->GetHeight(x, y, z);
float z_diff = 0.0f;
if ((z_diff = fabs(ground_Z - z)) < 0.1f)
return false;
GetMotionMaster()->MoveFall(ground_Z, EVENT_FALL_GROUND);
// Below formula for falling damage is from Player::HandleFall
if (FallMode == 2 && z_diff >= 14.57f)
{
uint32 damage = std::min(GetMaxHealth(), (uint32)((0.018f * z_diff - 0.2426f) * GetMaxHealth() * sWorld->getRate(RATE_DAMAGE_FALL)));
if (damage)
EnvironmentalDamage(DAMAGE_FALL, damage);
}
else if (FallMode == 0)
Unit::setDeathState(DEAD_FALLING);
return true;
}
void Player::KillPlayer()
{
if (IsFlying() && !GetTransport())
FallGround();
i_motionMaster.MoveFall();
SetMovement(MOVE_ROOT);
@@ -21474,8 +21442,6 @@ void Player::SendInitialVisiblePackets(Unit* target)
SendAurasForTarget(target);
if (target->isAlive())
{
if (target->GetMotionMaster()->GetCurrentMovementGeneratorType() != IDLE_MOTION_TYPE)
target->SendMonsterMoveWithSpeedToCurrentDestination(this);
if (target->HasUnitState(UNIT_STAT_MELEE_ATTACKING) && target->getVictim())
target->SendMeleeAttackStart(target->getVictim());
}
-1
View File
@@ -1964,7 +1964,6 @@ class Player : public Unit, public GridObject<Player>
Corpse* GetCorpse() const;
void SpawnCorpseBones();
void CreateCorpse();
bool FallGround(uint8 FallMode = 0);
void KillPlayer();
uint32 GetResurrectionSpellId();
void ResurrectPlayer(float restore_percent, bool applySickness = false);
+73 -185
View File
@@ -45,16 +45,16 @@
#include "InstanceSaveMgr.h"
#include "GridNotifiersImpl.h"
#include "CellImpl.h"
#include "Path.h"
#include "CreatureGroups.h"
#include "PetAI.h"
#include "PassiveAI.h"
#include "Traveller.h"
#include "TemporarySummon.h"
#include "Vehicle.h"
#include "Transport.h"
#include "InstanceScript.h"
#include "SpellInfo.h"
#include "MoveSplineInit.h"
#include "MoveSpline.h"
#include <math.h>
@@ -62,9 +62,9 @@ float baseMoveSpeed[MAX_MOVE_TYPE] =
{
2.5f, // MOVE_WALK
7.0f, // MOVE_RUN
2.5f, // MOVE_RUN_BACK
4.5f, // MOVE_RUN_BACK
4.722222f, // MOVE_SWIM
4.5f, // MOVE_SWIM_BACK
2.5f, // MOVE_SWIM_BACK
3.141594f, // MOVE_TURN_RATE
7.0f, // MOVE_FLIGHT
4.5f, // MOVE_FLIGHT_BACK
@@ -73,9 +73,9 @@ float baseMoveSpeed[MAX_MOVE_TYPE] =
float playerBaseMoveSpeed[MAX_MOVE_TYPE] = {
2.5f, // MOVE_WALK
7.0f, // MOVE_RUN
2.5f, // MOVE_RUN_BACK
4.5f, // MOVE_RUN_BACK
4.722222f, // MOVE_SWIM
4.5f, // MOVE_SWIM_BACK
2.5f, // MOVE_SWIM_BACK
3.141594f, // MOVE_TURN_RATE
7.0f, // MOVE_FLIGHT
4.5f, // MOVE_FLIGHT_BACK
@@ -148,7 +148,7 @@ Unit::Unit(bool isWorldObject): WorldObject(isWorldObject),
m_movedPlayer(NULL), m_lastSanctuaryTime(0), IsAIEnabled(false), NeedChangeAI(false),
m_ControlledByPlayer(false), i_AI(NULL), i_disabledAI(NULL), m_procDeep(0),
m_removedAurasCount(0), i_motionMaster(this), m_ThreatManager(this), m_vehicle(NULL),
m_vehicleKit(NULL), m_unitTypeMask(UNIT_MASK_NONE), m_HostileRefManager(this)
m_vehicleKit(NULL), m_unitTypeMask(UNIT_MASK_NONE), m_HostileRefManager(this), movespline(new Movement::MoveSpline())
{
#ifdef _MSC_VER
#pragma warning(default:4355)
@@ -280,6 +280,7 @@ Unit::~Unit()
delete m_charmInfo;
delete m_vehicleKit;
delete movespline;
ASSERT(!m_duringRemoveFromWorld);
ASSERT(!m_attacking);
@@ -346,6 +347,7 @@ void Unit::Update(uint32 p_time)
ModifyAuraState(AURA_STATE_HEALTH_ABOVE_75_PERCENT, HealthAbovePct(75));
}
UpdateSplineMovement(p_time);
i_motionMaster.UpdateMotion(p_time);
}
@@ -357,151 +359,46 @@ bool Unit::haveOffhandWeapon() const
return m_canDualWield;
}
void Unit::SendMonsterMoveWithSpeedToCurrentDestination(Player* player)
void Unit::MonsterMoveWithSpeed(float x, float y, float z, float speed)
{
float x, y, z;
if (GetMotionMaster()->GetDestination(x, y, z))
SendMonsterMoveWithSpeed(x, y, z, 0, player);
Movement::MoveSplineInit init(*this);
init.MoveTo(x,y,z);
init.SetVelocity(speed);
init.Launch();
}
void Unit::SendMonsterMoveWithSpeed(float x, float y, float z, uint32 transitTime, Player* player)
void Unit::UpdateSplineMovement(uint32 t_diff)
{
if (!transitTime)
enum{
POSITION_UPDATE_DELAY = 400,
};
if (movespline->Finalized())
return;
movespline->updateState(t_diff);
bool arrived = movespline->Finalized();
if (arrived)
DisableSpline();
m_movesplineTimer.Update(t_diff);
if (m_movesplineTimer.Passed() || arrived)
{
m_movesplineTimer.Reset(POSITION_UPDATE_DELAY);
Movement::Location loc = movespline->ComputePosition();
if (GetTypeId() == TYPEID_PLAYER)
{
Traveller<Player> traveller(*(Player*)this);
transitTime = traveller.GetTotalTrevelTimeTo(x, y, z);
}
((Player*)this)->UpdatePosition(loc.x,loc.y,loc.z,loc.orientation);
else
{
Traveller<Creature> traveller(*ToCreature());
transitTime = traveller.GetTotalTrevelTimeTo(x, y, z);
}
GetMap()->CreatureRelocation((Creature*)this,loc.x,loc.y,loc.z,loc.orientation);
}
//float orientation = (float)atan2((double)dy, (double)dx);
SendMonsterMove(x, y, z, transitTime, player);
}
void Unit::SetFacing(float ori, WorldObject* obj)
void Unit::DisableSpline()
{
SetOrientation(obj ? GetAngle(obj) : ori);
WorldPacket data(SMSG_MONSTER_MOVE, (1+12+4+1+(obj ? 8 : 4)+4+4+4+12+GetPackGUID().size()));
data.append(GetPackGUID());
data << uint8(0); // unk
data << GetPositionX() << GetPositionY() << GetPositionZ();
data << getMSTime();
if (obj)
{
data << uint8(SPLINETYPE_FACING_TARGET);
data << uint64(obj->GetGUID());
}
else
{
data << uint8(SPLINETYPE_FACING_ANGLE);
data << ori;
}
data << uint32(SPLINEFLAG_NONE);
data << uint32(0); // move time 0
data << uint32(1); // one point
data << GetPositionX() << GetPositionY() << GetPositionZ();
SendMessageToSet(&data, true);
}
void Unit::SendMonsterStop(bool on_death)
{
WorldPacket data(SMSG_MONSTER_MOVE, (17 + GetPackGUID().size()));
data.append(GetPackGUID());
data << uint8(0); // new in 3.1
data << GetPositionX() << GetPositionY() << GetPositionZ();
data << getMSTime();
if (on_death == true)
{
data << uint8(0);
data << uint32((GetUnitMovementFlags() & MOVEMENTFLAG_LEVITATING) ? SPLINEFLAG_FLYING : SPLINEFLAG_WALKING);
data << uint32(0); // Time in between points
data << uint32(1); // 1 single waypoint
data << GetPositionX() << GetPositionY() << GetPositionZ();
}
else
data << uint8(1);
SendMessageToSet(&data, true);
ClearUnitState(UNIT_STAT_MOVE);
}
void Unit::SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint32 Time, Player* player)
{
WorldPacket data(SMSG_MONSTER_MOVE, 1+12+4+1+4+4+4+12+GetPackGUID().size());
data.append(GetPackGUID());
data << uint8(0); // new in 3.1
data << GetPositionX() << GetPositionY() << GetPositionZ();
data << getMSTime();
data << uint8(0);
data << uint32((GetUnitMovementFlags() & MOVEMENTFLAG_LEVITATING) ? SPLINEFLAG_FLYING : SPLINEFLAG_WALKING);
data << Time; // Time in between points
data << uint32(1); // 1 single waypoint
data << NewPosX << NewPosY << NewPosZ; // the single waypoint Point B
if (player)
player->GetSession()->SendPacket(&data);
else
SendMessageToSet(&data, true);
AddUnitState(UNIT_STAT_MOVE);
}
void Unit::SendMonsterMove(MonsterMoveData const& moveData, Player* player)
{
WorldPacket data(SMSG_MONSTER_MOVE, GetPackGUID().size() + 1 + 12 + 4 + 1 + 4 + 8 + 4 + 4 + 12);
data.append(GetPackGUID());
data << uint8(0); // new in 3.1
data << GetPositionX() << GetPositionY() << GetPositionZ();
data << getMSTime();
data << uint8(0);
data << moveData.SplineFlag;
if (moveData.SplineFlag & SPLINEFLAG_ANIMATIONTIER)
{
data << uint8(moveData.AnimationState);
data << uint32(0);
}
data << moveData.Time;
if (moveData.SplineFlag & SPLINEFLAG_TRAJECTORY)
{
data << moveData.SpeedZ;
data << uint32(0); // walk time after jump
}
data << uint32(1); // waypoint count
data << moveData.DestLocation.GetPositionX();
data << moveData.DestLocation.GetPositionY();
data << moveData.DestLocation.GetPositionZ();
if (player)
player->GetSession()->SendPacket(&data);
else
SendMessageToSet(&data, true);
}
void Unit::SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint32 MoveFlags, uint32 time, float speedZ, Player* player)
{
MonsterMoveData data;
data.DestLocation.Relocate(NewPosX, NewPosY, NewPosZ);
data.SplineFlag = MoveFlags;
data.Time = time;
data.SpeedZ = speedZ;
SendMonsterMove(data, player);
m_movementInfo.RemoveMovementFlag(MovementFlags(MOVEMENTFLAG_SPLINE_ENABLED|MOVEMENTFLAG_FORWARD));
movespline->_Interrupt();
}
void Unit::SendMonsterMoveExitVehicle(Position const* newPos)
@@ -3106,17 +3003,6 @@ bool Unit::isInBackInMap(Unit const* target, float distance, float arc) const
return IsWithinDistInMap(target, distance) && !HasInArc(2 * M_PI - arc, target);
}
void Unit::SetFacingToObject(WorldObject* pObject)
{
// update orientation at server
SetOrientation(GetAngle(pObject));
// and client
WorldPacket data;
BuildHeartBeatMsg(&data);
SendMessageToSet(&data, false);
}
bool Unit::isInAccessiblePlaceFor(Creature const* c) const
{
if (IsInWater())
@@ -12744,7 +12630,7 @@ void Unit::setDeathState(DeathState s)
ClearDiminishings();
GetMotionMaster()->Clear(false);
GetMotionMaster()->MoveIdle();
SendMonsterStop(true);
StopMoving();
// without this when removing IncreaseMaxHealth aura player may stuck with 1 hp
// do not why since in IncreaseMaxHealth currenthealth is checked
SetHealth(0);
@@ -13565,7 +13451,7 @@ void Unit::SetHealth(uint32 val)
{
if (getDeathState() == JUST_DIED)
val = 0;
else if (GetTypeId() == TYPEID_PLAYER && (getDeathState() == DEAD || getDeathState() == DEAD_FALLING))
else if (GetTypeId() == TYPEID_PLAYER && getDeathState() == DEAD)
val = 1;
else
{
@@ -14682,22 +14568,20 @@ void Unit::StopMoving()
{
ClearUnitState(UNIT_STAT_MOVING);
// send explicit stop packet
// rely on vmaps here because for example stormwind is in air
//float z = sMapMgr->GetBaseMap(GetMapId())->GetHeight(GetPositionX(), GetPositionY(), GetPositionZ(), true);
//if (fabs(GetPositionZ() - z) < 2.0f)
// Relocate(GetPositionX(), GetPositionY(), z);
//Relocate(GetPositionX(), GetPositionY(), GetPositionZ());
// not need send any packets if not in world
if (!IsInWorld())
return;
if (!(GetUnitMovementFlags() & MOVEMENTFLAG_ONTRANSPORT))
SendMonsterStop();
Movement::MoveSplineInit init(*this);
init.SetFacing(GetOrientation());
init.Launch();
}
void Unit::SendMovementFlagUpdate()
{
WorldPacket data;
BuildHeartBeatMsg(&data);
SendMessageToSet(&data, false);
SendMessageToSet(&data, true);
}
bool Unit::IsSitState() const
@@ -17097,7 +16981,7 @@ void Unit::_ExitVehicle(Position const* exitPosition)
WorldPacket data2;
BuildHeartBeatMsg(&data2);
SendMessageToSet(&data2, false);
SendMessageToSet(&data2, true);
if (vehicle->GetBase()->HasUnitTypeMask(UNIT_MASK_MINION))
if (((Minion*)vehicle->GetBase())->GetOwner() == this)
@@ -17116,25 +17000,6 @@ void Unit::_ExitVehicle(Position const* exitPosition)
void Unit::BuildMovementPacket(ByteBuffer *data) const
{
switch (GetTypeId())
{
case TYPEID_UNIT:
if (canFly())
const_cast<Unit*>(this)->AddUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
break;
case TYPEID_PLAYER:
// remove unknown, unused etc flags for now
const_cast<Unit*>(this)->RemoveUnitMovementFlag(MOVEMENTFLAG_SPLINE_ENABLED);
if (isInFlight())
{
WPAssert(const_cast<Unit*>(this)->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE);
const_cast<Unit*>(this)->AddUnitMovementFlag(MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_SPLINE_ENABLED);
}
break;
default:
break;
}
*data << uint32(GetUnitMovementFlags()); // movement flags
*data << uint16(m_movementInfo.flags2); // 2.3.0
*data << uint32(getMSTime()); // time
@@ -17198,13 +17063,13 @@ void Unit::SetFlying(bool apply)
void Unit::NearTeleportTo(float x, float y, float z, float orientation, bool casting /*= false*/)
{
DisableSpline();
if (GetTypeId() == TYPEID_PLAYER)
ToPlayer()->TeleportTo(GetMapId(), x, y, z, orientation, TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (casting ? TELE_TO_SPELL : 0));
else
{
// FIXME: this interrupts spell visual
DestroyForNearbyPlayers();
UpdatePosition(x, y, z, orientation, true);
SendMovementFlagUpdate();
}
}
@@ -17486,3 +17351,26 @@ bool CharmInfo::IsReturning()
{
return m_isReturning;
}
void Unit::SetInFront(Unit const* target)
{
if (!HasUnitState(UNIT_STAT_CANNOT_TURN))
SetOrientation(GetAngle(target));
}
void Unit::SetFacingTo(float ori)
{
Movement::MoveSplineInit init(*this);
init.SetFacing(ori);
init.Launch();
}
void Unit::SetFacingToObject(WorldObject* pObject)
{
// never face when already moving
if (!IsStopped())
return;
// TODO: figure out under what conditions creature will move towards object instead of facing it where it currently is.
SetFacingTo(GetAngle(pObject));
}
+35 -91
View File
@@ -465,7 +465,6 @@ enum DeathState
CORPSE = 2,
DEAD = 3,
JUST_ALIVED = 4,
DEAD_FALLING= 5
};
enum UnitState
@@ -493,13 +492,22 @@ enum UnitState
UNIT_STAT_MOVE = 0x00100000,
UNIT_STAT_ROTATING = 0x00200000,
UNIT_STAT_EVADE = 0x00400000,
UNIT_STAT_ROAMING_MOVE = 0x00800000,
UNIT_STAT_CONFUSED_MOVE = 0x01000000,
UNIT_STAT_FLEEING_MOVE = 0x02000000,
UNIT_STAT_CHASE_MOVE = 0x04000000,
UNIT_STAT_FOLLOW_MOVE = 0x08000000,
UNIT_STAT_UNATTACKABLE = (UNIT_STAT_IN_FLIGHT | UNIT_STAT_ONVEHICLE),
UNIT_STAT_MOVING = (UNIT_STAT_ROAMING | UNIT_STAT_CHASE),
//UNIT_STAT_MOVING = (UNIT_STAT_ROAMING | UNIT_STAT_CHASE),
// for real move using movegen check and stop (except unstoppable flight)
UNIT_STAT_MOVING = UNIT_STAT_ROAMING_MOVE | UNIT_STAT_CONFUSED_MOVE | UNIT_STAT_FLEEING_MOVE| UNIT_STAT_CHASE_MOVE | UNIT_STAT_FOLLOW_MOVE ,
UNIT_STAT_CONTROLLED = (UNIT_STAT_CONFUSED | UNIT_STAT_STUNNED | UNIT_STAT_FLEEING),
UNIT_STAT_LOST_CONTROL = (UNIT_STAT_CONTROLLED | UNIT_STAT_JUMPING | UNIT_STAT_CHARGING),
UNIT_STAT_SIGHTLESS = (UNIT_STAT_LOST_CONTROL | UNIT_STAT_EVADE),
UNIT_STAT_CANNOT_AUTOATTACK = (UNIT_STAT_LOST_CONTROL | UNIT_STAT_CASTING),
UNIT_STAT_CANNOT_TURN = (UNIT_STAT_LOST_CONTROL | UNIT_STAT_ROTATING),
// stay by different reasons
UNIT_STAT_NOT_MOVE = UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DIED | UNIT_STAT_DISTRACTED,
UNIT_STAT_ALL_STATE = 0xffffffff //(UNIT_STAT_STOPPED | UNIT_STAT_MOVING | UNIT_STAT_IN_COMBAT | UNIT_STAT_IN_FLIGHT)
};
@@ -719,59 +727,18 @@ enum MovementFlags2
MOVEMENTFLAG2_UNK15 = 0x00004000,
MOVEMENTFLAG2_UNK16 = 0x00008000,
};
enum SplineFlags
{
SPLINEFLAG_NONE = 0x00000000,
SPLINEFLAG_FORWARD = 0x00000001,
SPLINEFLAG_BACKWARD = 0x00000002,
SPLINEFLAG_STRAFE_LEFT = 0x00000004,
SPLINEFLAG_STRAFE_RIGHT = 0x00000008,
SPLINEFLAG_LEFT = 0x00000010,
SPLINEFLAG_RIGHT = 0x00000020,
SPLINEFLAG_PITCH_UP = 0x00000040,
SPLINEFLAG_PITCH_DOWN = 0x00000080,
SPLINEFLAG_DONE = 0x00000100,
SPLINEFLAG_FALLING = 0x00000200,
SPLINEFLAG_NO_SPLINE = 0x00000400,
SPLINEFLAG_TRAJECTORY = 0x00000800,
SPLINEFLAG_WALKING = 0x00001000,
SPLINEFLAG_FLYING = 0x00002000,
SPLINEFLAG_KNOCKBACK = 0x00004000,
SPLINEFLAG_FINAL_POINT = 0x00008000,
SPLINEFLAG_FINAL_TARGET = 0x00010000,
SPLINEFLAG_FINAL_FACING = 0x00020000,
SPLINEFLAG_CATMULL_ROM = 0x00040000,
SPLINEFLAG_UNKNOWN20 = 0x00080000,
SPLINEFLAG_UNKNOWN21 = 0x00100000,
SPLINEFLAG_ANIMATIONTIER = 0x00200000,
SPLINEFLAG_UNKNOWN23 = 0x00400000,
SPLINEFLAG_TRANSPORT = 0x00800000,
SPLINEFLAG_EXIT_VEHICLE = 0x01000000,
SPLINEFLAG_UNKNOWN26 = 0x02000000,
SPLINEFLAG_UNKNOWN27 = 0x04000000,
SPLINEFLAG_UNKNOWN28 = 0x08000000,
SPLINEFLAG_UNKNOWN29 = 0x10000000,
SPLINEFLAG_ANIMATION = 0x20000000,
SPLINEFLAG_UNKNOWN31 = 0x40000000,
SPLINEFLAG_UNKNOWN32 = 0x80000000,
SPLINEFLAG_GLIDE = SPLINEFLAG_WALKING | SPLINEFLAG_FLYING,
};
enum SplineMode
{
SPLINEMODE_LINEAR = 0,
SPLINEMODE_CATMULL_ROM = 1,
SPLINEMODE_BEZIER3 = 2
SPLINEFLAG_WALKMODE = 0x00001000,
SPLINEFLAG_FLYING = 0x00002000,
SPLINEFLAG_TRANSPORT = 0x00800000,
SPLINEFLAG_EXIT_VEHICLE = 0x01000000,
};
enum SplineType
{
SPLINETYPE_NORMAL = 0,
SPLINETYPE_STOP = 1,
SPLINETYPE_FACING_SPOT = 2,
SPLINETYPE_FACING_TARGET = 3,
SPLINETYPE_FACING_ANGLE = 4
SPLINETYPE_FACING_ANGLE = 4,
};
enum UnitTypeMask
@@ -789,6 +756,10 @@ enum UnitTypeMask
UNIT_MASK_ACCESSORY = 0x00000200,
};
namespace Movement{
class MoveSpline;
}
enum DiminishingLevels
{
DIMINISHING_LEVEL_1 = 0,
@@ -1628,20 +1599,18 @@ class Unit : public WorldObject
void JumpTo(float speedXY, float speedZ, bool forward = true);
void JumpTo(WorldObject* obj, float speedZ);
void SetFacing(float ori, WorldObject* obj = NULL);
void SendMonsterStop(bool on_death = false);
void SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint32 Time, Player* player = NULL);
void SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint32 MoveFlags, uint32 time, float speedZ, Player* player = NULL);
void SendMonsterMove(MonsterMoveData const& moveData, Player* receiver = NULL);
void MonsterMoveWithSpeed(float x, float y, float z, float speed);
//void SetFacing(float ori, WorldObject* obj = NULL);
void SendMonsterMoveExitVehicle(Position const* newPos);
//void SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint8 type, uint32 MovementFlags, uint32 Time, Player* player = NULL);
void SendMonsterMoveTransport(Unit* vehicleOwner);
void SendMonsterMoveWithSpeed(float x, float y, float z, uint32 transitTime = 0, Player* player = NULL);
void SendMonsterMoveWithSpeedToCurrentDestination(Player* player = NULL);
void SendMovementFlagUpdate();
bool IsLevitating() const { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_LEVITATING);}
bool IsWalking() const { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_WALKING);}
template<typename PathElem, typename PathNode>
void SendMonsterMoveByPath(Path<PathElem, PathNode> const& path, uint32 start, uint32 end);
void SetInFront(Unit const* target);
void SetFacingTo(float ori);
void SetFacingToObject(WorldObject* pObject);
void SendChangeCurrentVictimOpcode(HostileReference* pHostileReference);
void SendClearThreatListOpcode();
@@ -1951,13 +1920,7 @@ class Unit : public WorldObject
void SetBaseWeaponDamage(WeaponAttackType attType, WeaponDamageRange damageRange, float value) { m_weaponDamage[attType][damageRange] = value; }
bool isInFrontInMap(Unit const* target, float distance, float arc = M_PI) const;
void SetInFront(Unit const* target)
{
if (!HasUnitState(UNIT_STAT_CANNOT_TURN))
SetOrientation(GetAngle(target));
}
bool isInBackInMap(Unit const* target, float distance, float arc = M_PI) const;
void SetFacingToObject(WorldObject* pObject);
// Visibility system
bool IsVisible() const { return (m_serverSideVisibility.GetValue(SERVERSIDE_VISIBILITY_GM) > SEC_PLAYER) ? false : true; }
@@ -2232,6 +2195,9 @@ class Unit : public WorldObject
SetUInt64Value(UNIT_FIELD_TARGET, 0);
}
// Movement info
Movement::MoveSpline * movespline;
protected:
explicit Unit (bool isWorldObject);
@@ -2303,6 +2269,8 @@ class Unit : public WorldObject
bool IsAlwaysVisibleFor(WorldObject const* seer) const;
bool IsAlwaysDetectableFor(WorldObject const* seer) const;
void DisableSpline();
private:
bool IsTriggeredAtSpellProcEvent(Unit* pVictim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const* & spellProcEvent);
bool HandleDummyAuraProc(Unit* pVictim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown);
@@ -2316,6 +2284,8 @@ class Unit : public WorldObject
bool HandleAuraRaidProcFromChargeWithValue(AuraEffect* triggeredByAura);
bool HandleAuraRaidProcFromCharge(AuraEffect* triggeredByAura);
void UpdateSplineMovement(uint32 t_diff);
// player or player's pet
float GetCombatRatingReduction(CombatRating cr) const;
uint32 GetCombatRatingDamageReduction(CombatRating cr, float rate, float cap, uint32 damage) const;
@@ -2330,6 +2300,7 @@ class Unit : public WorldObject
uint32 m_state; // Even derived shouldn't modify
uint32 m_CombatTimer;
uint32 m_lastManaUse; // msecs
TimeTrackerSmall m_movesplineTimer;
Diminishing m_Diminishing;
// Manage all Units that are threatened by us
@@ -2382,31 +2353,4 @@ namespace Trinity
const bool m_ascending;
};
}
template<typename Elem, typename Node>
inline void Unit::SendMonsterMoveByPath(Path<Elem, Node> const& path, uint32 start, uint32 end)
{
uint32 traveltime = uint32(path.GetTotalLength(start, end) * 32);
uint32 pathSize = end - start;
WorldPacket data(SMSG_MONSTER_MOVE, (GetPackGUID().size()+1+4+4+4+4+1+4+4+4+pathSize*4*3));
data.append(GetPackGUID());
data << uint8(0);
data << GetPositionX();
data << GetPositionY();
data << GetPositionZ();
data << uint32(getMSTime());
data << uint8(0);
data << uint32(((GetUnitMovementFlags() & MOVEMENTFLAG_LEVITATING) || isInFlight()) ? (SPLINEFLAG_FLYING|SPLINEFLAG_WALKING) : SPLINEFLAG_WALKING);
data << uint32(traveltime);
data << uint32(pathSize);
for (uint32 i = start; i < end; ++i)
{
data << float(path[i].x);
data << float(path[i].y);
data << float(path[i].z);
}
SendMessageToSet(&data, true);
}
#endif