mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-06 11:57:13 +00:00
feat: deletion restrictions
Deleting an item now follows the item's DeletionRestrictions row (ItemComponent.delResIndex), as the client's LWOInventoryComponent_Common::CanRemoveFromInventory (0x00ce0d20) and CheckDeletionRestrictionIndex (0x00c94c20) do: an item the player is driving or riding can't be deleted, an operator can delete anything, and otherwise the row decides: - LOTS_INCLUDED: only while the player has another item of any of the LOTs (the last rocket can't be deleted) - LOTS_EXCLUDED: only while the player has other items of all of the LOTs - ANY_RESTRICTION / ALL_RESTRICTIONS: any / all of the listed rows allow it (a row that names itself again allows it) - ZONE: only in the listed zones - ALWAYS_RESTRICTED: never A missing, unrestricted or bad row (no valid ids, unknown check type) allows it. The client checks first and shows the row's failure reason; the server checks again and ignores a client's forceDeletion. Refs issue 960 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -40,6 +40,11 @@
|
||||
#include "CDSkillBehaviorTable.h"
|
||||
#include "StringifiedEnum.h"
|
||||
#include "Amf3.h"
|
||||
#include "CDDeletionRestrictionsTable.h"
|
||||
#include "eDeletionRestrictionsCheckType.h"
|
||||
#include "eGameMasterLevel.h"
|
||||
#include "PossessorComponent.h"
|
||||
#include "dZoneManager.h"
|
||||
|
||||
#include <ranges>
|
||||
|
||||
@@ -1893,3 +1898,60 @@ bool InventoryComponent::OnGetObjectReportInfo(GameMessages::GetObjectReportInfo
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InventoryComponent::CanRemoveFromInventory(const LWOOBJID itemId, const LOT lot) const {
|
||||
// Not what the player is driving or riding
|
||||
const auto* possessor = m_Parent->GetComponent<PossessorComponent>();
|
||||
if (possessor && itemId != LWOOBJID_EMPTY && (possessor->GetPossessable() == itemId || possessor->GetMountItemID() == itemId)) return false;
|
||||
|
||||
if (m_Parent->GetGMLevel() == eGameMasterLevel::OPERATOR) return true;
|
||||
|
||||
const auto restrictionId = Inventory::FindItemComponent(lot).delResIndex;
|
||||
if (static_cast<int32_t>(restrictionId) < 0) return true;
|
||||
|
||||
std::set<uint32_t> checked;
|
||||
return CheckDeletionRestriction(itemId, restrictionId, checked);
|
||||
}
|
||||
|
||||
bool InventoryComponent::CheckDeletionRestriction(const LWOOBJID itemId, const uint32_t restrictionId, std::set<uint32_t>& checked) const {
|
||||
const auto restriction = CDClientManager::GetTable<CDDeletionRestrictionsTable>()->GetByID(restrictionId);
|
||||
bool allowed = true;
|
||||
if (!restriction || !restriction->restricted || restriction->checkType >= eDeletionRestrictionsCheckType::MAX) {
|
||||
// No row, not restricted or an unknown check type: the item can be removed
|
||||
} else if (restriction->checkType == eDeletionRestrictionsCheckType::ALWAYS_RESTRICTED) {
|
||||
allowed = false;
|
||||
} else if (restriction->ids.empty()) {
|
||||
LOG("No valid ids for row %u in DeletionRestrictions", restrictionId);
|
||||
} else if (restriction->checkType == eDeletionRestrictionsCheckType::LOTS_INCLUDED || restriction->checkType == eDeletionRestrictionsCheckType::LOTS_EXCLUDED) {
|
||||
allowed = HasOtherItemsOfLots(restriction->ids, itemId, restriction->checkType == eDeletionRestrictionsCheckType::LOTS_INCLUDED);
|
||||
} else if (restriction->checkType == eDeletionRestrictionsCheckType::ZONE) {
|
||||
allowed = std::ranges::find(restriction->ids, Game::zoneManager->GetZoneID().GetMapID()) != restriction->ids.end();
|
||||
} else if (!checked.insert(restrictionId).second) {
|
||||
LOG("Row %u in DeletionRestrictions refers back to itself", restrictionId);
|
||||
} else {
|
||||
// ANY_RESTRICTION: any of the rows allows it; ALL_RESTRICTIONS: every row does
|
||||
const bool any = restriction->checkType == eDeletionRestrictionsCheckType::ANY_RESTRICTION;
|
||||
allowed = !any;
|
||||
for (const auto row : restriction->ids) {
|
||||
const bool rowAllows = CheckDeletionRestriction(itemId, row, checked);
|
||||
allowed = any ? (allowed || rowAllows) : (allowed && rowAllows);
|
||||
}
|
||||
checked.erase(restrictionId);
|
||||
}
|
||||
|
||||
return allowed;
|
||||
}
|
||||
|
||||
bool InventoryComponent::HasOtherItemsOfLots(const std::vector<uint32_t>& lots, const LWOOBJID excludedItemId, const bool any) const {
|
||||
std::set<uint32_t> missing(lots.begin(), lots.end());
|
||||
for (const auto* inventory : m_Inventories | std::views::values) {
|
||||
if (!inventory) continue;
|
||||
for (const auto& [id, item] : const_cast<Inventory*>(inventory)->GetItems()) {
|
||||
if (id == excludedItemId || !item || !missing.contains(item->GetLot())) continue;
|
||||
if (any) return true;
|
||||
missing.erase(item->GetLot());
|
||||
if (missing.empty()) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#define INVENTORYCOMPONENT_H
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <stack>
|
||||
|
||||
|
||||
@@ -182,6 +183,32 @@ public:
|
||||
*/
|
||||
Item* FindItemById(LWOOBJID id) const;
|
||||
|
||||
/**
|
||||
* Whether the player may remove (delete) an item, as the client's LWOInventoryComponent_Common::CanRemoveFromInventory
|
||||
* (0x00ce0d20): not what they are driving or riding, always for an operator, otherwise as the item's
|
||||
* DeletionRestrictions row says (ItemComponent.delResIndex)
|
||||
* @param itemId the item's id
|
||||
* @param lot the item's LOT
|
||||
*/
|
||||
bool CanRemoveFromInventory(LWOOBJID itemId, LOT lot) const;
|
||||
|
||||
/**
|
||||
* Whether a DeletionRestrictions row allows removing an item (CheckDeletionRestrictionIndex 0x00c94c20)
|
||||
* @param itemId the item being removed
|
||||
* @param restrictionId the row
|
||||
* @param checked the rows being checked further up (ANY_RESTRICTION / ALL_RESTRICTIONS), so a row that names
|
||||
* itself again does not loop
|
||||
*/
|
||||
bool CheckDeletionRestriction(LWOOBJID itemId, uint32_t restrictionId, std::set<uint32_t>& checked) const;
|
||||
|
||||
/**
|
||||
* Whether the player has items of the given LOTs other than one item (HasOtherItemsOfLots 0x00c02db0)
|
||||
* @param lots the LOTs
|
||||
* @param excludedItemId the item not to count
|
||||
* @param any true: an item of any of the LOTs is enough; false: one of each LOT is needed
|
||||
*/
|
||||
bool HasOtherItemsOfLots(const std::vector<uint32_t>& lots, LWOOBJID excludedItemId, bool any) const;
|
||||
|
||||
/**
|
||||
* Returns an item in the inventory that matches the specified LOT
|
||||
* @param lot the lot of the item to find
|
||||
|
||||
Reference in New Issue
Block a user