diff --git a/src/game/MotionGenerators/TargetedMovementGenerator.cpp b/src/game/MotionGenerators/TargetedMovementGenerator.cpp index 67438adbb..31ee29161 100644 --- a/src/game/MotionGenerators/TargetedMovementGenerator.cpp +++ b/src/game/MotionGenerators/TargetedMovementGenerator.cpp @@ -96,7 +96,27 @@ void TargetedMovementGeneratorMedium::_setTargetLocation(T& owner, bool up // allow pets following their master to cheat while generating paths bool forceDest = (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->IsPet() && owner.hasUnitState(UNIT_STAT_FOLLOW)); + + if (forceDest && + (owner.m_movementInfo.HasMovementFlag(MOVEFLAG_ONTRANSPORT) || + i_target->m_movementInfo.HasMovementFlag(MOVEFLAG_ONTRANSPORT))) + { + bool outMoved = false; + if (((Creature*)&owner)->HandleTransportFollow( + i_target.getTarget(), i_offset, i_angle, ((D*)this)->EnableWalking(), outMoved)) + { + if (outMoved) + { + D::_addUnitStateMove(owner); + i_targetReached = false; + m_speedChanged = false; + } + return; + } + } + i_path->calculate(x, y, z, forceDest); + if (i_path->getPathType() & PATHFIND_NOPATH) { return; diff --git a/src/game/Object/Creature.h b/src/game/Object/Creature.h index 8e19d54a4..c8f132836 100644 --- a/src/game/Object/Creature.h +++ b/src/game/Object/Creature.h @@ -554,6 +554,8 @@ class Creature : public Unit bool IsSwimming() const { return (m_movementInfo.HasMovementFlag((MovementFlags)(MOVEFLAG_SWIMMING))); } bool CanFly() const override { return (GetCreatureInfo()->InhabitType & INHABIT_AIR) || m_movementInfo.HasMovementFlag((MovementFlags)(MOVEFLAG_LEVITATING | MOVEFLAG_CAN_FLY)); } bool IsFlying() const { return (m_movementInfo.HasMovementFlag((MovementFlags)(MOVEFLAG_FLYING | MOVEFLAG_LEVITATING))); } + + virtual bool HandleTransportFollow(Unit* /*target*/, float /*offset*/, float /*angle*/, bool /*walking*/, bool& outMoved) { outMoved = false; return false; } bool IsTrainerOf(Player* player, bool msg) const; bool CanInteractWithBattleMaster(Player* player, bool msg) const; bool CanTrainAndResetTalentsOf(Player* pPlayer) const; diff --git a/src/game/Object/Pet.cpp b/src/game/Object/Pet.cpp index 01d89f546..eb0fec97b 100644 --- a/src/game/Object/Pet.cpp +++ b/src/game/Object/Pet.cpp @@ -31,6 +31,10 @@ #include "Formulas.h" #include "SpellAuras.h" #include "Unit.h" +#include "Transports.h" +#include "movement/MoveSpline.h" +#include "movement/MoveSplineInit.h" + // numbers represent minutes * 100 while happy (you get 100 loyalty points per min while happy) uint32 const LevelUpLoyalty[6] = @@ -56,7 +60,7 @@ uint32 const LevelStartLoyalty[6] = Pet::Pet(PetType type) : Creature(CREATURE_SUBTYPE_PET), m_TrainingPoints(0), m_resetTalentsCost(0), m_resetTalentsTime(0), - m_removed(false), m_happinessTimer(7500), m_loyaltyTimer(12000), m_petType(type), m_duration(0), + m_removed(false), m_pendingTransportReboard(false), m_transport(nullptr), m_happinessTimer(7500), m_loyaltyTimer(12000), m_petType(type), m_duration(0), m_loyaltyPoints(0), m_bonusdamage(0), m_auraUpdateMask(0), m_loading(false), m_petModeFlags(PET_MODE_DEFAULT) { @@ -351,6 +355,20 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petentry, uint32 petnumber, bool c } + Player* p_owner = owner->GetTypeId() == TYPEID_PLAYER ? (Player*)owner : nullptr; + Transport* ownerTransport = p_owner ? p_owner->GetTransport() : nullptr; + + if (ownerTransport) + { + Position const* tpos = p_owner->m_movementInfo.GetTransportPos(); + float petTo = tpos->o; + float petTx = tpos->x + cos(petTo + PET_FOLLOW_ANGLE) * PET_FOLLOW_DIST; + float petTy = tpos->y + sin(petTo + PET_FOLLOW_ANGLE) * PET_FOLLOW_DIST; + float petTz = tpos->z; + m_movementInfo.SetTransportData(ownerTransport->GetObjectGuid(), petTx, petTy, petTz, petTo, 0); + m_movementInfo.AddMovementFlag(MOVEFLAG_ONTRANSPORT); + } + map->Add((Creature*)this); AIM_Initialize(); @@ -363,17 +381,25 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petentry, uint32 petnumber, bool c owner->SetPet(this); // in DB stored only full controlled creature DEBUG_LOG("New Pet has guid %u", GetGUIDLow()); - if (owner->GetTypeId() == TYPEID_PLAYER) + if (p_owner) { - ((Player*)owner)->PetSpellInitialize(); - if (((Player*)owner)->GetGroup()) + p_owner->PetSpellInitialize(); + if (p_owner->GetGroup()) { - ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_PET); + p_owner->SetGroupUpdateFlag(GROUP_UPDATE_PET); } } m_loading = false; + if (ownerTransport) + { + ownerTransport->AddPassenger(this); + m_transport = ownerTransport; + GetMotionMaster()->Clear(false); + m_pendingTransportReboard = true; + } + SynchronizeLevelWithOwner(); return true; } @@ -619,13 +645,14 @@ void Pet::Update(uint32 update_diff, uint32 diff) // unsummon pet that lost owner Unit* owner = GetOwner(); if (!owner || - (!IsWithinDistInMap(owner, GetMap()->GetVisibilityDistance()) && (owner->GetCharmGuid() && (owner->GetCharmGuid() != GetObjectGuid()))) || + (!m_transport && !IsWithinDistInMap(owner, GetMap()->GetVisibilityDistance()) && (owner->GetCharmGuid() && (owner->GetCharmGuid() != GetObjectGuid()))) || (isControlled() && !owner->GetPetGuid())) { Unsummon(PET_SAVE_REAGENTS); return; } + if (isControlled()) { if (owner->GetPetGuid() != GetObjectGuid()) @@ -647,6 +674,10 @@ void Pet::Update(uint32 update_diff, uint32 diff) return; } } + + if (Player* plOwner = owner->ToPlayer()) + UpdateTransport(plOwner); + break; } default: @@ -656,6 +687,144 @@ void Pet::Update(uint32 update_diff, uint32 diff) Creature::Update(update_diff, diff); } +void Pet::UpdateTransport(Player* plOwner) +{ + Transport* tr = plOwner->GetTransport(); + + // Disembark if the owner has left the transport but the pet hasn't yet. + if (m_transport && tr != m_transport) + { + m_transport->RemovePassenger(this); + m_transport = nullptr; + m_movementInfo.RemoveMovementFlag(MOVEFLAG_ONTRANSPORT); + m_movementInfo.ClearTransportData(); + NearTeleportTo(plOwner->GetPositionX(), plOwner->GetPositionY(), + plOwner->GetPositionZ(), plOwner->GetOrientation()); + } + else // Board pet onto transport once it has swum/walked close enough to the owner. + if (!m_transport && tr) + { + float dx = GetPositionX() - plOwner->GetPositionX(); + float dy = GetPositionY() - plOwner->GetPositionY(); + float dist2d = sqrt(dx*dx + dy*dy); + if (dist2d <= 6.0f) + { + tr->AddPassenger(this); + m_transport = tr; + Position const* tpos = plOwner->m_movementInfo.GetTransportPos(); + m_movementInfo.SetTransportData(tr->GetObjectGuid(), tpos->x, tpos->y, tpos->z, tpos->o, 0); + m_movementInfo.AddMovementFlag(MOVEFLAG_ONTRANSPORT); + m_movementInfo.ChangePosition(plOwner->GetPositionX(), plOwner->GetPositionY(), plOwner->GetPositionZ(), plOwner->GetOrientation()); + GetMotionMaster()->Clear(false); + if (GetCharmInfo()) + GetCharmInfo()->SetCommandState(COMMAND_FOLLOW); + NearTeleportTo(plOwner->GetPositionX(), plOwner->GetPositionY(), plOwner->GetPositionZ(), plOwner->GetOrientation()); + if (movespline) + { + WorldPacket moveData(SMSG_MONSTER_MOVE_TRANSPORT, 64); + moveData << GetPackGUID(); + moveData << tr->GetPackGUID(); + moveData << tpos->x << tpos->y << tpos->z; + moveData << movespline->GetId(); + moveData << uint8(Movement::MonsterMoveStop); + SendMessageToSet(&moveData, true); + } + SendCreateUpdateToPlayer(plOwner); + } + } + + if (m_pendingTransportReboard) + { + m_pendingTransportReboard = false; + plOwner->PetSpellInitialize(); + if (tr && movespline) + { + Position const* tpos = m_movementInfo.GetTransportPos(); + WorldPacket moveData(SMSG_MONSTER_MOVE_TRANSPORT, 64); + moveData << GetPackGUID(); + moveData << tr->GetPackGUID(); + moveData << tpos->x << tpos->y << tpos->z; + moveData << movespline->GetId(); + moveData << uint8(Movement::MonsterMoveStop); + plOwner->SendDirectMessage(&moveData); + } + SendCreateUpdateToPlayer(plOwner); + } +} + +bool Pet::HandleTransportFollow(Unit* target, float offset, float angle, bool walking, bool& outMoved) +{ + outMoved = false; + if (!target || target->GetTypeId() != TYPEID_PLAYER) + return false; + + Transport* masterTransport = static_cast(target)->GetTransport(); + if (!masterTransport) + return false; + + if (m_transport == masterTransport) + { + // Both on the same transport — move in transport-relative space. + float tx = masterTransport->GetPositionX(); + float ty = masterTransport->GetPositionY(); + float tz = masterTransport->GetPositionZ(); + float to = masterTransport->GetOrientation(); + float cos_o = cos(to), sin_o = sin(to); + + Position const* curRelPos = m_movementInfo.GetTransportPos(); + float startRelX = curRelPos->x, startRelY = curRelPos->y, startRelZ = curRelPos->z; + + Position const* masterTPos = target->m_movementInfo.GetTransportPos(); + float followDist = offset + GetObjectBoundingRadius() + target->GetObjectBoundingRadius(); + float destRelX = masterTPos->x + cos(angle) * followDist; + float destRelY = masterTPos->y + sin(angle) * followDist; + float destRelZ = masterTPos->z; + + float relDx = destRelX - startRelX, relDy = destRelY - startRelY, relDz = destRelZ - startRelZ; + float relDist = sqrt(relDx * relDx + relDy * relDy + relDz * relDz); + if (relDist < 0.5f) + return true; + + m_movementInfo.SetTransportData(masterTransport->GetObjectGuid(), destRelX, destRelY, destRelZ, 0.0f, 0); + + float speed = GetSpeed(walking ? MOVE_WALK : MOVE_RUN); + uint32 durationMs = (speed > 0.0f) ? uint32(relDist / speed * 1000.0f) : 0; + + float destWX = tx + cos_o * destRelX - sin_o * destRelY; + float destWY = ty + sin_o * destRelX + cos_o * destRelY; + float destWZ = tz + destRelZ; + GetMap()->CreatureRelocation(this, destWX, destWY, destWZ, GetOrientation()); + + if (durationMs > 0) + { + WorldPacket moveTransport(SMSG_MONSTER_MOVE_TRANSPORT, 80); + moveTransport << GetPackGUID(); + moveTransport << masterTransport->GetPackGUID(); + moveTransport << startRelX << startRelY << startRelZ; + moveTransport << movespline->GetId(); + moveTransport << uint8(Movement::MonsterMoveNormal); + moveTransport << uint32(Movement::MoveSplineFlag::Runmode); + moveTransport << durationMs; + moveTransport << uint32(0); + moveTransport << destRelX << destRelY << destRelZ; + SendMessageToSet(&moveTransport, true); + } + + outMoved = true; + return true; + } + + // Master is on a transport the pet hasn't boarded yet — approach in world space. + float tx, ty, tz; + target->GetPosition(tx, ty, tz); + Movement::MoveSplineInit init(*this); + init.MoveTo(tx, ty, tz); + init.SetWalk(walking); + init.Launch(); + outMoved = true; + return true; +} + void Pet::RegenerateAll(uint32 update_diff) { // regenerate focus @@ -1049,6 +1218,17 @@ void Pet::Unsummon(PetSaveMode mode, Unit* owner /*= NULL*/) } break; } + + if (m_transport) + { + m_transport->RemovePassenger(this); + m_transport = nullptr; + } + } + else if (m_transport) + { + m_transport->RemovePassenger(this); + m_transport = nullptr; } SavePetToDB(mode); diff --git a/src/game/Object/Pet.h b/src/game/Object/Pet.h index bcf031904..13bf1d624 100644 --- a/src/game/Object/Pet.h +++ b/src/game/Object/Pet.h @@ -30,6 +30,8 @@ #include "Creature.h" #include "Unit.h" +class Transport; + enum PetType { SUMMON_PET = 0, @@ -298,6 +300,13 @@ class Pet : public Creature const char* GetNameForLocaleIdx(int32 locale_idx) const override { return WorldObject::GetNameForLocaleIdx(locale_idx); } bool m_removed; // prevent overwrite pet state in DB at next Pet::Update if pet already removed(saved) + + bool HandleTransportFollow(Unit* target, float offset, float angle, bool walking, bool& outMoved) override; + + Transport* GetTransport() const { return m_transport; } + void SetTransport(Transport* t) { m_transport = t; } + void SetPendingTransportReboard() { m_pendingTransportReboard = true; } + protected: uint32 m_happinessTimer; uint32 m_loyaltyTimer; @@ -309,6 +318,11 @@ class Pet : public Creature bool m_loading; private: + void UpdateTransport(Player* plOwner); + + bool m_pendingTransportReboard; ///< fires PetSpellInitialize + SMSG_MONSTER_MOVE_TRANSPORT on next tick after map re-add + Transport* m_transport; ///< transport this pet is riding; set/cleared alongside AddPassenger/RemovePassenger + PetModeFlags m_petModeFlags; void SaveToDB(uint32) override // overwrited of Creature::SaveToDB - don't must be called diff --git a/src/game/Object/Player.cpp b/src/game/Object/Player.cpp index 6fe70655c..bb5f4bdc0 100644 --- a/src/game/Object/Player.cpp +++ b/src/game/Object/Player.cpp @@ -20901,6 +20901,9 @@ void Player::UpdateVisibilityOf(WorldObject const* viewPoint, WorldObject* targe if (target->GetTypeId() == TYPEID_UNIT) { + Creature* c = target->ToCreature(); + if (c->IsPet() && GetPetGuid() == c->GetObjectGuid() && ((Pet*)c)->GetTransport()) + return; BeforeVisibilityDestroy(target, this); } @@ -20939,6 +20942,13 @@ void Player::UpdateVisibilityOf(WorldObject const* viewPoint, WorldObject* targe { if (!target->IsVisibleForInState(this, viewPoint, true)) { + if (target->GetTypeId() == TYPEID_UNIT) + { + Creature* c = target->ToCreature(); + if (c->IsPet() && GetPetGuid() == c->GetObjectGuid() && ((Pet*)c)->GetTransport()) + return; + } + BeforeVisibilityDestroy(target, this); ObjectGuid t_guid = target->GetObjectGuid(); @@ -23049,6 +23059,12 @@ void Player::UnsummonPetTemporaryIfAny() m_temporaryUnsummonedPetNumber = pet->GetCharmInfo()->GetPetNumber(); } + if (Transport* petTransport = pet->GetTransport()) + { + petTransport->RemovePassenger(pet); + pet->SetTransport(nullptr); + } + pet->Unsummon(PET_SAVE_AS_CURRENT, this); } diff --git a/src/game/WorldHandlers/MovementHandler.cpp b/src/game/WorldHandlers/MovementHandler.cpp index 44b59aa1e..87afb14c4 100644 --- a/src/game/WorldHandlers/MovementHandler.cpp +++ b/src/game/WorldHandlers/MovementHandler.cpp @@ -37,6 +37,7 @@ #define MOVEMENT_PACKET_TIME_DELAY 300 + void WorldSession::HandleMoveWorldportAckOpcode(WorldPacket& /*recv_data*/) { DEBUG_LOG("WORLD: got MSG_MOVE_WORLDPORT_ACK."); diff --git a/src/game/WorldHandlers/Transports.cpp b/src/game/WorldHandlers/Transports.cpp index 125235d17..ede3b7d98 100644 --- a/src/game/WorldHandlers/Transports.cpp +++ b/src/game/WorldHandlers/Transports.cpp @@ -26,6 +26,7 @@ #include "Transports.h" #include "Map.h" +#include "Creature.h" #include "MapManager.h" #include "ObjectMgr.h" #include "ObjectGuid.h" @@ -161,6 +162,28 @@ Transport::~Transport() { } +void Transport::UpdateCreaturePassengerPositions() +{ + float tx = GetPositionX(); + float ty = GetPositionY(); + float tz = GetPositionZ(); + float to = GetOrientation(); + float cos_o = cos(to); + float sin_o = sin(to); + for (Unit* unit : m_passengers) + { + if (Creature* c = unit->ToCreature()) + { + Position const* tpos = c->m_movementInfo.GetTransportPos(); + float wx = tx + cos_o * tpos->x - sin_o * tpos->y; + float wy = ty + sin_o * tpos->x + cos_o * tpos->y; + float wz = tz + tpos->z; + float wo = to + tpos->o; + GetMap()->CreatureRelocation(c, wx, wy, wz, wo); + } + } +} + bool Transport::AddPassenger(Unit* passenger) { if (m_passengers.find(passenger) == m_passengers.end()) @@ -646,26 +669,19 @@ void GlobalTransport::TeleportTransport(uint32 newMapid, float x, float y, float } #endif - for (UnitSet::iterator itr = m_passengers.begin(); itr != m_passengers.end();) - { - UnitSet::iterator it2 = itr; - ++itr; - - Unit* unit = *it2; - if (!unit) - { - m_passengers.erase(it2); - continue; - } - + // Snapshot: TeleportTo() may call RemovePassenger(), invalidating a live iterator. + std::vector playersToTeleport; + for (Unit* unit : m_passengers) if (Player* plr = unit->ToPlayer()) + playersToTeleport.push_back(plr); + + for (Player* plr : playersToTeleport) + { + if (plr->IsDead() && !plr->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) { - if (plr->IsDead() && !plr->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) - { - plr->ResurrectPlayer(1.0); - } - plr->TeleportTo(newMapid, x, y, z, GetOrientation(), TELE_TO_NOT_LEAVE_TRANSPORT); + plr->ResurrectPlayer(1.0); } + plr->TeleportTo(newMapid, x, y, z, GetOrientation(), TELE_TO_NOT_LEAVE_TRANSPORT); } if (oldMap != newMap) @@ -695,6 +711,7 @@ void GlobalTransport::Update(uint32 /*update_diff*/, uint32 /*p_time*/) else { Relocate(m_curr->second.x, m_curr->second.y, m_curr->second.z); + UpdateCreaturePassengerPositions(); } m_nextNodeTime = m_curr->first; diff --git a/src/game/WorldHandlers/Transports.h b/src/game/WorldHandlers/Transports.h index 3c32bddbc..f3b8aa6be 100644 --- a/src/game/WorldHandlers/Transports.h +++ b/src/game/WorldHandlers/Transports.h @@ -49,6 +49,7 @@ class Transport : public GameObject protected: UnitSet m_passengers; + void UpdateCreaturePassengerPositions(); // relocate creature passengers to transport's world position };