diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index c9434cffb..2a9403954 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -2082,7 +2082,7 @@ std::string Entity::GetVarAsString(const std::u16string& name) const { void Entity::Resurrect() { if (IsPlayer()) { - GameMessages::SendResurrect(this); + DestroyableComponent::Resurrect(*this); } } diff --git a/dGame/dComponents/BuffComponent.cpp b/dGame/dComponents/BuffComponent.cpp index 95a9c4508..82b856ce0 100644 --- a/dGame/dComponents/BuffComponent.cpp +++ b/dGame/dComponents/BuffComponent.cpp @@ -7,6 +7,7 @@ #include "Logger.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "CombatMessages.h" #include "SkillComponent.h" #include "ControllablePhysicsComponent.h" #include "EntityManager.h" @@ -157,9 +158,22 @@ void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOO addedByTeammate = std::count(team->members.begin(), team->members.end(), m_Parent->GetObjectID()) > 0; } - GameMessages::SendAddBuff(const_cast(m_Parent->GetObjectID()), source, static_cast(id), - static_cast(duration) * 1000, addImmunity, cancelOnDamaged, cancelOnDeath, - cancelOnLogout, cancelOnRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone, addedByTeammate, applyOnTeammates); + GameMessages::AddBuff addBuff; + addBuff.target = m_Parent->GetObjectID(); + addBuff.bAddedByTeammate = addedByTeammate; + addBuff.bApplyOnTeammates = applyOnTeammates; + addBuff.bCancelOnDamaged = cancelOnDamaged; + addBuff.bCancelOnDeath = cancelOnDeath; + addBuff.bCancelOnLogOut = cancelOnLogout; + addBuff.bCancelOnRemoveBuff = cancelOnRemoveBuff; + addBuff.bCancelOnUI = cancelOnUi; + addBuff.bCancelOnUnEquip = cancelOnUnequip; + addBuff.bCancelOnZone = cancelOnZone; + addBuff.bIsImmunity = addImmunity; + addBuff.i64AddedBy = source; + addBuff.uiBuffID = static_cast(id); + addBuff.uiDurationMS = static_cast(duration) * 1000; + addBuff.Send(UNASSIGNED_SYSTEM_ADDRESS); float tick = 0; float stacks = 0; @@ -223,7 +237,12 @@ void BuffComponent::RemoveBuff(int32_t id, bool fromUnEquip, bool removeImmunity } } - GameMessages::SendRemoveBuff(m_Parent, fromUnEquip, removeImmunity, id); + GameMessages::RemoveBuff removeBuff; + removeBuff.target = m_Parent->GetObjectID(); + removeBuff.bFromUnEquip = fromUnEquip; + removeBuff.bRemoveImmunity = removeImmunity; + removeBuff.uiBuffID = static_cast(id); + removeBuff.Send(UNASSIGNED_SYSTEM_ADDRESS); m_BuffsToRemove.insert(id); diff --git a/dGame/dComponents/ControllablePhysicsComponent.cpp b/dGame/dComponents/ControllablePhysicsComponent.cpp index 9778ae301..99ae1b663 100644 --- a/dGame/dComponents/ControllablePhysicsComponent.cpp +++ b/dGame/dComponents/ControllablePhysicsComponent.cpp @@ -1,4 +1,5 @@ #include "ControllablePhysicsComponent.h" +#include "CombatMessages.h" #include "Entity.h" #include "BitStream.h" #include "Logger.h" @@ -347,16 +348,18 @@ void ControllablePhysicsComponent::SetStunImmunity( if (bImmuneToStunUseItem) m_ImmuneToStunUseItemCount += 1; } - GameMessages::SendSetStunImmunity( - m_Parent->GetObjectID(), state, m_Parent->GetSystemAddress(), originator, - bImmuneToStunAttack, - bImmuneToStunEquip, - bImmuneToStunInteract, - bImmuneToStunJump, - bImmuneToStunMove, - bImmuneToStunTurn, - bImmuneToStunUseItem - ); + GameMessages::SetStunImmunity stunImmunity; + stunImmunity.target = m_Parent->GetObjectID(); + stunImmunity.Caster = originator; + stunImmunity.StateChangeType = state; + stunImmunity.bImmuneToStunAttack = bImmuneToStunAttack; + stunImmunity.bImmuneToStunEquip = bImmuneToStunEquip; + stunImmunity.bImmuneToStunInteract = bImmuneToStunInteract; + stunImmunity.bImmuneToStunJump = bImmuneToStunJump; + stunImmunity.bImmuneToStunMove = bImmuneToStunMove; + stunImmunity.bImmuneToStunTurn = bImmuneToStunTurn; + stunImmunity.bImmuneToStunUseItem = bImmuneToStunUseItem; + stunImmunity.Send(m_Parent->GetSystemAddress()); } bool ControllablePhysicsComponent::OnGetObjectReportInfo(GameMessages::GetObjectReportInfo& reportInfo) { diff --git a/dGame/dComponents/DestroyableComponent.cpp b/dGame/dComponents/DestroyableComponent.cpp index 297dd574a..1579b7eb0 100644 --- a/dGame/dComponents/DestroyableComponent.cpp +++ b/dGame/dComponents/DestroyableComponent.cpp @@ -1,4 +1,6 @@ #include "DestroyableComponent.h" +#include "CombatMessages.h" +#include "LevelProgressionComponent.h" #include "EconomyLedger.h" #include "BitStream.h" #include "Logger.h" @@ -779,7 +781,15 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType const auto isPlayer = m_Parent->IsPlayer(); - GameMessages::SendDie(m_Parent, source, source, true, killType, deathType, 0, 0, 0, isPlayer, true, 1); + GameMessages::Die die; + die.target = m_Parent->GetObjectID(); + die.bClientDeath = isPlayer; + die.bSpawnLoot = true; + die.deathType = deathType; + die.killType = killType; + die.killerID = source; + die.lootOwnerID = source; + die.Send(UNASSIGNED_SYSTEM_ADDRESS); //NANI?! if (!isPlayer) { @@ -870,18 +880,19 @@ void DestroyableComponent::SetStatusImmunity( if (bImmuneToPullToPoint) m_ImmuneToPullToPointCount += 1; } - GameMessages::SendSetStatusImmunity( - m_Parent->GetObjectID(), state, m_Parent->GetSystemAddress(), - bImmuneToBasicAttack, - bImmuneToDamageOverTime, - bImmuneToKnockback, - bImmuneToInterrupt, - bImmuneToSpeed, - bImmuneToImaginationGain, - bImmuneToImaginationLoss, - bImmuneToQuickbuildInterrupt, - bImmuneToPullToPoint - ); + GameMessages::SetStatusImmunity statusImmunity; + statusImmunity.target = m_Parent->GetObjectID(); + statusImmunity.StateChangeType = state; + statusImmunity.bImmuneToBasicAttack = bImmuneToBasicAttack; + statusImmunity.bImmuneToDOT = bImmuneToDamageOverTime; + statusImmunity.bImmuneToKnockback = bImmuneToKnockback; + statusImmunity.bImmuneToInterrupt = bImmuneToInterrupt; + statusImmunity.bImmuneToSpeed = bImmuneToSpeed; + statusImmunity.bImmuneToImaginationGain = bImmuneToImaginationGain; + statusImmunity.bImmuneToImaginationLoss = bImmuneToImaginationLoss; + statusImmunity.bImmuneToQuickbuildInterrupt = bImmuneToQuickbuildInterrupt; + statusImmunity.bImmuneToPullToPoint = bImmuneToPullToPoint; + statusImmunity.Send(m_Parent->GetSystemAddress()); } void DestroyableComponent::FixStats() { @@ -1222,3 +1233,36 @@ bool DestroyableComponent::OnIsDead(GameMessages::IsDead& isDead) { isDead.bDead = m_IsDead || (GetHealth() == 0 && GetArmor() == 0); return true; } + +void DestroyableComponent::Resurrect(Entity& entityRef) { + Entity* entity = &entityRef; + // Restore the players health after the animation for respawning has finished. + // This is when the health appered back in live, not immediately upon requesting respawn + // Add a half second in case someone decides to cheat and move during the death animation + // and just make sure the client has time to be ready. + constexpr float respawnTime = 3.66700005531311f + 0.5f; + entity->AddCallbackTimer(respawnTime, [=]() { + GameMessages::PlayerResurrectionFinished msg; + entity->NotifyPlayerResurrectionFinished(msg); + auto* destroyableComponent = entity->GetComponent(); + + if (destroyableComponent != nullptr && entity->GetLOT() == 1) { + destroyableComponent->SetIsDead(false); + auto* levelComponent = entity->GetComponent(); + if (levelComponent) { + int32_t healthToRestore = levelComponent->GetLevel() >= 45 ? 8 : 4; + if (healthToRestore > destroyableComponent->GetMaxHealth()) healthToRestore = destroyableComponent->GetMaxHealth(); + destroyableComponent->SetHealth(healthToRestore); + + int32_t imaginationToRestore = levelComponent->GetLevel() >= 45 ? 20 : 6; + if (imaginationToRestore > destroyableComponent->GetMaxImagination()) imaginationToRestore = destroyableComponent->GetMaxImagination(); + destroyableComponent->SetImagination(imaginationToRestore); + } + } + }); + + GameMessages::Resurrect resurrect; + resurrect.target = entity->GetObjectID(); + resurrect.bRezImmediately = false; + resurrect.Send(UNASSIGNED_SYSTEM_ADDRESS); +} diff --git a/dGame/dComponents/DestroyableComponent.h b/dGame/dComponents/DestroyableComponent.h index 7ef55a210..820deee55 100644 --- a/dGame/dComponents/DestroyableComponent.h +++ b/dGame/dComponents/DestroyableComponent.h @@ -404,6 +404,12 @@ public: */ void Smash(LWOOBJID source, eKillType killType = eKillType::VIOLENT, const std::u16string& deathType = u"", uint32_t skillID = 0); + /** + * Brings entity back to life: tells every client (Resurrect), then, once the respawn animation is done, + * finishes the resurrection and restores a player's health and imagination. + */ + static void Resurrect(Entity& entity); + /** * Push or Pop a layer of status immunity to this entity */ diff --git a/dGame/dComponents/ModelComponent.cpp b/dGame/dComponents/ModelComponent.cpp index a701e85ec..ffc325339 100644 --- a/dGame/dComponents/ModelComponent.cpp +++ b/dGame/dComponents/ModelComponent.cpp @@ -1,4 +1,5 @@ #include "ModelComponent.h" +#include "CombatMessages.h" #include "Entity.h" #include "Game.h" diff --git a/dGame/dComponents/QuickBuildComponent.cpp b/dGame/dComponents/QuickBuildComponent.cpp index 11737c82a..f41cc72c5 100644 --- a/dGame/dComponents/QuickBuildComponent.cpp +++ b/dGame/dComponents/QuickBuildComponent.cpp @@ -3,6 +3,7 @@ #include "DestroyableComponent.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "CombatMessages.h" #include "EntityManager.h" #include "Game.h" #include "Logger.h" @@ -132,7 +133,12 @@ void QuickBuildComponent::Update(float deltaTime) { if (m_TimerIncomplete >= m_TimeBeforeSmash) { m_Builder = LWOOBJID_EMPTY; - GameMessages::SendDieNoImplCode(m_Parent, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 0.0f, false, true); + GameMessages::Die die; + die.target = m_Parent->GetObjectID(); + die.bClientDeath = false; + die.bSpawnLoot = true; + die.killerID = LWOOBJID_EMPTY; + die.Send(UNASSIGNED_SYSTEM_ADDRESS); ResetQuickBuild(false); } @@ -154,7 +160,13 @@ void QuickBuildComponent::Update(float deltaTime) { if (m_Timer >= m_ResetTime) { - GameMessages::SendDieNoImplCode(m_Parent, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 7.0f, false, true); + GameMessages::Die die; + die.target = m_Parent->GetObjectID(); + die.bClientDeath = false; + die.bSpawnLoot = true; + die.directionRelative_Force = 7.0f; + die.killerID = LWOOBJID_EMPTY; + die.Send(UNASSIGNED_SYSTEM_ADDRESS); ResetQuickBuild(false); } @@ -215,7 +227,12 @@ void QuickBuildComponent::Update(float deltaTime) { if (m_TimerIncomplete >= m_TimeBeforeSmash) { m_Builder = LWOOBJID_EMPTY; - GameMessages::SendDieNoImplCode(m_Parent, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 0.0f, false, true); + GameMessages::Die die; + die.target = m_Parent->GetObjectID(); + die.bClientDeath = false; + die.bSpawnLoot = true; + die.killerID = LWOOBJID_EMPTY; + die.Send(UNASSIGNED_SYSTEM_ADDRESS); ResetQuickBuild(false); } diff --git a/dGame/dComponents/RacingControlComponent.cpp b/dGame/dComponents/RacingControlComponent.cpp index 9f5e29ddf..e86364e93 100644 --- a/dGame/dComponents/RacingControlComponent.cpp +++ b/dGame/dComponents/RacingControlComponent.cpp @@ -8,6 +8,7 @@ #include "DestroyableComponent.h" #include "EntityManager.h" #include "GameMessages.h" +#include "CombatMessages.h" #include "ActivityMessages.h" #include "RacingMessages.h" #include "InventoryComponent.h" @@ -83,7 +84,18 @@ void RacingControlComponent::OnPlayerLoaded(Entity* player) { m_LoadedPlayers++; // not live accurate to stun the player but prevents them from using skills during the race that are not meant to be used. - GameMessages::SendSetStunned(player->GetObjectID(), eStateChangeType::PUSH, player->GetSystemAddress(), LWOOBJID_EMPTY, true, true, true, true, true, true, true, true, true); + GameMessages::SetStunned stun; + stun.target = player->GetObjectID(); + stun.StateChangeType = eStateChangeType::PUSH; + stun.bCantAttack = true; + stun.bCantEquip = true; + stun.bCantInteract = true; + stun.bCantJump = true; + stun.bCantMove = true; + stun.bCantTurn = true; + stun.bCantUseItem = true; + stun.bDontTerminateInteract = true; + stun.Send(player->GetSystemAddress()); LOG("Loading player %i", m_LoadedPlayers); @@ -316,8 +328,14 @@ void RacingControlComponent::OnRequestDie(Entity* player, const std::u16string& if (!racingPlayer.noSmashOnReload) { racingPlayer.smashedTimes++; LOG("Death type %s", GeneralUtils::UTF16ToWTF8(deathType).c_str()); - GameMessages::SendDie(vehicle, vehicle->GetObjectID(), LWOOBJID_EMPTY, true, - eKillType::VIOLENT, deathType, 0, 0, 90.0f, false, true, 0); + GameMessages::Die die; + die.target = vehicle->GetObjectID(); + die.bClientDeath = false; + die.bSpawnLoot = true; + die.deathType = deathType; + die.directionRelative_Force = 90.0f; + die.killerID = vehicle->GetObjectID(); + die.Send(UNASSIGNED_SYSTEM_ADDRESS); auto* destroyableComponent = vehicle->GetComponent(); int32_t respawnImagination = 0; @@ -325,7 +343,10 @@ void RacingControlComponent::OnRequestDie(Entity* player, const std::u16string& // Do not actually change the value yet. Do that on respawn. if (destroyableComponent) { respawnImagination = static_cast(ceil(destroyableComponent->GetImagination() / 2.0f / 10.0f)) * 10.0f; - GameMessages::SendSetResurrectRestoreValues(vehicle, -1, -1, respawnImagination); + GameMessages::SetResurrectRestoreValues restoreValues; + restoreValues.target = vehicle->GetObjectID(); + restoreValues.iImaginationRestore = respawnImagination; + restoreValues.Send(UNASSIGNED_SYSTEM_ADDRESS); } // Respawn the player in 2 seconds, as was done in live. Not sure if this value is in a setting somewhere else... @@ -350,7 +371,7 @@ void RacingControlComponent::OnRequestDie(Entity* player, const std::u16string& resetInfo.upcomingPlane = racingPlayer.respawnIndex + 1; resetInfo.Send(UNASSIGNED_SYSTEM_ADDRESS); - GameMessages::SendResurrect(vehicle); + DestroyableComponent::Resurrect(*vehicle); auto* destroyableComponent = vehicle->GetComponent(); // Reset imagination to half its current value, rounded up to the nearest value divisible by 10, as it was done in live. if (destroyableComponent) destroyableComponent->SetImagination(respawnImagination); @@ -771,9 +792,12 @@ void RacingControlComponent::Update(float deltaTime) { // If the player is this far below the map, safe to assume they should // be smashed by death plane if (vehiclePosition.y < -500) { - GameMessages::SendDie(vehicle, m_Parent->GetObjectID(), - LWOOBJID_EMPTY, true, eKillType::VIOLENT, u"", 0, 0, 0, - true, false, 0); + GameMessages::Die die; + die.target = vehicle->GetObjectID(); + die.bClientDeath = true; + die.bSpawnLoot = false; + die.killerID = m_Parent->GetObjectID(); + die.Send(UNASSIGNED_SYSTEM_ADDRESS); OnRequestDie(playerEntity); diff --git a/dGame/dComponents/RailActivatorComponent.cpp b/dGame/dComponents/RailActivatorComponent.cpp index ce54ce057..1df1b9917 100644 --- a/dGame/dComponents/RailActivatorComponent.cpp +++ b/dGame/dComponents/RailActivatorComponent.cpp @@ -5,6 +5,7 @@ #include "Entity.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "CombatMessages.h" #include "QuickBuildComponent.h" #include "Game.h" #include "Logger.h" @@ -83,9 +84,17 @@ void RailActivatorComponent::OnUse(Entity* originator) { void RailActivatorComponent::OnRailMovementReady(Entity* originator) const { // Stun the originator - GameMessages::SendSetStunned(originator->GetObjectID(), eStateChangeType::PUSH, originator->GetSystemAddress(), LWOOBJID_EMPTY, - true, true, true, true, true, true, true - ); + GameMessages::SetStunned stun; + stun.target = originator->GetObjectID(); + stun.StateChangeType = eStateChangeType::PUSH; + stun.bCantAttack = true; + stun.bCantEquip = true; + stun.bCantInteract = true; + stun.bCantJump = true; + stun.bCantMove = true; + stun.bCantTurn = true; + stun.bCantUseItem = true; + stun.Send(originator->GetSystemAddress()); if (std::find(m_EntitiesOnRail.begin(), m_EntitiesOnRail.end(), originator->GetObjectID()) != m_EntitiesOnRail.end()) { // Stop the initial effects @@ -110,9 +119,17 @@ void RailActivatorComponent::OnRailMovementReady(Entity* originator) const { void RailActivatorComponent::OnCancelRailMovement(Entity* originator) { // Remove the stun from the originator - GameMessages::SendSetStunned(originator->GetObjectID(), eStateChangeType::POP, originator->GetSystemAddress(), LWOOBJID_EMPTY, - true, true, true, true, true, true, true - ); + GameMessages::SetStunned stun; + stun.target = originator->GetObjectID(); + stun.StateChangeType = eStateChangeType::POP; + stun.bCantAttack = true; + stun.bCantEquip = true; + stun.bCantInteract = true; + stun.bCantJump = true; + stun.bCantMove = true; + stun.bCantTurn = true; + stun.bCantUseItem = true; + stun.Send(originator->GetSystemAddress()); auto* quickBuildComponent = m_Parent->GetComponent(); diff --git a/dGame/dGameMessages/CMakeLists.txt b/dGame/dGameMessages/CMakeLists.txt index 7c189ec03..1f95cdd73 100644 --- a/dGame/dGameMessages/CMakeLists.txt +++ b/dGame/dGameMessages/CMakeLists.txt @@ -1,6 +1,7 @@ set(DGAME_DGAMEMESSAGES_SOURCES "ActivityMessages.cpp" "BuildingMessages.cpp" + "CombatMessages.cpp" "EffectsMessages.cpp" "GameMessageDecoder.cpp" "GameMessageHandler.cpp" diff --git a/dGame/dGameMessages/CombatMessages.cpp b/dGame/dGameMessages/CombatMessages.cpp new file mode 100644 index 000000000..9702cac60 --- /dev/null +++ b/dGame/dGameMessages/CombatMessages.cpp @@ -0,0 +1,415 @@ +#include "CombatMessages.h" + +#include "BitStreamUtils.h" +#include "ControllablePhysicsComponent.h" +#include "CppScripts.h" +#include "DestroyableComponent.h" +#include "eBubbleType.h" +#include "eKillType.h" +#include "eReplicaComponentType.h" +#include "eStateChangeType.h" +#include "Entity.h" +#include "EntityManager.h" +#include "Game.h" +#include "Logger.h" +#include "PossessableComponent.h" +#include "RacingControlComponent.h" +#include "dZoneManager.h" + +namespace GameMessages { + void Die::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bClientDeath); + bitStream.Write(bSpawnLoot); + BitStreamUtils::WriteLengthPrefixed(bitStream, deathType); + bitStream.Write(directionRelative_AngleXZ); + bitStream.Write(directionRelative_AngleY); + bitStream.Write(directionRelative_Force); + BitStreamUtils::WriteOptional(bitStream, killType, eKillType::VIOLENT); + bitStream.Write(killerID); + BitStreamUtils::WriteOptional(bitStream, lootOwnerID, LWOOBJID_EMPTY); + } + + bool Die::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bClientDeath)); + VALIDATE_READ(bitStream.Read(bSpawnLoot)); + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, deathType)); + VALIDATE_READ(bitStream.Read(directionRelative_AngleXZ)); + VALIDATE_READ(bitStream.Read(directionRelative_AngleY)); + VALIDATE_READ(bitStream.Read(directionRelative_Force)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, killType, eKillType::VIOLENT)); + VALIDATE_READ(bitStream.Read(killerID)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, lootOwnerID, LWOOBJID_EMPTY)); + return true; + } + + void RequestDie::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bClientDeath); + bitStream.Write(bSpawnLoot); + BitStreamUtils::WriteLengthPrefixed(bitStream, deathType); + bitStream.Write(directionRelative_AngleXZ); + bitStream.Write(directionRelative_AngleY); + bitStream.Write(directionRelative_Force); + BitStreamUtils::WriteOptional(bitStream, killType, eKillType::VIOLENT); + bitStream.Write(killerID); + BitStreamUtils::WriteOptional(bitStream, lootOwnerID, LWOOBJID_EMPTY); + } + + bool RequestDie::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bClientDeath)); + VALIDATE_READ(bitStream.Read(bSpawnLoot)); + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, deathType)); + VALIDATE_READ(bitStream.Read(directionRelative_AngleXZ)); + VALIDATE_READ(bitStream.Read(directionRelative_AngleY)); + VALIDATE_READ(bitStream.Read(directionRelative_Force)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, killType, eKillType::VIOLENT)); + VALIDATE_READ(bitStream.Read(killerID)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, lootOwnerID, LWOOBJID_EMPTY)); + return true; + } + + void RequestDie::Handle(Entity& entityRef, const SystemAddress& sysAddr) { + Entity* entity = &entityRef; + auto* zoneController = Game::zoneManager->GetZoneControlObject(); + + auto* racingControlComponent = zoneController->GetComponent(); + + LOG("Got die request: %i", entity->GetLOT()); + + if (racingControlComponent != nullptr) { + auto* possessableComponent = entity->GetComponent(); + + if (possessableComponent != nullptr) { + entity = Game::entityManager->GetEntity(possessableComponent->GetPossessor()); + + if (entity == nullptr) { + return; + } + } + + racingControlComponent->OnRequestDie(entity); + } else { + auto* destroyableComponent = entity->GetComponent(); + + if (!destroyableComponent) return; + + destroyableComponent->Smash(killerID, killType, deathType); + } + } + + void RequestSmashPlayer::Serialize(RakNet::BitStream& bitStream) const {} + + bool RequestSmashPlayer::Deserialize(RakNet::BitStream& bitStream) { + return true; + } + + void RequestSmashPlayer::Handle(Entity& entity, const SystemAddress& sysAddr) { + entity.Smash(entity.GetObjectID()); + } + + void RequestResurrect::Serialize(RakNet::BitStream& bitStream) const {} + + bool RequestResurrect::Deserialize(RakNet::BitStream& bitStream) { + return true; + } + + void RequestResurrect::Handle(Entity& entity, const SystemAddress& sysAddr) { + DestroyableComponent::Resurrect(entity); + } + + void Resurrect::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bRezImmediately); + } + + bool Resurrect::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bRezImmediately)); + return true; + } + + void Resurrect::Handle(Entity& entityRef, const SystemAddress& sysAddr) { + Entity* entity = &entityRef; + Entity* zoneControl = Game::entityManager->GetZoneControlEntity(); + if (zoneControl) { + zoneControl->GetScript()->OnPlayerResurrected(zoneControl, entity); + } + + std::vector scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY); + for (Entity* scriptEntity : scriptedActs) { + if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds + scriptEntity->GetScript()->OnPlayerResurrected(scriptEntity, entity); + } + } + } + + void Smash::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bIgnoreObjectVisibility); + bitStream.Write(force); + bitStream.Write(ghostOpacity); + bitStream.Write(killerID); + } + + bool Smash::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bIgnoreObjectVisibility)); + VALIDATE_READ(bitStream.Read(force)); + VALIDATE_READ(bitStream.Read(ghostOpacity)); + VALIDATE_READ(bitStream.Read(killerID)); + return true; + } + + void UnSmash::Serialize(RakNet::BitStream& bitStream) const { + // Both fields are optional with a default, like the client's GameMessage::UnSmash::Serialize. + BitStreamUtils::WriteOptional(bitStream, builderID, LWOOBJID_EMPTY); + BitStreamUtils::WriteOptional(bitStream, duration, 3.0f); + } + + bool UnSmash::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, builderID, LWOOBJID_EMPTY)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, duration, 3.0f)); + return true; + } + + void SetResurrectRestoreValues::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteOptional(bitStream, iArmorRestore, -1); + BitStreamUtils::WriteOptional(bitStream, iHealthRestore, -1); + BitStreamUtils::WriteOptional(bitStream, iImaginationRestore, -1); + } + + bool SetResurrectRestoreValues::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, iArmorRestore, -1)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, iHealthRestore, -1)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, iImaginationRestore, -1)); + return true; + } + + void SetPlayerAllowedRespawn::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(dontPromptForRespawn); + } + + bool SetPlayerAllowedRespawn::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(dontPromptForRespawn)); + return true; + } + + void Knockback::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteOptional(bitStream, Caster, LWOOBJID_EMPTY); + BitStreamUtils::WriteOptional(bitStream, Originator, LWOOBJID_EMPTY); + BitStreamUtils::WriteOptional(bitStream, iKnockBackTimeMS, 0); + bitStream.Write(vector); + } + + bool Knockback::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, Caster, LWOOBJID_EMPTY)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, Originator, LWOOBJID_EMPTY)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, iKnockBackTimeMS, 0)); + VALIDATE_READ(bitStream.Read(vector)); + return true; + } + + void SetStunned::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteOptional(bitStream, Originator, LWOOBJID_EMPTY); + bitStream.Write(StateChangeType); + bitStream.Write(bCantAttack); + bitStream.Write(bCantAttackOutChangeWasApplied); + bitStream.Write(bCantEquip); + bitStream.Write(bCantEquipOutChangeWasApplied); + bitStream.Write(bCantInteract); + bitStream.Write(bCantInteractOutChangeWasApplied); + bitStream.Write(bCantJump); + bitStream.Write(bCantJumpOutChangeWasApplied); + bitStream.Write(bCantMove); + bitStream.Write(bCantMoveOutChangeWasApplied); + bitStream.Write(bCantTurn); + bitStream.Write(bCantTurnOutChangeWasApplied); + bitStream.Write(bCantUseItem); + bitStream.Write(bCantUseItemOutChangeWasApplied); + bitStream.Write(bDontTerminateInteract); + bitStream.Write(bIgnoreImmunity); + } + + bool SetStunned::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, Originator, LWOOBJID_EMPTY)); + VALIDATE_READ(bitStream.Read(StateChangeType)); + VALIDATE_READ(bitStream.Read(bCantAttack)); + VALIDATE_READ(bitStream.Read(bCantAttackOutChangeWasApplied)); + VALIDATE_READ(bitStream.Read(bCantEquip)); + VALIDATE_READ(bitStream.Read(bCantEquipOutChangeWasApplied)); + VALIDATE_READ(bitStream.Read(bCantInteract)); + VALIDATE_READ(bitStream.Read(bCantInteractOutChangeWasApplied)); + VALIDATE_READ(bitStream.Read(bCantJump)); + VALIDATE_READ(bitStream.Read(bCantJumpOutChangeWasApplied)); + VALIDATE_READ(bitStream.Read(bCantMove)); + VALIDATE_READ(bitStream.Read(bCantMoveOutChangeWasApplied)); + VALIDATE_READ(bitStream.Read(bCantTurn)); + VALIDATE_READ(bitStream.Read(bCantTurnOutChangeWasApplied)); + VALIDATE_READ(bitStream.Read(bCantUseItem)); + VALIDATE_READ(bitStream.Read(bCantUseItemOutChangeWasApplied)); + VALIDATE_READ(bitStream.Read(bDontTerminateInteract)); + VALIDATE_READ(bitStream.Read(bIgnoreImmunity)); + return true; + } + + void SetStunImmunity::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteOptional(bitStream, Caster, LWOOBJID_EMPTY); + bitStream.Write(StateChangeType); + bitStream.Write(bImmuneToStunAttack); + bitStream.Write(bImmuneToStunEquip); + bitStream.Write(bImmuneToStunInteract); + bitStream.Write(bImmuneToStunJump); + bitStream.Write(bImmuneToStunMove); + bitStream.Write(bImmuneToStunTurn); + bitStream.Write(bImmuneToStunUseItem); + } + + bool SetStunImmunity::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, Caster, LWOOBJID_EMPTY)); + VALIDATE_READ(bitStream.Read(StateChangeType)); + VALIDATE_READ(bitStream.Read(bImmuneToStunAttack)); + VALIDATE_READ(bitStream.Read(bImmuneToStunEquip)); + VALIDATE_READ(bitStream.Read(bImmuneToStunInteract)); + VALIDATE_READ(bitStream.Read(bImmuneToStunJump)); + VALIDATE_READ(bitStream.Read(bImmuneToStunMove)); + VALIDATE_READ(bitStream.Read(bImmuneToStunTurn)); + VALIDATE_READ(bitStream.Read(bImmuneToStunUseItem)); + return true; + } + + void SetStatusImmunity::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(StateChangeType); + bitStream.Write(bImmuneToBasicAttack); + bitStream.Write(bImmuneToDOT); + bitStream.Write(bImmuneToKnockback); + bitStream.Write(bImmuneToInterrupt); + bitStream.Write(bImmuneToSpeed); + bitStream.Write(bImmuneToImaginationGain); + bitStream.Write(bImmuneToImaginationLoss); + bitStream.Write(bImmuneToQuickbuildInterrupt); + bitStream.Write(bImmuneToPullToPoint); + } + + bool SetStatusImmunity::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(StateChangeType)); + VALIDATE_READ(bitStream.Read(bImmuneToBasicAttack)); + VALIDATE_READ(bitStream.Read(bImmuneToDOT)); + VALIDATE_READ(bitStream.Read(bImmuneToKnockback)); + VALIDATE_READ(bitStream.Read(bImmuneToInterrupt)); + VALIDATE_READ(bitStream.Read(bImmuneToSpeed)); + VALIDATE_READ(bitStream.Read(bImmuneToImaginationGain)); + VALIDATE_READ(bitStream.Read(bImmuneToImaginationLoss)); + VALIDATE_READ(bitStream.Read(bImmuneToQuickbuildInterrupt)); + VALIDATE_READ(bitStream.Read(bImmuneToPullToPoint)); + return true; + } + + void AddBuff::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bAddedByTeammate); + bitStream.Write(bApplyOnTeammates); + bitStream.Write(bCancelOnDamaged); + bitStream.Write(bCancelOnDeath); + bitStream.Write(bCancelOnLogOut); + bitStream.Write(bCancelOnMove); + bitStream.Write(bCancelOnRemoveBuff); + bitStream.Write(bCancelOnUI); + bitStream.Write(bCancelOnUnEquip); + bitStream.Write(bCancelOnZone); + bitStream.Write(bIgnoreImmunities); + bitStream.Write(bIsImmunity); + bitStream.Write(bUseRefCount); + BitStreamUtils::WriteOptional(bitStream, i64AddedBy, LWOOBJID_EMPTY); + bitStream.Write(uiBuffID); + BitStreamUtils::WriteOptional(bitStream, uiDurationMS, 0u); + } + + bool AddBuff::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bAddedByTeammate)); + VALIDATE_READ(bitStream.Read(bApplyOnTeammates)); + VALIDATE_READ(bitStream.Read(bCancelOnDamaged)); + VALIDATE_READ(bitStream.Read(bCancelOnDeath)); + VALIDATE_READ(bitStream.Read(bCancelOnLogOut)); + VALIDATE_READ(bitStream.Read(bCancelOnMove)); + VALIDATE_READ(bitStream.Read(bCancelOnRemoveBuff)); + VALIDATE_READ(bitStream.Read(bCancelOnUI)); + VALIDATE_READ(bitStream.Read(bCancelOnUnEquip)); + VALIDATE_READ(bitStream.Read(bCancelOnZone)); + VALIDATE_READ(bitStream.Read(bIgnoreImmunities)); + VALIDATE_READ(bitStream.Read(bIsImmunity)); + VALIDATE_READ(bitStream.Read(bUseRefCount)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, i64AddedBy, LWOOBJID_EMPTY)); + VALIDATE_READ(bitStream.Read(uiBuffID)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, uiDurationMS, 0u)); + return true; + } + + void RemoveBuff::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bFromRemoveBehavior); + bitStream.Write(bFromUnEquip); + bitStream.Write(bRemoveImmunity); + bitStream.Write(uiBuffID); + } + + bool RemoveBuff::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bFromRemoveBehavior)); + VALIDATE_READ(bitStream.Read(bFromUnEquip)); + VALIDATE_READ(bitStream.Read(bRemoveImmunity)); + VALIDATE_READ(bitStream.Read(uiBuffID)); + return true; + } + + void AddRunSpeedModifier::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteOptional(bitStream, i64Caster, LWOOBJID_EMPTY); + BitStreamUtils::WriteOptional(bitStream, uiModifier, 500u); + } + + bool AddRunSpeedModifier::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, i64Caster, LWOOBJID_EMPTY)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, uiModifier, 500u)); + return true; + } + + void RemoveRunSpeedModifier::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteOptional(bitStream, uiModifier, 500u); + } + + bool RemoveRunSpeedModifier::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, uiModifier, 500u)); + return true; + } + + void ActivateBubbleBuff::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bSpecialAnims); + BitStreamUtils::WriteLengthPrefixed(bitStream, wszType); + } + + bool ActivateBubbleBuff::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bSpecialAnims)); + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, wszType)); + return true; + } + + void ActivateBubbleBuff::Handle(Entity& entity, const SystemAddress& sysAddr) { + auto bubbleType = eBubbleType::DEFAULT; + if (wszType == u"skunk") bubbleType = eBubbleType::SKUNK; + else if (wszType == u"energy") bubbleType = eBubbleType::ENERGY; + + auto controllablePhysicsComponent = entity.GetComponent(); + if (controllablePhysicsComponent) controllablePhysicsComponent->ActivateBubbleBuff(bubbleType, bSpecialAnims); + } + + void DeactivateBubbleBuff::Serialize(RakNet::BitStream& bitStream) const {} + + bool DeactivateBubbleBuff::Deserialize(RakNet::BitStream& bitStream) { + return true; + } + + void DeactivateBubbleBuff::Handle(Entity& entity, const SystemAddress& sysAddr) { + auto controllablePhysicsComponent = entity.GetComponent(); + if (controllablePhysicsComponent) controllablePhysicsComponent->DeactivateBubbleBuff(); + DeactivateBubbleBuffFromServer deactivate; + deactivate.target = entity.GetObjectID(); + deactivate.Send(entity.GetSystemAddress()); + } + + void DeactivateBubbleBuffFromServer::Serialize(RakNet::BitStream& bitStream) const {} + + bool DeactivateBubbleBuffFromServer::Deserialize(RakNet::BitStream& bitStream) { + return true; + } +} diff --git a/dGame/dGameMessages/CombatMessages.h b/dGame/dGameMessages/CombatMessages.h new file mode 100644 index 000000000..2e0d2d2e0 --- /dev/null +++ b/dGame/dGameMessages/CombatMessages.h @@ -0,0 +1,278 @@ +#ifndef COMBATMESSAGES_H +#define COMBATMESSAGES_H + +#include "GameMessages.h" +#include "NiPoint3.h" + +#include + +enum class eKillType : uint32_t; +enum class eStateChangeType : uint32_t; + +// Game messages for combat: death and resurrection, stuns and immunities, buffs, knockback and the bubble buff. +// Field names follow the client (legouniverse.exe 1.10.64); fields are listed in wire order. +// Unless noted, server -> client messages are sent with Send (UNASSIGNED_SYSTEM_ADDRESS broadcasts), as before. +namespace GameMessages { + // Server -> client, broadcast. + struct Die : public NetGameMsg { + Die() : NetGameMsg(MessageType::Game::DIE) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + bool bClientDeath{ false }; + bool bSpawnLoot{ true }; + std::u16string deathType{}; // u32 length prefixed + float directionRelative_AngleXZ{}; + float directionRelative_AngleY{}; + float directionRelative_Force{}; + eKillType killType{ 0 }; // optional, eKillType::VIOLENT when unset + LWOOBJID killerID{}; + LWOOBJID lootOwnerID{ LWOOBJID_EMPTY }; // optional + }; + + // Client -> server. Read with the same layout as Die, as DLU always has. + struct RequestDie : public NetGameMsg { + RequestDie() : NetGameMsg(MessageType::Game::REQUEST_DIE) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + bool bClientDeath{ false }; + bool bSpawnLoot{ false }; + std::u16string deathType{}; // u32 length prefixed + float directionRelative_AngleXZ{}; + float directionRelative_AngleY{}; + float directionRelative_Force{}; + eKillType killType{ 0 }; // optional, eKillType::VIOLENT when unset + LWOOBJID killerID{}; + LWOOBJID lootOwnerID{ LWOOBJID_EMPTY }; // optional + }; + + // Client -> server. No payload. + struct RequestSmashPlayer : public NetGameMsg { + RequestSmashPlayer() : NetGameMsg(MessageType::Game::REQUEST_SMASH_PLAYER) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + }; + + // Client -> server. The player pressed respawn. No payload. + struct RequestResurrect : public NetGameMsg { + RequestResurrect() : NetGameMsg(MessageType::Game::REQUEST_RESURRECT) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + }; + + // Server -> client (broadcast) when an entity comes back to life, and client -> server when the player has + // resurrected. + struct Resurrect : public NetGameMsg { + Resurrect() : NetGameMsg(MessageType::Game::RESURRECT) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + bool bRezImmediately{ false }; + }; + + // Server -> client. Smashes the target into bricks without destroying it. + struct Smash : public NetGameMsg { + Smash() : NetGameMsg(MessageType::Game::SMASH) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + bool bIgnoreObjectVisibility{ false }; + float force{}; + float ghostOpacity{}; + LWOOBJID killerID{}; + }; + + // Server -> client. Rebuilds a smashed target over duration seconds. + struct UnSmash : public NetGameMsg { + UnSmash() : NetGameMsg(MessageType::Game::UN_SMASH) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + LWOOBJID builderID{ LWOOBJID_EMPTY }; // optional + float duration{ 3.0f }; // optional + }; + + // Server -> client, broadcast. + struct SetResurrectRestoreValues : public NetGameMsg { + SetResurrectRestoreValues() : NetGameMsg(MessageType::Game::SET_RESURRECT_RESTORE_VALUES) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + int32_t iArmorRestore{ -1 }; // optional + int32_t iHealthRestore{ -1 }; // optional + int32_t iImaginationRestore{ -1 }; // optional + }; + + // Server -> client. Sent with SendToClient: the old function never broadcast. + struct SetPlayerAllowedRespawn : public NetGameMsg { + SetPlayerAllowedRespawn() : NetGameMsg(MessageType::Game::SET_PLAYER_ALLOWED_RESPAWN) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + bool dontPromptForRespawn{}; + }; + + // Server -> client, broadcast. + struct Knockback : public NetGameMsg { + Knockback() : NetGameMsg(MessageType::Game::KNOCKBACK) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + LWOOBJID Caster{ LWOOBJID_EMPTY }; // optional + LWOOBJID Originator{ LWOOBJID_EMPTY }; // optional + int32_t iKnockBackTimeMS{ 0 }; // optional + NiPoint3 vector{}; + }; + + // Server -> client. + struct SetStunned : public NetGameMsg { + SetStunned() : NetGameMsg(MessageType::Game::SET_STUNNED) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + LWOOBJID Originator{ LWOOBJID_EMPTY }; // optional + eStateChangeType StateChangeType{}; + bool bCantAttack{ false }; + bool bCantAttackOutChangeWasApplied{ false }; + bool bCantEquip{ false }; + bool bCantEquipOutChangeWasApplied{ false }; + bool bCantInteract{ false }; + bool bCantInteractOutChangeWasApplied{ false }; + bool bCantJump{ false }; + bool bCantJumpOutChangeWasApplied{ false }; + bool bCantMove{ false }; + bool bCantMoveOutChangeWasApplied{ false }; + bool bCantTurn{ false }; + bool bCantTurnOutChangeWasApplied{ false }; + bool bCantUseItem{ false }; + bool bCantUseItemOutChangeWasApplied{ false }; + bool bDontTerminateInteract{ false }; + bool bIgnoreImmunity{ true }; + }; + + // Server -> client. + struct SetStunImmunity : public NetGameMsg { + SetStunImmunity() : NetGameMsg(MessageType::Game::SET_STUN_IMMUNITY) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + LWOOBJID Caster{ LWOOBJID_EMPTY }; // optional + eStateChangeType StateChangeType{}; + bool bImmuneToStunAttack{ false }; + bool bImmuneToStunEquip{ false }; + bool bImmuneToStunInteract{ false }; + bool bImmuneToStunJump{ false }; + bool bImmuneToStunMove{ false }; + bool bImmuneToStunTurn{ false }; + bool bImmuneToStunUseItem{ false }; + }; + + // Server -> client. + // NOTE: DLU writes the flags in this order; the client reads them in alphabetical order + // (GameMessage::SetStatusImmunity::Serialize @ 00d8f140 in 1.10.64). Kept as DLU sent it in this conversion. + struct SetStatusImmunity : public NetGameMsg { + SetStatusImmunity() : NetGameMsg(MessageType::Game::SET_STATUS_IMMUNITY) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + eStateChangeType StateChangeType{}; + bool bImmuneToBasicAttack{ false }; + bool bImmuneToDOT{ false }; + bool bImmuneToKnockback{ false }; + bool bImmuneToInterrupt{ false }; + bool bImmuneToSpeed{ false }; + bool bImmuneToImaginationGain{ false }; + bool bImmuneToImaginationLoss{ false }; + bool bImmuneToQuickbuildInterrupt{ false }; + bool bImmuneToPullToPoint{ false }; + }; + + // Server -> client. + struct AddBuff : public NetGameMsg { + AddBuff() : NetGameMsg(MessageType::Game::ADD_BUFF) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + bool bAddedByTeammate{ false }; + bool bApplyOnTeammates{ false }; + bool bCancelOnDamaged{ false }; + bool bCancelOnDeath{ false }; + bool bCancelOnLogOut{ false }; + bool bCancelOnMove{ false }; + bool bCancelOnRemoveBuff{ false }; + bool bCancelOnUI{ false }; + bool bCancelOnUnEquip{ false }; + bool bCancelOnZone{ false }; + bool bIgnoreImmunities{ false }; + bool bIsImmunity{ false }; + bool bUseRefCount{ false }; + LWOOBJID i64AddedBy{ LWOOBJID_EMPTY }; // optional + uint32_t uiBuffID{}; + uint32_t uiDurationMS{ 0 }; // optional + }; + + // Server -> client, broadcast. + struct RemoveBuff : public NetGameMsg { + RemoveBuff() : NetGameMsg(MessageType::Game::REMOVE_BUFF) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + // DLU always sends false: setting it makes the client ignore the message. + bool bFromRemoveBehavior{ false }; + bool bFromUnEquip{ false }; + bool bRemoveImmunity{ false }; + uint32_t uiBuffID{}; + }; + + // Server -> client. + struct AddRunSpeedModifier : public NetGameMsg { + AddRunSpeedModifier() : NetGameMsg(MessageType::Game::ADD_RUN_SPEED_MODIFIER) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + LWOOBJID i64Caster{ LWOOBJID_EMPTY }; // optional + uint32_t uiModifier{ 500 }; // optional + }; + + // Server -> client. + struct RemoveRunSpeedModifier : public NetGameMsg { + RemoveRunSpeedModifier() : NetGameMsg(MessageType::Game::REMOVE_RUN_SPEED_MODIFIER) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + uint32_t uiModifier{ 500 }; // optional + }; + + // Client -> server. The player went under water (or into skunk stink) and wants the bubble helmet. + struct ActivateBubbleBuff : public NetGameMsg { + ActivateBubbleBuff() : NetGameMsg(MessageType::Game::ACTIVATE_BUBBLE_BUFF) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + bool bSpecialAnims{}; + std::u16string wszType{}; // u32 length prefixed + }; + + // Client -> server. No payload. + struct DeactivateBubbleBuff : public NetGameMsg { + DeactivateBubbleBuff() : NetGameMsg(MessageType::Game::DECTIVATE_BUBBLE_BUFF) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + }; + + // Server -> client. No payload. + struct DeactivateBubbleBuffFromServer : public NetGameMsg { + DeactivateBubbleBuffFromServer() : NetGameMsg(MessageType::Game::DEACTIVATE_BUBBLE_BUFF_FROM_SERVER) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + }; +}; + +#endif // COMBATMESSAGES_H diff --git a/dGame/dGameMessages/GameMessageHandler.cpp b/dGame/dGameMessages/GameMessageHandler.cpp index 9523f353f..89fb16d01 100644 --- a/dGame/dGameMessages/GameMessageHandler.cpp +++ b/dGame/dGameMessages/GameMessageHandler.cpp @@ -30,6 +30,7 @@ #include "BuildingMessages.h" #include "RacingMessages.h" #include "MissionMessages.h" +#include "CombatMessages.h" #include "SkillMessages.h" #include "TradeMessages.h" #include "VendorMessages.h" @@ -159,6 +160,14 @@ namespace { { START_SKILL, []() { return std::make_unique(); } }, { SYNC_SKILL, []() { return std::make_unique(); } }, { REQUEST_SERVER_PROJECTILE_IMPACT, []() { return std::make_unique(); } }, + + // Combat + { REQUEST_DIE, []() { return std::make_unique(); } }, + { REQUEST_SMASH_PLAYER, []() { return std::make_unique(); } }, + { REQUEST_RESURRECT, []() { return std::make_unique(); } }, + { RESURRECT, []() { return std::make_unique(); } }, + { ACTIVATE_BUBBLE_BUFF, []() { return std::make_unique(); } }, + { DECTIVATE_BUBBLE_BUFF, []() { return std::make_unique(); } }, }; }; @@ -352,19 +361,6 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System break; } - case MessageType::Game::RESURRECT: { - GameMessages::HandleResurrect(inStream, entity); - break; - } - - case MessageType::Game::REQUEST_RESURRECT: { - GameMessages::SendResurrect(entity); - break; - } - case MessageType::Game::REQUEST_SMASH_PLAYER: - entity->Smash(entity->GetObjectID()); - break; - case MessageType::Game::REBUILD_CANCEL: GameMessages::HandleQuickBuildCancel(inStream, entity); break; @@ -382,10 +378,6 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System GameMessages::HandleAcknowledgePossession(inStream, entity, sysAddr); break; - case MessageType::Game::REQUEST_DIE: - GameMessages::HandleRequestDie(inStream, entity, sysAddr); - break; - // SG case MessageType::Game::UPDATE_SHOOTING_GALLERY_ROTATION: GameMessages::HandleUpdateShootingGalleryRotation(inStream, entity, sysAddr); break; @@ -431,12 +423,6 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System case MessageType::Game::DISMOUNT_COMPLETE: GameMessages::HandleDismountComplete(inStream, entity, sysAddr); break; - case MessageType::Game::DECTIVATE_BUBBLE_BUFF: - GameMessages::HandleDeactivateBubbleBuff(inStream, entity); - break; - case MessageType::Game::ACTIVATE_BUBBLE_BUFF: - GameMessages::HandleActivateBubbleBuff(inStream, entity); - break; case MessageType::Game::ZONE_SUMMARY_DISMISSED: GameMessages::HandleZoneSummaryDismissed(inStream, entity); break; diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 7b7a83363..82bca7225 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -172,17 +172,6 @@ void GameMessages::SendPlayerReady(Entity* entity, const SystemAddress& sysAddr) SEND_PACKET; } -void GameMessages::SendPlayerAllowedRespawn(LWOOBJID entityID, bool doNotPromptRespawn, const SystemAddress& sysAddr) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(entityID); - bitStream.Write(MessageType::Game::SET_PLAYER_ALLOWED_RESPAWN); - bitStream.Write(doNotPromptRespawn); - - SEND_PACKET; -} - void GameMessages::SendInvalidZoneTransferList(Entity* entity, const SystemAddress& sysAddr, const std::u16string& feedbackURL, const std::u16string& invalidMapTransferList, bool feedbackOnExit, bool feedbackOnInvalidTransfer) { CBITSTREAM; CMSGHEADER; @@ -208,28 +197,6 @@ void GameMessages::SendInvalidZoneTransferList(Entity* entity, const SystemAddre SEND_PACKET; } -void GameMessages::SendKnockback(const LWOOBJID& objectID, const LWOOBJID& caster, const LWOOBJID& originator, int knockBackTimeMS, const NiPoint3& vector) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectID); - bitStream.Write(MessageType::Game::KNOCKBACK); - - bool casterFlag = caster != LWOOBJID_EMPTY; - bool originatorFlag = originator != LWOOBJID_EMPTY; - bool knockBackTimeMSFlag = knockBackTimeMS != 0; - - bitStream.Write(casterFlag); - if (casterFlag) bitStream.Write(caster); - bitStream.Write(originatorFlag); - if (originatorFlag) bitStream.Write(originator); - bitStream.Write(knockBackTimeMSFlag); - if (knockBackTimeMSFlag) bitStream.Write(knockBackTimeMS); - bitStream.Write(vector); - - SEND_PACKET_BROADCAST; -} - void GameMessages::SendPlayerSetCameraCyclingMode(const LWOOBJID& objectID, const SystemAddress& sysAddr, bool bAllowCyclingWhileDeadOnly, eCyclingMode cyclingMode) { CBITSTREAM; @@ -452,70 +419,6 @@ void GameMessages::SendTerminateInteraction(const LWOOBJID& objectID, eTerminate SEND_PACKET_BROADCAST; } -void GameMessages::SendDieNoImplCode(Entity* entity, const LWOOBJID& killerID, const LWOOBJID& lootOwnerID, eKillType killType, std::u16string deathType, float directionRelative_AngleY, float directionRelative_AngleXZ, float directionRelative_Force, bool bClientDeath, bool bSpawnLoot) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::DIE); - - bitStream.Write(bClientDeath); - bitStream.Write(bSpawnLoot); - bitStream.Write(deathType.size()); - bitStream.Write(deathType); - bitStream.Write(directionRelative_AngleXZ); - bitStream.Write(directionRelative_AngleY); - bitStream.Write(directionRelative_Force); - - bitStream.Write(killType != eKillType::VIOLENT); - if (killType != eKillType::VIOLENT) bitStream.Write(killType); - - bitStream.Write(killerID); - bitStream.Write(lootOwnerID != LWOOBJID_EMPTY); - if (lootOwnerID != LWOOBJID_EMPTY) { - bitStream.Write(lootOwnerID); - } - - SEND_PACKET_BROADCAST; -} - -void GameMessages::SendDie(Entity* entity, const LWOOBJID& killerID, const LWOOBJID& lootOwnerID, bool bDieAccepted, eKillType killType, std::u16string deathType, float directionRelative_AngleY, float directionRelative_AngleXZ, float directionRelative_Force, bool bClientDeath, bool bSpawnLoot, float coinSpawnTime) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(entity->GetObjectID()); - - bitStream.Write(MessageType::Game::DIE); - - bitStream.Write(bClientDeath); - bitStream.Write(bSpawnLoot); - - //bitStream.Write(coinSpawnTime != -1.0f); - //if (coinSpawnTime != -1.0f) bitStream.Write(coinSpawnTime); - - uint32_t deathTypeLength = deathType.size(); - bitStream.Write(deathTypeLength); - for (uint32_t k = 0; k < deathTypeLength; k++) { - bitStream.Write(deathType[k]); - } - - bitStream.Write(directionRelative_AngleXZ); - bitStream.Write(directionRelative_AngleY); - bitStream.Write(directionRelative_Force); - - bitStream.Write(killType != eKillType::VIOLENT); - if (killType != eKillType::VIOLENT) bitStream.Write(killType); - - bitStream.Write(killerID); - - bitStream.Write(lootOwnerID != LWOOBJID_EMPTY); - if (lootOwnerID != LWOOBJID_EMPTY) { - bitStream.Write(lootOwnerID); - } - - SEND_PACKET_BROADCAST; -} - void GameMessages::SendSetJetPackMode(Entity* entity, bool use, bool bypassChecks, bool doHover, int effectID, float airspeed, float maxAirspeed, float verticalVelocity, int warningEffectID) { /* historical jamesster jetpack values if (bIsJamessterPhysics) { @@ -553,44 +456,6 @@ void GameMessages::SendSetJetPackMode(Entity* entity, bool use, bool bypassCheck SEND_PACKET_BROADCAST; } -void GameMessages::SendResurrect(Entity* entity) { - // Restore the players health after the animation for respawning has finished. - // This is when the health appered back in live, not immediately upon requesting respawn - // Add a half second in case someone decides to cheat and move during the death animation - // and just make sure the client has time to be ready. - constexpr float respawnTime = 3.66700005531311f + 0.5f; - entity->AddCallbackTimer(respawnTime, [=]() { - GameMessages::PlayerResurrectionFinished msg; - entity->NotifyPlayerResurrectionFinished(msg); - auto* destroyableComponent = entity->GetComponent(); - - if (destroyableComponent != nullptr && entity->GetLOT() == 1) { - destroyableComponent->SetIsDead(false); - auto* levelComponent = entity->GetComponent(); - if (levelComponent) { - int32_t healthToRestore = levelComponent->GetLevel() >= 45 ? 8 : 4; - if (healthToRestore > destroyableComponent->GetMaxHealth()) healthToRestore = destroyableComponent->GetMaxHealth(); - destroyableComponent->SetHealth(healthToRestore); - - int32_t imaginationToRestore = levelComponent->GetLevel() >= 45 ? 20 : 6; - if (imaginationToRestore > destroyableComponent->GetMaxImagination()) imaginationToRestore = destroyableComponent->GetMaxImagination(); - destroyableComponent->SetImagination(imaginationToRestore); - } - } - }); - - CBITSTREAM; - CMSGHEADER; - - bool bRezImmediately = false; - - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::RESURRECT); - bitStream.Write(bRezImmediately); - - SEND_PACKET_BROADCAST; -} - void GameMessages::SendSetNetworkScriptVar(Entity* entity, const SystemAddress& sysAddr, std::string data) { CBITSTREAM; CMSGHEADER; @@ -1062,149 +927,6 @@ void GameMessages::SendLockNodeRotation(Entity* entity, std::string nodeName) { SEND_PACKET_BROADCAST; } -void GameMessages::SendSmash(Entity* entity, float force, float ghostOpacity, LWOOBJID killerID, bool ignoreObjectVisibility) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::SMASH); - - bitStream.Write(ignoreObjectVisibility); - bitStream.Write(force); - bitStream.Write(ghostOpacity); - bitStream.Write(killerID); - - SEND_PACKET_BROADCAST; -} - -void GameMessages::SendUnSmash(Entity* entity, LWOOBJID builderID, float duration) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::UN_SMASH); - - bitStream.Write(builderID != LWOOBJID_EMPTY); - if (builderID != LWOOBJID_EMPTY) bitStream.Write(builderID); - - bitStream.Write(duration != 3.0f); - if (duration != 3.0f) bitStream.Write(duration); - - SEND_PACKET_BROADCAST; -} - -void GameMessages::SendSetStunned(LWOOBJID objectId, eStateChangeType stateChangeType, const SystemAddress& sysAddr, - LWOOBJID originator, bool bCantAttack, bool bCantEquip, - bool bCantInteract, bool bCantJump, bool bCantMove, bool bCantTurn, - bool bCantUseItem, bool bDontTerminateInteract, bool bIgnoreImmunity, - bool bCantAttackOutChangeWasApplied, bool bCantEquipOutChangeWasApplied, - bool bCantInteractOutChangeWasApplied, bool bCantJumpOutChangeWasApplied, - bool bCantMoveOutChangeWasApplied, bool bCantTurnOutChangeWasApplied, - bool bCantUseItemOutChangeWasApplied) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectId); - bitStream.Write(MessageType::Game::SET_STUNNED); - - bitStream.Write(originator != LWOOBJID_EMPTY); - if (originator != LWOOBJID_EMPTY) bitStream.Write(originator); - - bitStream.Write(stateChangeType); - - bitStream.Write(bCantAttack); - bitStream.Write(bCantAttackOutChangeWasApplied); - - bitStream.Write(bCantEquip); - bitStream.Write(bCantEquipOutChangeWasApplied); - - bitStream.Write(bCantInteract); - bitStream.Write(bCantInteractOutChangeWasApplied); - - bitStream.Write(bCantJump); - bitStream.Write(bCantJumpOutChangeWasApplied); - - bitStream.Write(bCantMove); - bitStream.Write(bCantMoveOutChangeWasApplied); - - bitStream.Write(bCantTurn); - bitStream.Write(bCantTurnOutChangeWasApplied); - - bitStream.Write(bCantUseItem); - bitStream.Write(bCantUseItemOutChangeWasApplied); - - bitStream.Write(bDontTerminateInteract); - - bitStream.Write(bIgnoreImmunity); - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - -void GameMessages::SendSetStunImmunity(LWOOBJID target, eStateChangeType state, const SystemAddress& sysAddr, - LWOOBJID originator, - bool bImmuneToStunAttack, - bool bImmuneToStunEquip, - bool bImmuneToStunInteract, - bool bImmuneToStunJump, - bool bImmuneToStunMove, - bool bImmuneToStunTurn, - bool bImmuneToStunUseItem) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(target); - bitStream.Write(MessageType::Game::SET_STUN_IMMUNITY); - - bitStream.Write(originator != LWOOBJID_EMPTY); - if (originator != LWOOBJID_EMPTY) bitStream.Write(originator); - - bitStream.Write(state); - - bitStream.Write(bImmuneToStunAttack); - bitStream.Write(bImmuneToStunEquip); - bitStream.Write(bImmuneToStunInteract); - bitStream.Write(bImmuneToStunJump); - bitStream.Write(bImmuneToStunMove); - bitStream.Write(bImmuneToStunTurn); - bitStream.Write(bImmuneToStunUseItem); - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - -void GameMessages::SendSetStatusImmunity(LWOOBJID objectId, eStateChangeType state, const SystemAddress& sysAddr, - bool bImmuneToBasicAttack, - bool bImmuneToDamageOverTime, - bool bImmuneToKnockback, - bool bImmuneToInterrupt, - bool bImmuneToSpeed, - bool bImmuneToImaginationGain, - bool bImmuneToImaginationLoss, - bool bImmuneToQuickbuildInterrupt, - bool bImmuneToPullToPoint) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectId); - bitStream.Write(MessageType::Game::SET_STATUS_IMMUNITY); - - bitStream.Write(state); - - bitStream.Write(bImmuneToBasicAttack); - bitStream.Write(bImmuneToDamageOverTime); - bitStream.Write(bImmuneToKnockback); - bitStream.Write(bImmuneToInterrupt); - bitStream.Write(bImmuneToSpeed); - bitStream.Write(bImmuneToImaginationGain); - bitStream.Write(bImmuneToImaginationLoss); - bitStream.Write(bImmuneToQuickbuildInterrupt); - bitStream.Write(bImmuneToPullToPoint); - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - void GameMessages::SendOrientToAngle(LWOOBJID objectId, bool bRelativeToCurrent, float fAngle, const SystemAddress& sysAddr) { CBITSTREAM; CMSGHEADER; @@ -1220,37 +942,6 @@ void GameMessages::SendOrientToAngle(LWOOBJID objectId, bool bRelativeToCurrent, } -void GameMessages::SendAddRunSpeedModifier(LWOOBJID objectId, LWOOBJID caster, uint32_t modifier, const SystemAddress& sysAddr) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectId); - bitStream.Write(MessageType::Game::ADD_RUN_SPEED_MODIFIER); - - bitStream.Write(caster != LWOOBJID_EMPTY); - if (caster != LWOOBJID_EMPTY) bitStream.Write(caster); - - bitStream.Write(modifier != 500); - if (modifier != 500) bitStream.Write(modifier); - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - -void GameMessages::SendRemoveRunSpeedModifier(LWOOBJID objectId, uint32_t modifier, const SystemAddress& sysAddr) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectId); - bitStream.Write(MessageType::Game::REMOVE_RUN_SPEED_MODIFIER); - - bitStream.Write(modifier != 500); - if (modifier != 500) bitStream.Write(modifier); - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - void GameMessages::SendNotifyObject(LWOOBJID objectId, LWOOBJID objIDSender, std::u16string name, const SystemAddress& sysAddr, int param1, int param2) { CBITSTREAM; CMSGHEADER; @@ -1308,21 +999,6 @@ void GameMessages::SendTeamPickupItem(LWOOBJID objectId, LWOOBJID lootID, LWOOBJ //Pets: -void GameMessages::SendRemoveBuff(Entity* entity, bool fromUnEquip, bool removeImmunity, uint32_t buffId) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::REMOVE_BUFF); - - bitStream.Write(false); // bFromRemoveBehavior but setting this to true makes the GM not do anything on the client? - bitStream.Write(fromUnEquip); - bitStream.Write(removeImmunity); - bitStream.Write(buffId); - - SEND_PACKET_BROADCAST; -} - void GameMessages::SendDisplayZoneSummary(LWOOBJID objectId, const SystemAddress& sysAddr, bool isPropertyMap, bool isZoneStart, LWOOBJID sender) { CBITSTREAM; CMSGHEADER; @@ -1426,74 +1102,6 @@ void GameMessages::HandleAcknowledgePossession(RakNet::BitStream& inStream, Enti -void GameMessages::HandleRequestDie(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) { - bool bClientDeath; - bool bSpawnLoot; - std::u16string deathType; - float directionRelativeAngleXZ; - float directionRelativeAngleY; - float directionRelativeForce; - eKillType killType = eKillType::VIOLENT; - LWOOBJID killerID; - LWOOBJID lootOwnerID = LWOOBJID_EMPTY; - - bClientDeath = inStream.ReadBit(); - bSpawnLoot = inStream.ReadBit(); - - uint32_t deathTypeLength = 0; - inStream.Read(deathTypeLength); - - for (size_t i = 0; i < deathTypeLength; i++) { - char16_t character; - inStream.Read(character); - - deathType.push_back(character); - } - - inStream.Read(directionRelativeAngleXZ); - inStream.Read(directionRelativeAngleY); - inStream.Read(directionRelativeForce); - - if (inStream.ReadBit()) { - inStream.Read(killType); - } - - inStream.Read(killerID); - - if (inStream.ReadBit()) { - inStream.Read(lootOwnerID); - } - - auto* zoneController = Game::zoneManager->GetZoneControlObject(); - - auto* racingControlComponent = zoneController->GetComponent(); - - LOG("Got die request: %i", entity->GetLOT()); - - if (racingControlComponent != nullptr) { - auto* possessableComponent = entity->GetComponent(); - - if (possessableComponent != nullptr) { - entity = Game::entityManager->GetEntity(possessableComponent->GetPossessor()); - - if (entity == nullptr) { - return; - } - } - - racingControlComponent->OnRequestDie(entity); - } else { - auto* destroyableComponent = entity->GetComponent(); - - if (!destroyableComponent) return; - - destroyableComponent->Smash(killerID, killType, deathType); - } -} - - - - void GameMessages::SendUpdateReputation(const LWOOBJID objectId, const int64_t reputation, const SystemAddress& sysAddr) { CBITSTREAM; CMSGHEADER; @@ -1506,71 +1114,6 @@ void GameMessages::SendUpdateReputation(const LWOOBJID objectId, const int64_t r SEND_PACKET; } -void GameMessages::SendSetResurrectRestoreValues(Entity* targetEntity, int32_t armorRestore, int32_t healthRestore, int32_t imaginationRestore) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(targetEntity->GetObjectID()); - bitStream.Write(MessageType::Game::SET_RESURRECT_RESTORE_VALUES); - - bitStream.Write(armorRestore != -1); - if (armorRestore != -1) bitStream.Write(armorRestore); - - bitStream.Write(healthRestore != -1); - if (healthRestore != -1) bitStream.Write(healthRestore); - - bitStream.Write(imaginationRestore != -1); - if (imaginationRestore != -1) bitStream.Write(imaginationRestore); - - SEND_PACKET_BROADCAST; -} - - - - - - - - -void GameMessages::SendAddBuff(LWOOBJID& objectID, const LWOOBJID& casterID, uint32_t buffID, uint32_t msDuration, - bool addImmunity, bool cancelOnDamaged, bool cancelOnDeath, bool cancelOnLogout, - bool cancelOnRemoveBuff, bool cancelOnUi, bool cancelOnUnequip, bool cancelOnZone, bool addedByTeammate, bool applyOnTeammates, - const SystemAddress& sysAddr) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectID); - bitStream.Write(MessageType::Game::ADD_BUFF); - - bitStream.Write(addedByTeammate); // Added by teammate - bitStream.Write(applyOnTeammates); // Apply on teammates - bitStream.Write(cancelOnDamaged); - bitStream.Write(cancelOnDeath); - bitStream.Write(cancelOnLogout); - - bitStream.Write(false); // Cancel on move - bitStream.Write(cancelOnRemoveBuff); - bitStream.Write(cancelOnUi); - bitStream.Write(cancelOnUnequip); - bitStream.Write(cancelOnZone); - - bitStream.Write(false); // Ignore immunities - bitStream.Write(addImmunity); - bitStream.Write(false); // Use ref count - - bitStream.Write(casterID != LWOOBJID_EMPTY); - if (casterID != LWOOBJID_EMPTY) bitStream.Write(casterID); - - bitStream.Write(buffID); - - bitStream.Write(msDuration != 0); - if (msDuration != 0) bitStream.Write(msDuration); - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - - // NT //----------------------------------------------------------------------------------------------------------------------------------------------- @@ -1800,45 +1343,6 @@ void GameMessages::HandlePickupCurrency(RakNet::BitStream& inStream, Entity* ent } } -void GameMessages::HandleRequestDie(RakNet::BitStream& inStream, Entity* entity) { - LWOOBJID killerID; - LWOOBJID lootOwnerID; - bool bDieAccepted = false; - eKillType killType; - std::u16string deathType; - float directionRelative_AngleY; - float directionRelative_AngleXZ; - float directionRelative_Force; - bool bClientDeath = false; - bool bSpawnLoot = true; - float coinSpawnTime = -1.0f; - - inStream.Read(bClientDeath); - inStream.Read(bDieAccepted); - inStream.Read(bSpawnLoot); - - bool coinSpawnTimeIsDefault{}; - inStream.Read(coinSpawnTimeIsDefault); - if (coinSpawnTimeIsDefault != 0) inStream.Read(coinSpawnTime); - - /*uint32_t deathTypeLength = deathType.size(); - inStream.Read(deathTypeLength); - for (uint32_t k = 0; k < deathTypeLength; k++) { - inStream.Read(deathType[k]); - }*/ - - inStream.Read(directionRelative_AngleXZ); - inStream.Read(directionRelative_AngleY); - inStream.Read(directionRelative_Force); - - bool killTypeIsDefault{}; - inStream.Read(killTypeIsDefault); - if (killTypeIsDefault != 0) inStream.Read(killType); - - inStream.Read(lootOwnerID); - inStream.Read(killerID); -} - void GameMessages::SendSetGravityScale(const LWOOBJID& target, const float effectScale, const SystemAddress& sysAddr) { CBITSTREAM; CMSGHEADER; @@ -1851,22 +1355,6 @@ void GameMessages::SendSetGravityScale(const LWOOBJID& target, const float effec SEND_PACKET; } -void GameMessages::HandleResurrect(RakNet::BitStream& inStream, Entity* entity) { - bool immediate = inStream.ReadBit(); - - Entity* zoneControl = Game::entityManager->GetZoneControlEntity(); - if (zoneControl) { - zoneControl->GetScript()->OnPlayerResurrected(zoneControl, entity); - } - - std::vector scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY); - for (Entity* scriptEntity : scriptedActs) { - if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds - scriptEntity->GetScript()->OnPlayerResurrected(scriptEntity, entity); - } - } -} - void GameMessages::HandleMatchRequest(RakNet::BitStream& inStream, Entity* entity) { LWOOBJID activator; uint32_t playerChoicesLen; @@ -2055,47 +1543,6 @@ void GameMessages::HandleUpdatePlayerStatistic(RakNet::BitStream& inStream, Enti } } -void GameMessages::HandleDeactivateBubbleBuff(RakNet::BitStream& inStream, Entity* entity) { - auto controllablePhysicsComponent = entity->GetComponent(); - if (controllablePhysicsComponent) controllablePhysicsComponent->DeactivateBubbleBuff(); - GameMessages::SendDeactivateBubbleBuffFromServer(entity->GetObjectID(), entity->GetSystemAddress()); -} - -void GameMessages::HandleActivateBubbleBuff(RakNet::BitStream& inStream, Entity* entity) { - bool specialAnimations; - if (!inStream.Read(specialAnimations)) return; - - std::u16string type = GeneralUtils::ReadWString(inStream); - auto bubbleType = eBubbleType::DEFAULT; - if (type == u"skunk") bubbleType = eBubbleType::SKUNK; - else if (type == u"energy") bubbleType = eBubbleType::ENERGY; - - auto controllablePhysicsComponent = entity->GetComponent(); - if (controllablePhysicsComponent) controllablePhysicsComponent->ActivateBubbleBuff(bubbleType, specialAnimations); -} - -void GameMessages::SendActivateBubbleBuffFromServer(LWOOBJID objectId, const SystemAddress& sysAddr) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectId); - bitStream.Write(MessageType::Game::ACTIVATE_BUBBLE_BUFF_FROM_SERVER); - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - -void GameMessages::SendDeactivateBubbleBuffFromServer(LWOOBJID objectId, const SystemAddress& sysAddr) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectId); - bitStream.Write(MessageType::Game::DEACTIVATE_BUBBLE_BUFF_FROM_SERVER); - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - void GameMessages::HandleZoneSummaryDismissed(RakNet::BitStream& inStream, Entity* entity) { LWOOBJID player_id; inStream.Read(player_id); @@ -2292,24 +1739,6 @@ namespace GameMessages { missionComponent->Progress(eMissionTaskType::INTERACT, interactedObject->GetLOT(), interactedObject->GetObjectID()); } - void Smash::Serialize(RakNet::BitStream& stream) const { - stream.Write(bIgnoreObjectVisibility); - stream.Write(force); - stream.Write(ghostCapacity); - stream.Write(killerID); - } - - void UnSmash::Serialize(RakNet::BitStream& stream) const { - // Both fields are optional with a default, like the client's GameMessage::UnSmash::Serialize. - BitStreamUtils::WriteOptional(stream, builderID, LWOOBJID_EMPTY); - BitStreamUtils::WriteOptional(stream, duration, 3.0f); - } - bool UnSmash::Deserialize(RakNet::BitStream& stream) { - VALIDATE_READ(BitStreamUtils::ReadOptional(stream, builderID, LWOOBJID_EMPTY)); - VALIDATE_READ(BitStreamUtils::ReadOptional(stream, duration, 3.0f)); - return true; - } - void PlayBehaviorSound::Serialize(RakNet::BitStream& stream) const { stream.Write(soundID != -1); if (soundID != -1) stream.Write(soundID); diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index 1478cac01..e0d2c2b8e 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -132,9 +132,7 @@ namespace GameMessages { void SendFireEventClientSide(const LWOOBJID& objectID, const SystemAddress& sysAddr, std::u16string args, const LWOOBJID& object, int64_t param1, int param2, const LWOOBJID& sender); void SendTeleport(const LWOOBJID& objectID, const NiPoint3& pos, const NiQuaternion& rot, const SystemAddress& sysAddr, bool bSetRotation = false); void SendPlayerReady(Entity* entity, const SystemAddress& sysAddr); - void SendPlayerAllowedRespawn(LWOOBJID entityID, bool doNotPromptRespawn, const SystemAddress& systemAddress); void SendInvalidZoneTransferList(Entity* entity, const SystemAddress& sysAddr, const std::u16string& feedbackURL, const std::u16string& invalidMapTransferList, bool feedbackOnExit, bool feedbackOnInvalidTransfer); - void SendKnockback(const LWOOBJID& objectID, const LWOOBJID& caster, const LWOOBJID& originator, int knockBackTimeMS, const NiPoint3& vector); void SendPlayerSetCameraCyclingMode(const LWOOBJID& objectID, const SystemAddress& sysAddr, bool bAllowCyclingWhileDeadOnly = true, eCyclingMode cyclingMode = eCyclingMode::ALLOW_CYCLE_TEAMMATES); @@ -161,13 +159,9 @@ namespace GameMessages { void AddActivityOwner(Entity* entity, LWOOBJID& ownerID); void SendTerminateInteraction(const LWOOBJID& objectID, eTerminateType type, const LWOOBJID& terminator); - void SendDieNoImplCode(Entity* entity, const LWOOBJID& killerID, const LWOOBJID& lootOwnerID, eKillType killType, std::u16string deathType, float directionRelative_AngleY, float directionRelative_AngleXZ, float directionRelative_Force, bool bClientDeath, bool bSpawnLoot); - void SendDie(Entity* entity, const LWOOBJID& killerID, const LWOOBJID& lootOwnerID, bool bDieAccepted, eKillType killType, std::u16string deathType, float directionRelative_AngleY, float directionRelative_AngleXZ, float directionRelative_Force, bool bClientDeath, bool bSpawnLoot, float coinSpawnTime); - void SendSetGravityScale(const LWOOBJID& target, const float effectScale, const SystemAddress& sysAddr); void SendSetJetPackMode(Entity* entity, bool use, bool bypassChecks = false, bool doHover = false, int effectID = -1, float airspeed = 10, float maxAirspeed = 15, float verticalVelocity = 1, int warningEffectID = -1); - void SendResurrect(Entity* entity); void SendSetNetworkScriptVar(Entity* entity, const SystemAddress& sysAddr, std::string data); void SendSetPlayerControlScheme(Entity* entity, eControlScheme controlScheme); @@ -176,28 +170,6 @@ namespace GameMessages { void SendMatchResponse(Entity* entity, const SystemAddress& sysAddr, int response); void SendMatchUpdate(Entity* entity, const SystemAddress& sysAddr, std::string data, eMatchUpdate type); - void SendSetResurrectRestoreValues(Entity* targetEntity, int32_t armorRestore, int32_t healthRestore, int32_t imaginationRestore); - - /** - * Sends a message to an Entity to smash itself, but not delete or destroy itself from the world - * - * @param entity The Entity that will smash itself into bricks - * @param force The force the Entity will be smashed with - * @param ghostOpacity The ghosting opacity of the smashed Entity - * @param killerID The Entity that invoked the smash, if none exists, this should be LWOOBJID_EMPTY - * @param ignoreObjectVisibility Whether or not to ignore the objects visibility - */ - void SendSmash(Entity* entity, float force, float ghostOpacity, LWOOBJID killerID, bool ignoreObjectVisibility = false); - - /** - * Sends a message to an Entity to UnSmash itself (aka rebuild itself over a duration) - * - * @param entity The Entity that will UnSmash itself - * @param builderID The Entity that invoked the build (LWOOBJID_EMPTY if none exists or invoked the rebuild) - * @param duration The duration for the Entity to rebuild over. 3 seconds by default - */ - void SendUnSmash(Entity* entity, LWOOBJID builderID = LWOOBJID_EMPTY, float duration = 3.0f); - // Rails stuff void SendSetRailMovement(const LWOOBJID& objectID, bool pathGoForward, std::u16string pathName, uint32_t pathStart, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS, @@ -219,59 +191,12 @@ namespace GameMessages { void SendNotifyClientFailedPrecondition(LWOOBJID objectId, const SystemAddress& sysAddr, const std::u16string& failedReason, int preconditionID); - void SendAddBuff(LWOOBJID& objectID, const LWOOBJID& casterID, uint32_t buffID, uint32_t msDuration, - bool addImmunity = false, bool cancelOnDamaged = false, bool cancelOnDeath = true, - bool cancelOnLogout = false, bool cancelOnRemoveBuff = true, bool cancelOnUi = false, - bool cancelOnUnequip = false, bool cancelOnZone = false, bool addedByTeammate = false, bool applyOnTeammates = false, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS); - void SendSetName(LWOOBJID objectID, std::u16string name, const SystemAddress& sysAddr); void SendLockNodeRotation(Entity* entity, std::string nodeName); - void SendSetStunned(LWOOBJID objectId, eStateChangeType stateChangeType, const SystemAddress& sysAddr, - LWOOBJID originator = LWOOBJID_EMPTY, bool bCantAttack = false, bool bCantEquip = false, - bool bCantInteract = false, bool bCantJump = false, bool bCantMove = false, bool bCantTurn = false, - bool bCantUseItem = false, bool bDontTerminateInteract = false, bool bIgnoreImmunity = true, - bool bCantAttackOutChangeWasApplied = false, bool bCantEquipOutChangeWasApplied = false, - bool bCantInteractOutChangeWasApplied = false, bool bCantJumpOutChangeWasApplied = false, - bool bCantMoveOutChangeWasApplied = false, bool bCantTurnOutChangeWasApplied = false, - bool bCantUseItemOutChangeWasApplied = false); - - void SendSetStunImmunity( - LWOOBJID target, - eStateChangeType state, - const SystemAddress& sysAddr, - LWOOBJID originator = LWOOBJID_EMPTY, - bool bImmuneToStunAttack = false, - bool bImmuneToStunEquip = false, - bool bImmuneToStunInteract = false, - bool bImmuneToStunJump = false, - bool bImmuneToStunMove = false, - bool bImmuneToStunTurn = false, - bool bImmuneToStunUseItem = false - ); - - void SendSetStatusImmunity( - LWOOBJID objectId, - eStateChangeType state, - const SystemAddress& sysAddr, - bool bImmuneToBasicAttack = false, - bool bImmuneToDamageOverTime = false, - bool bImmuneToKnockback = false, - bool bImmuneToInterrupt = false, - bool bImmuneToSpeed = false, - bool bImmuneToImaginationGain = false, - bool bImmuneToImaginationLoss = false, - bool bImmuneToQuickbuildInterrupt = false, - bool bImmuneToPullToPoint = false - ); - void SendOrientToAngle(LWOOBJID objectId, bool bRelativeToCurrent, float fAngle, const SystemAddress& sysAddr); - void SendAddRunSpeedModifier(LWOOBJID objectId, LWOOBJID caster, uint32_t modifier, const SystemAddress& sysAddr); - - void SendRemoveRunSpeedModifier(LWOOBJID objectId, uint32_t modifier, const SystemAddress& sysAddr); - void SendNotifyObject(LWOOBJID objectId, LWOOBJID objIDSender, std::u16string name, const SystemAddress& sysAddr, int param1 = 0, int param2 = 0); void HandleVerifyAck(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); @@ -311,7 +236,6 @@ namespace GameMessages { void HandleAcknowledgePossession(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); //Racing: - void HandleRequestDie(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); // SG: @@ -363,9 +287,7 @@ namespace GameMessages { void HandleQuickBuildCancel(RakNet::BitStream& inStream, Entity* entity); void HandleNotifyServerLevelProcessingComplete(RakNet::BitStream& inStream, Entity* entity); void HandlePickupCurrency(RakNet::BitStream& inStream, Entity* entity); - void HandleRequestDie(RakNet::BitStream& inStream, Entity* entity); void HandlePickupItem(RakNet::BitStream& inStream, Entity* entity); - void HandleResurrect(RakNet::BitStream& inStream, Entity* entity); void HandleModifyPlayerZoneStatistic(RakNet::BitStream& inStream, Entity* entity); void HandleUpdatePlayerStatistic(RakNet::BitStream& inStream, Entity* entity); @@ -373,16 +295,7 @@ namespace GameMessages { void HandleReportBug(RakNet::BitStream& inStream, Entity* entity); - void SendRemoveBuff(Entity* entity, bool fromUnEquip, bool removeImmunity, uint32_t buffId); - // bubble - void HandleDeactivateBubbleBuff(RakNet::BitStream& inStream, Entity* entity); - - void HandleActivateBubbleBuff(RakNet::BitStream& inStream, Entity* entity); - - void SendActivateBubbleBuffFromServer(LWOOBJID objectId, const SystemAddress& sysAddr); - - void SendDeactivateBubbleBuffFromServer(LWOOBJID objectId, const SystemAddress& sysAddr); void HandleZoneSummaryDismissed(RakNet::BitStream& inStream, Entity* entity); @@ -505,27 +418,6 @@ namespace GameMessages { }; using RequestUseEvent = NetGameMsgEvent; - struct Smash : public NetGameMsg { - Smash() : NetGameMsg(MessageType::Game::SMASH) {} - - void Serialize(RakNet::BitStream& stream) const override; - - bool bIgnoreObjectVisibility{}; - bool force{}; - float ghostCapacity{}; - LWOOBJID killerID{}; - }; - - struct UnSmash : public NetGameMsg { - UnSmash() : NetGameMsg(MessageType::Game::UN_SMASH) {} - - void Serialize(RakNet::BitStream& stream) const override; - bool Deserialize(RakNet::BitStream& stream) override; - - LWOOBJID builderID{ LWOOBJID_EMPTY }; - float duration{ 3.0f }; - }; - struct PlayBehaviorSound : public NetGameMsg { PlayBehaviorSound() : NetGameMsg(MessageType::Game::PLAY_BEHAVIOR_SOUND) {} diff --git a/dGame/dPropertyBehaviors/Strip.cpp b/dGame/dPropertyBehaviors/Strip.cpp index 82a937282..da1958b02 100644 --- a/dGame/dPropertyBehaviors/Strip.cpp +++ b/dGame/dPropertyBehaviors/Strip.cpp @@ -1,4 +1,5 @@ #include "Strip.h" +#include "CombatMessages.h" #include "Amf3.h" #include "ControlBehaviorMsgs.h" diff --git a/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp b/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp index 92b189960..ff1e3d140 100644 --- a/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp +++ b/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp @@ -251,7 +251,7 @@ namespace GMZeroCommands { return; } - GameMessages::SendResurrect(entity); + DestroyableComponent::Resurrect(*entity); } void RequestMailCount(Entity* entity, const SystemAddress& sysAddr, const std::string args) { diff --git a/dScripts/02_server/Enemy/General/BaseEnemyApe.cpp b/dScripts/02_server/Enemy/General/BaseEnemyApe.cpp index 8c38cbceb..2ba6449bd 100644 --- a/dScripts/02_server/Enemy/General/BaseEnemyApe.cpp +++ b/dScripts/02_server/Enemy/General/BaseEnemyApe.cpp @@ -3,6 +3,7 @@ #include "DestroyableComponent.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "CombatMessages.h" #include "EntityManager.h" #include "EntityInfo.h" #include "SkillComponent.h" @@ -139,9 +140,19 @@ void BaseEnemyApe::StunApe(Entity* self, bool stunState) { skillComponent->Interrupt(); } - GameMessages::SendSetStunned(self->GetObjectID(), stunState ? eStateChangeType::PUSH : eStateChangeType::POP, UNASSIGNED_SYSTEM_ADDRESS, self->GetObjectID(), - true, true, true, true, true, - true, true, true, true); + GameMessages::SetStunned stun; + stun.target = self->GetObjectID(); + stun.StateChangeType = stunState ? eStateChangeType::PUSH : eStateChangeType::POP; + stun.Originator = self->GetObjectID(); + stun.bCantAttack = true; + stun.bCantEquip = true; + stun.bCantInteract = true; + stun.bCantJump = true; + stun.bCantMove = true; + stun.bCantTurn = true; + stun.bCantUseItem = true; + stun.bDontTerminateInteract = true; + stun.Send(UNASSIGNED_SYSTEM_ADDRESS); self->SetBoolean(u"knockedOut", stunState); } diff --git a/dScripts/02_server/Map/AM/AmSkullkinDrill.cpp b/dScripts/02_server/Map/AM/AmSkullkinDrill.cpp index bf0eb6f7e..891a81e1b 100644 --- a/dScripts/02_server/Map/AM/AmSkullkinDrill.cpp +++ b/dScripts/02_server/Map/AM/AmSkullkinDrill.cpp @@ -1,6 +1,7 @@ #include "AmSkullkinDrill.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "CombatMessages.h" #include "MovingPlatformComponent.h" #include "DestroyableComponent.h" #include "ProximityMonitorComponent.h" @@ -165,9 +166,15 @@ void AmSkullkinDrill::FreezePlayer(Entity* self, Entity* player, bool bFreeze) { } } - GameMessages::SendSetStunned(player->GetObjectID(), StateChangeType, player->GetSystemAddress(), self->GetObjectID(), - true, false, true, false, true, false, true - ); + GameMessages::SetStunned stun; + stun.target = player->GetObjectID(); + stun.StateChangeType = StateChangeType; + stun.Originator = self->GetObjectID(); + stun.bCantAttack = true; + stun.bCantInteract = true; + stun.bCantMove = true; + stun.bCantUseItem = true; + stun.Send(player->GetSystemAddress()); } void AmSkullkinDrill::OnArrived(Entity* self, uint32_t waypointIndex) { diff --git a/dScripts/02_server/Map/AM/AmSkullkinDrillStand.cpp b/dScripts/02_server/Map/AM/AmSkullkinDrillStand.cpp index f1919ac9d..cde7b0c86 100644 --- a/dScripts/02_server/Map/AM/AmSkullkinDrillStand.cpp +++ b/dScripts/02_server/Map/AM/AmSkullkinDrillStand.cpp @@ -1,6 +1,7 @@ #include "AmSkullkinDrillStand.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "CombatMessages.h" #include "dpEntity.h" #include "Entity.h" #include "RenderComponent.h" @@ -30,7 +31,12 @@ void AmSkullkinDrillStand::OnProximityUpdate(Entity* self, Entity* entering, std NiPoint3 newVec = { (objPos.x - myPos.x) * 4.5f, 15, (objPos.z - myPos.z) * 4.5f }; - GameMessages::SendKnockback(entering->GetObjectID(), self->GetObjectID(), self->GetObjectID(), 0, newVec); + GameMessages::Knockback knockback; + knockback.target = entering->GetObjectID(); + knockback.Caster = self->GetObjectID(); + knockback.Originator = self->GetObjectID(); + knockback.vector = newVec; + knockback.Send(UNASSIGNED_SYSTEM_ADDRESS); GameMessages::PlayFXEffect(entering->GetObjectID(), 1378, u"create", "pushBack").Send(UNASSIGNED_SYSTEM_ADDRESS); diff --git a/dScripts/02_server/Map/FV/Racing/RaceMaelstromGeiser.cpp b/dScripts/02_server/Map/FV/Racing/RaceMaelstromGeiser.cpp index 2866f7c8e..9941bab19 100644 --- a/dScripts/02_server/Map/FV/Racing/RaceMaelstromGeiser.cpp +++ b/dScripts/02_server/Map/FV/Racing/RaceMaelstromGeiser.cpp @@ -1,6 +1,7 @@ #include "RaceMaelstromGeiser.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "CombatMessages.h" #include "PossessableComponent.h" #include "PossessorComponent.h" #include "EntityManager.h" @@ -56,7 +57,12 @@ void RaceMaelstromGeiser::OnProximityUpdate(Entity* self, Entity* entering, std: } - GameMessages::SendDie(vehicle, self->GetObjectID(), LWOOBJID_EMPTY, true, eKillType::VIOLENT, u"", 0, 0, 0, true, false, 0); + GameMessages::Die die; + die.target = vehicle->GetObjectID(); + die.bClientDeath = true; + die.bSpawnLoot = false; + die.killerID = self->GetObjectID(); + die.Send(UNASSIGNED_SYSTEM_ADDRESS); auto* zoneController = Game::zoneManager->GetZoneControlObject(); diff --git a/dScripts/02_server/Map/GF/GfCaptainsCannon.cpp b/dScripts/02_server/Map/GF/GfCaptainsCannon.cpp index 759d8817e..73f5ba3d4 100644 --- a/dScripts/02_server/Map/GF/GfCaptainsCannon.cpp +++ b/dScripts/02_server/Map/GF/GfCaptainsCannon.cpp @@ -1,6 +1,7 @@ #include "GfCaptainsCannon.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "CombatMessages.h" #include "EntityManager.h" #include "MissionComponent.h" #include "RenderComponent.h" @@ -17,9 +18,18 @@ void GfCaptainsCannon::OnUse(Entity* self, Entity* user) { self->SetVar(u"bIsInUse", true); self->SetNetworkVar(u"bIsInUse", true); - GameMessages::SendSetStunned(user->GetObjectID(), eStateChangeType::PUSH, user->GetSystemAddress(), - LWOOBJID_EMPTY, true, true, true, true, true, true, true, true - ); + GameMessages::SetStunned stun; + stun.target = user->GetObjectID(); + stun.StateChangeType = eStateChangeType::PUSH; + stun.bCantAttack = true; + stun.bCantEquip = true; + stun.bCantInteract = true; + stun.bCantJump = true; + stun.bCantMove = true; + stun.bCantTurn = true; + stun.bCantUseItem = true; + stun.bDontTerminateInteract = true; + stun.Send(user->GetSystemAddress()); auto position = self->GetPosition(); auto forward = QuatUtils::Forward(self->GetRotation()); @@ -73,9 +83,18 @@ void GfCaptainsCannon::OnTimerDone(Entity* self, std::string timerName) { ambientSound.audioGUID = "{7457d85c-4537-4317-ac9d-2f549219ea87}"; ambientSound.SendToClient(player->GetSystemAddress()); } else if (timerName == "cinematicTimer") { - GameMessages::SendSetStunned(playerId, eStateChangeType::POP, player->GetSystemAddress(), - LWOOBJID_EMPTY, true, true, true, true, true, true, true, true - ); + GameMessages::SetStunned stun; + stun.target = playerId; + stun.StateChangeType = eStateChangeType::POP; + stun.bCantAttack = true; + stun.bCantEquip = true; + stun.bCantInteract = true; + stun.bCantJump = true; + stun.bCantMove = true; + stun.bCantTurn = true; + stun.bCantUseItem = true; + stun.bDontTerminateInteract = true; + stun.Send(player->GetSystemAddress()); self->SetVar(u"bIsInUse", false); self->SetNetworkVar(u"bIsInUse", false); diff --git a/dScripts/02_server/Map/GF/MastTeleport.cpp b/dScripts/02_server/Map/GF/MastTeleport.cpp index 7c1633846..34f97e1fa 100644 --- a/dScripts/02_server/Map/GF/MastTeleport.cpp +++ b/dScripts/02_server/Map/GF/MastTeleport.cpp @@ -2,6 +2,7 @@ #include "EntityManager.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "CombatMessages.h" #include "Preconditions.h" #include "eEndBehavior.h" #include "DestroyableComponent.h" @@ -21,9 +22,17 @@ void MastTeleport::OnQuickBuildComplete(Entity* self, Entity* target) { if (Preconditions::Check(target, 154) && Preconditions::Check(target, 44)) { self->SetVar(u"userID", target->GetObjectID()); - GameMessages::SendSetStunned(target->GetObjectID(), eStateChangeType::PUSH, target->GetSystemAddress(), - LWOOBJID_EMPTY, true, true, true, true, true, true, true - ); + GameMessages::SetStunned stun; + stun.target = target->GetObjectID(); + stun.StateChangeType = eStateChangeType::PUSH; + stun.bCantAttack = true; + stun.bCantEquip = true; + stun.bCantInteract = true; + stun.bCantJump = true; + stun.bCantMove = true; + stun.bCantTurn = true; + stun.bCantUseItem = true; + stun.Send(target->GetSystemAddress()); auto* destroyableComponent = target->GetComponent(); if (destroyableComponent) destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, true, true, true, true, true, false, false, true, true); @@ -92,9 +101,17 @@ void MastTeleport::OnTimerDone(Entity* self, std::string timerName) { GameMessages::SendTeleport(playerId, position, QuatUtils::IDENTITY, player->GetSystemAddress()); - GameMessages::SendSetStunned(playerId, eStateChangeType::POP, player->GetSystemAddress(), - LWOOBJID_EMPTY, true, true, true, true, true, true, true - ); + GameMessages::SetStunned stun; + stun.target = playerId; + stun.StateChangeType = eStateChangeType::POP; + stun.bCantAttack = true; + stun.bCantEquip = true; + stun.bCantInteract = true; + stun.bCantJump = true; + stun.bCantMove = true; + stun.bCantTurn = true; + stun.bCantUseItem = true; + stun.Send(player->GetSystemAddress()); auto* destroyableComponent = player->GetComponent(); if (destroyableComponent) destroyableComponent->SetStatusImmunity(eStateChangeType::POP, true, true, true, true, true, false, false, true, true); Game::entityManager->SerializeEntity(player); diff --git a/dScripts/02_server/Map/NT/NtAssemblyTubeServer.cpp b/dScripts/02_server/Map/NT/NtAssemblyTubeServer.cpp index 9d30ccd60..debcaf054 100644 --- a/dScripts/02_server/Map/NT/NtAssemblyTubeServer.cpp +++ b/dScripts/02_server/Map/NT/NtAssemblyTubeServer.cpp @@ -1,6 +1,7 @@ #include "NtAssemblyTubeServer.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "CombatMessages.h" #include "EntityManager.h" #include "MissionComponent.h" #include "eMissionTaskType.h" @@ -42,9 +43,17 @@ void NtAssemblyTubeServer::RunAssemblyTube(Entity* self, Entity* player) { if (player->IsPlayer() && !bPlayerBeingTeleported) { auto teleCinematic = self->GetVar(u"Cinematic"); - GameMessages::SendSetStunned(playerID, eStateChangeType::PUSH, player->GetSystemAddress(), LWOOBJID_EMPTY, - true, true, true, true, true, true, true - ); + GameMessages::SetStunned stun; + stun.target = playerID; + stun.StateChangeType = eStateChangeType::PUSH; + stun.bCantAttack = true; + stun.bCantEquip = true; + stun.bCantInteract = true; + stun.bCantJump = true; + stun.bCantMove = true; + stun.bCantTurn = true; + stun.bCantUseItem = true; + stun.Send(player->GetSystemAddress()); if (!teleCinematic.empty()) { const auto teleCinematicUname = teleCinematic; @@ -124,7 +133,15 @@ void NtAssemblyTubeServer::UnlockPlayer(Entity* self, Entity* player) { m_TeleportingPlayerTable[playerID] = false; - GameMessages::SendSetStunned(playerID, eStateChangeType::POP, player->GetSystemAddress(), LWOOBJID_EMPTY, - true, true, true, true, true, true, true - ); + GameMessages::SetStunned stun; + stun.target = playerID; + stun.StateChangeType = eStateChangeType::POP; + stun.bCantAttack = true; + stun.bCantEquip = true; + stun.bCantInteract = true; + stun.bCantJump = true; + stun.bCantMove = true; + stun.bCantTurn = true; + stun.bCantUseItem = true; + stun.Send(player->GetSystemAddress()); } diff --git a/dScripts/02_server/Map/NT/NtParadoxPanelServer.cpp b/dScripts/02_server/Map/NT/NtParadoxPanelServer.cpp index 1d81a1192..9b65168dd 100644 --- a/dScripts/02_server/Map/NT/NtParadoxPanelServer.cpp +++ b/dScripts/02_server/Map/NT/NtParadoxPanelServer.cpp @@ -1,6 +1,7 @@ #include "NtParadoxPanelServer.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "CombatMessages.h" #include "MissionComponent.h" #include "EntityManager.h" #include "Character.h" @@ -40,11 +41,23 @@ void NtParadoxPanelServer::OnUse(Entity* self, Entity* user) { RenderComponent::PlayAnimation(player, u"rebuild-celebrate"); GameMessages::SendNotifyClientObject(self->GetObjectID(), u"SparkStop", 0, 0, player->GetObjectID(), "", player->GetSystemAddress()); - GameMessages::SendSetStunned(player->GetObjectID(), eStateChangeType::POP, player->GetSystemAddress(), LWOOBJID_EMPTY, false, false, true, false, true, true, false, false, true); + GameMessages::SetStunned stun; + stun.target = player->GetObjectID(); + stun.StateChangeType = eStateChangeType::POP; + stun.bCantInteract = true; + stun.bCantMove = true; + stun.bCantTurn = true; + stun.Send(player->GetSystemAddress()); self->SetVar(u"bActive", false); }); RenderComponent::PlayAnimation(user, u"nexus-powerpanel", 6.0f); - GameMessages::SendSetStunned(user->GetObjectID(), eStateChangeType::PUSH, user->GetSystemAddress(), LWOOBJID_EMPTY, false, false, true, false, true, true, false, false, true); + GameMessages::SetStunned stun; + stun.target = user->GetObjectID(); + stun.StateChangeType = eStateChangeType::PUSH; + stun.bCantInteract = true; + stun.bCantMove = true; + stun.bCantTurn = true; + stun.Send(user->GetSystemAddress()); return; } @@ -52,7 +65,12 @@ void NtParadoxPanelServer::OnUse(Entity* self, Entity* user) { const auto dir = QuatUtils::Right(self->GetRotation()); - GameMessages::SendKnockback(user->GetObjectID(), self->GetObjectID(), self->GetObjectID(), 0, { dir.x * 15, 5, dir.z * 15 }); + GameMessages::Knockback knockback; + knockback.target = user->GetObjectID(); + knockback.Caster = self->GetObjectID(); + knockback.Originator = self->GetObjectID(); + knockback.vector = NiPoint3{ dir.x * 15, 5, dir.z * 15 }; + knockback.Send(UNASSIGNED_SYSTEM_ADDRESS); GameMessages::PlayFXEffect(self->GetObjectID(), 6432, u"create", "console_sparks").Send(UNASSIGNED_SYSTEM_ADDRESS); diff --git a/dScripts/02_server/Map/NT/NtParadoxTeleServer.cpp b/dScripts/02_server/Map/NT/NtParadoxTeleServer.cpp index 65670fb28..7e63bb5e9 100644 --- a/dScripts/02_server/Map/NT/NtParadoxTeleServer.cpp +++ b/dScripts/02_server/Map/NT/NtParadoxTeleServer.cpp @@ -1,6 +1,7 @@ #include "NtParadoxTeleServer.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "CombatMessages.h" #include "EntityManager.h" #include "MissionComponent.h" #include "eMissionTaskType.h" @@ -26,9 +27,17 @@ void NtParadoxTeleServer::OnProximityUpdate(Entity* self, Entity* entering, std: const auto bPlayerBeingTeleported = m_TeleportingPlayerTable[playerID]; if (player->IsPlayer() && !bPlayerBeingTeleported) { - GameMessages::SendSetStunned(playerID, eStateChangeType::PUSH, player->GetSystemAddress(), LWOOBJID_EMPTY, - true, true, true, true, true, true, true - ); + GameMessages::SetStunned stun; + stun.target = playerID; + stun.StateChangeType = eStateChangeType::PUSH; + stun.bCantAttack = true; + stun.bCantEquip = true; + stun.bCantInteract = true; + stun.bCantJump = true; + stun.bCantMove = true; + stun.bCantTurn = true; + stun.bCantUseItem = true; + stun.Send(player->GetSystemAddress()); auto animTime = RenderComponent::PlayAnimation(player, u"teledeath", 4.0f); if (animTime == 0.0f) animTime = 2.0f; @@ -106,9 +115,17 @@ void NtParadoxTeleServer::UnlockPlayer(Entity* self, Entity* player) { m_TeleportingPlayerTable[playerID] = false; - GameMessages::SendSetStunned(playerID, eStateChangeType::POP, player->GetSystemAddress(), LWOOBJID_EMPTY, - true, true, true, true, true, true, true - ); + GameMessages::SetStunned stun; + stun.target = playerID; + stun.StateChangeType = eStateChangeType::POP; + stun.bCantAttack = true; + stun.bCantEquip = true; + stun.bCantInteract = true; + stun.bCantJump = true; + stun.bCantMove = true; + stun.bCantTurn = true; + stun.bCantUseItem = true; + stun.Send(player->GetSystemAddress()); auto teleCinematic = self->GetVar(u"Cinematic"); diff --git a/dScripts/02_server/Map/NT/NtVentureCannonServer.cpp b/dScripts/02_server/Map/NT/NtVentureCannonServer.cpp index d35950f9d..ebf7beaea 100644 --- a/dScripts/02_server/Map/NT/NtVentureCannonServer.cpp +++ b/dScripts/02_server/Map/NT/NtVentureCannonServer.cpp @@ -1,6 +1,7 @@ #include "NtVentureCannonServer.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "CombatMessages.h" #include "EntityManager.h" #include "Entity.h" #include "GeneralUtils.h" @@ -21,9 +22,17 @@ void NtVentureCannonServer::OnUse(Entity* self, Entity* user) { self->SetNetworkVar(u"bIsInUse", true); - GameMessages::SendSetStunned(playerID, eStateChangeType::PUSH, player->GetSystemAddress(), LWOOBJID_EMPTY, - true, true, true, true, true, true, true - ); + GameMessages::SetStunned stun; + stun.target = playerID; + stun.StateChangeType = eStateChangeType::PUSH; + stun.bCantAttack = true; + stun.bCantEquip = true; + stun.bCantInteract = true; + stun.bCantJump = true; + stun.bCantMove = true; + stun.bCantTurn = true; + stun.bCantUseItem = true; + stun.Send(player->GetSystemAddress()); auto destPosition = self->GetPosition(); @@ -117,9 +126,17 @@ void NtVentureCannonServer::ExitCannonEnded(Entity* self, Entity* player) { } void NtVentureCannonServer::UnlockCannonPlayer(Entity* self, Entity* player) { - GameMessages::SendSetStunned(player->GetObjectID(), eStateChangeType::POP, player->GetSystemAddress(), LWOOBJID_EMPTY, - true, true, true, true, true, true, true - ); + GameMessages::SetStunned stun; + stun.target = player->GetObjectID(); + stun.StateChangeType = eStateChangeType::POP; + stun.bCantAttack = true; + stun.bCantEquip = true; + stun.bCantInteract = true; + stun.bCantJump = true; + stun.bCantMove = true; + stun.bCantTurn = true; + stun.bCantUseItem = true; + stun.Send(player->GetSystemAddress()); self->SetNetworkVar(u"bIsInUse", false); diff --git a/dScripts/02_server/Map/njhub/FlameJetServer.cpp b/dScripts/02_server/Map/njhub/FlameJetServer.cpp index bf7609582..f9947c06d 100644 --- a/dScripts/02_server/Map/njhub/FlameJetServer.cpp +++ b/dScripts/02_server/Map/njhub/FlameJetServer.cpp @@ -1,6 +1,7 @@ #include "FlameJetServer.h" #include "SkillComponent.h" #include "GameMessages.h" +#include "CombatMessages.h" void FlameJetServer::OnStartup(Entity* self) { if (self->GetVar(u"NotActive")) { @@ -33,7 +34,13 @@ void FlameJetServer::OnCollisionPhantom(Entity* self, Entity* target) { dir.x = -dir.x * 15; dir.z = -dir.z * 15; - GameMessages::SendKnockback(target->GetObjectID(), self->GetObjectID(), self->GetObjectID(), 1000, dir); + GameMessages::Knockback knockback; + knockback.target = target->GetObjectID(); + knockback.Caster = self->GetObjectID(); + knockback.Originator = self->GetObjectID(); + knockback.iKnockBackTimeMS = 1000; + knockback.vector = dir; + knockback.Send(UNASSIGNED_SYSTEM_ADDRESS); } void FlameJetServer::OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) { diff --git a/dScripts/BaseConsoleTeleportServer.cpp b/dScripts/BaseConsoleTeleportServer.cpp index 896cc24a7..c64b8a569 100644 --- a/dScripts/BaseConsoleTeleportServer.cpp +++ b/dScripts/BaseConsoleTeleportServer.cpp @@ -1,6 +1,7 @@ #include "BaseConsoleTeleportServer.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "CombatMessages.h" #include "CharacterComponent.h" #include "RenderComponent.h" #include "EntityManager.h" @@ -28,10 +29,18 @@ void BaseConsoleTeleportServer::BaseOnMessageBoxResponse(Entity* self, Entity* s if (button == 1) { - GameMessages::SendSetStunned( - player->GetObjectID(), eStateChangeType::PUSH, player->GetSystemAddress(), player->GetObjectID(), - true, true, true, true, true, true, true - ); + GameMessages::SetStunned stun; + stun.target = player->GetObjectID(); + stun.StateChangeType = eStateChangeType::PUSH; + stun.Originator = player->GetObjectID(); + stun.bCantAttack = true; + stun.bCantEquip = true; + stun.bCantInteract = true; + stun.bCantJump = true; + stun.bCantMove = true; + stun.bCantTurn = true; + stun.bCantUseItem = true; + stun.Send(player->GetSystemAddress()); const auto teleportFXID = self->GetVar(u"teleportEffectID"); @@ -94,10 +103,18 @@ void BaseConsoleTeleportServer::TransferPlayer(Entity* self, Entity* player, int return; } - GameMessages::SendSetStunned( - player->GetObjectID(), eStateChangeType::POP, player->GetSystemAddress(), player->GetObjectID(), - true, true, true, true, true, true, true - ); + GameMessages::SetStunned stun; + stun.target = player->GetObjectID(); + stun.StateChangeType = eStateChangeType::POP; + stun.Originator = player->GetObjectID(); + stun.bCantAttack = true; + stun.bCantEquip = true; + stun.bCantInteract = true; + stun.bCantJump = true; + stun.bCantMove = true; + stun.bCantTurn = true; + stun.bCantUseItem = true; + stun.Send(player->GetSystemAddress()); GameMessages::SendTerminateInteraction(player->GetObjectID(), eTerminateType::FROM_INTERACTION, player->GetObjectID()); diff --git a/dScripts/BaseWavesServer.cpp b/dScripts/BaseWavesServer.cpp index 5c287e1bd..1ff875800 100644 --- a/dScripts/BaseWavesServer.cpp +++ b/dScripts/BaseWavesServer.cpp @@ -1,6 +1,7 @@ #include "BaseWavesServer.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "CombatMessages.h" #include "ActivityMessages.h" #include "DestroyableComponent.h" #include "EntityManager.h" @@ -22,7 +23,10 @@ void BaseWavesServer::SetGameVariables(Entity* self) { // Done void BaseWavesServer::BasePlayerLoaded(Entity* self, Entity* player) { GameMessages::SendPlayerSetCameraCyclingMode(player->GetObjectID(), player->GetSystemAddress()); - GameMessages::SendPlayerAllowedRespawn(player->GetObjectID(), true, player->GetSystemAddress()); + GameMessages::SetPlayerAllowedRespawn allowedRespawn; + allowedRespawn.target = player->GetObjectID(); + allowedRespawn.dontPromptForRespawn = true; + allowedRespawn.SendToClient(player->GetSystemAddress()); state.waitingPlayers.push_back(player->GetObjectID()); state.players.push_back(player->GetObjectID()); diff --git a/dScripts/EquipmentScripts/PersonalFortress.cpp b/dScripts/EquipmentScripts/PersonalFortress.cpp index 7e6d35661..582706e77 100644 --- a/dScripts/EquipmentScripts/PersonalFortress.cpp +++ b/dScripts/EquipmentScripts/PersonalFortress.cpp @@ -1,5 +1,6 @@ #include "PersonalFortress.h" #include "GameMessages.h" +#include "CombatMessages.h" #include "SkillComponent.h" #include "DestroyableComponent.h" #include "ControllablePhysicsComponent.h" @@ -22,9 +23,18 @@ void PersonalFortress::OnStartup(Entity* self) { true, true, true, true, true, true ); - GameMessages::SendSetStunned(owner->GetObjectID(), eStateChangeType::PUSH, owner->GetSystemAddress(), LWOOBJID_EMPTY, - true, true, true, true, true, true, true, true, true - ); + GameMessages::SetStunned stun; + stun.target = owner->GetObjectID(); + stun.StateChangeType = eStateChangeType::PUSH; + stun.bCantAttack = true; + stun.bCantEquip = true; + stun.bCantInteract = true; + stun.bCantJump = true; + stun.bCantMove = true; + stun.bCantTurn = true; + stun.bCantUseItem = true; + stun.bDontTerminateInteract = true; + stun.Send(owner->GetSystemAddress()); Game::entityManager->SerializeEntity(owner); } @@ -43,9 +53,18 @@ void PersonalFortress::OnDie(Entity* self, Entity* killer) { true, true, true, true, true, true ); - GameMessages::SendSetStunned(owner->GetObjectID(), eStateChangeType::POP, owner->GetSystemAddress(), LWOOBJID_EMPTY, - true, true, true, true, true, true, true, true, true - ); + GameMessages::SetStunned stun; + stun.target = owner->GetObjectID(); + stun.StateChangeType = eStateChangeType::POP; + stun.bCantAttack = true; + stun.bCantEquip = true; + stun.bCantInteract = true; + stun.bCantJump = true; + stun.bCantMove = true; + stun.bCantTurn = true; + stun.bCantUseItem = true; + stun.bDontTerminateInteract = true; + stun.Send(owner->GetSystemAddress()); Game::entityManager->SerializeEntity(owner); } diff --git a/dScripts/ai/ACT/ActVehicleDeathTrigger.cpp b/dScripts/ai/ACT/ActVehicleDeathTrigger.cpp index 90333063d..113e1aa17 100644 --- a/dScripts/ai/ACT/ActVehicleDeathTrigger.cpp +++ b/dScripts/ai/ACT/ActVehicleDeathTrigger.cpp @@ -1,6 +1,7 @@ #include "ActVehicleDeathTrigger.h" #include "PossessableComponent.h" #include "GameMessages.h" +#include "CombatMessages.h" #include "RacingControlComponent.h" #include "dZoneManager.h" #include "EntityManager.h" @@ -40,7 +41,12 @@ void ActVehicleDeathTrigger::OnCollisionPhantom(Entity* self, Entity* target) { } - GameMessages::SendDie(vehicle, self->GetObjectID(), LWOOBJID_EMPTY, true, eKillType::VIOLENT, u"", 0, 0, 0, true, false, 0); + GameMessages::Die die; + die.target = vehicle->GetObjectID(); + die.bClientDeath = true; + die.bSpawnLoot = false; + die.killerID = self->GetObjectID(); + die.Send(UNASSIGNED_SYSTEM_ADDRESS); auto* zoneController = Game::zoneManager->GetZoneControlObject(); diff --git a/dScripts/ai/AG/AgShipPlayerShockServer.cpp b/dScripts/ai/AG/AgShipPlayerShockServer.cpp index c274a535d..a439f0bde 100644 --- a/dScripts/ai/AG/AgShipPlayerShockServer.cpp +++ b/dScripts/ai/AG/AgShipPlayerShockServer.cpp @@ -1,6 +1,7 @@ #include "AgShipPlayerShockServer.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "CombatMessages.h" #include "RenderComponent.h" #include "Entity.h" #include "eTerminateType.h" @@ -12,7 +13,12 @@ void AgShipPlayerShockServer::OnUse(Entity* self, Entity* user) { } active = true; RenderComponent::PlayAnimation(user, shockAnim); - GameMessages::SendKnockback(user->GetObjectID(), self->GetObjectID(), self->GetObjectID(), 0, NiPoint3(-20, 10, -20)); + GameMessages::Knockback knockback; + knockback.target = user->GetObjectID(); + knockback.Caster = self->GetObjectID(); + knockback.Originator = self->GetObjectID(); + knockback.vector = NiPoint3(-20, 10, -20); + knockback.Send(UNASSIGNED_SYSTEM_ADDRESS); GameMessages::PlayFXEffect(self->GetObjectID(), 1430, u"create", "console_sparks").Send(UNASSIGNED_SYSTEM_ADDRESS); self->AddTimer("FXTime", fxTime); diff --git a/dScripts/ai/AG/AgSpiderBossMessage.cpp b/dScripts/ai/AG/AgSpiderBossMessage.cpp index a9a7ce0c5..a02f9441c 100644 --- a/dScripts/ai/AG/AgSpiderBossMessage.cpp +++ b/dScripts/ai/AG/AgSpiderBossMessage.cpp @@ -3,6 +3,7 @@ #include "Entity.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "CombatMessages.h" #include "RenderComponent.h" @@ -44,7 +45,10 @@ void AgSpiderBossMessage::OnCollisionPhantom(Entity* self, Entity* target) { forward.y += 15; forward.x *= 100; forward.z *= 100; - GameMessages::SendKnockback(target->GetObjectID(), LWOOBJID_EMPTY, LWOOBJID_EMPTY, 0, forward); + GameMessages::Knockback knockback; + knockback.target = target->GetObjectID(); + knockback.vector = forward; + knockback.Send(UNASSIGNED_SYSTEM_ADDRESS); if (box.isTouch || box.isDisplayed) return; box.boxSelf = self->GetObjectID(); diff --git a/tests/dGameTests/dGameMessagesTests/CMakeLists.txt b/tests/dGameTests/dGameMessagesTests/CMakeLists.txt index d9e855536..551dc0637 100644 --- a/tests/dGameTests/dGameMessagesTests/CMakeLists.txt +++ b/tests/dGameTests/dGameMessagesTests/CMakeLists.txt @@ -1,6 +1,7 @@ SET(DGAMEMESSAGES_TESTS "ActivityMessagesTests.cpp" "BuildingMessagesTests.cpp" + "CombatMessagesTests.cpp" "GameMessageTests.cpp" "GameMsgSplitTests.cpp" "EffectsMessagesTests.cpp" diff --git a/tests/dGameTests/dGameMessagesTests/CombatMessagesTests.cpp b/tests/dGameTests/dGameMessagesTests/CombatMessagesTests.cpp new file mode 100644 index 000000000..21e812899 --- /dev/null +++ b/tests/dGameTests/dGameMessagesTests/CombatMessagesTests.cpp @@ -0,0 +1,381 @@ +#include "CombatMessages.h" +#include "GameDependencies.h" +#include "GameMessageTestUtils.h" +#include "Legacy/CombatMessagesLegacy.h" + +#include "eKillType.h" +#include "eStateChangeType.h" + +#include +#include +#include +#include +#include + +#include + +using namespace GameMessageTestUtils; + +namespace { + const std::vector g_Strings = { u"", u"electro-shock-death", u"é中", std::u16string(300, u'x') }; + const std::vector g_Ids = { LWOOBJID_EMPTY, 0x1000000000000001LL, 0x0102030405060708LL }; + const std::vector g_Floats = { 0.0f, 90.0f, -1.5f }; + + PacketBytes Payload(const GameMessages::NetGameMsg& msg) { + RakNet::BitStream bitStream; + msg.Serialize(bitStream); + return FromBitStream(bitStream); + } + + template + Result ReadWithLegacy(const GameMessages::NetGameMsg& msg, const std::function& read) { + RakNet::BitStream wire; + msg.Serialize(wire); + RakNet::BitStream legacyStream(wire.GetData(), wire.GetNumberOfBytesUsed(), false); + auto result = read(legacyStream); + EXPECT_EQ(legacyStream.GetReadOffset(), wire.GetNumberOfBitsUsed()); + return result; + } + + // For the old functions that always broadcast (SEND_PACKET_BROADCAST) whatever address they were given: + // the struct is sent with Send(UNASSIGNED_SYSTEM_ADDRESS) and must produce the same bytes and destination. + void ExpectSameAsLegacyBroadcast(const std::function& legacySend, const GameMessages::NetGameMsg& msg) { + const auto legacyPackets = Capture([&] { legacySend(UNASSIGNED_SYSTEM_ADDRESS); }); + const auto newPackets = Capture([&] { msg.Send(UNASSIGNED_SYSTEM_ADDRESS); }); + ASSERT_EQ(legacyPackets.size(), 1); + ASSERT_EQ(newPackets.size(), 1); + EXPECT_PACKET_EQ(FromCapture(legacyPackets[0]), FromCapture(newPackets[0])); + EXPECT_TRUE(legacyPackets[0].broadcast); + EXPECT_TRUE(newPackets[0].broadcast); + EXPECT_EQ(legacyPackets[0].sysAddr, newPackets[0].sysAddr); + } + + // Bit i of mask, for walking every combination of a message's bools. + bool Bit(uint32_t mask, int i) { return (mask >> i) & 1; } +} + +class CombatMessagesTests : public GameDependenciesTest { +protected: + void SetUp() override { SetUpDependencies(); } + void TearDown() override { TearDownDependencies(); } +}; + +TEST_F(CombatMessagesTests, DieMatchesLegacy) { + for (const auto target : g_Targets) { + Entity entity(target, info); + for (const auto& deathType : g_Strings) { + for (const auto killType : { eKillType::VIOLENT, eKillType::SILENT }) { + for (const auto killer : g_Ids) { + for (const auto lootOwner : g_Ids) { + for (uint32_t mask = 0; mask < 4; mask++) { + for (const float value : g_Floats) { + GameMessages::Die msg; + msg.target = target; + msg.bClientDeath = Bit(mask, 0); + msg.bSpawnLoot = Bit(mask, 1); + msg.deathType = deathType; + msg.directionRelative_AngleXZ = value; + msg.directionRelative_AngleY = value * 2; + msg.directionRelative_Force = value * 3; + msg.killType = killType; + msg.killerID = killer; + msg.lootOwnerID = lootOwner; + ExpectSameAsLegacyBroadcast([&](const SystemAddress&) { + LegacyGameMessages::SendDie(&entity, killer, lootOwner, true, killType, deathType, value * 2, value, value * 3, Bit(mask, 0), Bit(mask, 1), 1.0f); + }, msg); + ExpectSameAsLegacyBroadcast([&](const SystemAddress&) { + LegacyGameMessages::SendDieNoImplCode(&entity, killer, lootOwner, killType, deathType, value * 2, value, value * 3, Bit(mask, 0), Bit(mask, 1)); + }, msg); + const auto copy = RoundTrip(msg); + EXPECT_EQ(copy.deathType, deathType); + EXPECT_EQ(copy.killType, killType); + EXPECT_EQ(copy.lootOwnerID, lootOwner); + + // RequestDie is read with the same layout. + GameMessages::RequestDie request; + request.bClientDeath = msg.bClientDeath; + request.bSpawnLoot = msg.bSpawnLoot; + request.deathType = deathType; + request.directionRelative_AngleXZ = value; + request.directionRelative_AngleY = value * 2; + request.directionRelative_Force = value * 3; + request.killType = killType; + request.killerID = killer; + request.lootOwnerID = lootOwner; + const auto legacy = ReadWithLegacy(request, LegacyGameMessages::ReadRequestDie); + const auto requestCopy = RoundTrip(request); + EXPECT_EQ(requestCopy.bClientDeath, legacy.bClientDeath); + EXPECT_EQ(requestCopy.bSpawnLoot, legacy.bSpawnLoot); + EXPECT_EQ(requestCopy.deathType, legacy.deathType); + EXPECT_EQ(requestCopy.directionRelative_AngleXZ, legacy.directionRelativeAngleXZ); + EXPECT_EQ(requestCopy.directionRelative_AngleY, legacy.directionRelativeAngleY); + EXPECT_EQ(requestCopy.directionRelative_Force, legacy.directionRelativeForce); + EXPECT_EQ(requestCopy.killType, legacy.killType); + EXPECT_EQ(requestCopy.killerID, legacy.killerID); + EXPECT_EQ(requestCopy.lootOwnerID, legacy.lootOwnerID); + if (deathType.size() < 30 && value == 0.0f) ExpectTruncatedFails(request); + } + } + } + } + } + } + } +} + +TEST_F(CombatMessagesTests, ResurrectAndRespawnMatchLegacy) { + for (const auto target : g_Targets) { + Entity entity(target, info); + GameMessages::Resurrect msg; + msg.target = target; + ExpectSameAsLegacyBroadcast([&](const SystemAddress&) { LegacyGameMessages::SendResurrect(&entity); }, msg); + for (const bool immediately : { false, true }) { + msg.bRezImmediately = immediately; + EXPECT_EQ(ReadWithLegacy(msg, LegacyGameMessages::ReadResurrect), immediately); + EXPECT_EQ(RoundTrip(msg).bRezImmediately, immediately); + ExpectTruncatedFails(msg); + + GameMessages::SetPlayerAllowedRespawn respawn; + respawn.target = target; + respawn.dontPromptForRespawn = immediately; + ExpectSameAsLegacy([&](const SystemAddress& a) { LegacyGameMessages::SendPlayerAllowedRespawn(target, immediately, a); }, respawn, SendMode::SendToClient); + EXPECT_EQ(RoundTrip(respawn).dontPromptForRespawn, immediately); + } + + for (const int32_t armor : { -1, 0, 5 }) { + for (const int32_t health : { -1, 0, 7 }) { + for (const int32_t imagination : { -1, 0, std::numeric_limits::max() }) { + GameMessages::SetResurrectRestoreValues restore; + restore.target = target; + restore.iArmorRestore = armor; + restore.iHealthRestore = health; + restore.iImaginationRestore = imagination; + ExpectSameAsLegacyBroadcast([&](const SystemAddress&) { LegacyGameMessages::SendSetResurrectRestoreValues(&entity, armor, health, imagination); }, restore); + const auto copy = RoundTrip(restore); + EXPECT_EQ(copy.iArmorRestore, armor); + EXPECT_EQ(copy.iImaginationRestore, imagination); + } + } + } + } +} + +TEST_F(CombatMessagesTests, KnockbackAndSmashMatchLegacy) { + for (const auto target : g_Targets) { + Entity entity(target, info); + for (const auto caster : g_Ids) { + for (const auto originator : g_Ids) { + for (const int32_t time : { 0, 1000, -1 }) { + GameMessages::Knockback msg; + msg.target = target; + msg.Caster = caster; + msg.Originator = originator; + msg.iKnockBackTimeMS = time; + msg.vector = NiPoint3(-20.0f, 10.0f, 0.5f); + ExpectSameAsLegacyBroadcast([&](const SystemAddress&) { LegacyGameMessages::SendKnockback(target, caster, originator, time, msg.vector); }, msg); + const auto copy = RoundTrip(msg); + EXPECT_EQ(copy.Caster, caster); + EXPECT_EQ(copy.iKnockBackTimeMS, time); + EXPECT_EQ(copy.vector, msg.vector); + } + } + for (const float force : g_Floats) { + for (const bool ignore : { false, true }) { + GameMessages::Smash smash; + smash.target = target; + smash.bIgnoreObjectVisibility = ignore; + smash.force = force; + smash.ghostOpacity = force / 2; + smash.killerID = caster; + ExpectSameAsLegacyBroadcast([&](const SystemAddress&) { LegacyGameMessages::SendSmash(&entity, force, force / 2, caster, ignore); }, smash); + EXPECT_EQ(RoundTrip(smash).killerID, caster); + } + GameMessages::UnSmash unsmash; + unsmash.target = target; + unsmash.builderID = caster; + unsmash.duration = force == 0.0f ? 3.0f : force; + ExpectSameAsLegacyBroadcast([&](const SystemAddress&) { LegacyGameMessages::SendUnSmash(&entity, caster, unsmash.duration); }, unsmash); + } + } + } +} + +TEST_F(CombatMessagesTests, StunsAndImmunitiesMatchLegacy) { + for (const auto target : g_Targets) { + for (const auto originator : g_Ids) { + for (const auto state : { eStateChangeType::PUSH, eStateChangeType::POP }) { + // 17 bools: walk each one alone, all of them, none, and alternating patterns. + std::vector masks = { 0, 0x1FFFF, 0x0AAAA, 0x15555 }; + for (int i = 0; i < 17; i++) masks.push_back(1u << i); + for (const auto mask : masks) { + GameMessages::SetStunned msg; + msg.target = target; + msg.Originator = originator; + msg.StateChangeType = state; + msg.bCantAttack = Bit(mask, 0); + msg.bCantEquip = Bit(mask, 1); + msg.bCantInteract = Bit(mask, 2); + msg.bCantJump = Bit(mask, 3); + msg.bCantMove = Bit(mask, 4); + msg.bCantTurn = Bit(mask, 5); + msg.bCantUseItem = Bit(mask, 6); + msg.bDontTerminateInteract = Bit(mask, 7); + msg.bIgnoreImmunity = Bit(mask, 8); + msg.bCantAttackOutChangeWasApplied = Bit(mask, 9); + msg.bCantEquipOutChangeWasApplied = Bit(mask, 10); + msg.bCantInteractOutChangeWasApplied = Bit(mask, 11); + msg.bCantJumpOutChangeWasApplied = Bit(mask, 12); + msg.bCantMoveOutChangeWasApplied = Bit(mask, 13); + msg.bCantTurnOutChangeWasApplied = Bit(mask, 14); + msg.bCantUseItemOutChangeWasApplied = Bit(mask, 15); + ExpectSameAsLegacy([&](const SystemAddress& a) { + LegacyGameMessages::SendSetStunned(target, state, a, originator, Bit(mask, 0), Bit(mask, 1), Bit(mask, 2), Bit(mask, 3), Bit(mask, 4), Bit(mask, 5), Bit(mask, 6), Bit(mask, 7), Bit(mask, 8), + Bit(mask, 9), Bit(mask, 10), Bit(mask, 11), Bit(mask, 12), Bit(mask, 13), Bit(mask, 14), Bit(mask, 15)); + }, msg); + const auto copy = RoundTrip(msg); + EXPECT_EQ(copy.bCantUseItemOutChangeWasApplied, msg.bCantUseItemOutChangeWasApplied); + EXPECT_EQ(copy.Originator, originator); + + GameMessages::SetStunImmunity immunity; + immunity.target = target; + immunity.Caster = originator; + immunity.StateChangeType = state; + immunity.bImmuneToStunAttack = Bit(mask, 0); + immunity.bImmuneToStunEquip = Bit(mask, 1); + immunity.bImmuneToStunInteract = Bit(mask, 2); + immunity.bImmuneToStunJump = Bit(mask, 3); + immunity.bImmuneToStunMove = Bit(mask, 4); + immunity.bImmuneToStunTurn = Bit(mask, 5); + immunity.bImmuneToStunUseItem = Bit(mask, 6); + ExpectSameAsLegacy([&](const SystemAddress& a) { + LegacyGameMessages::SendSetStunImmunity(target, state, a, originator, Bit(mask, 0), Bit(mask, 1), Bit(mask, 2), Bit(mask, 3), Bit(mask, 4), Bit(mask, 5), Bit(mask, 6)); + }, immunity); + EXPECT_EQ(RoundTrip(immunity).bImmuneToStunUseItem, Bit(mask, 6)); + + GameMessages::SetStatusImmunity status; + status.target = target; + status.StateChangeType = state; + status.bImmuneToBasicAttack = Bit(mask, 0); + status.bImmuneToDOT = Bit(mask, 1); + status.bImmuneToKnockback = Bit(mask, 2); + status.bImmuneToInterrupt = Bit(mask, 3); + status.bImmuneToSpeed = Bit(mask, 4); + status.bImmuneToImaginationGain = Bit(mask, 5); + status.bImmuneToImaginationLoss = Bit(mask, 6); + status.bImmuneToQuickbuildInterrupt = Bit(mask, 7); + status.bImmuneToPullToPoint = Bit(mask, 8); + ExpectSameAsLegacy([&](const SystemAddress& a) { + LegacyGameMessages::SendSetStatusImmunity(target, state, a, Bit(mask, 0), Bit(mask, 1), Bit(mask, 2), Bit(mask, 3), Bit(mask, 4), Bit(mask, 5), Bit(mask, 6), Bit(mask, 7), Bit(mask, 8)); + }, status); + EXPECT_EQ(RoundTrip(status).bImmuneToPullToPoint, Bit(mask, 8)); + } + } + } + } +} + +TEST_F(CombatMessagesTests, BuffsMatchLegacy) { + for (const auto target : g_Targets) { + Entity entity(target, info); + for (const auto caster : g_Ids) { + for (const uint32_t buffID : { 0u, 42u, 0xFFFFFFFFu }) { + for (const uint32_t duration : { 0u, 5000u }) { + for (uint32_t mask = 0; mask < (1u << 11); mask += 37) { + GameMessages::AddBuff msg; + msg.target = target; + msg.bAddedByTeammate = Bit(mask, 0); + msg.bApplyOnTeammates = Bit(mask, 1); + msg.bCancelOnDamaged = Bit(mask, 2); + msg.bCancelOnDeath = Bit(mask, 3); + msg.bCancelOnLogOut = Bit(mask, 4); + msg.bCancelOnRemoveBuff = Bit(mask, 5); + msg.bCancelOnUI = Bit(mask, 6); + msg.bCancelOnUnEquip = Bit(mask, 7); + msg.bCancelOnZone = Bit(mask, 8); + msg.bIsImmunity = Bit(mask, 9); + msg.i64AddedBy = caster; + msg.uiBuffID = buffID; + msg.uiDurationMS = duration; + auto objectID = target; + ExpectSameAsLegacy([&](const SystemAddress& a) { + LegacyGameMessages::SendAddBuff(objectID, caster, buffID, duration, Bit(mask, 9), Bit(mask, 2), Bit(mask, 3), Bit(mask, 4), Bit(mask, 5), Bit(mask, 6), Bit(mask, 7), Bit(mask, 8), Bit(mask, 0), Bit(mask, 1), a); + }, msg); + const auto copy = RoundTrip(msg); + EXPECT_EQ(copy.uiDurationMS, duration); + EXPECT_EQ(copy.i64AddedBy, caster); + } + } + for (uint32_t mask = 0; mask < 4; mask++) { + GameMessages::RemoveBuff remove; + remove.target = target; + remove.bFromUnEquip = Bit(mask, 0); + remove.bRemoveImmunity = Bit(mask, 1); + remove.uiBuffID = buffID; + ExpectSameAsLegacyBroadcast([&](const SystemAddress&) { LegacyGameMessages::SendRemoveBuff(&entity, Bit(mask, 0), Bit(mask, 1), buffID); }, remove); + EXPECT_EQ(RoundTrip(remove).uiBuffID, buffID); + } + } + for (const uint32_t modifier : { 500u, 0u, 750u }) { + GameMessages::AddRunSpeedModifier add; + add.target = target; + add.i64Caster = caster; + add.uiModifier = modifier; + ExpectSameAsLegacy([&](const SystemAddress& a) { LegacyGameMessages::SendAddRunSpeedModifier(target, caster, modifier, a); }, add); + EXPECT_EQ(RoundTrip(add).uiModifier, modifier); + + GameMessages::RemoveRunSpeedModifier remove; + remove.target = target; + remove.uiModifier = modifier; + ExpectSameAsLegacy([&](const SystemAddress& a) { LegacyGameMessages::SendRemoveRunSpeedModifier(target, modifier, a); }, remove); + } + } + + GameMessages::DeactivateBubbleBuffFromServer deactivate; + deactivate.target = target; + ExpectSameAsLegacy([&](const SystemAddress& a) { LegacyGameMessages::SendDeactivateBubbleBuffFromServer(target, a); }, deactivate); + } + + for (const auto& type : { std::u16string(u""), std::u16string(u"skunk"), std::u16string(u"energy") }) { + for (const bool special : { false, true }) { + GameMessages::ActivateBubbleBuff activate; + activate.bSpecialAnims = special; + activate.wszType = type; + const auto legacy = ReadWithLegacy(activate, LegacyGameMessages::ReadActivateBubbleBuff); + EXPECT_TRUE(legacy.read); + EXPECT_EQ(legacy.specialAnimations, special); + EXPECT_EQ(legacy.type, type); + EXPECT_EQ(RoundTrip(activate).wszType, type); + ExpectTruncatedFails(activate); + } + } + + RakNet::BitStream empty; + EXPECT_TRUE(GameMessages::DeactivateBubbleBuff().Deserialize(empty)); + EXPECT_TRUE(GameMessages::RequestSmashPlayer().Deserialize(empty)); + EXPECT_TRUE(GameMessages::RequestResurrect().Deserialize(empty)); +} + +// Independent of the legacy code: hand computed payloads (RakNet writes MSB first). +TEST_F(CombatMessagesTests, GoldenBytes) { + GameMessages::Die die; + die.killerID = 0x11; + die.deathType = u"A"; + // bClientDeath 0, bSpawnLoot 1, u32 1, 'A' u16, three zero floats, killType flag 0, killer, loot owner flag 0. + EXPECT_PACKET_EQ(FromHex("40 40 00 00 10 40 00 00 00 00 00 00 00 00 00 00 00 00 02 20 00 00 00 00 00 00 00", 212), Payload(die)); + + GameMessages::Knockback knockback; + knockback.iKnockBackTimeMS = 1000; + knockback.vector = NiPoint3(1.0f, 0.0f, 0.0f); + EXPECT_PACKET_EQ(FromHex("3d 00 60 00 00 00 10 07 e0 00 00 00 00 00 00 00 00", 131), Payload(knockback)); + + GameMessages::SetStunned stun; + stun.StateChangeType = eStateChangeType::POP; + stun.bCantMove = true; + // originator flag 0, u32 1, 14 flags (bCantMove is the 9th), bDontTerminateInteract 0, bIgnoreImmunity 1. + EXPECT_PACKET_EQ(FromHex("00 80 00 00 00 40 80", 49), Payload(stun)); + + GameMessages::RemoveBuff remove; + remove.bRemoveImmunity = true; + remove.uiBuffID = 3; + EXPECT_PACKET_EQ(FromHex("20 60 00 00 00", 35), Payload(remove)); +} diff --git a/tests/dGameTests/dGameMessagesTests/Legacy/CombatMessagesLegacy.h b/tests/dGameTests/dGameMessagesTests/Legacy/CombatMessagesLegacy.h new file mode 100644 index 000000000..a45da064e --- /dev/null +++ b/tests/dGameTests/dGameMessagesTests/Legacy/CombatMessagesLegacy.h @@ -0,0 +1,480 @@ +#ifndef COMBATMESSAGESLEGACY_H +#define COMBATMESSAGESLEGACY_H + +// FROZEN ORACLE - DO NOT EDIT. +// Verbatim copies of the hand written GameMessages functions that CombatMessages.h replaced +// (dGame/dGameMessages/GameMessages.cpp, branched from origin/main 129199e4). Only the namespace changed. +// The Read* functions are the read sequences of the replaced GameMessages::Handle* functions, verbatim up to +// the point where the handler starts using what it read. + +#include "BitStreamUtils.h" +#include "dCommonVars.h" +#include "dServer.h" +#include "DestroyableComponent.h" +#include "eKillType.h" +#include "eStateChangeType.h" +#include "Entity.h" +#include "Game.h" +#include "GameMessages.h" +#include "GeneralUtils.h" +#include "LevelProgressionComponent.h" +#include "MessageType/Client.h" +#include "MessageType/Game.h" +#include "NiPoint3.h" +#include "ServiceType.h" + +#include + +namespace LegacyGameMessages { + inline void SendPlayerAllowedRespawn(LWOOBJID entityID, bool doNotPromptRespawn, const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(entityID); + bitStream.Write(MessageType::Game::SET_PLAYER_ALLOWED_RESPAWN); + bitStream.Write(doNotPromptRespawn); + + SEND_PACKET; + } + + inline void SendKnockback(const LWOOBJID& objectID, const LWOOBJID& caster, const LWOOBJID& originator, int knockBackTimeMS, const NiPoint3& vector) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectID); + bitStream.Write(MessageType::Game::KNOCKBACK); + + bool casterFlag = caster != LWOOBJID_EMPTY; + bool originatorFlag = originator != LWOOBJID_EMPTY; + bool knockBackTimeMSFlag = knockBackTimeMS != 0; + + bitStream.Write(casterFlag); + if (casterFlag) bitStream.Write(caster); + bitStream.Write(originatorFlag); + if (originatorFlag) bitStream.Write(originator); + bitStream.Write(knockBackTimeMSFlag); + if (knockBackTimeMSFlag) bitStream.Write(knockBackTimeMS); + bitStream.Write(vector); + + SEND_PACKET_BROADCAST; + } + + inline void SendDieNoImplCode(Entity* entity, const LWOOBJID& killerID, const LWOOBJID& lootOwnerID, eKillType killType, std::u16string deathType, float directionRelative_AngleY, float directionRelative_AngleXZ, float directionRelative_Force, bool bClientDeath, bool bSpawnLoot) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::DIE); + + bitStream.Write(bClientDeath); + bitStream.Write(bSpawnLoot); + bitStream.Write(deathType.size()); + bitStream.Write(deathType); + bitStream.Write(directionRelative_AngleXZ); + bitStream.Write(directionRelative_AngleY); + bitStream.Write(directionRelative_Force); + + bitStream.Write(killType != eKillType::VIOLENT); + if (killType != eKillType::VIOLENT) bitStream.Write(killType); + + bitStream.Write(killerID); + bitStream.Write(lootOwnerID != LWOOBJID_EMPTY); + if (lootOwnerID != LWOOBJID_EMPTY) { + bitStream.Write(lootOwnerID); + } + + SEND_PACKET_BROADCAST; + } + + inline void SendDie(Entity* entity, const LWOOBJID& killerID, const LWOOBJID& lootOwnerID, bool bDieAccepted, eKillType killType, std::u16string deathType, float directionRelative_AngleY, float directionRelative_AngleXZ, float directionRelative_Force, bool bClientDeath, bool bSpawnLoot, float coinSpawnTime) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(entity->GetObjectID()); + + bitStream.Write(MessageType::Game::DIE); + + bitStream.Write(bClientDeath); + bitStream.Write(bSpawnLoot); + + //bitStream.Write(coinSpawnTime != -1.0f); + //if (coinSpawnTime != -1.0f) bitStream.Write(coinSpawnTime); + + uint32_t deathTypeLength = deathType.size(); + bitStream.Write(deathTypeLength); + for (uint32_t k = 0; k < deathTypeLength; k++) { + bitStream.Write(deathType[k]); + } + + bitStream.Write(directionRelative_AngleXZ); + bitStream.Write(directionRelative_AngleY); + bitStream.Write(directionRelative_Force); + + bitStream.Write(killType != eKillType::VIOLENT); + if (killType != eKillType::VIOLENT) bitStream.Write(killType); + + bitStream.Write(killerID); + + bitStream.Write(lootOwnerID != LWOOBJID_EMPTY); + if (lootOwnerID != LWOOBJID_EMPTY) { + bitStream.Write(lootOwnerID); + } + + SEND_PACKET_BROADCAST; + } + + inline void SendResurrect(Entity* entity) { + // Restore the players health after the animation for respawning has finished. + // This is when the health appered back in live, not immediately upon requesting respawn + // Add a half second in case someone decides to cheat and move during the death animation + // and just make sure the client has time to be ready. + constexpr float respawnTime = 3.66700005531311f + 0.5f; + entity->AddCallbackTimer(respawnTime, [=]() { + GameMessages::PlayerResurrectionFinished msg; + entity->NotifyPlayerResurrectionFinished(msg); + auto* destroyableComponent = entity->GetComponent(); + + if (destroyableComponent != nullptr && entity->GetLOT() == 1) { + destroyableComponent->SetIsDead(false); + auto* levelComponent = entity->GetComponent(); + if (levelComponent) { + int32_t healthToRestore = levelComponent->GetLevel() >= 45 ? 8 : 4; + if (healthToRestore > destroyableComponent->GetMaxHealth()) healthToRestore = destroyableComponent->GetMaxHealth(); + destroyableComponent->SetHealth(healthToRestore); + + int32_t imaginationToRestore = levelComponent->GetLevel() >= 45 ? 20 : 6; + if (imaginationToRestore > destroyableComponent->GetMaxImagination()) imaginationToRestore = destroyableComponent->GetMaxImagination(); + destroyableComponent->SetImagination(imaginationToRestore); + } + } + }); + + CBITSTREAM; + CMSGHEADER; + + bool bRezImmediately = false; + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::RESURRECT); + bitStream.Write(bRezImmediately); + + SEND_PACKET_BROADCAST; + } + + inline void SendSetStunned(LWOOBJID objectId, eStateChangeType stateChangeType, const SystemAddress& sysAddr, + LWOOBJID originator, bool bCantAttack, bool bCantEquip, + bool bCantInteract, bool bCantJump, bool bCantMove, bool bCantTurn, + bool bCantUseItem, bool bDontTerminateInteract, bool bIgnoreImmunity, + bool bCantAttackOutChangeWasApplied, bool bCantEquipOutChangeWasApplied, + bool bCantInteractOutChangeWasApplied, bool bCantJumpOutChangeWasApplied, + bool bCantMoveOutChangeWasApplied, bool bCantTurnOutChangeWasApplied, + bool bCantUseItemOutChangeWasApplied) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectId); + bitStream.Write(MessageType::Game::SET_STUNNED); + + bitStream.Write(originator != LWOOBJID_EMPTY); + if (originator != LWOOBJID_EMPTY) bitStream.Write(originator); + + bitStream.Write(stateChangeType); + + bitStream.Write(bCantAttack); + bitStream.Write(bCantAttackOutChangeWasApplied); + + bitStream.Write(bCantEquip); + bitStream.Write(bCantEquipOutChangeWasApplied); + + bitStream.Write(bCantInteract); + bitStream.Write(bCantInteractOutChangeWasApplied); + + bitStream.Write(bCantJump); + bitStream.Write(bCantJumpOutChangeWasApplied); + + bitStream.Write(bCantMove); + bitStream.Write(bCantMoveOutChangeWasApplied); + + bitStream.Write(bCantTurn); + bitStream.Write(bCantTurnOutChangeWasApplied); + + bitStream.Write(bCantUseItem); + bitStream.Write(bCantUseItemOutChangeWasApplied); + + bitStream.Write(bDontTerminateInteract); + + bitStream.Write(bIgnoreImmunity); + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; + } + + inline void SendSetStunImmunity(LWOOBJID target, eStateChangeType state, const SystemAddress& sysAddr, + LWOOBJID originator, + bool bImmuneToStunAttack, + bool bImmuneToStunEquip, + bool bImmuneToStunInteract, + bool bImmuneToStunJump, + bool bImmuneToStunMove, + bool bImmuneToStunTurn, + bool bImmuneToStunUseItem) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(target); + bitStream.Write(MessageType::Game::SET_STUN_IMMUNITY); + + bitStream.Write(originator != LWOOBJID_EMPTY); + if (originator != LWOOBJID_EMPTY) bitStream.Write(originator); + + bitStream.Write(state); + + bitStream.Write(bImmuneToStunAttack); + bitStream.Write(bImmuneToStunEquip); + bitStream.Write(bImmuneToStunInteract); + bitStream.Write(bImmuneToStunJump); + bitStream.Write(bImmuneToStunMove); + bitStream.Write(bImmuneToStunTurn); + bitStream.Write(bImmuneToStunUseItem); + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; + } + + inline void SendSetStatusImmunity(LWOOBJID objectId, eStateChangeType state, const SystemAddress& sysAddr, + bool bImmuneToBasicAttack, + bool bImmuneToDamageOverTime, + bool bImmuneToKnockback, + bool bImmuneToInterrupt, + bool bImmuneToSpeed, + bool bImmuneToImaginationGain, + bool bImmuneToImaginationLoss, + bool bImmuneToQuickbuildInterrupt, + bool bImmuneToPullToPoint) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectId); + bitStream.Write(MessageType::Game::SET_STATUS_IMMUNITY); + + bitStream.Write(state); + + bitStream.Write(bImmuneToBasicAttack); + bitStream.Write(bImmuneToDamageOverTime); + bitStream.Write(bImmuneToKnockback); + bitStream.Write(bImmuneToInterrupt); + bitStream.Write(bImmuneToSpeed); + bitStream.Write(bImmuneToImaginationGain); + bitStream.Write(bImmuneToImaginationLoss); + bitStream.Write(bImmuneToQuickbuildInterrupt); + bitStream.Write(bImmuneToPullToPoint); + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; + } + + inline void SendAddRunSpeedModifier(LWOOBJID objectId, LWOOBJID caster, uint32_t modifier, const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectId); + bitStream.Write(MessageType::Game::ADD_RUN_SPEED_MODIFIER); + + bitStream.Write(caster != LWOOBJID_EMPTY); + if (caster != LWOOBJID_EMPTY) bitStream.Write(caster); + + bitStream.Write(modifier != 500); + if (modifier != 500) bitStream.Write(modifier); + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; + } + + inline void SendRemoveRunSpeedModifier(LWOOBJID objectId, uint32_t modifier, const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectId); + bitStream.Write(MessageType::Game::REMOVE_RUN_SPEED_MODIFIER); + + bitStream.Write(modifier != 500); + if (modifier != 500) bitStream.Write(modifier); + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; + } + + inline void SendRemoveBuff(Entity* entity, bool fromUnEquip, bool removeImmunity, uint32_t buffId) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::REMOVE_BUFF); + + bitStream.Write(false); // bFromRemoveBehavior but setting this to true makes the GM not do anything on the client? + bitStream.Write(fromUnEquip); + bitStream.Write(removeImmunity); + bitStream.Write(buffId); + + SEND_PACKET_BROADCAST; + } + + inline void SendSetResurrectRestoreValues(Entity* targetEntity, int32_t armorRestore, int32_t healthRestore, int32_t imaginationRestore) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(targetEntity->GetObjectID()); + bitStream.Write(MessageType::Game::SET_RESURRECT_RESTORE_VALUES); + + bitStream.Write(armorRestore != -1); + if (armorRestore != -1) bitStream.Write(armorRestore); + + bitStream.Write(healthRestore != -1); + if (healthRestore != -1) bitStream.Write(healthRestore); + + bitStream.Write(imaginationRestore != -1); + if (imaginationRestore != -1) bitStream.Write(imaginationRestore); + + SEND_PACKET_BROADCAST; + } + + inline void SendAddBuff(LWOOBJID& objectID, const LWOOBJID& casterID, uint32_t buffID, uint32_t msDuration, + bool addImmunity, bool cancelOnDamaged, bool cancelOnDeath, bool cancelOnLogout, + bool cancelOnRemoveBuff, bool cancelOnUi, bool cancelOnUnequip, bool cancelOnZone, bool addedByTeammate, bool applyOnTeammates, + const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectID); + bitStream.Write(MessageType::Game::ADD_BUFF); + + bitStream.Write(addedByTeammate); // Added by teammate + bitStream.Write(applyOnTeammates); // Apply on teammates + bitStream.Write(cancelOnDamaged); + bitStream.Write(cancelOnDeath); + bitStream.Write(cancelOnLogout); + + bitStream.Write(false); // Cancel on move + bitStream.Write(cancelOnRemoveBuff); + bitStream.Write(cancelOnUi); + bitStream.Write(cancelOnUnequip); + bitStream.Write(cancelOnZone); + + bitStream.Write(false); // Ignore immunities + bitStream.Write(addImmunity); + bitStream.Write(false); // Use ref count + + bitStream.Write(casterID != LWOOBJID_EMPTY); + if (casterID != LWOOBJID_EMPTY) bitStream.Write(casterID); + + bitStream.Write(buffID); + + bitStream.Write(msDuration != 0); + if (msDuration != 0) bitStream.Write(msDuration); + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; + } + + inline void SendDeactivateBubbleBuffFromServer(LWOOBJID objectId, const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectId); + bitStream.Write(MessageType::Game::DEACTIVATE_BUBBLE_BUFF_FROM_SERVER); + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; + } + + inline void SendSmash(Entity* entity, float force, float ghostOpacity, LWOOBJID killerID, bool ignoreObjectVisibility) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::SMASH); + + bitStream.Write(ignoreObjectVisibility); + bitStream.Write(force); + bitStream.Write(ghostOpacity); + bitStream.Write(killerID); + + SEND_PACKET_BROADCAST; + } + + inline void SendUnSmash(Entity* entity, LWOOBJID builderID, float duration) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::UN_SMASH); + + bitStream.Write(builderID != LWOOBJID_EMPTY); + if (builderID != LWOOBJID_EMPTY) bitStream.Write(builderID); + + bitStream.Write(duration != 3.0f); + if (duration != 3.0f) bitStream.Write(duration); + + SEND_PACKET_BROADCAST; + } + + // GameMessages::HandleRequestDie (the three argument overload the switch called) + struct LegacyRequestDie { bool bClientDeath{}; bool bSpawnLoot{}; std::u16string deathType{}; float directionRelativeAngleXZ{}; float directionRelativeAngleY{}; float directionRelativeForce{}; eKillType killType{}; LWOOBJID killerID{}; LWOOBJID lootOwnerID{}; }; + inline LegacyRequestDie ReadRequestDie(RakNet::BitStream& inStream) { + bool bClientDeath; + bool bSpawnLoot; + std::u16string deathType; + float directionRelativeAngleXZ; + float directionRelativeAngleY; + float directionRelativeForce; + eKillType killType = eKillType::VIOLENT; + LWOOBJID killerID; + LWOOBJID lootOwnerID = LWOOBJID_EMPTY; + + bClientDeath = inStream.ReadBit(); + bSpawnLoot = inStream.ReadBit(); + + uint32_t deathTypeLength = 0; + inStream.Read(deathTypeLength); + + for (size_t i = 0; i < deathTypeLength; i++) { + char16_t character; + inStream.Read(character); + + deathType.push_back(character); + } + + inStream.Read(directionRelativeAngleXZ); + inStream.Read(directionRelativeAngleY); + inStream.Read(directionRelativeForce); + + if (inStream.ReadBit()) { + inStream.Read(killType); + } + + inStream.Read(killerID); + + if (inStream.ReadBit()) { + inStream.Read(lootOwnerID); + } + return { bClientDeath, bSpawnLoot, deathType, directionRelativeAngleXZ, directionRelativeAngleY, directionRelativeForce, killType, killerID, lootOwnerID }; + } + + // GameMessages::HandleResurrect + inline bool ReadResurrect(RakNet::BitStream& inStream) { + bool immediate = inStream.ReadBit(); + return immediate; + } + + // GameMessages::HandleActivateBubbleBuff + struct LegacyActivateBubbleBuff { bool read{}; bool specialAnimations{}; std::u16string type{}; }; + inline LegacyActivateBubbleBuff ReadActivateBubbleBuff(RakNet::BitStream& inStream) { + bool specialAnimations; + if (!inStream.Read(specialAnimations)) return {}; + + std::u16string type = GeneralUtils::ReadWString(inStream); + return { true, specialAnimations, type }; + } +} + +#endif // COMBATMESSAGESLEGACY_H diff --git a/tests/dGameTests/dGameMessagesTests/UnSmashTests.cpp b/tests/dGameTests/dGameMessagesTests/UnSmashTests.cpp index 435b2ebd4..00e50f366 100644 --- a/tests/dGameTests/dGameMessagesTests/UnSmashTests.cpp +++ b/tests/dGameTests/dGameMessagesTests/UnSmashTests.cpp @@ -1,4 +1,5 @@ #include "GameMessages.h" +#include "CombatMessages.h" #include "GameDependencies.h" #include "PacketTestUtils.h"