mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-11-20 00:52:33 +00:00
feat: dont drop loot for dead players if configured in the zone activity settings (#1935)
* feat: dont drop loot for dead players if configured in the zone activity settings * fix errors Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update dGame/dComponents/ActivityComponent.h Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update dGame/dUtilities/Loot.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -21,6 +21,8 @@
|
||||
#include "TeamManager.h"
|
||||
#include "CDObjectsTable.h"
|
||||
#include "ObjectIDManager.h"
|
||||
#include "CDActivitiesTable.h"
|
||||
#include "ScriptedActivityComponent.h"
|
||||
|
||||
namespace {
|
||||
std::unordered_set<uint32_t> CachedMatrices;
|
||||
@@ -142,13 +144,17 @@ void DropFactionLoot(Entity& player, GameMessages::DropClientLoot& lootMsg) {
|
||||
|
||||
// Drops 1 token for each player on a team
|
||||
// token drops are always given to every player on the team.
|
||||
void DropFactionLoot(const Team& team, GameMessages::DropClientLoot& lootMsg) {
|
||||
void DropFactionLoot(const Team& team, GameMessages::DropClientLoot& lootMsg, const bool noTeamLootOnDeath = false) {
|
||||
for (const auto member : team.members) {
|
||||
GameMessages::GetPosition memberPosMsg{};
|
||||
memberPosMsg.target = member;
|
||||
memberPosMsg.Send();
|
||||
if (NiPoint3::Distance(memberPosMsg.pos, lootMsg.spawnPos) > g_MAX_DROP_RADIUS) continue;
|
||||
|
||||
GameMessages::IsDead isDeadMsg{};
|
||||
// Skip dead players
|
||||
if (noTeamLootOnDeath && isDeadMsg.Send(member) && isDeadMsg.bDead) continue;
|
||||
|
||||
GameMessages::GetFactionTokenType factionTokenType{};
|
||||
factionTokenType.target = member;
|
||||
// If we're not in a faction, this message will return false
|
||||
@@ -186,7 +192,7 @@ void DropPowerupLoot(Entity& player, GameMessages::DropClientLoot& lootMsg) {
|
||||
// Drop the power up with no owner
|
||||
// Power ups can be picked up by anyone on a team, however unlike actual loot items,
|
||||
// if multiple clients say they picked one up, we let them pick it up.
|
||||
void DropPowerupLoot(const Team& team, GameMessages::DropClientLoot& lootMsg) {
|
||||
void DropPowerupLoot(const Team& team, GameMessages::DropClientLoot& lootMsg, const bool noTeamLootOnDeath = false) {
|
||||
lootMsg.lootID = ObjectIDManager::GenerateObjectID();
|
||||
lootMsg.ownerID = LWOOBJID_EMPTY; // By setting ownerID to empty, any client that gets this DropClientLoot message can pick up the item.
|
||||
CalcFinalDropPos(lootMsg);
|
||||
@@ -198,6 +204,10 @@ void DropPowerupLoot(const Team& team, GameMessages::DropClientLoot& lootMsg) {
|
||||
memberPosMsg.Send();
|
||||
if (NiPoint3::Distance(memberPosMsg.pos, lootMsg.spawnPos) > g_MAX_DROP_RADIUS) continue;
|
||||
|
||||
GameMessages::IsDead isDeadMsg{};
|
||||
// Skip dead players
|
||||
if (noTeamLootOnDeath && isDeadMsg.Send(member) && isDeadMsg.bDead) continue;
|
||||
|
||||
lootMsg.target = member;
|
||||
// By sending this message with the same ID to all players on the team, all players on the team are allowed to pick it up.
|
||||
lootMsg.Send();
|
||||
@@ -230,7 +240,7 @@ void DropMissionLoot(Entity& player, GameMessages::DropClientLoot& lootMsg) {
|
||||
|
||||
// Check if the item needs to be dropped for anyone on the team
|
||||
// Only players who need the item will have it dropped
|
||||
void DropMissionLoot(const Team& team, GameMessages::DropClientLoot& lootMsg) {
|
||||
void DropMissionLoot(const Team& team, GameMessages::DropClientLoot& lootMsg, const bool noTeamLootOnDeath = false) {
|
||||
GameMessages::MissionNeedsLot needMsg{};
|
||||
needMsg.item = lootMsg.item;
|
||||
for (const auto member : team.members) {
|
||||
@@ -239,6 +249,10 @@ void DropMissionLoot(const Team& team, GameMessages::DropClientLoot& lootMsg) {
|
||||
memberPosMsg.Send();
|
||||
if (NiPoint3::Distance(memberPosMsg.pos, lootMsg.spawnPos) > g_MAX_DROP_RADIUS) continue;
|
||||
|
||||
GameMessages::IsDead isDeadMsg{};
|
||||
// Skip dead players
|
||||
if (noTeamLootOnDeath && isDeadMsg.Send(member) && isDeadMsg.bDead) continue;
|
||||
|
||||
needMsg.target = member;
|
||||
// Will return false if the item is not required
|
||||
if (needMsg.Send()) {
|
||||
@@ -274,19 +288,24 @@ void DropRegularLoot(Entity& player, GameMessages::DropClientLoot& lootMsg) {
|
||||
// Drop a regular piece of loot.
|
||||
// Most items will go through this.
|
||||
// Finds the next loot owner on the team the is in range of the kill and gives them this reward.
|
||||
void DropRegularLoot(Team& team, GameMessages::DropClientLoot& lootMsg) {
|
||||
void DropRegularLoot(Team& team, GameMessages::DropClientLoot& lootMsg, const bool noTeamLootOnDeath = false) {
|
||||
auto earningPlayer = LWOOBJID_EMPTY;
|
||||
lootMsg.lootID = ObjectIDManager::GenerateObjectID();
|
||||
CalcFinalDropPos(lootMsg);
|
||||
GameMessages::GetPosition memberPosMsg{};
|
||||
GameMessages::IsDead isDeadMsg{};
|
||||
// Find the next loot owner. Eventually this will run into the `player` passed into this function, since those will
|
||||
// have the same ID, this loop will only ever run at most 4 times.
|
||||
do {
|
||||
earningPlayer = team.GetNextLootOwner();
|
||||
memberPosMsg.target = earningPlayer;
|
||||
memberPosMsg.Send();
|
||||
} while (NiPoint3::Distance(memberPosMsg.pos, lootMsg.spawnPos) > g_MAX_DROP_RADIUS);
|
||||
|
||||
if (noTeamLootOnDeath) {
|
||||
isDeadMsg.target = earningPlayer;
|
||||
// Skip dead players
|
||||
isDeadMsg.Send();
|
||||
}
|
||||
} while (isDeadMsg.bDead || (NiPoint3::Distance(memberPosMsg.pos, lootMsg.spawnPos) > g_MAX_DROP_RADIUS));
|
||||
if (team.lootOption == 0 /* Shared loot */) {
|
||||
lootMsg.target = earningPlayer;
|
||||
lootMsg.ownerID = earningPlayer;
|
||||
@@ -304,7 +323,7 @@ void DropRegularLoot(Team& team, GameMessages::DropClientLoot& lootMsg) {
|
||||
DistrbuteMsgToTeam(lootMsg, team);
|
||||
}
|
||||
|
||||
void DropLoot(Entity* player, const LWOOBJID source, const std::map<LOT, LootDropInfo>& rolledItems, uint32_t minCoins, uint32_t maxCoins) {
|
||||
void DropLoot(Entity* player, const LWOOBJID source, const std::map<LOT, LootDropInfo>& rolledItems, uint32_t minCoins, uint32_t maxCoins, const bool noTeamLootOnDeath) {
|
||||
player = player->GetOwner(); // if the owner is overwritten, we collect that here
|
||||
const auto playerID = player->GetObjectID();
|
||||
if (!player || !player->IsPlayer()) {
|
||||
@@ -342,45 +361,53 @@ void DropLoot(Entity* player, const LWOOBJID source, const std::map<LOT, LootDro
|
||||
const CDObjects& object = objectsTable->GetByID(lootLot);
|
||||
|
||||
if (lootLot == TOKEN_PROXY) {
|
||||
team ? DropFactionLoot(*team, lootMsg) : DropFactionLoot(*player, lootMsg);
|
||||
team ? DropFactionLoot(*team, lootMsg, noTeamLootOnDeath) : DropFactionLoot(*player, lootMsg);
|
||||
} else if (info.table.MissionDrop) {
|
||||
team ? DropMissionLoot(*team, lootMsg) : DropMissionLoot(*player, lootMsg);
|
||||
team ? DropMissionLoot(*team, lootMsg, noTeamLootOnDeath) : DropMissionLoot(*player, lootMsg);
|
||||
} else if (object.type == "Powerup") {
|
||||
team ? DropPowerupLoot(*team, lootMsg) : DropPowerupLoot(*player, lootMsg);
|
||||
team ? DropPowerupLoot(*team, lootMsg, noTeamLootOnDeath) : DropPowerupLoot(*player, lootMsg);
|
||||
} else {
|
||||
team ? DropRegularLoot(*team, lootMsg) : DropRegularLoot(*player, lootMsg);
|
||||
team ? DropRegularLoot(*team, lootMsg, noTeamLootOnDeath) : DropRegularLoot(*player, lootMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Coin roll is divided up between the members, rounded up, then dropped for each player
|
||||
const uint32_t coinRoll = static_cast<uint32_t>(minCoins + GeneralUtils::GenerateRandomNumber<float>(0, 1) * (maxCoins - minCoins));
|
||||
const auto droppedCoins = team ? std::ceil(static_cast<float>(coinRoll) / team->members.size()) : coinRoll;
|
||||
// Filter out dead player if we need to
|
||||
std::vector<LWOOBJID> lootEarners;
|
||||
if (team) {
|
||||
for (auto member : team->members) {
|
||||
GameMessages::DropClientLoot lootMsg{};
|
||||
lootMsg.target = member;
|
||||
lootMsg.ownerID = member;
|
||||
lootMsg.currency = droppedCoins;
|
||||
lootMsg.spawnPos = spawnPosition;
|
||||
lootMsg.sourceID = source;
|
||||
lootMsg.item = LOT_NULL;
|
||||
CalcFinalDropPos(lootMsg);
|
||||
lootMsg.Send();
|
||||
const auto* const memberEntity = Game::entityManager->GetEntity(member);
|
||||
if (memberEntity) lootMsg.Send(memberEntity->GetSystemAddress());
|
||||
if (noTeamLootOnDeath) {
|
||||
for (const auto member : team->members) {
|
||||
GameMessages::IsDead isDeadMsg{};
|
||||
isDeadMsg.target = member;
|
||||
if (isDeadMsg.Send() && !isDeadMsg.bDead) {
|
||||
lootEarners.push_back(member);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
lootEarners = team->members;
|
||||
}
|
||||
} else {
|
||||
lootEarners.push_back(playerID);
|
||||
}
|
||||
|
||||
// Coin roll is divided up between the members, rounded up, then dropped for each player
|
||||
const uint32_t coinRoll = static_cast<uint32_t>(minCoins + GeneralUtils::GenerateRandomNumber<float>(0, 1) * (maxCoins - minCoins));
|
||||
// Just in case its empty don't allow divide by 0
|
||||
const auto droppedCoins = lootEarners.empty() ? coinRoll : static_cast<uint32_t>(std::ceil(static_cast<float>(coinRoll) / lootEarners.size()));
|
||||
|
||||
// Drops coins for each alive member of a team (or just a player)
|
||||
for (auto member : lootEarners) {
|
||||
GameMessages::DropClientLoot lootMsg{};
|
||||
lootMsg.target = playerID;
|
||||
lootMsg.ownerID = playerID;
|
||||
lootMsg.target = member;
|
||||
lootMsg.ownerID = member;
|
||||
lootMsg.currency = droppedCoins;
|
||||
lootMsg.spawnPos = spawnPosition;
|
||||
lootMsg.sourceID = source;
|
||||
lootMsg.item = LOT_NULL;
|
||||
CalcFinalDropPos(lootMsg);
|
||||
lootMsg.Send();
|
||||
lootMsg.Send(player->GetSystemAddress());
|
||||
const auto* const memberEntity = Game::entityManager->GetEntity(member);
|
||||
if (memberEntity) lootMsg.Send(memberEntity->GetSystemAddress());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -533,6 +560,9 @@ void Loot::GiveActivityLoot(Entity* player, const LWOOBJID source, uint32_t acti
|
||||
void Loot::DropLoot(Entity* player, const LWOOBJID source, uint32_t matrixIndex, uint32_t minCoins, uint32_t maxCoins) {
|
||||
player = player->GetOwner(); // if the owner is overwritten, we collect that here
|
||||
|
||||
auto* scriptedActivityComponent = Game::entityManager->GetZoneControlEntity()->GetComponent<ScriptedActivityComponent>();
|
||||
const bool noTeamLootOnDeath = scriptedActivityComponent ? scriptedActivityComponent->GetNoTeamLootOnDeath() : false;
|
||||
|
||||
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
|
||||
|
||||
if (!inventoryComponent)
|
||||
@@ -540,7 +570,7 @@ void Loot::DropLoot(Entity* player, const LWOOBJID source, uint32_t matrixIndex,
|
||||
|
||||
const auto result = ::RollLootMatrix(matrixIndex);
|
||||
|
||||
::DropLoot(player, source, result, minCoins, maxCoins);
|
||||
::DropLoot(player, source, result, minCoins, maxCoins, noTeamLootOnDeath);
|
||||
}
|
||||
|
||||
void Loot::DropActivityLoot(Entity* player, const LWOOBJID source, uint32_t activityID, int32_t rating) {
|
||||
|
||||
Reference in New Issue
Block a user