feat: Loot rework (#1909)

* feat: Loot rework

* Allow dupe powerup pickups

* change default team loot to shared
This commit is contained in:
David Markowitz
2025-10-14 22:53:39 -07:00
committed by GitHub
parent fd6029ae10
commit 74630b56c8
22 changed files with 685 additions and 324 deletions

View File

@@ -48,6 +48,7 @@ namespace {
{ REQUEST_USE, []() { return std::make_unique<RequestUse>(); }},
{ REQUEST_SERVER_OBJECT_INFO, []() { return std::make_unique<RequestServerObjectInfo>(); } },
{ SHOOTING_GALLERY_FIRE, []() { return std::make_unique<ShootingGalleryFire>(); } },
{ PICKUP_ITEM, []() { return std::make_unique<PickupItem>(); } },
};
};
@@ -281,11 +282,6 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
break;
}
case MessageType::Game::PICKUP_ITEM: {
GameMessages::HandlePickupItem(inStream, entity);
break;
}
case MessageType::Game::RESURRECT: {
GameMessages::HandleResurrect(inStream, entity);
break;

View File

@@ -1103,52 +1103,6 @@ void GameMessages::SendDropClientLoot(Entity* entity, const LWOOBJID& sourceID,
finalPosition = NiPoint3(static_cast<float>(spawnPos.GetX() + sin_v), spawnPos.GetY(), static_cast<float>(spawnPos.GetZ() + cos_v));
}
//Write data to packet & send:
CBITSTREAM;
CMSGHEADER;
bitStream.Write(entity->GetObjectID());
bitStream.Write(MessageType::Game::DROP_CLIENT_LOOT);
bitStream.Write(bUsePosition);
bitStream.Write(finalPosition != NiPoint3Constant::ZERO);
if (finalPosition != NiPoint3Constant::ZERO) bitStream.Write(finalPosition);
bitStream.Write(currency);
bitStream.Write(item);
bitStream.Write(lootID);
bitStream.Write(owner);
bitStream.Write(sourceID);
bitStream.Write(spawnPos != NiPoint3Constant::ZERO);
if (spawnPos != NiPoint3Constant::ZERO) bitStream.Write(spawnPos);
auto* team = TeamManager::Instance()->GetTeam(owner);
// Currency and powerups should not sync
if (team != nullptr && currency == 0) {
CDObjectsTable* objectsTable = CDClientManager::GetTable<CDObjectsTable>();
const CDObjects& object = objectsTable->GetByID(item);
if (object.type != "Powerup") {
for (const auto memberId : team->members) {
auto* member = Game::entityManager->GetEntity(memberId);
if (member == nullptr) continue;
SystemAddress sysAddr = member->GetSystemAddress();
SEND_PACKET;
}
return;
}
}
SystemAddress sysAddr = entity->GetSystemAddress();
SEND_PACKET;
}
void GameMessages::SendSetPlayerControlScheme(Entity* entity, eControlScheme controlScheme) {
@@ -5725,27 +5679,6 @@ void GameMessages::HandleModularBuildMoveAndEquip(RakNet::BitStream& inStream, E
inv->MoveItemToInventory(item, eInventoryType::MODELS, 1, false, true);
}
void GameMessages::HandlePickupItem(RakNet::BitStream& inStream, Entity* entity) {
LWOOBJID lootObjectID;
LWOOBJID playerID;
inStream.Read(lootObjectID);
inStream.Read(playerID);
entity->PickupItem(lootObjectID);
auto* team = TeamManager::Instance()->GetTeam(entity->GetObjectID());
if (team != nullptr) {
for (const auto memberId : team->members) {
auto* member = Game::entityManager->GetEntity(memberId);
if (member == nullptr || memberId == playerID) continue;
SendTeamPickupItem(lootObjectID, lootObjectID, playerID, member->GetSystemAddress());
}
}
}
void GameMessages::HandleResurrect(RakNet::BitStream& inStream, Entity* entity) {
bool immediate = inStream.ReadBit();
@@ -6329,6 +6262,11 @@ namespace GameMessages {
return Game::entityManager->SendMessage(*this);
}
bool GameMsg::Send(const LWOOBJID _target) {
target = _target;
return Send();
}
void GameMsg::Send(const SystemAddress& sysAddr) const {
CBITSTREAM;
CMSGHEADER;
@@ -6496,4 +6434,49 @@ namespace GameMessages {
stream.Write(emoteID);
stream.Write(targetID);
}
void DropClientLoot::Serialize(RakNet::BitStream& stream) const {
stream.Write(bUsePosition);
stream.Write(finalPosition != NiPoint3Constant::ZERO);
if (finalPosition != NiPoint3Constant::ZERO) stream.Write(finalPosition);
stream.Write(currency);
stream.Write(item);
stream.Write(lootID);
stream.Write(ownerID);
stream.Write(sourceID);
stream.Write(spawnPos != NiPoint3Constant::ZERO);
if (spawnPos != NiPoint3Constant::ZERO) stream.Write(spawnPos);
}
bool PickupItem::Deserialize(RakNet::BitStream& stream) {
if (!stream.Read(lootID)) return false;
if (!stream.Read(lootOwnerID)) return false;
return true;
}
void PickupItem::Handle(Entity& entity, const SystemAddress& sysAddr) {
auto* team = TeamManager::Instance()->GetTeam(entity.GetObjectID());
LOG("Has team %i picking up %llu:%llu", team != nullptr, lootID, lootOwnerID);
if (team) {
for (const auto memberId : team->members) {
this->Send(memberId);
TeamPickupItem teamPickupMsg{};
teamPickupMsg.target = lootID;
teamPickupMsg.lootID = lootID;
teamPickupMsg.lootOwnerID = lootOwnerID;
const auto* const memberEntity = Game::entityManager->GetEntity(memberId);
if (memberEntity) teamPickupMsg.Send(memberEntity->GetSystemAddress());
}
} else {
entity.PickupItem(lootID);
}
}
void TeamPickupItem::Serialize(RakNet::BitStream& stream) const {
stream.Write(lootID);
stream.Write(lootOwnerID);
}
}

View File

@@ -43,6 +43,7 @@ enum class eQuickBuildState : uint32_t;
enum class BehaviorSlot : int32_t;
enum class eVendorTransactionResult : uint32_t;
enum class eReponseMoveItemBetweenInventoryTypeCode : int32_t;
enum class eMissionState : int;
enum class eCameraTargetCyclingMode : int32_t {
ALLOW_CYCLE_TEAMMATES,
@@ -57,6 +58,7 @@ namespace GameMessages {
// Sends a message to the entity manager to route to the target
bool Send();
bool Send(const LWOOBJID _target);
// Sends the message to the specified client or
// all clients if UNASSIGNED_SYSTEM_ADDRESS is specified
@@ -850,9 +852,9 @@ namespace GameMessages {
struct EmotePlayed : public GameMsg {
EmotePlayed() : GameMsg(MessageType::Game::EMOTE_PLAYED), emoteID(0), targetID(0) {}
void Serialize(RakNet::BitStream& stream) const override;
int32_t emoteID;
LWOOBJID targetID;
};
@@ -870,5 +872,65 @@ namespace GameMessages {
bool bIgnoreChecks{ false };
};
struct DropClientLoot : public GameMsg {
DropClientLoot() : GameMsg(MessageType::Game::DROP_CLIENT_LOOT) {}
void Serialize(RakNet::BitStream& stream) const override;
LWOOBJID sourceID{ LWOOBJID_EMPTY };
LOT item{ LOT_NULL };
int32_t currency{};
NiPoint3 spawnPos{};
NiPoint3 finalPosition{};
int32_t count{};
bool bUsePosition{};
LWOOBJID lootID{ LWOOBJID_EMPTY };
LWOOBJID ownerID{ LWOOBJID_EMPTY };
};
struct GetMissionState : public GameMsg {
GetMissionState() : GameMsg(MessageType::Game::GET_MISSION_STATE) {}
int32_t missionID{};
eMissionState missionState{};
bool cooldownInfoRequested{};
bool cooldownFinished{};
};
struct GetFlag : public GameMsg {
GetFlag() : GameMsg(MessageType::Game::GET_FLAG) {}
uint32_t flagID{};
bool flag{};
};
struct GetFactionTokenType : public GameMsg {
GetFactionTokenType() : GameMsg(MessageType::Game::GET_FACTION_TOKEN_TYPE) {}
LOT tokenType{ LOT_NULL };
};
struct MissionNeedsLot : public GameMsg {
MissionNeedsLot() : GameMsg(MessageType::Game::MISSION_NEEDS_LOT) {}
LOT item{};
};
struct PickupItem : public GameMsg {
PickupItem() : GameMsg(MessageType::Game::PICKUP_ITEM) {}
void Handle(Entity& entity, const SystemAddress& sysAddr) override;
bool Deserialize(RakNet::BitStream& stream) override;
LWOOBJID lootID{};
LWOOBJID lootOwnerID{};
};
struct TeamPickupItem : public GameMsg {
TeamPickupItem() : GameMsg(MessageType::Game::TEAM_PICKUP_ITEM) {}
void Serialize(RakNet::BitStream& stream) const override;
LWOOBJID lootID{};
LWOOBJID lootOwnerID{};
};
};
#endif // GAMEMESSAGES_H