Skip to content

Commit 2ec8982

Browse files
authored
Pets on Transports (#271)
1 parent 37120ec commit 2ec8982

8 files changed

Lines changed: 274 additions & 23 deletions

File tree

src/game/MotionGenerators/TargetedMovementGenerator.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,27 @@ void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T& owner, bool up
9696
// allow pets following their master to cheat while generating paths
9797
bool forceDest = (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->IsPet()
9898
&& owner.hasUnitState(UNIT_STAT_FOLLOW));
99+
100+
if (forceDest &&
101+
(owner.m_movementInfo.HasMovementFlag(MOVEFLAG_ONTRANSPORT) ||
102+
i_target->m_movementInfo.HasMovementFlag(MOVEFLAG_ONTRANSPORT)))
103+
{
104+
bool outMoved = false;
105+
if (((Creature*)&owner)->HandleTransportFollow(
106+
i_target.getTarget(), i_offset, i_angle, ((D*)this)->EnableWalking(), outMoved))
107+
{
108+
if (outMoved)
109+
{
110+
D::_addUnitStateMove(owner);
111+
i_targetReached = false;
112+
m_speedChanged = false;
113+
}
114+
return;
115+
}
116+
}
117+
99118
i_path->calculate(x, y, z, forceDest);
119+
100120
if (i_path->getPathType() & PATHFIND_NOPATH)
101121
{
102122
return;

src/game/Object/Creature.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,8 @@ class Creature : public Unit
554554
bool IsSwimming() const { return (m_movementInfo.HasMovementFlag((MovementFlags)(MOVEFLAG_SWIMMING))); }
555555
bool CanFly() const override { return (GetCreatureInfo()->InhabitType & INHABIT_AIR) || m_movementInfo.HasMovementFlag((MovementFlags)(MOVEFLAG_LEVITATING | MOVEFLAG_CAN_FLY)); }
556556
bool IsFlying() const { return (m_movementInfo.HasMovementFlag((MovementFlags)(MOVEFLAG_FLYING | MOVEFLAG_LEVITATING))); }
557+
558+
virtual bool HandleTransportFollow(Unit* /*target*/, float /*offset*/, float /*angle*/, bool /*walking*/, bool& outMoved) { outMoved = false; return false; }
557559
bool IsTrainerOf(Player* player, bool msg) const;
558560
bool CanInteractWithBattleMaster(Player* player, bool msg) const;
559561
bool CanTrainAndResetTalentsOf(Player* pPlayer) const;

src/game/Object/Pet.cpp

Lines changed: 186 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@
3131
#include "Formulas.h"
3232
#include "SpellAuras.h"
3333
#include "Unit.h"
34+
#include "Transports.h"
35+
#include "movement/MoveSpline.h"
36+
#include "movement/MoveSplineInit.h"
37+
3438

3539
// numbers represent minutes * 100 while happy (you get 100 loyalty points per min while happy)
3640
uint32 const LevelUpLoyalty[6] =
@@ -56,7 +60,7 @@ uint32 const LevelStartLoyalty[6] =
5660
Pet::Pet(PetType type) :
5761
Creature(CREATURE_SUBTYPE_PET),
5862
m_TrainingPoints(0), m_resetTalentsCost(0), m_resetTalentsTime(0),
59-
m_removed(false), m_happinessTimer(7500), m_loyaltyTimer(12000), m_petType(type), m_duration(0),
63+
m_removed(false), m_pendingTransportReboard(false), m_transport(nullptr), m_happinessTimer(7500), m_loyaltyTimer(12000), m_petType(type), m_duration(0),
6064
m_loyaltyPoints(0), m_bonusdamage(0), m_auraUpdateMask(0), m_loading(false),
6165
m_petModeFlags(PET_MODE_DEFAULT)
6266
{
@@ -351,6 +355,20 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petentry, uint32 petnumber, bool c
351355

352356
}
353357

358+
Player* p_owner = owner->GetTypeId() == TYPEID_PLAYER ? (Player*)owner : nullptr;
359+
Transport* ownerTransport = p_owner ? p_owner->GetTransport() : nullptr;
360+
361+
if (ownerTransport)
362+
{
363+
Position const* tpos = p_owner->m_movementInfo.GetTransportPos();
364+
float petTo = tpos->o;
365+
float petTx = tpos->x + cos(petTo + PET_FOLLOW_ANGLE) * PET_FOLLOW_DIST;
366+
float petTy = tpos->y + sin(petTo + PET_FOLLOW_ANGLE) * PET_FOLLOW_DIST;
367+
float petTz = tpos->z;
368+
m_movementInfo.SetTransportData(ownerTransport->GetObjectGuid(), petTx, petTy, petTz, petTo, 0);
369+
m_movementInfo.AddMovementFlag(MOVEFLAG_ONTRANSPORT);
370+
}
371+
354372
map->Add((Creature*)this);
355373
AIM_Initialize();
356374

@@ -363,17 +381,25 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petentry, uint32 petnumber, bool c
363381
owner->SetPet(this); // in DB stored only full controlled creature
364382
DEBUG_LOG("New Pet has guid %u", GetGUIDLow());
365383

366-
if (owner->GetTypeId() == TYPEID_PLAYER)
384+
if (p_owner)
367385
{
368-
((Player*)owner)->PetSpellInitialize();
369-
if (((Player*)owner)->GetGroup())
386+
p_owner->PetSpellInitialize();
387+
if (p_owner->GetGroup())
370388
{
371-
((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_PET);
389+
p_owner->SetGroupUpdateFlag(GROUP_UPDATE_PET);
372390
}
373391
}
374392

375393
m_loading = false;
376394

395+
if (ownerTransport)
396+
{
397+
ownerTransport->AddPassenger(this);
398+
m_transport = ownerTransport;
399+
GetMotionMaster()->Clear(false);
400+
m_pendingTransportReboard = true;
401+
}
402+
377403
SynchronizeLevelWithOwner();
378404
return true;
379405
}
@@ -619,13 +645,14 @@ void Pet::Update(uint32 update_diff, uint32 diff)
619645
// unsummon pet that lost owner
620646
Unit* owner = GetOwner();
621647
if (!owner ||
622-
(!IsWithinDistInMap(owner, GetMap()->GetVisibilityDistance()) && (owner->GetCharmGuid() && (owner->GetCharmGuid() != GetObjectGuid()))) ||
648+
(!m_transport && !IsWithinDistInMap(owner, GetMap()->GetVisibilityDistance()) && (owner->GetCharmGuid() && (owner->GetCharmGuid() != GetObjectGuid()))) ||
623649
(isControlled() && !owner->GetPetGuid()))
624650
{
625651
Unsummon(PET_SAVE_REAGENTS);
626652
return;
627653
}
628654

655+
629656
if (isControlled())
630657
{
631658
if (owner->GetPetGuid() != GetObjectGuid())
@@ -647,6 +674,10 @@ void Pet::Update(uint32 update_diff, uint32 diff)
647674
return;
648675
}
649676
}
677+
678+
if (Player* plOwner = owner->ToPlayer())
679+
UpdateTransport(plOwner);
680+
650681
break;
651682
}
652683
default:
@@ -656,6 +687,144 @@ void Pet::Update(uint32 update_diff, uint32 diff)
656687
Creature::Update(update_diff, diff);
657688
}
658689

690+
void Pet::UpdateTransport(Player* plOwner)
691+
{
692+
Transport* tr = plOwner->GetTransport();
693+
694+
// Disembark if the owner has left the transport but the pet hasn't yet.
695+
if (m_transport && tr != m_transport)
696+
{
697+
m_transport->RemovePassenger(this);
698+
m_transport = nullptr;
699+
m_movementInfo.RemoveMovementFlag(MOVEFLAG_ONTRANSPORT);
700+
m_movementInfo.ClearTransportData();
701+
NearTeleportTo(plOwner->GetPositionX(), plOwner->GetPositionY(),
702+
plOwner->GetPositionZ(), plOwner->GetOrientation());
703+
}
704+
else // Board pet onto transport once it has swum/walked close enough to the owner.
705+
if (!m_transport && tr)
706+
{
707+
float dx = GetPositionX() - plOwner->GetPositionX();
708+
float dy = GetPositionY() - plOwner->GetPositionY();
709+
float dist2d = sqrt(dx*dx + dy*dy);
710+
if (dist2d <= 6.0f)
711+
{
712+
tr->AddPassenger(this);
713+
m_transport = tr;
714+
Position const* tpos = plOwner->m_movementInfo.GetTransportPos();
715+
m_movementInfo.SetTransportData(tr->GetObjectGuid(), tpos->x, tpos->y, tpos->z, tpos->o, 0);
716+
m_movementInfo.AddMovementFlag(MOVEFLAG_ONTRANSPORT);
717+
m_movementInfo.ChangePosition(plOwner->GetPositionX(), plOwner->GetPositionY(), plOwner->GetPositionZ(), plOwner->GetOrientation());
718+
GetMotionMaster()->Clear(false);
719+
if (GetCharmInfo())
720+
GetCharmInfo()->SetCommandState(COMMAND_FOLLOW);
721+
NearTeleportTo(plOwner->GetPositionX(), plOwner->GetPositionY(), plOwner->GetPositionZ(), plOwner->GetOrientation());
722+
if (movespline)
723+
{
724+
WorldPacket moveData(SMSG_MONSTER_MOVE_TRANSPORT, 64);
725+
moveData << GetPackGUID();
726+
moveData << tr->GetPackGUID();
727+
moveData << tpos->x << tpos->y << tpos->z;
728+
moveData << movespline->GetId();
729+
moveData << uint8(Movement::MonsterMoveStop);
730+
SendMessageToSet(&moveData, true);
731+
}
732+
SendCreateUpdateToPlayer(plOwner);
733+
}
734+
}
735+
736+
if (m_pendingTransportReboard)
737+
{
738+
m_pendingTransportReboard = false;
739+
plOwner->PetSpellInitialize();
740+
if (tr && movespline)
741+
{
742+
Position const* tpos = m_movementInfo.GetTransportPos();
743+
WorldPacket moveData(SMSG_MONSTER_MOVE_TRANSPORT, 64);
744+
moveData << GetPackGUID();
745+
moveData << tr->GetPackGUID();
746+
moveData << tpos->x << tpos->y << tpos->z;
747+
moveData << movespline->GetId();
748+
moveData << uint8(Movement::MonsterMoveStop);
749+
plOwner->SendDirectMessage(&moveData);
750+
}
751+
SendCreateUpdateToPlayer(plOwner);
752+
}
753+
}
754+
755+
bool Pet::HandleTransportFollow(Unit* target, float offset, float angle, bool walking, bool& outMoved)
756+
{
757+
outMoved = false;
758+
if (!target || target->GetTypeId() != TYPEID_PLAYER)
759+
return false;
760+
761+
Transport* masterTransport = static_cast<Player*>(target)->GetTransport();
762+
if (!masterTransport)
763+
return false;
764+
765+
if (m_transport == masterTransport)
766+
{
767+
// Both on the same transport — move in transport-relative space.
768+
float tx = masterTransport->GetPositionX();
769+
float ty = masterTransport->GetPositionY();
770+
float tz = masterTransport->GetPositionZ();
771+
float to = masterTransport->GetOrientation();
772+
float cos_o = cos(to), sin_o = sin(to);
773+
774+
Position const* curRelPos = m_movementInfo.GetTransportPos();
775+
float startRelX = curRelPos->x, startRelY = curRelPos->y, startRelZ = curRelPos->z;
776+
777+
Position const* masterTPos = target->m_movementInfo.GetTransportPos();
778+
float followDist = offset + GetObjectBoundingRadius() + target->GetObjectBoundingRadius();
779+
float destRelX = masterTPos->x + cos(angle) * followDist;
780+
float destRelY = masterTPos->y + sin(angle) * followDist;
781+
float destRelZ = masterTPos->z;
782+
783+
float relDx = destRelX - startRelX, relDy = destRelY - startRelY, relDz = destRelZ - startRelZ;
784+
float relDist = sqrt(relDx * relDx + relDy * relDy + relDz * relDz);
785+
if (relDist < 0.5f)
786+
return true;
787+
788+
m_movementInfo.SetTransportData(masterTransport->GetObjectGuid(), destRelX, destRelY, destRelZ, 0.0f, 0);
789+
790+
float speed = GetSpeed(walking ? MOVE_WALK : MOVE_RUN);
791+
uint32 durationMs = (speed > 0.0f) ? uint32(relDist / speed * 1000.0f) : 0;
792+
793+
float destWX = tx + cos_o * destRelX - sin_o * destRelY;
794+
float destWY = ty + sin_o * destRelX + cos_o * destRelY;
795+
float destWZ = tz + destRelZ;
796+
GetMap()->CreatureRelocation(this, destWX, destWY, destWZ, GetOrientation());
797+
798+
if (durationMs > 0)
799+
{
800+
WorldPacket moveTransport(SMSG_MONSTER_MOVE_TRANSPORT, 80);
801+
moveTransport << GetPackGUID();
802+
moveTransport << masterTransport->GetPackGUID();
803+
moveTransport << startRelX << startRelY << startRelZ;
804+
moveTransport << movespline->GetId();
805+
moveTransport << uint8(Movement::MonsterMoveNormal);
806+
moveTransport << uint32(Movement::MoveSplineFlag::Runmode);
807+
moveTransport << durationMs;
808+
moveTransport << uint32(0);
809+
moveTransport << destRelX << destRelY << destRelZ;
810+
SendMessageToSet(&moveTransport, true);
811+
}
812+
813+
outMoved = true;
814+
return true;
815+
}
816+
817+
// Master is on a transport the pet hasn't boarded yet — approach in world space.
818+
float tx, ty, tz;
819+
target->GetPosition(tx, ty, tz);
820+
Movement::MoveSplineInit init(*this);
821+
init.MoveTo(tx, ty, tz);
822+
init.SetWalk(walking);
823+
init.Launch();
824+
outMoved = true;
825+
return true;
826+
}
827+
659828
void Pet::RegenerateAll(uint32 update_diff)
660829
{
661830
// regenerate focus
@@ -1049,6 +1218,17 @@ void Pet::Unsummon(PetSaveMode mode, Unit* owner /*= NULL*/)
10491218
}
10501219
break;
10511220
}
1221+
1222+
if (m_transport)
1223+
{
1224+
m_transport->RemovePassenger(this);
1225+
m_transport = nullptr;
1226+
}
1227+
}
1228+
else if (m_transport)
1229+
{
1230+
m_transport->RemovePassenger(this);
1231+
m_transport = nullptr;
10521232
}
10531233

10541234
SavePetToDB(mode);

src/game/Object/Pet.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include "Creature.h"
3131
#include "Unit.h"
3232

33+
class Transport;
34+
3335
enum PetType
3436
{
3537
SUMMON_PET = 0,
@@ -298,6 +300,13 @@ class Pet : public Creature
298300
const char* GetNameForLocaleIdx(int32 locale_idx) const override { return WorldObject::GetNameForLocaleIdx(locale_idx); }
299301

300302
bool m_removed; // prevent overwrite pet state in DB at next Pet::Update if pet already removed(saved)
303+
304+
bool HandleTransportFollow(Unit* target, float offset, float angle, bool walking, bool& outMoved) override;
305+
306+
Transport* GetTransport() const { return m_transport; }
307+
void SetTransport(Transport* t) { m_transport = t; }
308+
void SetPendingTransportReboard() { m_pendingTransportReboard = true; }
309+
301310
protected:
302311
uint32 m_happinessTimer;
303312
uint32 m_loyaltyTimer;
@@ -309,6 +318,11 @@ class Pet : public Creature
309318
bool m_loading;
310319

311320
private:
321+
void UpdateTransport(Player* plOwner);
322+
323+
bool m_pendingTransportReboard; ///< fires PetSpellInitialize + SMSG_MONSTER_MOVE_TRANSPORT on next tick after map re-add
324+
Transport* m_transport; ///< transport this pet is riding; set/cleared alongside AddPassenger/RemovePassenger
325+
312326
PetModeFlags m_petModeFlags;
313327

314328
void SaveToDB(uint32) override // overwrited of Creature::SaveToDB - don't must be called

src/game/Object/Player.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20901,6 +20901,9 @@ void Player::UpdateVisibilityOf(WorldObject const* viewPoint, WorldObject* targe
2090120901

2090220902
if (target->GetTypeId() == TYPEID_UNIT)
2090320903
{
20904+
Creature* c = target->ToCreature();
20905+
if (c->IsPet() && GetPetGuid() == c->GetObjectGuid() && ((Pet*)c)->GetTransport())
20906+
return;
2090420907
BeforeVisibilityDestroy(target, this);
2090520908
}
2090620909

@@ -20939,6 +20942,13 @@ void Player::UpdateVisibilityOf(WorldObject const* viewPoint, WorldObject* targe
2093920942
{
2094020943
if (!target->IsVisibleForInState(this, viewPoint, true))
2094120944
{
20945+
if (target->GetTypeId() == TYPEID_UNIT)
20946+
{
20947+
Creature* c = target->ToCreature();
20948+
if (c->IsPet() && GetPetGuid() == c->GetObjectGuid() && ((Pet*)c)->GetTransport())
20949+
return;
20950+
}
20951+
2094220952
BeforeVisibilityDestroy(target, this);
2094320953

2094420954
ObjectGuid t_guid = target->GetObjectGuid();
@@ -23049,6 +23059,12 @@ void Player::UnsummonPetTemporaryIfAny()
2304923059
m_temporaryUnsummonedPetNumber = pet->GetCharmInfo()->GetPetNumber();
2305023060
}
2305123061

23062+
if (Transport* petTransport = pet->GetTransport())
23063+
{
23064+
petTransport->RemovePassenger(pet);
23065+
pet->SetTransport(nullptr);
23066+
}
23067+
2305223068
pet->Unsummon(PET_SAVE_AS_CURRENT, this);
2305323069
}
2305423070

src/game/WorldHandlers/MovementHandler.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
#define MOVEMENT_PACKET_TIME_DELAY 300
3939

40+
4041
void WorldSession::HandleMoveWorldportAckOpcode(WorldPacket& /*recv_data*/)
4142
{
4243
DEBUG_LOG("WORLD: got MSG_MOVE_WORLDPORT_ACK.");

0 commit comments

Comments
 (0)