diff --git a/changelog.txt b/changelog.txt index 7ba6c61c9..4b717ed5b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,105 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 10/09/2018 == +Uleat: Added bot owner options + - usage: ^owneroption [option] (or aliased as: ^oo [option]) + - options are saved in the database and therefore, persistent + - Implemented option 'deathmarquee' + -- toggles client owner flag to show marquee message when a bot dies (default: disabled) + +== 10/07/2018 == +Uleat: Fixed a few bot issues.. + - Fix for bot item trades not attuning + - Fix for bot mana, endurance not updating properly + +== 10/06/2018 == +Uleat: Fixed a few bot issues.. + - Fix for bot 'stop melee level' not honoring setting level over rule level + - Fix for missing bot combat spell casting when within melee range + - Fix (in-work) for bots 'forgetting' current target when it flees + +== 09/18/2018 == +Uleat: Notes for manual conversion of quest script inventory slot values + - You should use reference/lookup values provided by the lua and perl apis to avoid skirting safety checks + and to ensure that the values used are the correct ones for your needs + + [perl api examples] + old: + 1) my $charmitem = $client->GetItemIDAt(0); + 2) for($for_x = 22; $for_x < 30; $for_x++) {...} + 3) for($slot1 = 0; $slot1 <= 30; $slot1++) {...} + + new: + 1) my $charmitem = $client->GetItemIDAt(quest::getinventoryslotid("charm")); + 2) for($for_x = quest::getinventoryslotid("general.begin"); $for_x <= quest::getinventoryslotid("general.end"); $for_x++) {...} ** notice change of conditional + 3) for($slot1 = quest::getinventoryslotid("possessions.begin"); $slot1 <= quest::getinventoryslotid("possessions.end"); $slot1++) {...} + + [lua api examples] + old: + 1) if(e.self:GetItemIDAt(30) == 31599) then ... + 2) for i = 0, 30, 1 do ... + + new: + 1) if(e.self:GetItemIDAt(Slot.Cursor) == 31599) then ... + 2) for i = Slot.PossessionsBegin, Slot.PossessionsEnd, 1 do ... + + - If you need to manually assign bag slot ranges to individual 'general' slots, use this assignment for now: + -- General1 (23) = 251 .. 260 + -- General2 (24) = 261 .. 270 + -- General3 (25) = 271 .. 280 + -- General4 (26) = 281 .. 290 + -- General5 (27) = 291 .. 300 + -- General6 (28) = 301 .. 310 + -- General7 (29) = 311 .. 320 + -- General8 (30) = 321 .. 330 + -- General9 (31) = 331 .. 340 + -- General10 (32) = 341 .. 350 + -- Cursor (33) = 351 .. 360 + + - If you need to manually assign ammo or powersource slots, use these values: + -- PowerSource = 21 + -- Ammo = 22 + + - All slot values not addressed above remain the same + + - Additional information can be found at: + -- https://github.com/EQEmu/Server/wiki/Inventory-Slots + -- https://github.com/EQEmu/Server/wiki/Perl-API + -- https://github.com/EQEmu/Server/wiki/Lua-API + +== 09/03/2018 == +Uleat: Rework of 'invsnapshot' command and implementation of automatic inventory snapshots. + - Inventory snapshots are now taken automatically using the interval rule values - if snapshots are enabled + - Command 'invsnapshot' now has more options available to include a restore feature + -- A pop-up help menu is available + -- argument 'capture' is available to anyone with status high enough to register the command + -- Advanced options are only available to players with 150 status or greater + -- argument 'list' provides a list of "timestamp : item count" entries + -- argument 'parse' displays a "slot : item id : item name" listing of valid snapshots by timestamp + -- argument 'compare' shows a 'difference' comparison of "snapshot-to-inventory" changes + -- argument 'restore' applies a saved snapshot to the player's inventory (with a pre-clearing call) + +== 08/13/2018 == +Uleat: Activation of RoF+ clients' two additional general slots and integration of SoF+ clients' PowerSource slot + - Inventory 'Possessions' main slots are now contiguous and implemented to RoF2 standards + -- 'slotGeneral9'(31) and 'slotGeneral10'(32) are now active + -- Possessions slot enumerations are now defined as between 'slotCharm'(0) and 'slotCursor'(33) + -- 'slotPowerSource'(21) is no longer a special case slot(9999) + - Special code has been added to exclude 'slotPowerSource,' 'slotGeneral9' and 'slotGeneral10' from server + actions in clients that do not support them. + - The lua api has been updated to use the new slot enumeration as well as having some slot range definitions added + - The perl api now has a look-up function to provide token-to-value translations .. definitions are similar to lua + - In both lua and perl, it is HIGHLY recommended that any custom scripts using hard-coded inventory slot values be + updated to use the constants/lookup methods as any old values are no longer guaranteed to be accurate + - Database will have existing inventory slot values modified to the new standard and table `items` entries will + also be updated to the 'correct' equipable slot bitmask + - Script (quest) updates are required with this change + +Note: The proper way to reference inventory slots is to use either instrinsic lookups (c/c++ & perl) or to use valid + const ref declarations (c/c++ & lua). Any other method is not guaranteed to be accurate and may result in item loss + and/or unexpected/undefined behavior. + + == 07/10/2018 == Akkadius: Adjusted DataBuckets to use other acceptable time formats Example: quest::set_data('key', 'value', '1d'); diff --git a/common/database.cpp b/common/database.cpp index 447037ae7..2ec4675b3 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -2111,10 +2111,27 @@ void Database::LoadLogSettings(EQEmuLogSys::LogSettings* log_settings) } } -void Database::ClearInvSnapshots(bool use_rule) -{ +int Database::CountInvSnapshots() { + std::string query = StringFormat("SELECT COUNT(*) FROM (SELECT * FROM `inventory_snapshots` a GROUP BY `charid`, `time_index`) b"); + auto results = QueryDatabase(query); + + if (!results.Success()) + return -1; + + auto row = results.begin(); + + int64 count = atoll(row[0]); + if (count > 2147483647) + return -2; + if (count < 0) + return -3; + + return count; +} + +void Database::ClearInvSnapshots(bool from_now) { uint32 del_time = time(nullptr); - if (use_rule) { del_time -= RuleI(Character, InvSnapshotHistoryD) * 86400; } + if (!from_now) { del_time -= RuleI(Character, InvSnapshotHistoryD) * 86400; } std::string query = StringFormat("DELETE FROM inventory_snapshots WHERE time_index <= %lu", (unsigned long)del_time); QueryDatabase(query); diff --git a/common/database.h b/common/database.h index c6d90e422..307390edf 100644 --- a/common/database.h +++ b/common/database.h @@ -264,7 +264,8 @@ public: void SetLFP(uint32 CharID, bool LFP); void SetLoginFlags(uint32 CharID, bool LFP, bool LFG, uint8 firstlogon); - void ClearInvSnapshots(bool use_rule = true); + int CountInvSnapshots(); + void ClearInvSnapshots(bool from_now = false); /* EQEmuLogSys */ void LoadLogSettings(EQEmuLogSys::LogSettings* log_settings); diff --git a/common/emu_constants.h b/common/emu_constants.h index fcddeac09..db49252a3 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -78,28 +78,30 @@ namespace EQEmu } // namespace invtype namespace invslot { - using namespace Titanium::invslot::enum_; - - const int16 SLOT_POWER_SOURCE = 9999; + using namespace RoF2::invslot::enum_; using RoF2::invslot::SLOT_INVALID; using RoF2::invslot::SLOT_BEGIN; - using Titanium::invslot::POSSESSIONS_BEGIN; - using Titanium::invslot::POSSESSIONS_END; - using SoF::invslot::POSSESSIONS_COUNT; + using Titanium::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE; - using Titanium::invslot::EQUIPMENT_BEGIN; - using Titanium::invslot::EQUIPMENT_END; - using Titanium::invslot::EQUIPMENT_COUNT; + const int16 SLOT_AUGMENT_GENERIC_RETURN = 1001; // clients don't appear to use this method... (internal inventory return value) - using Titanium::invslot::GENERAL_BEGIN; - using Titanium::invslot::GENERAL_END; - using Titanium::invslot::GENERAL_COUNT; + using RoF2::invslot::POSSESSIONS_BEGIN; + using RoF2::invslot::POSSESSIONS_END; + using RoF2::invslot::POSSESSIONS_COUNT; - using Titanium::invslot::BONUS_BEGIN; - using Titanium::invslot::BONUS_STAT_END; - using Titanium::invslot::BONUS_SKILL_END; + using RoF2::invslot::EQUIPMENT_BEGIN; + using RoF2::invslot::EQUIPMENT_END; + using RoF2::invslot::EQUIPMENT_COUNT; + + using RoF2::invslot::GENERAL_BEGIN; + using RoF2::invslot::GENERAL_END; + using RoF2::invslot::GENERAL_COUNT; + + using RoF2::invslot::BONUS_BEGIN; + using RoF2::invslot::BONUS_STAT_END; + using RoF2::invslot::BONUS_SKILL_END; using Titanium::invslot::BANK_BEGIN; using SoF::invslot::BANK_END; @@ -145,7 +147,7 @@ namespace EQEmu const int16 GENERAL_BAGS_8_COUNT = 8 * SLOT_COUNT; const int16 GENERAL_BAGS_8_END = (GENERAL_BAGS_BEGIN + GENERAL_BAGS_8_COUNT) - 1; - const int16 CURSOR_BAG_BEGIN = 331; + const int16 CURSOR_BAG_BEGIN = 351; const int16 CURSOR_BAG_COUNT = SLOT_COUNT; const int16 CURSOR_BAG_END = (CURSOR_BAG_BEGIN + CURSOR_BAG_COUNT) - 1; diff --git a/common/eq_constants.h b/common/eq_constants.h index 9ccca95a0..d20bfdf4a 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -523,18 +523,4 @@ static const uint8 SkillDamageTypes[EQEmu::skills::HIGHEST_SKILL + 1] = // chang static const uint32 MAX_SPELL_DB_ID_VAL = 65535; -namespace EQEmu -{ - namespace legacy { - enum InventorySlot { - SLOT_CURSOR_END = (int16)0xFFFE, // I hope no one is using this... - SLOT_TRADESKILL = 1000, - SLOT_AUGMENT = 1001, - //SLOT_INVALID = (int16)0xFFFF, - }; - - } // namespace legacy - -} - #endif /*COMMON_EQ_CONSTANTS_H*/ diff --git a/common/features.h b/common/features.h index 0fce08c96..3f42b062d 100644 --- a/common/features.h +++ b/common/features.h @@ -268,7 +268,8 @@ enum { commandBanPlayers = 100, //can set bans on players commandChangeDatarate = 201, //edit client's data rate commandZoneToCoords = 0, //can #zone with coords - commandInterrogateInv = 100 //below this == only log on error state and self-only target dump + commandInterrogateInv = 100, //below this == only log on error state and self-only target dump + commandInvSnapshot = 150 //ability to clear/restore snapshots }; //default states for logging flag on NPCs and clients (having NPCs on by default is prolly a bad idea) diff --git a/common/inventory_profile.cpp b/common/inventory_profile.cpp index cb20cd8a8..47068a492 100644 --- a/common/inventory_profile.cpp +++ b/common/inventory_profile.cpp @@ -177,7 +177,7 @@ EQEmu::ItemInstance* EQEmu::InventoryProfile::GetItem(int16 slot_id) const result = _GetItem(m_inv, slot_id); } else if ((slot_id >= invslot::EQUIPMENT_BEGIN && slot_id <= invslot::EQUIPMENT_END) || - (slot_id >= invslot::TRIBUTE_BEGIN && slot_id <= invslot::TRIBUTE_END) || (slot_id == invslot::SLOT_POWER_SOURCE)) { + (slot_id >= invslot::TRIBUTE_BEGIN && slot_id <= invslot::TRIBUTE_END)) { // Equippable slots (on body) result = _GetItem(m_worn, slot_id); } @@ -231,6 +231,25 @@ EQEmu::ItemInstance* EQEmu::InventoryProfile::GetItem(int16 slot_id, uint8 bagid // Put an item snto specified slot int16 EQEmu::InventoryProfile::PutItem(int16 slot_id, const ItemInstance& inst) { + if (slot_id <= EQEmu::invslot::POSSESSIONS_END && slot_id >= EQEmu::invslot::POSSESSIONS_BEGIN) { + if ((((uint64)1 << slot_id) & m_lookup->PossessionsBitmask) == 0) + return EQEmu::invslot::SLOT_INVALID; + } + else if (slot_id <= EQEmu::invbag::GENERAL_BAGS_END && slot_id >= EQEmu::invbag::GENERAL_BAGS_BEGIN) { + auto temp_slot = EQEmu::invslot::GENERAL_BEGIN + ((slot_id - EQEmu::invbag::GENERAL_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT); + if ((((uint64)1 << temp_slot) & m_lookup->PossessionsBitmask) == 0) + return EQEmu::invslot::SLOT_INVALID; + } + else if (slot_id <= EQEmu::invslot::BANK_END && slot_id >= EQEmu::invslot::BANK_BEGIN) { + if ((slot_id - EQEmu::invslot::BANK_BEGIN) >= m_lookup->InventoryTypeSize[EQEmu::invtype::typeBank]) + return EQEmu::invslot::SLOT_INVALID; + } + else if (slot_id <= EQEmu::invbag::BANK_BAGS_END && slot_id >= EQEmu::invbag::BANK_BAGS_BEGIN) { + auto temp_slot = (slot_id - EQEmu::invbag::BANK_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT; + if (temp_slot >= m_lookup->InventoryTypeSize[EQEmu::invtype::typeBank]) + return EQEmu::invslot::SLOT_INVALID; + } + // Clean up item already in slot (if exists) DeleteItem(slot_id); @@ -244,80 +263,139 @@ int16 EQEmu::InventoryProfile::PutItem(int16 slot_id, const ItemInstance& inst) return _PutItem(slot_id, inst.Clone()); } -int16 EQEmu::InventoryProfile::PushCursor(const ItemInstance& inst) -{ +int16 EQEmu::InventoryProfile::PushCursor(const ItemInstance &inst) { m_cursor.push(inst.Clone()); return invslot::slotCursor; } -EQEmu::ItemInstance* EQEmu::InventoryProfile::GetCursorItem() -{ +EQEmu::ItemInstance* EQEmu::InventoryProfile::GetCursorItem() { return m_cursor.peek_front(); } // Swap items in inventory -bool EQEmu::InventoryProfile::SwapItem(int16 slot_a, int16 slot_b, SwapItemFailState& fail_state, uint16 race_id, uint8 class_id, uint16 deity_id, uint8 level) -{ +bool EQEmu::InventoryProfile::SwapItem( + int16 source_slot, + int16 destination_slot, + SwapItemFailState &fail_state, + uint16 race_id, + uint8 class_id, + uint16 deity_id, + uint8 level +) { fail_state = swapInvalid; - - // Temp holding areas for a and b - ItemInstance* inst_a = GetItem(slot_a); - ItemInstance* inst_b = GetItem(slot_b); - if (inst_a) { - if (!inst_a->IsSlotAllowed(slot_b)) { + if (source_slot <= EQEmu::invslot::POSSESSIONS_END && source_slot >= EQEmu::invslot::POSSESSIONS_BEGIN) { + if ((((uint64) 1 << source_slot) & m_lookup->PossessionsBitmask) == 0) { fail_state = swapNotAllowed; return false; } - if ((slot_b >= invslot::EQUIPMENT_BEGIN && slot_b <= invslot::EQUIPMENT_END) || slot_b == invslot::SLOT_POWER_SOURCE) { - auto item_a = inst_a->GetItem(); - if (!item_a) { + } + else if (source_slot <= EQEmu::invbag::GENERAL_BAGS_END && source_slot >= EQEmu::invbag::GENERAL_BAGS_BEGIN) { + auto temp_slot = EQEmu::invslot::GENERAL_BEGIN + ((source_slot - EQEmu::invbag::GENERAL_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT); + if ((((uint64)1 << temp_slot) & m_lookup->PossessionsBitmask) == 0) { + fail_state = swapNotAllowed; + return false; + } + } + else if (source_slot <= EQEmu::invslot::BANK_END && source_slot >= EQEmu::invslot::BANK_BEGIN) { + if ((source_slot - EQEmu::invslot::BANK_BEGIN) >= m_lookup->InventoryTypeSize[EQEmu::invtype::typeBank]) { + fail_state = swapNotAllowed; + return false; + } + } + else if (source_slot <= EQEmu::invbag::BANK_BAGS_END && source_slot >= EQEmu::invbag::BANK_BAGS_BEGIN) { + auto temp_slot = (source_slot - EQEmu::invbag::BANK_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT; + if (temp_slot >= m_lookup->InventoryTypeSize[EQEmu::invtype::typeBank]) { + fail_state = swapNotAllowed; + return false; + } + } + + if (destination_slot <= EQEmu::invslot::POSSESSIONS_END && destination_slot >= EQEmu::invslot::POSSESSIONS_BEGIN) { + if ((((uint64)1 << destination_slot) & m_lookup->PossessionsBitmask) == 0) { + fail_state = swapNotAllowed; + return false; + } + } + else if (destination_slot <= EQEmu::invbag::GENERAL_BAGS_END && destination_slot >= EQEmu::invbag::GENERAL_BAGS_BEGIN) { + auto temp_slot = EQEmu::invslot::GENERAL_BEGIN + ((destination_slot - EQEmu::invbag::GENERAL_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT); + if ((((uint64)1 << temp_slot) & m_lookup->PossessionsBitmask) == 0) { + fail_state = swapNotAllowed; + return false; + } + } + else if (destination_slot <= EQEmu::invslot::BANK_END && destination_slot >= EQEmu::invslot::BANK_BEGIN) { + if ((destination_slot - EQEmu::invslot::BANK_BEGIN) >= m_lookup->InventoryTypeSize[EQEmu::invtype::typeBank]) { + fail_state = swapNotAllowed; + return false; + } + } + else if (destination_slot <= EQEmu::invbag::BANK_BAGS_END && destination_slot >= EQEmu::invbag::BANK_BAGS_BEGIN) { + auto temp_slot = (destination_slot - EQEmu::invbag::BANK_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT; + if (temp_slot >= m_lookup->InventoryTypeSize[EQEmu::invtype::typeBank]) { + fail_state = swapNotAllowed; + return false; + } + } + + // Temp holding areas for source and destination + ItemInstance *source_item_instance = GetItem(source_slot); + ItemInstance *destination_item_instance = GetItem(destination_slot); + + if (source_item_instance) { + if (!source_item_instance->IsSlotAllowed(destination_slot)) { + fail_state = swapNotAllowed; + return false; + } + if ((destination_slot >= invslot::EQUIPMENT_BEGIN && destination_slot <= invslot::EQUIPMENT_END)) { + auto source_item = source_item_instance->GetItem(); + if (!source_item) { fail_state = swapNullData; return false; } - if (race_id && class_id && !item_a->IsEquipable(race_id, class_id)) { + if (race_id && class_id && !source_item->IsEquipable(race_id, class_id)) { fail_state = swapRaceClass; return false; } - if (deity_id && item_a->Deity && !(deity::ConvertDeityTypeToDeityTypeBit((deity::DeityType)deity_id) & item_a->Deity)) { + if (deity_id && source_item->Deity && !(deity::ConvertDeityTypeToDeityTypeBit((deity::DeityType)deity_id) & source_item->Deity)) { fail_state = swapDeity; return false; } - if (level && item_a->ReqLevel && level < item_a->ReqLevel) { + if (level && source_item->ReqLevel && level < source_item->ReqLevel) { fail_state = swapLevel; return false; } } } - if (inst_b) { - if (!inst_b->IsSlotAllowed(slot_a)) { + if (destination_item_instance) { + if (!destination_item_instance->IsSlotAllowed(source_slot)) { fail_state = swapNotAllowed; return false; } - if ((slot_a >= invslot::EQUIPMENT_BEGIN && slot_a <= invslot::EQUIPMENT_END) || slot_a == invslot::SLOT_POWER_SOURCE) { - auto item_b = inst_b->GetItem(); - if (!item_b) { + if ((source_slot >= invslot::EQUIPMENT_BEGIN && source_slot <= invslot::EQUIPMENT_END)) { + auto destination_item = destination_item_instance->GetItem(); + if (!destination_item) { fail_state = swapNullData; return false; } - if (race_id && class_id && !item_b->IsEquipable(race_id, class_id)) { + if (race_id && class_id && !destination_item->IsEquipable(race_id, class_id)) { fail_state = swapRaceClass; return false; } - if (deity_id && item_b->Deity && !(deity::ConvertDeityTypeToDeityTypeBit((deity::DeityType)deity_id) & item_b->Deity)) { + if (deity_id && destination_item->Deity && !(deity::ConvertDeityTypeToDeityTypeBit((deity::DeityType)deity_id) & destination_item->Deity)) { fail_state = swapDeity; return false; } - if (level && item_b->ReqLevel && level < item_b->ReqLevel) { + if (level && destination_item->ReqLevel && level < destination_item->ReqLevel) { fail_state = swapLevel; return false; } } } - _PutItem(slot_a, inst_b); // Assign b->a - _PutItem(slot_b, inst_a); // Assign a->b + _PutItem(source_slot, destination_item_instance); // Assign destination -> source + _PutItem(destination_slot, source_item_instance); // Assign source -> destination fail_state = swapPass; @@ -325,10 +403,9 @@ bool EQEmu::InventoryProfile::SwapItem(int16 slot_a, int16 slot_b, SwapItemFailS } // Remove item from inventory (with memory delete) -bool EQEmu::InventoryProfile::DeleteItem(int16 slot_id, uint8 quantity) -{ +bool EQEmu::InventoryProfile::DeleteItem(int16 slot_id, uint8 quantity) { // Pop item out of inventory map (or queue) - ItemInstance* item_to_delete = PopItem(slot_id); + ItemInstance *item_to_delete = PopItem(slot_id); // Determine if object should be fully deleted, or // just a quantity of charges of the item can be deleted @@ -342,7 +419,7 @@ bool EQEmu::InventoryProfile::DeleteItem(int16 slot_id, uint8 quantity) // the item is not stackable, and is not a charged item, or is expendable, delete it if (item_to_delete->IsStackable() || (!item_to_delete->IsStackable() && - ((item_to_delete->GetItem()->MaxCharges == 0) || item_to_delete->IsExpendable())) + ((item_to_delete->GetItem()->MaxCharges == 0) || item_to_delete->IsExpendable())) ) { // Item can now be destroyed InventoryProfile::MarkDirty(item_to_delete); @@ -379,11 +456,11 @@ EQEmu::ItemInstance* EQEmu::InventoryProfile::PopItem(int16 slot_id) if (slot_id == invslot::slotCursor) { p = m_cursor.pop(); } - else if ((slot_id >= invslot::EQUIPMENT_BEGIN && slot_id <= invslot::EQUIPMENT_END) || (slot_id == invslot::SLOT_POWER_SOURCE)) { + else if (slot_id >= invslot::EQUIPMENT_BEGIN && slot_id <= invslot::EQUIPMENT_END) { p = m_worn[slot_id]; m_worn.erase(slot_id); } - else if ((slot_id >= invslot::GENERAL_BEGIN && slot_id <= invslot::GENERAL_END)) { + else if (slot_id >= invslot::GENERAL_BEGIN && slot_id <= invslot::GENERAL_END) { p = m_inv[slot_id]; m_inv.erase(slot_id); } @@ -420,6 +497,8 @@ bool EQEmu::InventoryProfile::HasSpaceForItem(const ItemData *ItemToTry, int16 Q if (ItemToTry->Stackable) { for (int16 i = invslot::GENERAL_BEGIN; i <= invslot::GENERAL_END; i++) { + if ((((uint64)1 << i) & m_lookup->PossessionsBitmask) == 0) + continue; ItemInstance* InvItem = GetItem(i); @@ -457,6 +536,8 @@ bool EQEmu::InventoryProfile::HasSpaceForItem(const ItemData *ItemToTry, int16 Q } for (int16 i = invslot::GENERAL_BEGIN; i <= invslot::GENERAL_END; i++) { + if ((((uint64)1 << i) & m_lookup->PossessionsBitmask) == 0) + continue; ItemInstance* InvItem = GetItem(i); @@ -483,7 +564,7 @@ bool EQEmu::InventoryProfile::HasSpaceForItem(const ItemData *ItemToTry, int16 Q uint8 BagSize = InvItem->GetItem()->BagSlots; - for (uint8 BagSlot = invbag::SLOT_BEGIN; BagSlotPossessionsBitmask) == 0) + continue; + if (!GetItem(i)) // Found available slot in personal inventory return i; @@ -670,6 +754,9 @@ int16 EQEmu::InventoryProfile::FindFreeSlot(bool for_bag, bool try_cursor, uint8 if (!for_bag) { for (int16 i = invslot::GENERAL_BEGIN; i <= invslot::GENERAL_END; i++) { + if ((((uint64)1 << i) & m_lookup->PossessionsBitmask) == 0) + continue; + const ItemInstance* inst = GetItem(i); if (inst && inst->IsClassBag() && inst->GetItem()->BagSize >= min_size) { @@ -720,6 +807,9 @@ int16 EQEmu::InventoryProfile::FindFreeSlotForTradeItem(const ItemInstance* inst // step 1: find room for bags (caller should really ask for slots for bags first to avoid sending them to cursor..and bag item loss) if (inst->IsClassBag()) { for (int16 free_slot = general_start; free_slot <= invslot::GENERAL_END; ++free_slot) { + if ((((uint64)1 << free_slot) & m_lookup->PossessionsBitmask) == 0) + continue; + if (!m_inv[free_slot]) return free_slot; } @@ -730,6 +820,9 @@ int16 EQEmu::InventoryProfile::FindFreeSlotForTradeItem(const ItemInstance* inst // step 2: find partial room for stackables if (inst->IsStackable()) { for (int16 free_slot = general_start; free_slot <= invslot::GENERAL_END; ++free_slot) { + if ((((uint64)1 << free_slot) & m_lookup->PossessionsBitmask) == 0) + continue; + const ItemInstance* main_inst = m_inv[free_slot]; if (!main_inst) @@ -740,6 +833,9 @@ int16 EQEmu::InventoryProfile::FindFreeSlotForTradeItem(const ItemInstance* inst } for (int16 free_slot = general_start; free_slot <= invslot::GENERAL_END; ++free_slot) { + if ((((uint64)1 << free_slot) & m_lookup->PossessionsBitmask) == 0) + continue; + const ItemInstance* main_inst = m_inv[free_slot]; if (!main_inst) @@ -763,6 +859,9 @@ int16 EQEmu::InventoryProfile::FindFreeSlotForTradeItem(const ItemInstance* inst // step 3a: find room for container-specific items (ItemClassArrow) if (inst->GetItem()->ItemType == item::ItemTypeArrow) { for (int16 free_slot = general_start; free_slot <= invslot::GENERAL_END; ++free_slot) { + if ((((uint64)1 << free_slot) & m_lookup->PossessionsBitmask) == 0) + continue; + const ItemInstance* main_inst = m_inv[free_slot]; if (!main_inst || (main_inst->GetItem()->BagType != item::BagTypeQuiver) || !main_inst->IsClassBag()) @@ -779,6 +878,9 @@ int16 EQEmu::InventoryProfile::FindFreeSlotForTradeItem(const ItemInstance* inst // step 3b: find room for container-specific items (ItemClassSmallThrowing) if (inst->GetItem()->ItemType == item::ItemTypeSmallThrowing) { for (int16 free_slot = general_start; free_slot <= invslot::GENERAL_END; ++free_slot) { + if ((((uint64)1 << free_slot) & m_lookup->PossessionsBitmask) == 0) + continue; + const ItemInstance* main_inst = m_inv[free_slot]; if (!main_inst || (main_inst->GetItem()->BagType != item::BagTypeBandolier) || !main_inst->IsClassBag()) @@ -794,6 +896,9 @@ int16 EQEmu::InventoryProfile::FindFreeSlotForTradeItem(const ItemInstance* inst // step 4: just find an empty slot for (int16 free_slot = general_start; free_slot <= invslot::GENERAL_END; ++free_slot) { + if ((((uint64)1 << free_slot) & m_lookup->PossessionsBitmask) == 0) + continue; + const ItemInstance* main_inst = m_inv[free_slot]; if (!main_inst) @@ -801,6 +906,9 @@ int16 EQEmu::InventoryProfile::FindFreeSlotForTradeItem(const ItemInstance* inst } for (int16 free_slot = general_start; free_slot <= invslot::GENERAL_END; ++free_slot) { + if ((((uint64)1 << free_slot) & m_lookup->PossessionsBitmask) == 0) + continue; + const ItemInstance* main_inst = m_inv[free_slot]; if (main_inst && main_inst->IsClassBag()) { @@ -977,12 +1085,13 @@ bool EQEmu::InventoryProfile::CanItemFitInContainer(const ItemData *ItemToTry, c bool EQEmu::InventoryProfile::SupportsClickCasting(int16 slot_id) { // there are a few non-potion items that identify as ItemTypePotion..so, we still need to ubiquitously include the equipment range - if ((uint16)slot_id <= invslot::GENERAL_END || slot_id == invslot::SLOT_POWER_SOURCE) - { + if (slot_id >= invslot::EQUIPMENT_BEGIN && slot_id <= invslot::EQUIPMENT_END) { return true; } - else if (slot_id >= invbag::GENERAL_BAGS_BEGIN && slot_id <= invbag::GENERAL_BAGS_END) - { + else if (slot_id >= invslot::GENERAL_BEGIN && slot_id <= invslot::GENERAL_END) { + return true; + } + else if (slot_id >= invbag::GENERAL_BAGS_BEGIN && slot_id <= invbag::GENERAL_BAGS_END) { if (inventory::Lookup(m_mob_version)->AllowClickCastFromBag) return true; } @@ -992,8 +1101,16 @@ bool EQEmu::InventoryProfile::SupportsClickCasting(int16 slot_id) bool EQEmu::InventoryProfile::SupportsPotionBeltCasting(int16 slot_id) { - if ((uint16)slot_id <= invslot::GENERAL_END || slot_id == invslot::SLOT_POWER_SOURCE || (slot_id >= invbag::GENERAL_BAGS_BEGIN && slot_id <= invbag::GENERAL_BAGS_END)) + // does this have the same criteria as 'SupportsClickCasting' above? (bag clicking per client) + if (slot_id >= invslot::EQUIPMENT_BEGIN && slot_id <= invslot::EQUIPMENT_END) { return true; + } + else if (slot_id >= invslot::GENERAL_BEGIN && slot_id <= invslot::GENERAL_END) { + return true; + } + else if (slot_id >= invbag::GENERAL_BAGS_BEGIN && slot_id <= invbag::GENERAL_BAGS_END) { + return true; + } return false; } @@ -1006,7 +1123,7 @@ bool EQEmu::InventoryProfile::SupportsContainers(int16 slot_id) (slot_id >= invslot::BANK_BEGIN && slot_id <= invslot::BANK_END) || (slot_id >= invslot::SHARED_BANK_BEGIN && slot_id <= invslot::SHARED_BANK_END) || (slot_id >= invslot::TRADE_BEGIN && slot_id <= invslot::TRADE_END) - ) { + ) { return true; } @@ -1054,13 +1171,19 @@ uint8 EQEmu::InventoryProfile::FindBrightestLightType() uint8 brightest_light_type = 0; for (auto iter = m_worn.begin(); iter != m_worn.end(); ++iter) { - if ((iter->first < invslot::EQUIPMENT_BEGIN || iter->first > invslot::EQUIPMENT_END) && iter->first != invslot::SLOT_POWER_SOURCE) { continue; } - if (iter->first == invslot::slotAmmo) { continue; } + if ((iter->first < invslot::EQUIPMENT_BEGIN || iter->first > invslot::EQUIPMENT_END)) + continue; + + if (iter->first == invslot::slotAmmo) + continue; auto inst = iter->second; - if (inst == nullptr) { continue; } + if (inst == nullptr) + continue; + auto item = inst->GetItem(); - if (item == nullptr) { continue; } + if (item == nullptr) + continue; if (lightsource::IsLevelGreater(item->Light, brightest_light_type)) brightest_light_type = item->Light; @@ -1068,15 +1191,21 @@ uint8 EQEmu::InventoryProfile::FindBrightestLightType() uint8 general_light_type = 0; for (auto iter = m_inv.begin(); iter != m_inv.end(); ++iter) { - if (iter->first < invslot::GENERAL_BEGIN || iter->first > invslot::GENERAL_END) { continue; } + if (iter->first < invslot::GENERAL_BEGIN || iter->first > invslot::GENERAL_END) + continue; auto inst = iter->second; - if (inst == nullptr) { continue; } - auto item = inst->GetItem(); - if (item == nullptr) { continue; } + if (inst == nullptr) + continue; - if (!item->IsClassCommon()) { continue; } - if (item->Light < 9 || item->Light > 13) { continue; } + auto item = inst->GetItem(); + if (item == nullptr) + continue; + + if (!item->IsClassCommon()) + continue; + if (item->Light < 9 || item->Light > 13) + continue; if (lightsource::TypeToLevel(item->Light)) general_light_type = item->Light; @@ -1136,7 +1265,7 @@ int EQEmu::InventoryProfile::GetSlotByItemInstCollection(const std::map &collection) @@ -1174,6 +1303,15 @@ void EQEmu::InventoryProfile::dumpBagContents(ItemInstance *inst, std::map& bucket, int16 slot_id) const { + if (slot_id <= EQEmu::invslot::POSSESSIONS_END && slot_id >= EQEmu::invslot::POSSESSIONS_BEGIN) { + if ((((uint64)1 << slot_id) & m_lookup->PossessionsBitmask) == 0) + return nullptr; + } + else if (slot_id <= EQEmu::invslot::BANK_END && slot_id >= EQEmu::invslot::BANK_BEGIN) { + if (slot_id - EQEmu::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize[EQEmu::invtype::typeBank]) + return nullptr; + } + auto it = bucket.find(slot_id); if (it != bucket.end()) { return it->second; @@ -1205,21 +1343,27 @@ int16 EQEmu::InventoryProfile::_PutItem(int16 slot_id, ItemInstance* inst) m_cursor.push_front(inst); result = slot_id; } - else if ((slot_id >= invslot::EQUIPMENT_BEGIN && slot_id <= invslot::EQUIPMENT_END) || (slot_id == invslot::SLOT_POWER_SOURCE)) { - m_worn[slot_id] = inst; - result = slot_id; + else if (slot_id >= invslot::EQUIPMENT_BEGIN && slot_id <= invslot::EQUIPMENT_END) { + if ((((uint64)1 << slot_id) & m_lookup->PossessionsBitmask) != 0) { + m_worn[slot_id] = inst; + result = slot_id; + } } else if ((slot_id >= invslot::GENERAL_BEGIN && slot_id <= invslot::GENERAL_END)) { - m_inv[slot_id] = inst; - result = slot_id; + if ((((uint64)1 << slot_id) & m_lookup->PossessionsBitmask) != 0) { + m_inv[slot_id] = inst; + result = slot_id; + } } else if (slot_id >= invslot::TRIBUTE_BEGIN && slot_id <= invslot::TRIBUTE_END) { m_worn[slot_id] = inst; result = slot_id; } else if (slot_id >= invslot::BANK_BEGIN && slot_id <= invslot::BANK_END) { - m_bank[slot_id] = inst; - result = slot_id; + if (slot_id - EQEmu::invslot::BANK_BEGIN < m_lookup->InventoryTypeSize[EQEmu::invtype::typeBank]) { + m_bank[slot_id] = inst; + result = slot_id; + } } else if (slot_id >= invslot::SHARED_BANK_BEGIN && slot_id <= invslot::SHARED_BANK_END) { m_shbank[slot_id] = inst; @@ -1254,6 +1398,15 @@ int16 EQEmu::InventoryProfile::_HasItem(std::map& bucket, uint32 quantity_found = 0; for (auto iter = bucket.begin(); iter != bucket.end(); ++iter) { + if (iter->first <= EQEmu::invslot::POSSESSIONS_END && iter->first >= EQEmu::invslot::POSSESSIONS_BEGIN) { + if ((((uint64)1 << iter->first) & m_lookup->PossessionsBitmask) == 0) + continue; + } + else if (iter->first <= EQEmu::invslot::BANK_END && iter->first >= EQEmu::invslot::BANK_BEGIN) { + if (iter->first - EQEmu::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize[EQEmu::invtype::typeBank]) + continue; + } + auto inst = iter->second; if (inst == nullptr) { continue; } @@ -1265,7 +1418,7 @@ int16 EQEmu::InventoryProfile::_HasItem(std::map& bucket, for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { if (inst->GetAugmentItemID(index) == item_id && quantity <= 1) - return legacy::SLOT_AUGMENT; + return invslot::SLOT_AUGMENT_GENERIC_RETURN; } if (!inst->IsClassBag()) { continue; } @@ -1282,7 +1435,7 @@ int16 EQEmu::InventoryProfile::_HasItem(std::map& bucket, for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { if (bag_inst->GetAugmentItemID(index) == item_id && quantity <= 1) - return legacy::SLOT_AUGMENT; + return invslot::SLOT_AUGMENT_GENERIC_RETURN; } } } @@ -1313,7 +1466,7 @@ int16 EQEmu::InventoryProfile::_HasItem(ItemInstQueue& iqueue, uint32 item_id, u for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { if (inst->GetAugmentItemID(index) == item_id && quantity <= 1) - return legacy::SLOT_AUGMENT; + return invslot::SLOT_AUGMENT_GENERIC_RETURN; } if (!inst->IsClassBag()) { continue; } @@ -1330,7 +1483,7 @@ int16 EQEmu::InventoryProfile::_HasItem(ItemInstQueue& iqueue, uint32 item_id, u for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { if (bag_inst->GetAugmentItemID(index) == item_id && quantity <= 1) - return legacy::SLOT_AUGMENT; + return invslot::SLOT_AUGMENT_GENERIC_RETURN; } } @@ -1347,6 +1500,15 @@ int16 EQEmu::InventoryProfile::_HasItemByUse(std::map& buc uint32 quantity_found = 0; for (auto iter = bucket.begin(); iter != bucket.end(); ++iter) { + if (iter->first <= EQEmu::invslot::POSSESSIONS_END && iter->first >= EQEmu::invslot::POSSESSIONS_BEGIN) { + if ((((uint64)1 << iter->first) & m_lookup->PossessionsBitmask) == 0) + continue; + } + else if (iter->first <= EQEmu::invslot::BANK_END && iter->first >= EQEmu::invslot::BANK_BEGIN) { + if (iter->first - EQEmu::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize[EQEmu::invtype::typeBank]) + continue; + } + auto inst = iter->second; if (inst == nullptr) { continue; } @@ -1411,6 +1573,15 @@ int16 EQEmu::InventoryProfile::_HasItemByUse(ItemInstQueue& iqueue, uint8 use, u int16 EQEmu::InventoryProfile::_HasItemByLoreGroup(std::map& bucket, uint32 loregroup) { for (auto iter = bucket.begin(); iter != bucket.end(); ++iter) { + if (iter->first <= EQEmu::invslot::POSSESSIONS_END && iter->first >= EQEmu::invslot::POSSESSIONS_BEGIN) { + if ((((uint64)1 << iter->first) & m_lookup->PossessionsBitmask) == 0) + continue; + } + else if (iter->first <= EQEmu::invslot::BANK_END && iter->first >= EQEmu::invslot::BANK_BEGIN) { + if (iter->first - EQEmu::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize[EQEmu::invtype::typeBank]) + continue; + } + auto inst = iter->second; if (inst == nullptr) { continue; } @@ -1422,7 +1593,7 @@ int16 EQEmu::InventoryProfile::_HasItemByLoreGroup(std::mapGetItem()->LoreGroup == loregroup) - return legacy::SLOT_AUGMENT; + return invslot::SLOT_AUGMENT_GENERIC_RETURN; } if (!inst->IsClassBag()) { continue; } @@ -1439,12 +1610,12 @@ int16 EQEmu::InventoryProfile::_HasItemByLoreGroup(std::mapGetItem()->LoreGroup == loregroup) - return legacy::SLOT_AUGMENT; + return invslot::SLOT_AUGMENT_GENERIC_RETURN; } } } - return INVALID_INDEX; + return EQEmu::invslot::SLOT_INVALID; } // Internal Method: Checks an inventory queue type bucket for a particular item @@ -1462,7 +1633,7 @@ int16 EQEmu::InventoryProfile::_HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 if (aug_inst == nullptr) { continue; } if (aug_inst->GetItem()->LoreGroup == loregroup) - return legacy::SLOT_AUGMENT; + return invslot::SLOT_AUGMENT_GENERIC_RETURN; } if (!inst->IsClassBag()) { continue; } @@ -1479,7 +1650,7 @@ int16 EQEmu::InventoryProfile::_HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 if (aug_inst == nullptr) { continue; } if (aug_inst->GetItem()->LoreGroup == loregroup) - return legacy::SLOT_AUGMENT; + return invslot::SLOT_AUGMENT_GENERIC_RETURN; } } @@ -1487,5 +1658,5 @@ int16 EQEmu::InventoryProfile::_HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 break; } - return INVALID_INDEX; + return EQEmu::invslot::SLOT_INVALID; } diff --git a/common/inventory_profile.h b/common/inventory_profile.h index 1f28b00f9..3a90e847d 100644 --- a/common/inventory_profile.h +++ b/common/inventory_profile.h @@ -126,7 +126,7 @@ namespace EQEmu // Swap items in inventory enum SwapItemFailState : int8 { swapInvalid = -1, swapPass = 0, swapNotAllowed, swapNullData, swapRaceClass, swapDeity, swapLevel }; - bool SwapItem(int16 slot_a, int16 slot_b, SwapItemFailState& fail_state, uint16 race_id = 0, uint8 class_id = 0, uint16 deity_id = 0, uint8 level = 0); + bool SwapItem(int16 source_slot, int16 destination_slot, SwapItemFailState& fail_state, uint16 race_id = 0, uint8 class_id = 0, uint16 deity_id = 0, uint8 level = 0); // Remove item from inventory bool DeleteItem(int16 slot_id, uint8 quantity = 0); diff --git a/common/inventory_slot.cpp b/common/inventory_slot.cpp index ac4aaa412..21af3eb45 100644 --- a/common/inventory_slot.cpp +++ b/common/inventory_slot.cpp @@ -110,9 +110,7 @@ bool EQEmu::InventorySlot::IsDeleteSlot() const bool EQEmu::InventorySlot::IsEquipmentIndex(int16 slot_index) { - /*if (slot_index < inventory::EquipmentBegin || slot_index > inventory::EquipmentEnd) - return false;*/ - if ((slot_index < invslot::EQUIPMENT_BEGIN || slot_index > invslot::EQUIPMENT_END) && slot_index != invslot::SLOT_POWER_SOURCE) + if (slot_index < invslot::EQUIPMENT_BEGIN || slot_index > invslot::EQUIPMENT_END) return false; return true; @@ -120,8 +118,6 @@ bool EQEmu::InventorySlot::IsEquipmentIndex(int16 slot_index) bool EQEmu::InventorySlot::IsGeneralIndex(int16 slot_index) { - /*if (slot_index < inventory::GeneralBegin || slot_index > inventory::GeneralEnd) - return false;*/ if (slot_index < invslot::GENERAL_BEGIN || slot_index > invslot::GENERAL_END) return false; @@ -130,22 +126,18 @@ bool EQEmu::InventorySlot::IsGeneralIndex(int16 slot_index) bool EQEmu::InventorySlot::IsCursorIndex(int16 slot_index) { - /*if (slot_index != inventory::slotCursor) - return false;*/ - if (slot_index != invslot::slotCursor) - return false; + if (slot_index == invslot::slotCursor) + return true; - return true; + return false; } bool EQEmu::InventorySlot::IsWeaponIndex(int16 slot_index) { - /*if ((slot_index != inventory::slotRange) && (slot_index != inventory::slotPrimary) && (slot_index != inventory::slotSecondary)) - return false;*/ - if ((slot_index != invslot::slotRange) && (slot_index != invslot::slotPrimary) && (slot_index != invslot::slotSecondary)) - return false; - - return true; + if (slot_index == invslot::slotPrimary || slot_index == invslot::slotSecondary || slot_index == invslot::slotRange) + return true; + + return false; } bool EQEmu::InventorySlot::IsTextureIndex(int16 slot_index) diff --git a/common/item_instance.cpp b/common/item_instance.cpp index 922d0e032..ea7e94428 100644 --- a/common/item_instance.cpp +++ b/common/item_instance.cpp @@ -271,17 +271,10 @@ bool EQEmu::ItemInstance::IsEquipable(int16 slot_id) const if (!m_item) return false; - // another "shouldn't do" fix..will be fixed in future updates (requires code and database work) - int16 use_slot = INVALID_INDEX; - if (slot_id == invslot::SLOT_POWER_SOURCE) { use_slot = invslot::slotGeneral1; } - if ((uint16)slot_id <= invslot::EQUIPMENT_END) { use_slot = slot_id; } + if (slot_id < EQEmu::invslot::EQUIPMENT_BEGIN || slot_id > EQEmu::invslot::EQUIPMENT_END) + return false; - if (use_slot != INVALID_INDEX) { - if (m_item->Slots & (1 << use_slot)) - return true; - } - - return false; + return ((m_item->Slots & (1 << slot_id)) != 0); } bool EQEmu::ItemInstance::IsAugmentable() const @@ -298,20 +291,18 @@ bool EQEmu::ItemInstance::IsAugmentable() const } bool EQEmu::ItemInstance::AvailableWearSlot(uint32 aug_wear_slots) const { - // TODO: check to see if incoming 'aug_wear_slots' "switches" bit assignments like above... - // (if wrong, would only affect MainAmmo and MainPowerSource augments) if (!m_item || !m_item->IsClassCommon()) return false; int index = invslot::EQUIPMENT_BEGIN; - for (; index <= invslot::slotGeneral1; ++index) { // MainGeneral1 should be legacy::EQUIPMENT_END + for (; index <= invslot::EQUIPMENT_END; ++index) { if (m_item->Slots & (1 << index)) { if (aug_wear_slots & (1 << index)) break; } } - return (index < 23) ? true : false; + return (index <= EQEmu::invslot::EQUIPMENT_END); } int8 EQEmu::ItemInstance::AvailableAugmentSlot(int32 augtype) const @@ -810,12 +801,10 @@ EQEmu::ItemInstance* EQEmu::ItemInstance::Clone() const } bool EQEmu::ItemInstance::IsSlotAllowed(int16 slot_id) const { - // 'SupportsContainers' and 'slot_id > 21' previously saw the reassigned PowerSource slot (9999 to 22) as valid if (!m_item) { return false; } else if (InventoryProfile::SupportsContainers(slot_id)) { return true; } else if (m_item->Slots & (1 << slot_id)) { return true; } - else if (slot_id == invslot::SLOT_POWER_SOURCE && (m_item->Slots & (1 << 22))) { return true; } // got lazy... - else if (slot_id != invslot::SLOT_POWER_SOURCE && slot_id > invslot::EQUIPMENT_END) { return true; } + else if (slot_id > invslot::EQUIPMENT_END) { return true; } // why do we call 'InventoryProfile::SupportsContainers' with this here? else { return false; } } diff --git a/common/memory_buffer.h b/common/memory_buffer.h index 880b860ae..474953f91 100644 --- a/common/memory_buffer.h +++ b/common/memory_buffer.h @@ -124,7 +124,7 @@ namespace EQEmu class OutBuffer : public std::stringstream { public: - inline size_t size() { return tellp(); } + inline size_t size() { return static_cast(tellp()); } void overwrite(OutBuffer::pos_type position, const char *_Str, std::streamsize _Count); uchar* detach(); }; diff --git a/common/misc_functions.h b/common/misc_functions.h index 16191916f..d5d90d98f 100644 --- a/common/misc_functions.h +++ b/common/misc_functions.h @@ -73,7 +73,7 @@ uint32 SwapBits21And22(uint32 mask); uint32 Catch22(uint32 mask); // macro to catch fp errors (provided by noudness) -#define FCMP(a,b) (fabs(a-b) < FLT_EPSILON) +#define FCMP(a,b) (std::abs(a-b) < FLT_EPSILON) #define _ITOA_BUFLEN 25 const char *itoa(int num); //not thread safe diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 4364c9cb1..cb8eddced 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -45,18 +45,20 @@ namespace RoF static OpcodeManager *opcodes = nullptr; static Strategy struct_strategy; - void SerializeItem(EQEmu::OutBuffer& ob, const EQEmu::ItemInstance *inst, int16 slot_id, uint8 depth); + void SerializeItem(EQEmu::OutBuffer& ob, const EQEmu::ItemInstance *inst, int16 slot_id, uint8 depth, ItemPacketType packet_type); // server to client inventory location converters - static inline structs::InventorySlot_Struct ServerToRoFSlot(uint32 serverSlot); - static inline structs::TypelessInventorySlot_Struct ServerToRoFTypelessSlot(uint32 serverSlot); - static inline uint32 ServerToRoFCorpseSlot(uint32 serverCorpseSlot); - + static inline structs::InventorySlot_Struct ServerToRoFSlot(uint32 server_slot); + static inline structs::InventorySlot_Struct ServerToRoFCorpseSlot(uint32 server_corpse_slot); + static inline uint32 ServerToRoFCorpseMainSlot(uint32 server_corpse_slot); + static inline structs::TypelessInventorySlot_Struct ServerToRoFTypelessSlot(uint32 server_slot, int16 server_type); + // client to server inventory location converters - static inline uint32 RoFToServerSlot(structs::InventorySlot_Struct rofSlot); - static inline uint32 RoFToServerTypelessSlot(structs::TypelessInventorySlot_Struct rofSlot); - static inline uint32 RoFToServerCorpseSlot(uint32 rofCorpseSlot); - + static inline uint32 RoFToServerSlot(structs::InventorySlot_Struct rof_slot); + static inline uint32 RoFToServerCorpseSlot(structs::InventorySlot_Struct rof_corpse_slot); + static inline uint32 RoFToServerCorpseMainSlot(uint32 rof_corpse_slot); + static inline uint32 RoFToServerTypelessSlot(structs::TypelessInventorySlot_Struct rof_slot, int16 rof_type); + // server to client say link converter static inline void ServerToRoFSayLink(std::string& rofSayLink, const std::string& serverSayLink); @@ -191,7 +193,7 @@ namespace RoF eq->unknown000 = 1; OUT(npcid); - eq->inventory_slot = ServerToRoFTypelessSlot(emu->slot); + eq->inventory_slot = ServerToRoFTypelessSlot(emu->slot, EQEmu::invtype::typePossessions); OUT(charges); OUT(sell_price); @@ -244,7 +246,7 @@ namespace RoF SETUP_DIRECT_ENCODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); OUT(merchant_entity_id); - eq->inventory_slot = ServerToRoFTypelessSlot(emu->slot_id); + eq->inventory_slot = ServerToRoFTypelessSlot(emu->slot_id, EQEmu::invtype::typePossessions); OUT(charges); OUT(cost); @@ -268,7 +270,7 @@ namespace RoF ENCODE_LENGTH_EXACT(ApplyPoison_Struct); SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); - eq->inventory_slot = ServerToRoFTypelessSlot(emu->inventorySlot); + eq->inventory_slot = ServerToRoFTypelessSlot(emu->inventorySlot, EQEmu::invtype::typePossessions); OUT(success); FINISH_ENCODE(); @@ -585,9 +587,9 @@ namespace RoF ob.write((const char*)&item_count, sizeof(uint32)); for (int index = 0; index < item_count; ++index, ++eq) { - SerializeItem(ob, (const EQEmu::ItemInstance*)eq->inst, eq->slot_id, 0); + SerializeItem(ob, (const EQEmu::ItemInstance*)eq->inst, eq->slot_id, 0, ItemPacketCharInventory); if (ob.tellp() == last_pos) - Log(Logs::General, Logs::Netcode, "[STRUCTS] Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + Log(Logs::General, Logs::Netcode, "RoF::ENCODE(OP_CharInventory) Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); last_pos = ob.tellp(); } @@ -667,7 +669,12 @@ namespace RoF FINISH_ENCODE(); } - ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } + ENCODE(OP_DeleteCharge) + { + Log(Logs::Moderate, Logs::Netcode, "RoF::ENCODE(OP_DeleteCharge)"); + + ENCODE_FORWARD(OP_MoveItem); + } ENCODE(OP_DeleteItem) { @@ -1504,6 +1511,7 @@ namespace RoF //store away the emu struct uchar* __emu_buffer = in->pBuffer; + ItemPacket_Struct* old_item_pkt = (ItemPacket_Struct*)__emu_buffer; EQEmu::InternalSerializedItem_Struct* int_struct = (EQEmu::InternalSerializedItem_Struct*)(&__emu_buffer[4]); EQEmu::OutBuffer ob; @@ -1511,9 +1519,9 @@ namespace RoF ob.write((const char*)__emu_buffer, 4); - SerializeItem(ob, (const EQEmu::ItemInstance*)int_struct->inst, int_struct->slot_id, 0); + SerializeItem(ob, (const EQEmu::ItemInstance*)int_struct->inst, int_struct->slot_id, 0, old_item_pkt->PacketType); if (ob.tellp() == last_pos) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Serialization failed on item slot %d.", int_struct->slot_id); + Log(Logs::General, Logs::Netcode, "RoF::ENCODE(OP_ItemPacket) Serialization failed on item slot %d.", int_struct->slot_id); delete in; return; } @@ -1586,9 +1594,11 @@ namespace RoF ENCODE_LENGTH_EXACT(LootingItem_Struct); SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); + Log(Logs::Moderate, Logs::Netcode, "RoF::ENCODE(OP_LootItem)"); + OUT(lootee); OUT(looter); - eq->slot_id = ServerToRoFCorpseSlot(emu->slot_id); + eq->slot_id = ServerToRoFCorpseMainSlot(emu->slot_id); OUT(auto_loot); FINISH_ENCODE(); @@ -1756,6 +1766,8 @@ namespace RoF ENCODE_LENGTH_EXACT(MoveItem_Struct); SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); + Log(Logs::Moderate, Logs::Netcode, "RoF::ENCODE(OP_MoveItem)"); + eq->from_slot = ServerToRoFSlot(emu->from_slot); eq->to_slot = ServerToRoFSlot(emu->to_slot); OUT(number_in_stack); @@ -2226,7 +2238,7 @@ namespace RoF outapp->WriteUInt32(profile::BANDOLIERS_SIZE); - // Copy bandoliers where server and client indexes converge + // Copy bandoliers where server and client indices converge for (uint32 r = 0; r < EQEmu::profile::BANDOLIERS_SIZE && r < profile::BANDOLIERS_SIZE; ++r) { outapp->WriteString(emu->bandoliers[r].Name); for (uint32 j = 0; j < profile::BANDOLIER_ITEM_COUNT; ++j) { // Will need adjusting if 'server != client' is ever true @@ -2241,7 +2253,7 @@ namespace RoF } } } - // Nullify bandoliers where server and client indexes diverge, with a client bias + // Nullify bandoliers where server and client indices diverge, with a client bias for (uint32 r = EQEmu::profile::BANDOLIERS_SIZE; r < profile::BANDOLIERS_SIZE; ++r) { outapp->WriteString(""); for (uint32 j = 0; j < profile::BANDOLIER_ITEM_COUNT; ++j) { // Will need adjusting if 'server != client' is ever true @@ -2253,7 +2265,7 @@ namespace RoF outapp->WriteUInt32(profile::POTION_BELT_SIZE); - // Copy potion belt where server and client indexes converge + // Copy potion belt where server and client indices converge for (uint32 r = 0; r < EQEmu::profile::POTION_BELT_SIZE && r < profile::POTION_BELT_SIZE; ++r) { outapp->WriteString(emu->potionbelt.Items[r].Name); outapp->WriteUInt32(emu->potionbelt.Items[r].ID); @@ -2265,7 +2277,7 @@ namespace RoF outapp->WriteSInt32(-1); } } - // Nullify potion belt where server and client indexes diverge, with a client bias + // Nullify potion belt where server and client indices diverge, with a client bias for (uint32 r = EQEmu::profile::POTION_BELT_SIZE; r < profile::POTION_BELT_SIZE; ++r) { outapp->WriteString(""); outapp->WriteUInt32(0); @@ -2795,7 +2807,7 @@ namespace RoF std::vector skill; std::vector points; - for(auto i = 0; i < emu->total_prereqs; ++i) { + for(auto i = 0u; i < emu->total_prereqs; ++i) { skill.push_back(inapp->ReadUInt32()); points.push_back(inapp->ReadUInt32()); } @@ -2849,7 +2861,7 @@ namespace RoF outapp->WriteUInt32(emu->total_effects); inapp->SetReadPosition(sizeof(AARankInfo_Struct)); - for(auto i = 0; i < emu->total_effects; ++i) { + for(auto i = 0u; i < emu->total_effects; ++i) { outapp->WriteUInt32(inapp->ReadUInt32()); // skill_id outapp->WriteUInt32(inapp->ReadUInt32()); // base1 outapp->WriteUInt32(inapp->ReadUInt32()); // base2 @@ -2905,7 +2917,7 @@ namespace RoF unsigned char *eq_ptr = __packet->pBuffer; eq_ptr += sizeof(structs::CharacterSelect_Struct); - for (int counter = 0; counter < character_count; ++counter) { + for (auto counter = 0u; counter < character_count; ++counter) { emu_cse = (CharacterSelectEntry_Struct *)emu_ptr; eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; // base address @@ -3041,7 +3053,7 @@ namespace RoF SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); OUT(npcid); - eq->inventory_slot = ServerToRoFTypelessSlot(emu->itemslot); + eq->inventory_slot = ServerToRoFTypelessSlot(emu->itemslot, EQEmu::invtype::typePossessions); //OUT(itemslot); OUT(quantity); OUT(price); @@ -3247,7 +3259,7 @@ namespace RoF InBuffer += title_size; TaskDescriptionData1_Struct *emu_tdd1 = (TaskDescriptionData1_Struct *)InBuffer; - emu_tdd1->StartTime = (time(nullptr) - emu_tdd1->StartTime); // RoF has elapsed time here rather than start time + emu_tdd1->StartTime = (static_cast(time(nullptr)) - emu_tdd1->StartTime); // RoF has elapsed time here rather than start time InBuffer += sizeof(TaskDescriptionData1_Struct); uint32 description_size = strlen(InBuffer) + 1; @@ -3598,10 +3610,10 @@ namespace RoF // calculate size of names, note the packet DOES NOT have null termed c-strings std::vector name_lengths; - for (int i = 0; i < count; ++i) { + for (auto i = 0u; i < count; ++i) { InternalVeteranReward *ivr = (InternalVeteranReward *)__emu_buffer; - for (int i = 0; i < ivr->claim_count; i++) { + for (auto i = 0u; i < ivr->claim_count; i++) { uint32 length = strnlen(ivr->items[i].item_name, 63); if (length) name_lengths.push_back(length); @@ -3621,7 +3633,7 @@ namespace RoF outapp->WriteUInt32(count); auto name_itr = name_lengths.begin(); - for (int i = 0; i < count; i++) { + for (auto i = 0u; i < count; i++) { InternalVeteranReward *ivr = (InternalVeteranReward *)__emu_buffer; outapp->WriteUInt32(ivr->claim_id); @@ -3629,7 +3641,7 @@ namespace RoF outapp->WriteUInt32(ivr->claim_count); outapp->WriteUInt8(1); // enabled - for (int j = 0; j < ivr->claim_count; j++) { + for (auto j = 0u; j < ivr->claim_count; j++) { assert(name_itr != name_lengths.end()); // the way it's written, it should never happen, so just assert outapp->WriteUInt32(*name_itr); outapp->WriteData(ivr->items[j].item_name, *name_itr); @@ -3857,7 +3869,7 @@ namespace RoF VARSTRUCT_ENCODE_STRING(Buffer, emu->name); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spawnId); VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level); - VARSTRUCT_ENCODE_TYPE(float, Buffer, SpawnSize - 0.7); // Eye Height? + VARSTRUCT_ENCODE_TYPE(float, Buffer, SpawnSize - 0.7f); // Eye Height? VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); structs::Spawn_Struct_Bitfields *Bitfields = (structs::Spawn_Struct_Bitfields*)Buffer; @@ -4057,7 +4069,7 @@ namespace RoF SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); IN(npcid); - emu->slot = RoFToServerTypelessSlot(eq->inventory_slot); + emu->slot = RoFToServerTypelessSlot(eq->inventory_slot, invtype::typePossessions); IN(charges); IN(sell_price); @@ -4070,7 +4082,7 @@ namespace RoF SETUP_DIRECT_DECODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); IN(merchant_entity_id); - emu->slot_id = RoFToServerTypelessSlot(eq->inventory_slot); + emu->slot_id = RoFToServerTypelessSlot(eq->inventory_slot, invtype::typePossessions); IN(charges); IN(cost); @@ -4083,7 +4095,7 @@ namespace RoF SETUP_DIRECT_DECODE(AltCurrencySelectItem_Struct, structs::AltCurrencySelectItem_Struct); IN(merchant_entity_id); - emu->slot_id = RoFToServerTypelessSlot(eq->inventory_slot); + emu->slot_id = RoFToServerTypelessSlot(eq->inventory_slot, invtype::typePossessions); FINISH_DIRECT_DECODE(); } @@ -4105,7 +4117,7 @@ namespace RoF DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); - emu->inventorySlot = RoFToServerTypelessSlot(eq->inventory_slot); + emu->inventorySlot = RoFToServerTypelessSlot(eq->inventory_slot, invtype::typePossessions); IN(success); FINISH_DIRECT_DECODE(); @@ -4741,9 +4753,11 @@ namespace RoF DECODE_LENGTH_EXACT(structs::LootingItem_Struct); SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); + Log(Logs::Moderate, Logs::Netcode, "RoF::DECODE(OP_LootItem)"); + IN(lootee); IN(looter); - emu->slot_id = RoFToServerCorpseSlot(eq->slot_id); + emu->slot_id = RoFToServerCorpseMainSlot(eq->slot_id); IN(auto_loot); FINISH_DIRECT_DECODE(); @@ -4754,8 +4768,8 @@ namespace RoF DECODE_LENGTH_EXACT(structs::MoveItem_Struct); SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); - //Log.LogDebugType(Logs::General, Logs::Netcode, "[ERROR] Moved item from %u to %u", eq->from_slot.MainSlot, eq->to_slot.MainSlot); - Log(Logs::General, Logs::Netcode, "[RoF] MoveItem SlotType from %i to %i, MainSlot from %i to %i, SubSlot from %i to %i, AugSlot from %i to %i, Unknown01 from %i to %i, Number %u", eq->from_slot.Type, eq->to_slot.Type, eq->from_slot.Slot, eq->to_slot.Slot, eq->from_slot.SubIndex, eq->to_slot.SubIndex, eq->from_slot.AugIndex, eq->to_slot.AugIndex, eq->from_slot.Unknown01, eq->to_slot.Unknown01, eq->number_in_stack); + Log(Logs::Moderate, Logs::Netcode, "RoF::DECODE(OP_MoveItem)"); + emu->from_slot = RoFToServerSlot(eq->from_slot); emu->to_slot = RoFToServerSlot(eq->to_slot); IN(number_in_stack); @@ -4904,7 +4918,7 @@ namespace RoF SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); IN(npcid); - emu->itemslot = RoFToServerTypelessSlot(eq->inventory_slot); + emu->itemslot = RoFToServerTypelessSlot(eq->inventory_slot, invtype::typePossessions); //IN(itemslot); IN(quantity); IN(price); @@ -4988,11 +5002,7 @@ namespace RoF DECODE_LENGTH_EXACT(structs::NewCombine_Struct); SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); - int16 slot_id = RoFToServerSlot(eq->container_slot); - if (slot_id == 4000) { - slot_id = EQEmu::legacy::SLOT_TRADESKILL; // 1000 - } - emu->container_slot = slot_id; + emu->container_slot = RoFToServerSlot(eq->container_slot); emu->guildtribute_slot = RoFToServerSlot(eq->guildtribute_slot); // this should only return INVALID_INDEX until implemented FINISH_DIRECT_DECODE(); @@ -5079,7 +5089,7 @@ namespace RoF return NextItemInstSerialNumber; } - void SerializeItem(EQEmu::OutBuffer& ob, const EQEmu::ItemInstance *inst, int16 slot_id_in, uint8 depth) + void SerializeItem(EQEmu::OutBuffer& ob, const EQEmu::ItemInstance *inst, int16 slot_id_in, uint8 depth, ItemPacketType packet_type) { const EQEmu::ItemData *item = inst->GetUnscaledItem(); @@ -5092,8 +5102,16 @@ namespace RoF hdr.stacksize = (inst->IsStackable() ? ((inst->GetCharges() > 1000) ? 0xFFFFFFFF : inst->GetCharges()) : 1); hdr.unknown004 = 0; - structs::InventorySlot_Struct slot_id = ServerToRoFSlot(slot_id_in); - + structs::InventorySlot_Struct slot_id; + switch (packet_type) { + case ItemPacketLoot: + slot_id = ServerToRoFCorpseSlot(slot_id_in); + break; + default: + slot_id = ServerToRoFSlot(slot_id_in); + break; + } + hdr.slot_type = (inst->GetMerchantSlot() ? invtype::typeMerchant : slot_id.Type); hdr.main_slot = (inst->GetMerchantSlot() ? inst->GetMerchantSlot() : slot_id.Slot); hdr.sub_slot = (inst->GetMerchantSlot() ? 0xffff : slot_id.SubIndex); @@ -5188,7 +5206,7 @@ namespace RoF ibs.nodrop = item->NoDrop; ibs.attune = item->Attuneable; ibs.size = item->Size; - ibs.slots = SwapBits21And22(item->Slots); + ibs.slots = item->Slots; ibs.price = item->Price; ibs.icon = item->Icon; ibs.unknown1 = 1; @@ -5492,318 +5510,504 @@ namespace RoF ob.write((const char*)&subitem_count, sizeof(uint32)); - for (uint32 index = EQEmu::invbag::SLOT_BEGIN; index <= EQEmu::invbag::SLOT_END; ++index) { - EQEmu::ItemInstance* sub = inst->GetItem(index); - if (!sub) - continue; + // moved outside of loop since it is not modified within that scope + int16 SubSlotNumber = EQEmu::invbag::SLOT_INVALID; - int SubSlotNumber = INVALID_INDEX; - if (slot_id_in >= EQEmu::invslot::GENERAL_BEGIN && slot_id_in <= EQEmu::invslot::GENERAL_END) - SubSlotNumber = (((slot_id_in + 3) * EQEmu::invbag::SLOT_COUNT) + index + 1); - else if (slot_id_in >= EQEmu::invslot::BANK_BEGIN && slot_id_in <= EQEmu::invslot::BANK_END) - SubSlotNumber = (((slot_id_in - EQEmu::invslot::BANK_BEGIN) * EQEmu::invbag::SLOT_COUNT) + EQEmu::invbag::BANK_BAGS_BEGIN + index); - else if (slot_id_in >= EQEmu::invslot::SHARED_BANK_BEGIN && slot_id_in <= EQEmu::invslot::SHARED_BANK_END) - SubSlotNumber = (((slot_id_in - EQEmu::invslot::SHARED_BANK_BEGIN) * EQEmu::invbag::SLOT_COUNT) + EQEmu::invbag::SHARED_BANK_BAGS_BEGIN + index); - else - SubSlotNumber = slot_id_in; + if (slot_id_in <= EQEmu::invslot::GENERAL_END && slot_id_in >= EQEmu::invslot::GENERAL_BEGIN) + SubSlotNumber = EQEmu::invbag::GENERAL_BAGS_BEGIN + ((slot_id_in - EQEmu::invslot::GENERAL_BEGIN) * EQEmu::invbag::SLOT_COUNT); + else if (slot_id_in == EQEmu::invslot::slotCursor) + SubSlotNumber = EQEmu::invbag::CURSOR_BAG_BEGIN; + else if (slot_id_in <= EQEmu::invslot::BANK_END && slot_id_in >= EQEmu::invslot::BANK_BEGIN) + SubSlotNumber = EQEmu::invbag::BANK_BAGS_BEGIN + ((slot_id_in - EQEmu::invslot::BANK_BEGIN) * EQEmu::invbag::SLOT_COUNT); + else if (slot_id_in <= EQEmu::invslot::SHARED_BANK_END && slot_id_in >= EQEmu::invslot::SHARED_BANK_BEGIN) + SubSlotNumber = EQEmu::invbag::SHARED_BANK_BAGS_BEGIN + ((slot_id_in - EQEmu::invslot::SHARED_BANK_BEGIN) * EQEmu::invbag::SLOT_COUNT); + else + SubSlotNumber = slot_id_in; // not sure if this is the best way to handle this..leaving for now - ob.write((const char*)&index, sizeof(uint32)); + if (SubSlotNumber != EQEmu::invbag::SLOT_INVALID) { + for (uint32 index = EQEmu::invbag::SLOT_BEGIN; index <= EQEmu::invbag::SLOT_END; ++index) { + EQEmu::ItemInstance* sub = inst->GetItem(index); + if (!sub) + continue; - SerializeItem(ob, sub, SubSlotNumber, (depth + 1)); - ++subitem_count; + ob.write((const char*)&index, sizeof(uint32)); + + SerializeItem(ob, sub, SubSlotNumber, (depth + 1), packet_type); + ++subitem_count; + } + + if (subitem_count) + ob.overwrite(count_pos, (const char*)&subitem_count, sizeof(uint32)); } - - if (subitem_count) - ob.overwrite(count_pos, (const char*)&subitem_count, sizeof(uint32)); } - static inline structs::InventorySlot_Struct ServerToRoFSlot(uint32 serverSlot) + static inline structs::InventorySlot_Struct ServerToRoFSlot(uint32 server_slot) { - structs::InventorySlot_Struct RoFSlot; - RoFSlot.Type = INVALID_INDEX; - RoFSlot.Unknown02 = 0; - RoFSlot.Slot = INVALID_INDEX; - RoFSlot.SubIndex = INVALID_INDEX; - RoFSlot.AugIndex = INVALID_INDEX; - RoFSlot.Unknown01 = 0; + structs::InventorySlot_Struct RoFSlot{}; - uint32 TempSlot = 0; + RoFSlot.Type = invtype::TYPE_INVALID; + RoFSlot.Unknown02 = INULL; + RoFSlot.Slot = invslot::SLOT_INVALID; + RoFSlot.SubIndex = invbag::SLOT_INVALID; + RoFSlot.AugIndex = invaug::SOCKET_INVALID; + RoFSlot.Unknown01 = INULL; - if (serverSlot < 56 || serverSlot == EQEmu::invslot::SLOT_POWER_SOURCE) { // Main Inventory and Cursor + uint32 temp_slot = (uint32) EQEmu::invslot::SLOT_INVALID; + + if (server_slot < EQEmu::invtype::POSSESSIONS_SIZE) { RoFSlot.Type = invtype::typePossessions; - RoFSlot.Slot = serverSlot; - - if (serverSlot == EQEmu::invslot::SLOT_POWER_SOURCE) - RoFSlot.Slot = invslot::slotPowerSource; - - else if (serverSlot >= EQEmu::invslot::slotCursor) // Cursor and Extended Corpse Inventory - RoFSlot.Slot += 3; - - else if (serverSlot >= EQEmu::invslot::slotAmmo) // (> 20) - RoFSlot.Slot += 1; + RoFSlot.Slot = server_slot; } - /*else if (ServerSlot < 51) { // Cursor Buffer - RoFSlot.SlotType = maps::MapLimbo; - RoFSlot.MainSlot = ServerSlot - 31; - }*/ + else if (server_slot <= EQEmu::invbag::CURSOR_BAG_END && server_slot >= EQEmu::invbag::GENERAL_BAGS_BEGIN) { + temp_slot = server_slot - EQEmu::invbag::GENERAL_BAGS_BEGIN; - else if (serverSlot >= EQEmu::invbag::GENERAL_BAGS_BEGIN && serverSlot <= EQEmu::invbag::CURSOR_BAG_END) { // (> 250 && < 341) - RoFSlot.Type = invtype::typePossessions; - TempSlot = serverSlot - 1; - RoFSlot.Slot = int(TempSlot / EQEmu::invbag::SLOT_COUNT) - 2; - RoFSlot.SubIndex = TempSlot - ((RoFSlot.Slot + 2) * EQEmu::invbag::SLOT_COUNT); - - if (RoFSlot.Slot >= invslot::slotGeneral9) // (> 30) - RoFSlot.Slot = invslot::slotCursor; + RoFSlot.Type = invtype::typePossessions; + RoFSlot.Slot = invslot::GENERAL_BEGIN + (temp_slot / EQEmu::invbag::SLOT_COUNT); + RoFSlot.SubIndex = temp_slot - ((RoFSlot.Slot - invslot::GENERAL_BEGIN) * EQEmu::invbag::SLOT_COUNT); } - else if (serverSlot >= EQEmu::invslot::TRIBUTE_BEGIN && serverSlot <= EQEmu::invslot::TRIBUTE_END) { // Tribute + else if (server_slot <= EQEmu::invslot::TRIBUTE_END && server_slot >= EQEmu::invslot::TRIBUTE_BEGIN) { RoFSlot.Type = invtype::typeTribute; - RoFSlot.Slot = serverSlot - EQEmu::invslot::TRIBUTE_BEGIN; + RoFSlot.Slot = server_slot - EQEmu::invslot::TRIBUTE_BEGIN; } - else if (serverSlot >= EQEmu::invslot::BANK_BEGIN && serverSlot <= EQEmu::invbag::BANK_BAGS_END) { - RoFSlot.Type = invtype::typeBank; - TempSlot = serverSlot - EQEmu::invslot::BANK_BEGIN; - RoFSlot.Slot = TempSlot; - - if (TempSlot > 30) { // (> 30) - RoFSlot.Slot = int(TempSlot / EQEmu::invbag::SLOT_COUNT) - 3; - RoFSlot.SubIndex = TempSlot - ((RoFSlot.Slot + 3) * EQEmu::invbag::SLOT_COUNT); - } + else if (server_slot <= EQEmu::invslot::GUILD_TRIBUTE_END && server_slot >= EQEmu::invslot::GUILD_TRIBUTE_BEGIN) { + RoFSlot.Type = invtype::typeGuildTribute; + RoFSlot.Slot = server_slot - EQEmu::invslot::GUILD_TRIBUTE_BEGIN; } - else if (serverSlot >= EQEmu::invslot::SHARED_BANK_BEGIN && serverSlot <= EQEmu::invbag::SHARED_BANK_BAGS_END) { - RoFSlot.Type = invtype::typeSharedBank; - TempSlot = serverSlot - EQEmu::invslot::SHARED_BANK_BEGIN; - RoFSlot.Slot = TempSlot; - - if (TempSlot > 30) { // (> 30) - RoFSlot.Slot = int(TempSlot / EQEmu::invbag::SLOT_COUNT) - 3; - RoFSlot.SubIndex = TempSlot - ((RoFSlot.Slot + 3) * EQEmu::invbag::SLOT_COUNT); - } - } - - else if (serverSlot >= EQEmu::invslot::TRADE_BEGIN && serverSlot <= EQEmu::invbag::TRADE_BAGS_END) { - RoFSlot.Type = invtype::typeTrade; - TempSlot = serverSlot - EQEmu::invslot::TRADE_BEGIN; - RoFSlot.Slot = TempSlot; - - if (TempSlot > 30) { - RoFSlot.Slot = int(TempSlot / EQEmu::invbag::SLOT_COUNT) - 3; - RoFSlot.SubIndex = TempSlot - ((RoFSlot.Slot + 3) * EQEmu::invbag::SLOT_COUNT); - } - - /* - // OLD CODE: - if (TempSlot > 99) { - if (TempSlot > 100) - RoFSlot.MainSlot = int((TempSlot - 100) / 10); - - else - RoFSlot.MainSlot = 0; - - RoFSlot.SubSlot = TempSlot - (100 + RoFSlot.MainSlot); - } - */ - } - - else if (serverSlot >= EQEmu::invslot::WORLD_BEGIN && serverSlot <= EQEmu::invslot::WORLD_END) { + else if (server_slot == EQEmu::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE) { RoFSlot.Type = invtype::typeWorld; - TempSlot = serverSlot - EQEmu::invslot::WORLD_BEGIN; - RoFSlot.Slot = TempSlot; } - Log(Logs::General, Logs::Netcode, "[ERROR] Convert Server Slot %i to RoF Slots: Type %i, Unk2 %i, Main %i, Sub %i, Aug %i, Unk1 %i", serverSlot, RoFSlot.Type, RoFSlot.Unknown02, RoFSlot.Slot, RoFSlot.SubIndex, RoFSlot.AugIndex, RoFSlot.Unknown01); + else if (server_slot <= EQEmu::invslot::BANK_END && server_slot >= EQEmu::invslot::BANK_BEGIN) { + RoFSlot.Type = invtype::typeBank; + RoFSlot.Slot = server_slot - EQEmu::invslot::BANK_BEGIN; + } + + else if (server_slot <= EQEmu::invbag::BANK_BAGS_END && server_slot >= EQEmu::invbag::BANK_BAGS_BEGIN) { + temp_slot = server_slot - EQEmu::invbag::BANK_BAGS_BEGIN; + + RoFSlot.Type = invtype::typeBank; + RoFSlot.Slot = temp_slot / EQEmu::invbag::SLOT_COUNT; + RoFSlot.SubIndex = temp_slot - (RoFSlot.Slot * EQEmu::invbag::SLOT_COUNT); + } + + else if (server_slot <= EQEmu::invslot::SHARED_BANK_END && server_slot >= EQEmu::invslot::SHARED_BANK_BEGIN) { + RoFSlot.Type = invtype::typeSharedBank; + RoFSlot.Slot = server_slot - EQEmu::invslot::SHARED_BANK_BEGIN; + } + + else if (server_slot <= EQEmu::invbag::SHARED_BANK_BAGS_END && server_slot >= EQEmu::invbag::SHARED_BANK_BAGS_BEGIN) { + temp_slot = server_slot - EQEmu::invbag::SHARED_BANK_BAGS_BEGIN; + + RoFSlot.Type = invtype::typeSharedBank; + RoFSlot.Slot = temp_slot / EQEmu::invbag::SLOT_COUNT; + RoFSlot.SubIndex = temp_slot - (RoFSlot.Slot * EQEmu::invbag::SLOT_COUNT); + } + + else if (server_slot <= EQEmu::invslot::TRADE_END && server_slot >= EQEmu::invslot::TRADE_BEGIN) { + RoFSlot.Type = invtype::typeTrade; + RoFSlot.Slot = server_slot - EQEmu::invslot::TRADE_BEGIN; + } + + else if (server_slot <= EQEmu::invbag::TRADE_BAGS_END && server_slot >= EQEmu::invbag::TRADE_BAGS_BEGIN) { + temp_slot = server_slot - EQEmu::invbag::TRADE_BAGS_BEGIN; + + RoFSlot.Type = invtype::typeTrade; + RoFSlot.Slot = temp_slot / EQEmu::invbag::SLOT_COUNT; + RoFSlot.SubIndex = temp_slot - (RoFSlot.Slot * EQEmu::invbag::SLOT_COUNT); + } + + else if (server_slot <= EQEmu::invslot::WORLD_END && server_slot >= EQEmu::invslot::WORLD_BEGIN) { + RoFSlot.Type = invtype::typeWorld; + RoFSlot.Slot = server_slot - EQEmu::invslot::WORLD_BEGIN; + } + + Log(Logs::Detail, + Logs::Netcode, + "Convert Server Slot %i to RoF Slot [%i, %i, %i, %i] (unk2: %i, unk1: %i)", + server_slot, + RoFSlot.Type, + RoFSlot.Slot, + RoFSlot.SubIndex, + RoFSlot.AugIndex, + RoFSlot.Unknown02, + RoFSlot.Unknown01); return RoFSlot; } - static inline structs::TypelessInventorySlot_Struct ServerToRoFTypelessSlot(uint32 serverSlot) + static inline structs::InventorySlot_Struct ServerToRoFCorpseSlot(uint32 server_corpse_slot) + { + structs::InventorySlot_Struct RoFSlot{}; + + RoFSlot.Type = invtype::TYPE_INVALID; + RoFSlot.Unknown02 = INULL; + RoFSlot.Slot = static_cast(ServerToRoFCorpseMainSlot(server_corpse_slot)); + RoFSlot.SubIndex = invbag::SLOT_INVALID; + RoFSlot.AugIndex = invaug::SOCKET_INVALID; + RoFSlot.Unknown01 = INULL; + + if (RoFSlot.Slot != invslot::SLOT_INVALID) + RoFSlot.Type = invtype::typeCorpse; + + Log(Logs::Detail, + Logs::Netcode, + "Convert Server Corpse Slot %i to RoF Corpse Slot [%i, %i, %i, %i] (unk2: %i, unk1: %i)", + server_corpse_slot, + RoFSlot.Type, + RoFSlot.Slot, + RoFSlot.SubIndex, + RoFSlot.AugIndex, + RoFSlot.Unknown02, + RoFSlot.Unknown01); + + return RoFSlot; + } + + static inline uint32 ServerToRoFCorpseMainSlot(uint32 server_corpse_slot) + { + uint32 RoFSlot = invslot::SLOT_INVALID; + + if (server_corpse_slot <= EQEmu::invslot::CORPSE_END && server_corpse_slot >= EQEmu::invslot::CORPSE_BEGIN) { + RoFSlot = server_corpse_slot; + } + + Log(Logs::Detail, Logs::Netcode, "Convert Server Corpse Slot %i to RoF Corpse Main Slot %i", server_corpse_slot, RoFSlot); + + return RoFSlot; + } + + static inline structs::TypelessInventorySlot_Struct ServerToRoFTypelessSlot(uint32 server_slot, int16 server_type) { structs::TypelessInventorySlot_Struct RoFSlot; - RoFSlot.Slot = INVALID_INDEX; - RoFSlot.SubIndex = INVALID_INDEX; - RoFSlot.AugIndex = INVALID_INDEX; - RoFSlot.Unknown01 = 0; + RoFSlot.Slot = invslot::SLOT_INVALID; + RoFSlot.SubIndex = invbag::SLOT_INVALID; + RoFSlot.AugIndex = invaug::SOCKET_INVALID; + RoFSlot.Unknown01 = INULL; - uint32 TempSlot = 0; + uint32 TempSlot = EQEmu::invslot::SLOT_INVALID; - if (serverSlot < 56 || serverSlot == EQEmu::invslot::SLOT_POWER_SOURCE) { // (< 52) - RoFSlot.Slot = serverSlot; + if (server_type == EQEmu::invtype::typePossessions) { + if (server_slot < EQEmu::invtype::POSSESSIONS_SIZE) { + RoFSlot.Slot = server_slot; + } - if (serverSlot == EQEmu::invslot::SLOT_POWER_SOURCE) - RoFSlot.Slot = invslot::slotPowerSource; + else if (server_slot <= EQEmu::invbag::CURSOR_BAG_END && server_slot >= EQEmu::invbag::GENERAL_BAGS_BEGIN) { + TempSlot = server_slot - EQEmu::invbag::GENERAL_BAGS_BEGIN; - else if (serverSlot >= EQEmu::invslot::slotCursor) // Cursor and Extended Corpse Inventory - RoFSlot.Slot += 3; - - else if (serverSlot >= EQEmu::invslot::slotAmmo) // Ammo and Personl Inventory - RoFSlot.Slot += 1; - - /*else if (ServerSlot >= MainCursor) { // Cursor - RoFSlot.MainSlot = slots::MainCursor; - - if (ServerSlot > 30) - RoFSlot.SubSlot = (ServerSlot + 3) - 33; - }*/ + RoFSlot.Slot = invslot::GENERAL_BEGIN + (TempSlot / EQEmu::invbag::SLOT_COUNT); + RoFSlot.SubIndex = TempSlot - ((RoFSlot.Slot - invslot::GENERAL_BEGIN) * EQEmu::invbag::SLOT_COUNT); + } } - else if (serverSlot >= EQEmu::invbag::GENERAL_BAGS_BEGIN && serverSlot <= EQEmu::invbag::CURSOR_BAG_END) { - TempSlot = serverSlot - 1; - RoFSlot.Slot = int(TempSlot / EQEmu::invbag::SLOT_COUNT) - 2; - RoFSlot.SubIndex = TempSlot - ((RoFSlot.Slot + 2) * EQEmu::invbag::SLOT_COUNT); - } - - Log(Logs::General, Logs::Netcode, "[ERROR] Convert Server Slot %i to RoF Slots: Main %i, Sub %i, Aug %i, Unk1 %i", serverSlot, RoFSlot.Slot, RoFSlot.SubIndex, RoFSlot.AugIndex, RoFSlot.Unknown01); + Log(Logs::Detail, Logs::Netcode, "Convert Server Slot %i to RoF Typeless Slot [%i, %i, %i] (implied type: %i, unk1: %i)", + server_slot, RoFSlot.Slot, RoFSlot.SubIndex, RoFSlot.AugIndex, server_type, RoFSlot.Unknown01); return RoFSlot; } - static inline uint32 ServerToRoFCorpseSlot(uint32 serverCorpseSlot) - { - return (serverCorpseSlot + 1); + static inline uint32 RoFToServerSlot(structs::InventorySlot_Struct rof_slot) { + if (rof_slot.AugIndex < invaug::SOCKET_INVALID || rof_slot.AugIndex >= invaug::SOCKET_COUNT) { + Log(Logs::Detail, + Logs::Netcode, + "Convert RoF Slot [%i, %i, %i, %i] (unk2: %i, unk1: %i) to Server Slot %i", + rof_slot.Type, + rof_slot.Slot, + rof_slot.SubIndex, + rof_slot.AugIndex, + rof_slot.Unknown02, + rof_slot.Unknown01, + EQEmu::invslot::SLOT_INVALID); + + return EQEmu::invslot::SLOT_INVALID; + } + + uint32 server_slot = EQEmu::invslot::SLOT_INVALID; + uint32 temp_slot = invslot::SLOT_INVALID; + + switch (rof_slot.Type) { + case invtype::typePossessions: { + if (rof_slot.Slot >= invslot::POSSESSIONS_BEGIN && rof_slot.Slot <= invslot::POSSESSIONS_END) { + if (rof_slot.SubIndex == invbag::SLOT_INVALID) { + server_slot = rof_slot.Slot; + } else if (rof_slot.SubIndex >= invbag::SLOT_BEGIN && rof_slot.SubIndex <= invbag::SLOT_END) { + if (rof_slot.Slot < invslot::GENERAL_BEGIN) + return EQEmu::invslot::SLOT_INVALID; + + temp_slot = (rof_slot.Slot - invslot::GENERAL_BEGIN) * invbag::SLOT_COUNT; + server_slot = EQEmu::invbag::GENERAL_BAGS_BEGIN + temp_slot + rof_slot.SubIndex; + } + } + + break; + } + case invtype::typeBank: { + if (rof_slot.Slot >= invslot::SLOT_BEGIN && rof_slot.Slot < invtype::BANK_SIZE) { + if (rof_slot.SubIndex == invbag::SLOT_INVALID) { + server_slot = EQEmu::invslot::BANK_BEGIN + rof_slot.Slot; + } else if (rof_slot.SubIndex >= invbag::SLOT_BEGIN && rof_slot.SubIndex <= invbag::SLOT_END) { + temp_slot = rof_slot.Slot * invbag::SLOT_COUNT; + server_slot = EQEmu::invbag::BANK_BAGS_BEGIN + temp_slot + rof_slot.SubIndex; + } + } + + break; + } + case invtype::typeSharedBank: { + if (rof_slot.Slot >= invslot::SLOT_BEGIN && rof_slot.Slot < invtype::SHARED_BANK_SIZE) { + if (rof_slot.SubIndex == invbag::SLOT_INVALID) { + server_slot = EQEmu::invslot::SHARED_BANK_BEGIN + rof_slot.Slot; + } else if (rof_slot.SubIndex >= invbag::SLOT_BEGIN && rof_slot.SubIndex <= invbag::SLOT_END) { + temp_slot = rof_slot.Slot * invbag::SLOT_COUNT; + server_slot = EQEmu::invbag::SHARED_BANK_BAGS_BEGIN + temp_slot + rof_slot.SubIndex; + } + } + + break; + } + case invtype::typeTrade: { + if (rof_slot.Slot >= invslot::SLOT_BEGIN && rof_slot.Slot < invtype::TRADE_SIZE) { + if (rof_slot.SubIndex == invbag::SLOT_INVALID) { + server_slot = EQEmu::invslot::TRADE_BEGIN + rof_slot.Slot; + } else if (rof_slot.SubIndex >= invbag::SLOT_BEGIN && rof_slot.SubIndex <= invbag::SLOT_END) { + temp_slot = rof_slot.Slot * invbag::SLOT_COUNT; + server_slot = EQEmu::invbag::TRADE_BAGS_BEGIN + temp_slot + rof_slot.SubIndex; + } + } + + break; + } + case invtype::typeWorld: { + if (rof_slot.Slot >= invslot::SLOT_BEGIN && rof_slot.Slot < invtype::WORLD_SIZE) { + server_slot = EQEmu::invslot::WORLD_BEGIN + rof_slot.Slot; + } + + else if (rof_slot.Slot == invslot::SLOT_INVALID) { + server_slot = EQEmu::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE; + } + + break; + } + case invtype::typeLimbo: { + if (rof_slot.Slot >= invslot::SLOT_BEGIN && rof_slot.Slot < invtype::LIMBO_SIZE) { + server_slot = EQEmu::invslot::slotCursor; + } + + break; + } + case invtype::typeTribute: { + if (rof_slot.Slot >= invslot::SLOT_BEGIN && rof_slot.Slot < invtype::TRIBUTE_SIZE) { + server_slot = EQEmu::invslot::TRIBUTE_BEGIN + rof_slot.Slot; + } + + break; + } + case invtype::typeGuildTribute: { + if (rof_slot.Slot >= invslot::SLOT_BEGIN && rof_slot.Slot < invtype::GUILD_TRIBUTE_SIZE) { + server_slot = EQEmu::invslot::GUILD_TRIBUTE_BEGIN + rof_slot.Slot; + } + + break; + } + case invtype::typeCorpse: { + if (rof_slot.Slot >= invslot::CORPSE_BEGIN && rof_slot.Slot <= invslot::CORPSE_END) { + server_slot = rof_slot.Slot; + } + + break; + } + default: { + + break; + } + } + + Log(Logs::Detail, + Logs::Netcode, + "Convert RoF Slot [%i, %i, %i, %i] (unk2: %i, unk1: %i) to Server Slot %i", + rof_slot.Type, + rof_slot.Slot, + rof_slot.SubIndex, + rof_slot.AugIndex, + rof_slot.Unknown02, + rof_slot.Unknown01, + server_slot); + + return server_slot; } - static inline uint32 RoFToServerSlot(structs::InventorySlot_Struct rofSlot) + static inline uint32 RoFToServerCorpseSlot(structs::InventorySlot_Struct rof_corpse_slot) { - uint32 ServerSlot = INVALID_INDEX; - uint32 TempSlot = 0; + uint32 server_slot = EQEmu::invslot::SLOT_INVALID; - if (rofSlot.Type == invtype::typePossessions && rofSlot.Slot < 57) { // Worn/Personal Inventory and Cursor (< 51) - if (rofSlot.Slot == invslot::slotPowerSource) - TempSlot = EQEmu::invslot::SLOT_POWER_SOURCE; - - else if (rofSlot.Slot >= invslot::slotCursor) // Cursor and Extended Corpse Inventory - TempSlot = rofSlot.Slot - 3; - - /*else if (RoFSlot.MainSlot == slots::MainGeneral9 || RoFSlot.MainSlot == slots::MainGeneral10) { // 9th and 10th RoF inventory/corpse slots - // Need to figure out what to do when we get these - - // The slot range of 0 - client_max is cross-utilized between player inventory and corpse inventory. - // In the case of RoF, player inventory is addressed as 0 - 33 and corpse inventory is addressed as 23 - 56. - // We 'could' assign the two new inventory slots as 9997 and 9998, and then work around their bag - // slot assignments, but doing so may disrupt our ability to utilize the corpse looting range properly. - - // For now, it's probably best to leave as-is and let this work itself out in the inventory rework. - }*/ - - else if (rofSlot.Slot >= invslot::slotAmmo) // Ammo and Main Inventory - TempSlot = rofSlot.Slot - 1; - - else // Worn Slots - TempSlot = rofSlot.Slot; - - if (rofSlot.SubIndex >= EQEmu::invbag::SLOT_BEGIN) // Bag Slots - TempSlot = ((TempSlot + 3) * EQEmu::invbag::SLOT_COUNT) + rofSlot.SubIndex + 1; - - ServerSlot = TempSlot; + if (rof_corpse_slot.Type != invtype::typeCorpse || rof_corpse_slot.SubIndex != invbag::SLOT_INVALID || rof_corpse_slot.AugIndex != invaug::SOCKET_INVALID) { + server_slot = EQEmu::invslot::SLOT_INVALID; } - else if (rofSlot.Type == invtype::typeBank) { - TempSlot = EQEmu::invslot::BANK_BEGIN; - - if (rofSlot.SubIndex >= EQEmu::invbag::SLOT_BEGIN) - TempSlot += ((rofSlot.Slot + 3) * EQEmu::invbag::SLOT_COUNT) + rofSlot.SubIndex + 1; - - else - TempSlot += rofSlot.Slot; - - ServerSlot = TempSlot; + else { + server_slot = RoFToServerCorpseMainSlot(rof_corpse_slot.Slot); } - else if (rofSlot.Type == invtype::typeSharedBank) { - TempSlot = EQEmu::invslot::SHARED_BANK_BEGIN; + Log(Logs::Detail, + Logs::Netcode, + "Convert RoF Slot [%i, %i, %i, %i] (unk2: %i, unk1: %i) to Server Slot %i", + rof_corpse_slot.Type, + rof_corpse_slot.Slot, + rof_corpse_slot.SubIndex, + rof_corpse_slot.AugIndex, + rof_corpse_slot.Unknown02, + rof_corpse_slot.Unknown01, + server_slot); - if (rofSlot.SubIndex >= EQEmu::invbag::SLOT_BEGIN) - TempSlot += ((rofSlot.Slot + 3) * EQEmu::invbag::SLOT_COUNT) + rofSlot.SubIndex + 1; - - else - TempSlot += rofSlot.Slot; - - ServerSlot = TempSlot; - } - - else if (rofSlot.Type == invtype::typeTrade) { - TempSlot = EQEmu::invslot::TRADE_BEGIN; - - if (rofSlot.SubIndex >= EQEmu::invbag::SLOT_BEGIN) - TempSlot += ((rofSlot.Slot + 3) * EQEmu::invbag::SLOT_COUNT) + rofSlot.SubIndex + 1; - // OLD CODE: - //TempSlot += 100 + (RoFSlot.MainSlot * EQEmu::inventory::ContainerCount) + RoFSlot.SubSlot; - - else - TempSlot += rofSlot.Slot; - - ServerSlot = TempSlot; - } - - else if (rofSlot.Type == invtype::typeWorld) { - TempSlot = EQEmu::invslot::WORLD_BEGIN; - - if (rofSlot.Slot >= EQEmu::invbag::SLOT_BEGIN) - TempSlot += rofSlot.Slot; - - ServerSlot = TempSlot; - } - - /*else if (RoFSlot.SlotType == maps::MapLimbo) { // Cursor Buffer - TempSlot = 31; - - if (RoFSlot.MainSlot >= 0) - TempSlot += RoFSlot.MainSlot; - - ServerSlot = TempSlot; - }*/ - - else if (rofSlot.Type == invtype::typeGuildTribute) { - ServerSlot = INVALID_INDEX; - } - - Log(Logs::General, Logs::Netcode, "[ERROR] Convert RoF Slots: Type %i, Unk2 %i, Main %i, Sub %i, Aug %i, Unk1 %i to Server Slot %i", rofSlot.Type, rofSlot.Unknown02, rofSlot.Slot, rofSlot.SubIndex, rofSlot.AugIndex, rofSlot.Unknown01, ServerSlot); - - return ServerSlot; + return server_slot; } - static inline uint32 RoFToServerTypelessSlot(structs::TypelessInventorySlot_Struct rofSlot) - { - uint32 ServerSlot = INVALID_INDEX; - uint32 TempSlot = 0; + static inline uint32 RoFToServerCorpseMainSlot(uint32 rof_corpse_slot) { + uint32 server_slot = EQEmu::invslot::SLOT_INVALID; - if (rofSlot.Slot < 57) { // Worn/Personal Inventory and Cursor (< 33) - if (rofSlot.Slot == invslot::slotPowerSource) - TempSlot = EQEmu::invslot::SLOT_POWER_SOURCE; - - else if (rofSlot.Slot >= invslot::slotCursor) // Cursor and Extended Corpse Inventory - TempSlot = rofSlot.Slot - 3; - - /*else if (RoFSlot.MainSlot == slots::MainGeneral9 || RoFSlot.MainSlot == slots::MainGeneral10) { // 9th and 10th RoF inventory slots - // Need to figure out what to do when we get these - - // Same as above - }*/ - - else if (rofSlot.Slot >= invslot::slotAmmo) // Main Inventory and Ammo Slots - TempSlot = rofSlot.Slot - 1; - - else - TempSlot = rofSlot.Slot; - - if (rofSlot.SubIndex >= EQEmu::invbag::SLOT_BEGIN) // Bag Slots - TempSlot = ((TempSlot + 3) * EQEmu::invbag::SLOT_COUNT) + rofSlot.SubIndex + 1; - - ServerSlot = TempSlot; + if (rof_corpse_slot <= invslot::CORPSE_END && rof_corpse_slot >= invslot::CORPSE_BEGIN) { + server_slot = rof_corpse_slot; } - Log(Logs::General, Logs::Netcode, "[ERROR] Convert RoF Slots: Main %i, Sub %i, Aug %i, Unk1 %i to Server Slot %i", rofSlot.Slot, rofSlot.SubIndex, rofSlot.AugIndex, rofSlot.Unknown01, ServerSlot); + Log(Logs::Detail, + Logs::Netcode, + "Convert RoF Corpse Main Slot %i to Server Corpse Slot %i", + rof_corpse_slot, + server_slot); - return ServerSlot; + return server_slot; } - static inline uint32 RoFToServerCorpseSlot(uint32 rofCorpseSlot) - { - return (rofCorpseSlot - 1); + static inline uint32 RoFToServerTypelessSlot(structs::TypelessInventorySlot_Struct rof_slot, int16 rof_type) { + + if (rof_slot.AugIndex < invaug::SOCKET_INVALID || rof_slot.AugIndex >= invaug::SOCKET_COUNT) { + Log(Logs::Detail, + Logs::Netcode, + "Convert RoF Typeless Slot [%i, %i, %i] (implied type: %i, unk1: %i) to Server Slot %i", + rof_slot.Slot, + rof_slot.SubIndex, + rof_slot.AugIndex, + rof_type, + rof_slot.Unknown01, + EQEmu::invslot::SLOT_INVALID); + + return (uint32) EQEmu::invslot::SLOT_INVALID; + } + + uint32 server_slot = EQEmu::invslot::SLOT_INVALID; + uint32 temp_slot = invslot::SLOT_INVALID; + + switch (rof_type) { + case invtype::typePossessions: { + if (rof_slot.Slot >= invslot::POSSESSIONS_BEGIN && rof_slot.Slot <= invslot::POSSESSIONS_END) { + if (rof_slot.SubIndex == invbag::SLOT_INVALID) { + server_slot = rof_slot.Slot; + } else if (rof_slot.SubIndex >= invbag::SLOT_BEGIN && rof_slot.SubIndex <= invbag::SLOT_END) { + if (rof_slot.Slot < invslot::GENERAL_BEGIN) + return EQEmu::invslot::SLOT_INVALID; + + temp_slot = (rof_slot.Slot - invslot::GENERAL_BEGIN) * invbag::SLOT_COUNT; + server_slot = EQEmu::invbag::GENERAL_BAGS_BEGIN + temp_slot + rof_slot.SubIndex; + } + } + + break; + } + case invtype::typeBank: { + if (rof_slot.Slot >= invslot::SLOT_BEGIN && rof_slot.Slot < invtype::BANK_SIZE) { + if (rof_slot.SubIndex == invbag::SLOT_INVALID) { + server_slot = EQEmu::invslot::BANK_BEGIN + rof_slot.Slot; + } else if (rof_slot.SubIndex >= invbag::SLOT_BEGIN && rof_slot.SubIndex <= invbag::SLOT_END) { + temp_slot = rof_slot.Slot * invbag::SLOT_COUNT; + server_slot = EQEmu::invbag::BANK_BAGS_BEGIN + temp_slot + rof_slot.SubIndex; + } + } + + break; + } + case invtype::typeSharedBank: { + if (rof_slot.Slot >= invslot::SLOT_BEGIN && rof_slot.Slot < invtype::SHARED_BANK_SIZE) { + if (rof_slot.SubIndex == invbag::SLOT_INVALID) { + server_slot = EQEmu::invslot::SHARED_BANK_BEGIN + rof_slot.Slot; + } else if (rof_slot.SubIndex >= invbag::SLOT_BEGIN && rof_slot.SubIndex <= invbag::SLOT_END) { + temp_slot = rof_slot.Slot * invbag::SLOT_COUNT; + server_slot = EQEmu::invbag::SHARED_BANK_BAGS_BEGIN + temp_slot + rof_slot.SubIndex; + } + } + + break; + } + case invtype::typeTrade: { + if (rof_slot.Slot >= invslot::SLOT_BEGIN && rof_slot.Slot < invtype::TRADE_SIZE) { + if (rof_slot.SubIndex == invbag::SLOT_INVALID) { + server_slot = EQEmu::invslot::TRADE_BEGIN + rof_slot.Slot; + } else if (rof_slot.SubIndex >= invbag::SLOT_BEGIN && rof_slot.SubIndex <= invbag::SLOT_END) { + temp_slot = rof_slot.Slot * invbag::SLOT_COUNT; + server_slot = EQEmu::invbag::TRADE_BAGS_BEGIN + temp_slot + rof_slot.SubIndex; + } + } + + break; + } + case invtype::typeWorld: { + if (rof_slot.Slot >= invslot::SLOT_BEGIN && rof_slot.Slot < invtype::WORLD_SIZE) { + server_slot = EQEmu::invslot::WORLD_BEGIN + rof_slot.Slot; + } + + else if (rof_slot.Slot == invslot::SLOT_INVALID) { + server_slot = EQEmu::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE; + } + + break; + } + case invtype::typeLimbo: { + if (rof_slot.Slot >= invslot::SLOT_BEGIN && rof_slot.Slot < invtype::LIMBO_SIZE) { + server_slot = EQEmu::invslot::slotCursor; + } + + break; + } + case invtype::typeTribute: { + if (rof_slot.Slot >= invslot::SLOT_BEGIN && rof_slot.Slot < invtype::TRIBUTE_SIZE) { + server_slot = EQEmu::invslot::TRIBUTE_BEGIN + rof_slot.Slot; + } + + break; + } + case invtype::typeGuildTribute: { + if (rof_slot.Slot >= invslot::SLOT_BEGIN && rof_slot.Slot < invtype::GUILD_TRIBUTE_SIZE) { + server_slot = EQEmu::invslot::GUILD_TRIBUTE_BEGIN + rof_slot.Slot; + } + + break; + } + case invtype::typeCorpse: { + if (rof_slot.Slot >= invslot::CORPSE_BEGIN && rof_slot.Slot <= invslot::CORPSE_END) { + server_slot = rof_slot.Slot; + } + + break; + } + default: { + + break; + } + } + + Log(Logs::Detail, + Logs::Netcode, + "Convert RoF Typeless Slot [%i, %i, %i] (implied type: %i, unk1: %i) to Server Slot %i", + rof_slot.Slot, + rof_slot.SubIndex, + rof_slot.AugIndex, + rof_type, + rof_slot.Unknown01, + server_slot); + + return server_slot; } static inline void ServerToRoFSayLink(std::string& rofSayLink, const std::string& serverSayLink) diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 99909cb16..d65ea4cba 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -48,20 +48,22 @@ namespace RoF2 void SerializeItem(EQEmu::OutBuffer& ob, const EQEmu::ItemInstance *inst, int16 slot_id, uint8 depth, ItemPacketType packet_type); // server to client inventory location converters - static inline structs::InventorySlot_Struct ServerToRoF2Slot(uint32 serverSlot, ItemPacketType PacketType = ItemPacketInvalid); - static inline structs::TypelessInventorySlot_Struct ServerToRoF2TypelessSlot(uint32 serverSlot); - static inline uint32 ServerToRoF2CorpseSlot(uint32 serverCorpseSlot); - + static inline structs::InventorySlot_Struct ServerToRoF2Slot(uint32 server_slot); + static inline structs::InventorySlot_Struct ServerToRoF2CorpseSlot(uint32 server_corpse_slot); + static inline uint32 ServerToRoF2CorpseMainSlot(uint32 server_corpse_slot); + static inline structs::TypelessInventorySlot_Struct ServerToRoF2TypelessSlot(uint32 server_slot, int16 server_type); + // client to server inventory location converters - static inline uint32 RoF2ToServerSlot(structs::InventorySlot_Struct rof2Slot, ItemPacketType PacketType = ItemPacketInvalid); - static inline uint32 RoF2ToServerTypelessSlot(structs::TypelessInventorySlot_Struct rof2Slot); - static inline uint32 RoF2ToServerCorpseSlot(uint32 rof2CorpseSlot); - + static inline uint32 RoF2ToServerSlot(structs::InventorySlot_Struct rof2_slot); + static inline uint32 RoF2ToServerCorpseSlot(structs::InventorySlot_Struct rof2_corpse_slot); + static inline uint32 RoF2ToServerCorpseMainSlot(uint32 rof2_corpse_slot); + static inline uint32 RoF2ToServerTypelessSlot(structs::TypelessInventorySlot_Struct rof2_slot, int16 rof2_type); + // server to client say link converter - static inline void ServerToRoF2SayLink(std::string& rof2SayLink, const std::string& serverSayLink); + static inline void ServerToRoF2SayLink(std::string &rof2_saylink, const std::string &server_saylink); // client to server say link converter - static inline void RoF2ToServerSayLink(std::string& serverSayLink, const std::string& rof2SayLink); + static inline void RoF2ToServerSayLink(std::string &server_saylink, const std::string &rof2_saylink); static inline CastingSlot ServerToRoF2CastingSlot(EQEmu::CastingSlot slot); static inline EQEmu::CastingSlot RoF2ToServerCastingSlot(CastingSlot slot); @@ -260,7 +262,7 @@ namespace RoF2 eq->unknown000 = 1; OUT(npcid); - eq->inventory_slot = ServerToRoF2TypelessSlot(emu->slot); + eq->inventory_slot = ServerToRoF2TypelessSlot(emu->slot, EQEmu::invtype::typePossessions); OUT(charges); OUT(sell_price); @@ -313,7 +315,7 @@ namespace RoF2 SETUP_DIRECT_ENCODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); OUT(merchant_entity_id); - eq->inventory_slot = ServerToRoF2TypelessSlot(emu->slot_id); + eq->inventory_slot = ServerToRoF2TypelessSlot(emu->slot_id, EQEmu::invtype::typePossessions); OUT(charges); OUT(cost); @@ -337,7 +339,7 @@ namespace RoF2 ENCODE_LENGTH_EXACT(ApplyPoison_Struct); SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); - eq->inventorySlot = ServerToRoF2TypelessSlot(emu->inventorySlot); + eq->inventorySlot = ServerToRoF2TypelessSlot(emu->inventorySlot, EQEmu::invtype::typePossessions); OUT(success); FINISH_ENCODE(); @@ -655,7 +657,7 @@ namespace RoF2 for (int index = 0; index < item_count; ++index, ++eq) { SerializeItem(ob, (const EQEmu::ItemInstance*)eq->inst, eq->slot_id, 0, ItemPacketCharInventory); if (ob.tellp() == last_pos) - Log(Logs::General, Logs::Netcode, "[STRUCTS] Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + Log(Logs::General, Logs::Netcode, "RoF2::ENCODE(OP_CharInventory) Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); last_pos = ob.tellp(); } @@ -735,7 +737,12 @@ namespace RoF2 FINISH_ENCODE(); } - ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } + ENCODE(OP_DeleteCharge) + { + Log(Logs::Moderate, Logs::Netcode, "RoF2::ENCODE(OP_DeleteCharge)"); + + ENCODE_FORWARD(OP_MoveItem); + } ENCODE(OP_DeleteItem) { @@ -1582,7 +1589,7 @@ namespace RoF2 SerializeItem(ob, (const EQEmu::ItemInstance*)int_struct->inst, int_struct->slot_id, 0, old_item_pkt->PacketType); if (ob.tellp() == last_pos) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Serialization failed on item slot %d.", int_struct->slot_id); + Log(Logs::General, Logs::Netcode, "RoF2::ENCODE(OP_ItemPacket) Serialization failed on item slot %d.", int_struct->slot_id); delete in; return; } @@ -1655,9 +1662,11 @@ namespace RoF2 ENCODE_LENGTH_EXACT(LootingItem_Struct); SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); + Log(Logs::Moderate, Logs::Netcode, "RoF2::ENCODE(OP_LootItem)"); + OUT(lootee); OUT(looter); - eq->slot_id = ServerToRoF2CorpseSlot(emu->slot_id); + eq->slot_id = ServerToRoF2CorpseMainSlot(emu->slot_id); OUT(auto_loot); FINISH_ENCODE(); @@ -1825,6 +1834,8 @@ namespace RoF2 ENCODE_LENGTH_EXACT(MoveItem_Struct); SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); + Log(Logs::Moderate, Logs::Netcode, "RoF2::ENCODE(OP_MoveItem)"); + eq->from_slot = ServerToRoF2Slot(emu->from_slot); eq->to_slot = ServerToRoF2Slot(emu->to_slot); OUT(number_in_stack); @@ -2303,7 +2314,7 @@ namespace RoF2 outapp->WriteUInt32(profile::BANDOLIERS_SIZE); - // Copy bandoliers where server and client indexes converge + // Copy bandoliers where server and client indices converge for (uint32 r = 0; r < EQEmu::profile::BANDOLIERS_SIZE && r < profile::BANDOLIERS_SIZE; ++r) { outapp->WriteString(emu->bandoliers[r].Name); for (uint32 j = 0; j < profile::BANDOLIER_ITEM_COUNT; ++j) { // Will need adjusting if 'server != client' is ever true @@ -2318,7 +2329,7 @@ namespace RoF2 } } } - // Nullify bandoliers where server and client indexes diverge, with a client bias + // Nullify bandoliers where server and client indices diverge, with a client bias for (uint32 r = EQEmu::profile::BANDOLIERS_SIZE; r < profile::BANDOLIERS_SIZE; ++r) { outapp->WriteString(""); for (uint32 j = 0; j < profile::BANDOLIER_ITEM_COUNT; ++j) { // Will need adjusting if 'server != client' is ever true @@ -2330,7 +2341,7 @@ namespace RoF2 outapp->WriteUInt32(profile::POTION_BELT_SIZE); - // Copy potion belt where server and client indexes converge + // Copy potion belt where server and client indices converge for (uint32 r = 0; r < EQEmu::profile::POTION_BELT_SIZE && r < profile::POTION_BELT_SIZE; ++r) { outapp->WriteString(emu->potionbelt.Items[r].Name); outapp->WriteUInt32(emu->potionbelt.Items[r].ID); @@ -2342,7 +2353,7 @@ namespace RoF2 outapp->WriteSInt32(-1); } } - // Nullify potion belt where server and client indexes diverge, with a client bias + // Nullify potion belt where server and client indices diverge, with a client bias for (uint32 r = EQEmu::profile::POTION_BELT_SIZE; r < profile::POTION_BELT_SIZE; ++r) { outapp->WriteString(""); outapp->WriteUInt32(0); @@ -3110,7 +3121,7 @@ namespace RoF2 SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); OUT(npcid); - eq->inventory_slot = ServerToRoF2TypelessSlot(emu->itemslot); + eq->inventory_slot = ServerToRoF2TypelessSlot(emu->itemslot, EQEmu::invtype::typePossessions); //OUT(itemslot); OUT(quantity); OUT(price); @@ -4299,7 +4310,7 @@ namespace RoF2 SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); IN(npcid); - emu->slot = RoF2ToServerTypelessSlot(eq->inventory_slot); + emu->slot = RoF2ToServerTypelessSlot(eq->inventory_slot, invtype::typePossessions); IN(charges); IN(sell_price); @@ -4312,7 +4323,7 @@ namespace RoF2 SETUP_DIRECT_DECODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); IN(merchant_entity_id); - emu->slot_id = RoF2ToServerTypelessSlot(eq->inventory_slot); + emu->slot_id = RoF2ToServerTypelessSlot(eq->inventory_slot, invtype::typePossessions); IN(charges); IN(cost); @@ -4325,7 +4336,7 @@ namespace RoF2 SETUP_DIRECT_DECODE(AltCurrencySelectItem_Struct, structs::AltCurrencySelectItem_Struct); IN(merchant_entity_id); - emu->slot_id = RoF2ToServerTypelessSlot(eq->inventory_slot); + emu->slot_id = RoF2ToServerTypelessSlot(eq->inventory_slot, invtype::typePossessions); FINISH_DIRECT_DECODE(); } @@ -4347,7 +4358,7 @@ namespace RoF2 DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); - emu->inventorySlot = RoF2ToServerTypelessSlot(eq->inventorySlot); + emu->inventorySlot = RoF2ToServerTypelessSlot(eq->inventorySlot, invtype::typePossessions); IN(success); FINISH_DIRECT_DECODE(); @@ -4981,9 +4992,11 @@ namespace RoF2 DECODE_LENGTH_EXACT(structs::LootingItem_Struct); SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); + Log(Logs::Moderate, Logs::Netcode, "RoF2::DECODE(OP_LootItem)"); + IN(lootee); IN(looter); - emu->slot_id = RoF2ToServerCorpseSlot(eq->slot_id); + emu->slot_id = RoF2ToServerCorpseMainSlot(eq->slot_id); IN(auto_loot); FINISH_DIRECT_DECODE(); @@ -4994,10 +5007,13 @@ namespace RoF2 DECODE_LENGTH_EXACT(structs::MoveItem_Struct); SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); - Log(Logs::General, Logs::Netcode, "[RoF2] MoveItem SlotType from %i to %i, MainSlot from %i to %i, SubSlot from %i to %i, AugSlot from %i to %i, Unknown01 from %i to %i, Number %u", eq->from_slot.Type, eq->to_slot.Type, eq->from_slot.Slot, eq->to_slot.Slot, eq->from_slot.SubIndex, eq->to_slot.SubIndex, eq->from_slot.AugIndex, eq->to_slot.AugIndex, eq->from_slot.Unknown01, eq->to_slot.Unknown01, eq->number_in_stack); + Log(Logs::Moderate, Logs::Netcode, "RoF2::DECODE(OP_MoveItem)"); + emu->from_slot = RoF2ToServerSlot(eq->from_slot); emu->to_slot = RoF2ToServerSlot(eq->to_slot); IN(number_in_stack); + + //Log(Logs::General, Logs::Netcode, "[RoF2] MoveItem Slot from %u to %u, Number %u", emu->from_slot, emu->to_slot, emu->number_in_stack); FINISH_DIRECT_DECODE(); } @@ -5144,7 +5160,7 @@ namespace RoF2 SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); IN(npcid); - emu->itemslot = RoF2ToServerTypelessSlot(eq->inventory_slot); + emu->itemslot = RoF2ToServerTypelessSlot(eq->inventory_slot, invtype::typePossessions); //IN(itemslot); IN(quantity); IN(price); @@ -5283,11 +5299,7 @@ namespace RoF2 DECODE_LENGTH_EXACT(structs::NewCombine_Struct); SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); - int16 slot_id = RoF2ToServerSlot(eq->container_slot); - if (slot_id == 4000) { - slot_id = EQEmu::legacy::SLOT_TRADESKILL; // 1000 - } - emu->container_slot = slot_id; + emu->container_slot = RoF2ToServerSlot(eq->container_slot); emu->guildtribute_slot = RoF2ToServerSlot(eq->guildtribute_slot); // this should only return INVALID_INDEX until implemented FINISH_DIRECT_DECODE(); @@ -5387,8 +5399,16 @@ namespace RoF2 hdr.stacksize = (inst->IsStackable() ? ((inst->GetCharges() > 1000) ? 0xFFFFFFFF : inst->GetCharges()) : 1); hdr.unknown004 = 0; - structs::InventorySlot_Struct slot_id = ServerToRoF2Slot(slot_id_in, packet_type); - + structs::InventorySlot_Struct slot_id; + switch (packet_type) { + case ItemPacketLoot: + slot_id = ServerToRoF2CorpseSlot(slot_id_in); + break; + default: + slot_id = ServerToRoF2Slot(slot_id_in); + break; + } + hdr.slot_type = (inst->GetMerchantSlot() ? invtype::typeMerchant : slot_id.Type); hdr.main_slot = (inst->GetMerchantSlot() ? inst->GetMerchantSlot() : slot_id.Slot); hdr.sub_slot = (inst->GetMerchantSlot() ? 0xffff : slot_id.SubIndex); @@ -5483,7 +5503,7 @@ namespace RoF2 ibs.nodrop = item->NoDrop; ibs.attune = item->Attuneable; ibs.size = item->Size; - ibs.slots = SwapBits21And22(item->Slots); + ibs.slots = item->Slots; ibs.price = item->Price; ibs.icon = item->Icon; ibs.unknown1 = 1; @@ -5797,345 +5817,477 @@ namespace RoF2 ob.write((const char*)&subitem_count, sizeof(uint32)); - for (uint32 index = EQEmu::invbag::SLOT_BEGIN; index <= EQEmu::invbag::SLOT_END; ++index) { - EQEmu::ItemInstance* sub = inst->GetItem(index); - if (!sub) - continue; + // moved outside of loop since it is not modified within that scope + int16 SubSlotNumber = EQEmu::invbag::SLOT_INVALID; - int SubSlotNumber = INVALID_INDEX; - if (slot_id_in >= EQEmu::invslot::GENERAL_BEGIN && slot_id_in <= EQEmu::invslot::GENERAL_END) - SubSlotNumber = (((slot_id_in + 3) * EQEmu::invbag::SLOT_COUNT) + index + 1); - else if (slot_id_in >= EQEmu::invslot::BANK_BEGIN && slot_id_in <= EQEmu::invslot::BANK_END) - SubSlotNumber = (((slot_id_in - EQEmu::invslot::BANK_BEGIN) * EQEmu::invbag::SLOT_COUNT) + EQEmu::invbag::BANK_BAGS_BEGIN + index); - else if (slot_id_in >= EQEmu::invslot::SHARED_BANK_BEGIN && slot_id_in <= EQEmu::invslot::SHARED_BANK_END) - SubSlotNumber = (((slot_id_in - EQEmu::invslot::SHARED_BANK_BEGIN) * EQEmu::invbag::SLOT_COUNT) + EQEmu::invbag::SHARED_BANK_BAGS_BEGIN + index); - else - SubSlotNumber = slot_id_in; + if (slot_id_in <= EQEmu::invslot::GENERAL_END && slot_id_in >= EQEmu::invslot::GENERAL_BEGIN) + SubSlotNumber = EQEmu::invbag::GENERAL_BAGS_BEGIN + ((slot_id_in - EQEmu::invslot::GENERAL_BEGIN) * EQEmu::invbag::SLOT_COUNT); + else if (slot_id_in == EQEmu::invslot::slotCursor) + SubSlotNumber = EQEmu::invbag::CURSOR_BAG_BEGIN; + else if (slot_id_in <= EQEmu::invslot::BANK_END && slot_id_in >= EQEmu::invslot::BANK_BEGIN) + SubSlotNumber = EQEmu::invbag::BANK_BAGS_BEGIN + ((slot_id_in - EQEmu::invslot::BANK_BEGIN) * EQEmu::invbag::SLOT_COUNT); + else if (slot_id_in <= EQEmu::invslot::SHARED_BANK_END && slot_id_in >= EQEmu::invslot::SHARED_BANK_BEGIN) + SubSlotNumber = EQEmu::invbag::SHARED_BANK_BAGS_BEGIN + ((slot_id_in - EQEmu::invslot::SHARED_BANK_BEGIN) * EQEmu::invbag::SLOT_COUNT); + else + SubSlotNumber = slot_id_in; // not sure if this is the best way to handle this..leaving for now - ob.write((const char*)&index, sizeof(uint32)); + if (SubSlotNumber != EQEmu::invbag::SLOT_INVALID) { + for (uint32 index = EQEmu::invbag::SLOT_BEGIN; index <= EQEmu::invbag::SLOT_END; ++index) { + EQEmu::ItemInstance* sub = inst->GetItem(index); + if (!sub) + continue; - SerializeItem(ob, sub, SubSlotNumber, (depth + 1), packet_type); - ++subitem_count; + ob.write((const char*)&index, sizeof(uint32)); + + SerializeItem(ob, sub, SubSlotNumber, (depth + 1), packet_type); + ++subitem_count; + } + + if (subitem_count) + ob.overwrite(count_pos, (const char*)&subitem_count, sizeof(uint32)); } - - if (subitem_count) - ob.overwrite(count_pos, (const char*)&subitem_count, sizeof(uint32)); } - static inline structs::InventorySlot_Struct ServerToRoF2Slot(uint32 serverSlot, ItemPacketType PacketType) + static inline structs::InventorySlot_Struct ServerToRoF2Slot(uint32 server_slot) { structs::InventorySlot_Struct RoF2Slot; - RoF2Slot.Type = INVALID_INDEX; - RoF2Slot.Unknown02 = 0; - RoF2Slot.Slot = INVALID_INDEX; - RoF2Slot.SubIndex = INVALID_INDEX; - RoF2Slot.AugIndex = INVALID_INDEX; - RoF2Slot.Unknown01 = 0; + RoF2Slot.Type = invtype::TYPE_INVALID; + RoF2Slot.Unknown02 = INULL; + RoF2Slot.Slot = invslot::SLOT_INVALID; + RoF2Slot.SubIndex = invbag::SLOT_INVALID; + RoF2Slot.AugIndex = invaug::SOCKET_INVALID; + RoF2Slot.Unknown01 = INULL; - uint32 TempSlot = 0; + uint32 TempSlot = EQEmu::invslot::SLOT_INVALID; - if (serverSlot < 56 || serverSlot == EQEmu::invslot::SLOT_POWER_SOURCE) { // Main Inventory and Cursor - if (PacketType == ItemPacketLoot) - { - RoF2Slot.Type = invtype::typeCorpse; - RoF2Slot.Slot = serverSlot - EQEmu::invslot::CORPSE_BEGIN; - } - else - { - RoF2Slot.Type = invtype::typePossessions; - RoF2Slot.Slot = serverSlot; - } - - if (serverSlot == EQEmu::invslot::SLOT_POWER_SOURCE) - RoF2Slot.Slot = invslot::slotPowerSource; - - else if (serverSlot >= EQEmu::invslot::slotCursor && PacketType != ItemPacketLoot) // Cursor and Extended Corpse Inventory - RoF2Slot.Slot += 3; - - else if (serverSlot >= EQEmu::invslot::slotAmmo) // (> 20) - RoF2Slot.Slot += 1; - } - - /*else if (ServerSlot < 51) { // Cursor Buffer - RoF2Slot.SlotType = maps::MapLimbo; - RoF2Slot.MainSlot = ServerSlot - 31; - }*/ - - else if (serverSlot >= EQEmu::invbag::GENERAL_BAGS_BEGIN && serverSlot <= EQEmu::invbag::CURSOR_BAG_END) { // (> 250 && < 341) + if (server_slot < EQEmu::invtype::POSSESSIONS_SIZE) { RoF2Slot.Type = invtype::typePossessions; - TempSlot = serverSlot - 1; - RoF2Slot.Slot = int(TempSlot / EQEmu::invbag::SLOT_COUNT) - 2; - RoF2Slot.SubIndex = TempSlot - ((RoF2Slot.Slot + 2) * EQEmu::invbag::SLOT_COUNT); - - if (RoF2Slot.Slot >= invslot::slotGeneral9) // (> 30) - RoF2Slot.Slot = invslot::slotCursor; + RoF2Slot.Slot = server_slot; } - else if (serverSlot >= EQEmu::invslot::TRIBUTE_BEGIN && serverSlot <= EQEmu::invslot::TRIBUTE_END) { // Tribute + else if (server_slot <= EQEmu::invbag::CURSOR_BAG_END && server_slot >= EQEmu::invbag::GENERAL_BAGS_BEGIN) { + TempSlot = server_slot - EQEmu::invbag::GENERAL_BAGS_BEGIN; + + RoF2Slot.Type = invtype::typePossessions; + RoF2Slot.Slot = invslot::GENERAL_BEGIN + (TempSlot / EQEmu::invbag::SLOT_COUNT); + RoF2Slot.SubIndex = TempSlot - ((RoF2Slot.Slot - invslot::GENERAL_BEGIN) * EQEmu::invbag::SLOT_COUNT); + } + + else if (server_slot <= EQEmu::invslot::TRIBUTE_END && server_slot >= EQEmu::invslot::TRIBUTE_BEGIN) { RoF2Slot.Type = invtype::typeTribute; - RoF2Slot.Slot = serverSlot - EQEmu::invslot::TRIBUTE_BEGIN; + RoF2Slot.Slot = server_slot - EQEmu::invslot::TRIBUTE_BEGIN; } - else if (serverSlot >= EQEmu::invslot::BANK_BEGIN && serverSlot <= EQEmu::invbag::BANK_BAGS_END) { - RoF2Slot.Type = invtype::typeBank; - TempSlot = serverSlot - EQEmu::invslot::BANK_BEGIN; - RoF2Slot.Slot = TempSlot; - - if (TempSlot > 30) { // (> 30) - RoF2Slot.Slot = int(TempSlot / EQEmu::invbag::SLOT_COUNT) - 3; - RoF2Slot.SubIndex = TempSlot - ((RoF2Slot.Slot + 3) * EQEmu::invbag::SLOT_COUNT); - } + else if (server_slot <= EQEmu::invslot::GUILD_TRIBUTE_END && server_slot >= EQEmu::invslot::GUILD_TRIBUTE_BEGIN) { + RoF2Slot.Type = invtype::typeGuildTribute; + RoF2Slot.Slot = server_slot - EQEmu::invslot::GUILD_TRIBUTE_BEGIN; } - else if (serverSlot >= EQEmu::invslot::SHARED_BANK_BEGIN && serverSlot <= EQEmu::invbag::SHARED_BANK_BAGS_END) { - RoF2Slot.Type = invtype::typeSharedBank; - TempSlot = serverSlot - EQEmu::invslot::SHARED_BANK_BEGIN; - RoF2Slot.Slot = TempSlot; - - if (TempSlot > 30) { // (> 30) - RoF2Slot.Slot = int(TempSlot / EQEmu::invbag::SLOT_COUNT) - 3; - RoF2Slot.SubIndex = TempSlot - ((RoF2Slot.Slot + 3) * EQEmu::invbag::SLOT_COUNT); - } - } - - else if (serverSlot >= EQEmu::invslot::TRADE_BEGIN && serverSlot <= EQEmu::invbag::TRADE_BAGS_END) { - RoF2Slot.Type = invtype::typeTrade; - TempSlot = serverSlot - EQEmu::invslot::TRADE_BEGIN; - RoF2Slot.Slot = TempSlot; - - if (TempSlot > 30) { - RoF2Slot.Slot = int(TempSlot / EQEmu::invbag::SLOT_COUNT) - 3; - RoF2Slot.SubIndex = TempSlot - ((RoF2Slot.Slot + 3) * EQEmu::invbag::SLOT_COUNT); - } - - /* - // OLD CODE: - if (TempSlot > 99) { - if (TempSlot > 100) - RoF2Slot.MainSlot = int((TempSlot - 100) / 10); - - else - RoF2Slot.MainSlot = 0; - - RoF2Slot.SubSlot = TempSlot - (100 + RoF2Slot.MainSlot); - } - */ - } - - else if (serverSlot >= EQEmu::invslot::WORLD_BEGIN && serverSlot <= EQEmu::invslot::WORLD_END) { + else if (server_slot == EQEmu::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE) { RoF2Slot.Type = invtype::typeWorld; - TempSlot = serverSlot - EQEmu::invslot::WORLD_BEGIN; - RoF2Slot.Slot = TempSlot; } - Log(Logs::General, Logs::Netcode, "[ERROR] Convert Server Slot %i to RoF2 Slots: Type %i, Unk2 %i, Main %i, Sub %i, Aug %i, Unk1 %i", serverSlot, RoF2Slot.Type, RoF2Slot.Unknown02, RoF2Slot.Slot, RoF2Slot.SubIndex, RoF2Slot.AugIndex, RoF2Slot.Unknown01); + else if (server_slot <= EQEmu::invslot::BANK_END && server_slot >= EQEmu::invslot::BANK_BEGIN) { + RoF2Slot.Type = invtype::typeBank; + RoF2Slot.Slot = server_slot - EQEmu::invslot::BANK_BEGIN; + } + + else if (server_slot <= EQEmu::invbag::BANK_BAGS_END && server_slot >= EQEmu::invbag::BANK_BAGS_BEGIN) { + TempSlot = server_slot - EQEmu::invbag::BANK_BAGS_BEGIN; + + RoF2Slot.Type = invtype::typeBank; + RoF2Slot.Slot = TempSlot / EQEmu::invbag::SLOT_COUNT; + RoF2Slot.SubIndex = TempSlot - (RoF2Slot.Slot * EQEmu::invbag::SLOT_COUNT); + } + + else if (server_slot <= EQEmu::invslot::SHARED_BANK_END && server_slot >= EQEmu::invslot::SHARED_BANK_BEGIN) { + RoF2Slot.Type = invtype::typeSharedBank; + RoF2Slot.Slot = server_slot - EQEmu::invslot::SHARED_BANK_BEGIN; + } + + else if (server_slot <= EQEmu::invbag::SHARED_BANK_BAGS_END && server_slot >= EQEmu::invbag::SHARED_BANK_BAGS_BEGIN) { + TempSlot = server_slot - EQEmu::invbag::SHARED_BANK_BAGS_BEGIN; + + RoF2Slot.Type = invtype::typeSharedBank; + RoF2Slot.Slot = TempSlot / EQEmu::invbag::SLOT_COUNT; + RoF2Slot.SubIndex = TempSlot - (RoF2Slot.Slot * EQEmu::invbag::SLOT_COUNT); + } + + else if (server_slot <= EQEmu::invslot::TRADE_END && server_slot >= EQEmu::invslot::TRADE_BEGIN) { + RoF2Slot.Type = invtype::typeTrade; + RoF2Slot.Slot = server_slot - EQEmu::invslot::TRADE_BEGIN; + } + + else if (server_slot <= EQEmu::invbag::TRADE_BAGS_END && server_slot >= EQEmu::invbag::TRADE_BAGS_BEGIN) { + TempSlot = server_slot - EQEmu::invbag::TRADE_BAGS_BEGIN; + + RoF2Slot.Type = invtype::typeTrade; + RoF2Slot.Slot = TempSlot / EQEmu::invbag::SLOT_COUNT; + RoF2Slot.SubIndex = TempSlot - (RoF2Slot.Slot * EQEmu::invbag::SLOT_COUNT); + } + + else if (server_slot <= EQEmu::invslot::WORLD_END && server_slot >= EQEmu::invslot::WORLD_BEGIN) { + RoF2Slot.Type = invtype::typeWorld; + RoF2Slot.Slot = server_slot - EQEmu::invslot::WORLD_BEGIN; + } + + Log(Logs::Detail, Logs::Netcode, "Convert Server Slot %i to RoF2 Slot [%i, %i, %i, %i] (unk2: %i, unk1: %i)", + server_slot, RoF2Slot.Type, RoF2Slot.Slot, RoF2Slot.SubIndex, RoF2Slot.AugIndex, RoF2Slot.Unknown02, RoF2Slot.Unknown01); return RoF2Slot; } - static inline structs::TypelessInventorySlot_Struct ServerToRoF2TypelessSlot(uint32 serverSlot) + static inline structs::InventorySlot_Struct ServerToRoF2CorpseSlot(uint32 server_corpse_slot) + { + structs::InventorySlot_Struct RoF2Slot; + RoF2Slot.Type = invtype::TYPE_INVALID; + RoF2Slot.Unknown02 = INULL; + RoF2Slot.Slot = ServerToRoF2CorpseMainSlot(server_corpse_slot); + RoF2Slot.SubIndex = invbag::SLOT_INVALID; + RoF2Slot.AugIndex = invaug::SOCKET_INVALID; + RoF2Slot.Unknown01 = INULL; + + if (RoF2Slot.Slot != invslot::SLOT_INVALID) + RoF2Slot.Type = invtype::typeCorpse; + + Log(Logs::Detail, Logs::Netcode, "Convert Server Corpse Slot %i to RoF2 Corpse Slot [%i, %i, %i, %i] (unk2: %i, unk1: %i)", + server_corpse_slot, RoF2Slot.Type, RoF2Slot.Slot, RoF2Slot.SubIndex, RoF2Slot.AugIndex, RoF2Slot.Unknown02, RoF2Slot.Unknown01); + + return RoF2Slot; + } + + static inline uint32 ServerToRoF2CorpseMainSlot(uint32 server_corpse_slot) + { + uint32 RoF2Slot = invslot::SLOT_INVALID; + + if (server_corpse_slot <= EQEmu::invslot::CORPSE_END && server_corpse_slot >= EQEmu::invslot::CORPSE_BEGIN) { + RoF2Slot = server_corpse_slot; + } + + Log(Logs::Detail, Logs::Netcode, "Convert Server Corpse Slot %i to RoF2 Corpse Main Slot %i", server_corpse_slot, RoF2Slot); + + return RoF2Slot; + } + + static inline structs::TypelessInventorySlot_Struct ServerToRoF2TypelessSlot(uint32 server_slot, int16 server_type) { structs::TypelessInventorySlot_Struct RoF2Slot; - RoF2Slot.Slot = INVALID_INDEX; - RoF2Slot.SubIndex = INVALID_INDEX; - RoF2Slot.AugIndex = INVALID_INDEX; - RoF2Slot.Unknown01 = 0; + RoF2Slot.Slot = invslot::SLOT_INVALID; + RoF2Slot.SubIndex = invbag::SLOT_INVALID; + RoF2Slot.AugIndex = invaug::SOCKET_INVALID; + RoF2Slot.Unknown01 = INULL; - uint32 TempSlot = 0; + uint32 TempSlot = EQEmu::invslot::SLOT_INVALID; - if (serverSlot < 56 || serverSlot == EQEmu::invslot::SLOT_POWER_SOURCE) { // (< 52) - RoF2Slot.Slot = serverSlot; + if (server_type == EQEmu::invtype::typePossessions) { + if (server_slot < EQEmu::invtype::POSSESSIONS_SIZE) { + RoF2Slot.Slot = server_slot; + } - if (serverSlot == EQEmu::invslot::SLOT_POWER_SOURCE) - RoF2Slot.Slot = invslot::slotPowerSource; + else if (server_slot <= EQEmu::invbag::CURSOR_BAG_END && server_slot >= EQEmu::invbag::GENERAL_BAGS_BEGIN) { + TempSlot = server_slot - EQEmu::invbag::GENERAL_BAGS_BEGIN; - else if (serverSlot >= EQEmu::invslot::slotCursor) // Cursor and Extended Corpse Inventory - RoF2Slot.Slot += 3; - - else if (serverSlot >= EQEmu::invslot::slotAmmo) // Ammo and Personl Inventory - RoF2Slot.Slot += 1; - - /*else if (ServerSlot >= MainCursor) { // Cursor - RoF2Slot.MainSlot = slots::MainCursor; - - if (ServerSlot > 30) - RoF2Slot.SubSlot = (ServerSlot + 3) - 33; - }*/ + RoF2Slot.Slot = invslot::GENERAL_BEGIN + (TempSlot / EQEmu::invbag::SLOT_COUNT); + RoF2Slot.SubIndex = TempSlot - ((RoF2Slot.Slot - invslot::GENERAL_BEGIN) * EQEmu::invbag::SLOT_COUNT); + } } - else if (serverSlot >= EQEmu::invbag::GENERAL_BAGS_BEGIN && serverSlot <= EQEmu::invbag::CURSOR_BAG_END) { - TempSlot = serverSlot - 1; - RoF2Slot.Slot = int(TempSlot / EQEmu::invbag::SLOT_COUNT) - 2; - RoF2Slot.SubIndex = TempSlot - ((RoF2Slot.Slot + 2) * EQEmu::invbag::SLOT_COUNT); - } - - Log(Logs::General, Logs::Netcode, "[ERROR] Convert Server Slot %i to RoF2 Slots: Main %i, Sub %i, Aug %i, Unk1 %i", serverSlot, RoF2Slot.Slot, RoF2Slot.SubIndex, RoF2Slot.AugIndex, RoF2Slot.Unknown01); + Log(Logs::Detail, Logs::Netcode, "Convert Server Slot %i to RoF2 Typeless Slot [%i, %i, %i] (implied type: %i, unk1: %i)", + server_slot, RoF2Slot.Slot, RoF2Slot.SubIndex, RoF2Slot.AugIndex, server_type, RoF2Slot.Unknown01); return RoF2Slot; } - static inline uint32 ServerToRoF2CorpseSlot(uint32 serverCorpseSlot) + static inline uint32 RoF2ToServerSlot(structs::InventorySlot_Struct rof2_slot) { - return (serverCorpseSlot - EQEmu::invslot::CORPSE_BEGIN + 1); + if (rof2_slot.AugIndex < invaug::SOCKET_INVALID || rof2_slot.AugIndex >= invaug::SOCKET_COUNT) { + Log(Logs::Detail, Logs::Netcode, "Convert RoF2 Slot [%i, %i, %i, %i] (unk2: %i, unk1: %i) to Server Slot %i", + rof2_slot.Type, rof2_slot.Slot, rof2_slot.SubIndex, rof2_slot.AugIndex, rof2_slot.Unknown02, rof2_slot.Unknown01, EQEmu::invslot::SLOT_INVALID); + + return EQEmu::invslot::SLOT_INVALID; + } + + uint32 server_slot = EQEmu::invslot::SLOT_INVALID; + uint32 temp_slot = invslot::SLOT_INVALID; + + switch (rof2_slot.Type) { + case invtype::typePossessions: { + if (rof2_slot.Slot >= invslot::POSSESSIONS_BEGIN && rof2_slot.Slot <= invslot::POSSESSIONS_END) { + if (rof2_slot.SubIndex == invbag::SLOT_INVALID) { + server_slot = rof2_slot.Slot; + } + + else if (rof2_slot.SubIndex >= invbag::SLOT_BEGIN && rof2_slot.SubIndex <= invbag::SLOT_END) { + if (rof2_slot.Slot < invslot::GENERAL_BEGIN) + return EQEmu::invslot::SLOT_INVALID; + + temp_slot = (rof2_slot.Slot - invslot::GENERAL_BEGIN) * invbag::SLOT_COUNT; + server_slot = EQEmu::invbag::GENERAL_BAGS_BEGIN + temp_slot + rof2_slot.SubIndex; + } + } + + break; + } + case invtype::typeBank: { + if (rof2_slot.Slot >= invslot::SLOT_BEGIN && rof2_slot.Slot < invtype::BANK_SIZE) { + if (rof2_slot.SubIndex == invbag::SLOT_INVALID) { + server_slot = EQEmu::invslot::BANK_BEGIN + rof2_slot.Slot; + } + + else if (rof2_slot.SubIndex >= invbag::SLOT_BEGIN && rof2_slot.SubIndex <= invbag::SLOT_END) { + temp_slot = rof2_slot.Slot * invbag::SLOT_COUNT; + server_slot = EQEmu::invbag::BANK_BAGS_BEGIN + temp_slot + rof2_slot.SubIndex; + } + } + + break; + } + case invtype::typeSharedBank: { + if (rof2_slot.Slot >= invslot::SLOT_BEGIN && rof2_slot.Slot < invtype::SHARED_BANK_SIZE) { + if (rof2_slot.SubIndex == invbag::SLOT_INVALID) { + server_slot = EQEmu::invslot::SHARED_BANK_BEGIN + rof2_slot.Slot; + } + + else if (rof2_slot.SubIndex >= invbag::SLOT_BEGIN && rof2_slot.SubIndex <= invbag::SLOT_END) { + temp_slot = rof2_slot.Slot * invbag::SLOT_COUNT; + server_slot = EQEmu::invbag::SHARED_BANK_BAGS_BEGIN + temp_slot + rof2_slot.SubIndex; + } + } + + break; + } + case invtype::typeTrade: { + if (rof2_slot.Slot >= invslot::SLOT_BEGIN && rof2_slot.Slot < invtype::TRADE_SIZE) { + if (rof2_slot.SubIndex == invbag::SLOT_INVALID) { + server_slot = EQEmu::invslot::TRADE_BEGIN + rof2_slot.Slot; + } + + else if (rof2_slot.SubIndex >= invbag::SLOT_BEGIN && rof2_slot.SubIndex <= invbag::SLOT_END) { + temp_slot = rof2_slot.Slot * invbag::SLOT_COUNT; + server_slot = EQEmu::invbag::TRADE_BAGS_BEGIN + temp_slot + rof2_slot.SubIndex; + } + } + + break; + } + case invtype::typeWorld: { + if (rof2_slot.Slot >= invslot::SLOT_BEGIN && rof2_slot.Slot < invtype::WORLD_SIZE) { + server_slot = EQEmu::invslot::WORLD_BEGIN + rof2_slot.Slot; + } + + else if (rof2_slot.Slot == invslot::SLOT_INVALID) { + server_slot = EQEmu::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE; + } + + break; + } + case invtype::typeLimbo: { + if (rof2_slot.Slot >= invslot::SLOT_BEGIN && rof2_slot.Slot < invtype::LIMBO_SIZE) { + server_slot = EQEmu::invslot::slotCursor; + } + + break; + } + case invtype::typeTribute: { + if (rof2_slot.Slot >= invslot::SLOT_BEGIN && rof2_slot.Slot < invtype::TRIBUTE_SIZE) { + server_slot = EQEmu::invslot::TRIBUTE_BEGIN + rof2_slot.Slot; + } + + break; + } + case invtype::typeGuildTribute: { + if (rof2_slot.Slot >= invslot::SLOT_BEGIN && rof2_slot.Slot < invtype::GUILD_TRIBUTE_SIZE) { + server_slot = EQEmu::invslot::GUILD_TRIBUTE_BEGIN + rof2_slot.Slot; + } + + break; + } + case invtype::typeCorpse: { + if (rof2_slot.Slot >= invslot::CORPSE_BEGIN && rof2_slot.Slot <= invslot::CORPSE_END) { + server_slot = rof2_slot.Slot; + } + + break; + } + default: { + + break; + } + } + + Log(Logs::Detail, Logs::Netcode, "Convert RoF2 Slot [%i, %i, %i, %i] (unk2: %i, unk1: %i) to Server Slot %i", + rof2_slot.Type, rof2_slot.Slot, rof2_slot.SubIndex, rof2_slot.AugIndex, rof2_slot.Unknown02, rof2_slot.Unknown01, server_slot); + + return server_slot; } - static inline uint32 RoF2ToServerSlot(structs::InventorySlot_Struct rof2Slot, ItemPacketType PacketType) + static inline uint32 RoF2ToServerCorpseSlot(structs::InventorySlot_Struct rof2_corpse_slot) { - uint32 ServerSlot = INVALID_INDEX; - uint32 TempSlot = 0; - - if (rof2Slot.Type == invtype::typePossessions && rof2Slot.Slot < 57) { // Worn/Personal Inventory and Cursor (< 51) - if (rof2Slot.Slot == invslot::slotPowerSource) - TempSlot = EQEmu::invslot::SLOT_POWER_SOURCE; - - else if (rof2Slot.Slot >= invslot::slotCursor) // Cursor and Extended Corpse Inventory - TempSlot = rof2Slot.Slot - 3; - - /*else if (RoF2Slot.MainSlot == slots::MainGeneral9 || RoF2Slot.MainSlot == slots::MainGeneral10) { // 9th and 10th RoF2 inventory/corpse slots - // Need to figure out what to do when we get these - - // The slot range of 0 - client_max is cross-utilized between player inventory and corpse inventory. - // In the case of RoF2, player inventory is addressed as 0 - 33 and corpse inventory is addressed as 23 - 56. - // We 'could' assign the two new inventory slots as 9997 and 9998, and then work around their bag - // slot assignments, but doing so may disrupt our ability to utilize the corpse looting range properly. - - // For now, it's probably best to leave as-is and let this work itself out in the inventory rework. - }*/ - - else if (rof2Slot.Slot >= invslot::slotAmmo) // Ammo and Main Inventory - TempSlot = rof2Slot.Slot - 1; - - else // Worn Slots - TempSlot = rof2Slot.Slot; - - if (rof2Slot.SubIndex >= EQEmu::invbag::SLOT_BEGIN) // Bag Slots - TempSlot = ((TempSlot + 3) * EQEmu::invbag::SLOT_COUNT) + rof2Slot.SubIndex + 1; - - ServerSlot = TempSlot; + uint32 ServerSlot = EQEmu::invslot::SLOT_INVALID; + + if (rof2_corpse_slot.Type != invtype::typeCorpse || rof2_corpse_slot.SubIndex != invbag::SLOT_INVALID || rof2_corpse_slot.AugIndex != invaug::SOCKET_INVALID) { + ServerSlot = EQEmu::invslot::SLOT_INVALID; + } + + else { + ServerSlot = RoF2ToServerCorpseMainSlot(rof2_corpse_slot.Slot); } - else if (rof2Slot.Type == invtype::typeBank) { - TempSlot = EQEmu::invslot::BANK_BEGIN; - - if (rof2Slot.SubIndex >= EQEmu::invbag::SLOT_BEGIN) - TempSlot += ((rof2Slot.Slot + 3) * EQEmu::invbag::SLOT_COUNT) + rof2Slot.SubIndex + 1; - - else - TempSlot += rof2Slot.Slot; - - ServerSlot = TempSlot; - } - - else if (rof2Slot.Type == invtype::typeSharedBank) { - TempSlot = EQEmu::invslot::SHARED_BANK_BEGIN; - - if (rof2Slot.SubIndex >= EQEmu::invbag::SLOT_BEGIN) - TempSlot += ((rof2Slot.Slot + 3) * EQEmu::invbag::SLOT_COUNT) + rof2Slot.SubIndex + 1; - - else - TempSlot += rof2Slot.Slot; - - ServerSlot = TempSlot; - } - - else if (rof2Slot.Type == invtype::typeTrade) { - TempSlot = EQEmu::invslot::TRADE_BEGIN; - - if (rof2Slot.SubIndex >= EQEmu::invbag::SLOT_BEGIN) - TempSlot += ((rof2Slot.Slot + 3) * EQEmu::invbag::SLOT_COUNT) + rof2Slot.SubIndex + 1; - // OLD CODE: - //TempSlot += 100 + (RoF2Slot.MainSlot * EmuConstants::ITEM_CONTAINER_SIZE) + RoF2Slot.SubSlot; - - else - TempSlot += rof2Slot.Slot; - - ServerSlot = TempSlot; - } - - else if (rof2Slot.Type == invtype::typeWorld) { - TempSlot = EQEmu::invslot::WORLD_BEGIN; - - if (rof2Slot.Slot >= EQEmu::invbag::SLOT_BEGIN) - TempSlot += rof2Slot.Slot; - - ServerSlot = TempSlot; - } - - /*else if (RoF2Slot.SlotType == maps::MapLimbo) { // Cursor Buffer - TempSlot = 31; - - if (RoF2Slot.MainSlot >= 0) - TempSlot += RoF2Slot.MainSlot; - - ServerSlot = TempSlot; - }*/ - - else if (rof2Slot.Type == invtype::typeGuildTribute) { - ServerSlot = INVALID_INDEX; - } - - else if (rof2Slot.Type == invtype::typeCorpse) { - ServerSlot = rof2Slot.Slot + EQEmu::invslot::CORPSE_BEGIN; - } - - Log(Logs::General, Logs::Netcode, "[ERROR] Convert RoF2 Slots: Type %i, Unk2 %i, Main %i, Sub %i, Aug %i, Unk1 %i to Server Slot %i", rof2Slot.Type, rof2Slot.Unknown02, rof2Slot.Slot, rof2Slot.SubIndex, rof2Slot.AugIndex, rof2Slot.Unknown01, ServerSlot); + Log(Logs::Detail, Logs::Netcode, "Convert RoF2 Slot [%i, %i, %i, %i] (unk2: %i, unk1: %i) to Server Slot %i", + rof2_corpse_slot.Type, rof2_corpse_slot.Slot, rof2_corpse_slot.SubIndex, rof2_corpse_slot.AugIndex, rof2_corpse_slot.Unknown02, rof2_corpse_slot.Unknown01, ServerSlot); return ServerSlot; } - static inline uint32 RoF2ToServerTypelessSlot(structs::TypelessInventorySlot_Struct rof2Slot) + static inline uint32 RoF2ToServerCorpseMainSlot(uint32 rof2_corpse_slot) { - uint32 ServerSlot = INVALID_INDEX; - uint32 TempSlot = 0; + uint32 ServerSlot = EQEmu::invslot::SLOT_INVALID; - if (rof2Slot.Slot < 57) { // Worn/Personal Inventory and Cursor (< 33) - if (rof2Slot.Slot == invslot::slotPowerSource) - TempSlot = EQEmu::invslot::SLOT_POWER_SOURCE; - - else if (rof2Slot.Slot >= invslot::slotCursor) // Cursor and Extended Corpse Inventory - TempSlot = rof2Slot.Slot - 3; - - /*else if (RoF2Slot.MainSlot == slots::MainGeneral9 || RoF2Slot.MainSlot == slots::MainGeneral10) { // 9th and 10th RoF2 inventory slots - // Need to figure out what to do when we get these - - // Same as above - }*/ - - else if (rof2Slot.Slot >= invslot::slotAmmo) // Main Inventory and Ammo Slots - TempSlot = rof2Slot.Slot - 1; - - else - TempSlot = rof2Slot.Slot; - - if (rof2Slot.SubIndex >= EQEmu::invbag::SLOT_BEGIN) // Bag Slots - TempSlot = ((TempSlot + 3) * EQEmu::invbag::SLOT_COUNT) + rof2Slot.SubIndex + 1; - - ServerSlot = TempSlot; + if (rof2_corpse_slot <= invslot::CORPSE_END && rof2_corpse_slot >= invslot::CORPSE_BEGIN) { + ServerSlot = rof2_corpse_slot; } - Log(Logs::General, Logs::Netcode, "[ERROR] Convert RoF2 Slots: Main %i, Sub %i, Aug %i, Unk1 %i to Server Slot %i", rof2Slot.Slot, rof2Slot.SubIndex, rof2Slot.AugIndex, rof2Slot.Unknown01, ServerSlot); + Log(Logs::Detail, Logs::Netcode, "Convert RoF2 Corpse Main Slot %i to Server Corpse Slot %i", rof2_corpse_slot, ServerSlot); return ServerSlot; } - static inline uint32 RoF2ToServerCorpseSlot(uint32 rof2CorpseSlot) + static inline uint32 RoF2ToServerTypelessSlot(structs::TypelessInventorySlot_Struct rof2_slot, int16 rof2_type) { - return (rof2CorpseSlot + EQEmu::invslot::CORPSE_BEGIN - 1); + if (rof2_slot.AugIndex < invaug::SOCKET_INVALID || rof2_slot.AugIndex >= invaug::SOCKET_COUNT) { + Log(Logs::Detail, Logs::Netcode, "Convert RoF2 Typeless Slot [%i, %i, %i] (implied type: %i, unk1: %i) to Server Slot %i", + rof2_slot.Slot, rof2_slot.SubIndex, rof2_slot.AugIndex, rof2_type, rof2_slot.Unknown01, EQEmu::invslot::SLOT_INVALID); + + return EQEmu::invslot::SLOT_INVALID; + } + + uint32 ServerSlot = EQEmu::invslot::SLOT_INVALID; + uint32 TempSlot = invslot::SLOT_INVALID; + + switch (rof2_type) { + case invtype::typePossessions: { + if (rof2_slot.Slot >= invslot::POSSESSIONS_BEGIN && rof2_slot.Slot <= invslot::POSSESSIONS_END) { + if (rof2_slot.SubIndex == invbag::SLOT_INVALID) { + ServerSlot = rof2_slot.Slot; + } + + else if (rof2_slot.SubIndex >= invbag::SLOT_BEGIN && rof2_slot.SubIndex <= invbag::SLOT_END) { + if (rof2_slot.Slot < invslot::GENERAL_BEGIN) + return EQEmu::invslot::SLOT_INVALID; + + TempSlot = (rof2_slot.Slot - invslot::GENERAL_BEGIN) * invbag::SLOT_COUNT; + ServerSlot = EQEmu::invbag::GENERAL_BAGS_BEGIN + TempSlot + rof2_slot.SubIndex; + } + } + + break; + } + case invtype::typeBank: { + if (rof2_slot.Slot >= invslot::SLOT_BEGIN && rof2_slot.Slot < invtype::BANK_SIZE) { + if (rof2_slot.SubIndex == invbag::SLOT_INVALID) { + ServerSlot = EQEmu::invslot::BANK_BEGIN + rof2_slot.Slot; + } + + else if (rof2_slot.SubIndex >= invbag::SLOT_BEGIN && rof2_slot.SubIndex <= invbag::SLOT_END) { + TempSlot = rof2_slot.Slot * invbag::SLOT_COUNT; + ServerSlot = EQEmu::invbag::BANK_BAGS_BEGIN + TempSlot + rof2_slot.SubIndex; + } + } + + break; + } + case invtype::typeSharedBank: { + if (rof2_slot.Slot >= invslot::SLOT_BEGIN && rof2_slot.Slot < invtype::SHARED_BANK_SIZE) { + if (rof2_slot.SubIndex == invbag::SLOT_INVALID) { + ServerSlot = EQEmu::invslot::SHARED_BANK_BEGIN + rof2_slot.Slot; + } + + else if (rof2_slot.SubIndex >= invbag::SLOT_BEGIN && rof2_slot.SubIndex <= invbag::SLOT_END) { + TempSlot = rof2_slot.Slot * invbag::SLOT_COUNT; + ServerSlot = EQEmu::invbag::SHARED_BANK_BAGS_BEGIN + TempSlot + rof2_slot.SubIndex; + } + } + + break; + } + case invtype::typeTrade: { + if (rof2_slot.Slot >= invslot::SLOT_BEGIN && rof2_slot.Slot < invtype::TRADE_SIZE) { + if (rof2_slot.SubIndex == invbag::SLOT_INVALID) { + ServerSlot = EQEmu::invslot::TRADE_BEGIN + rof2_slot.Slot; + } + + else if (rof2_slot.SubIndex >= invbag::SLOT_BEGIN && rof2_slot.SubIndex <= invbag::SLOT_END) { + TempSlot = rof2_slot.Slot * invbag::SLOT_COUNT; + ServerSlot = EQEmu::invbag::TRADE_BAGS_BEGIN + TempSlot + rof2_slot.SubIndex; + } + } + + break; + } + case invtype::typeWorld: { + if (rof2_slot.Slot >= invslot::SLOT_BEGIN && rof2_slot.Slot < invtype::WORLD_SIZE) { + ServerSlot = EQEmu::invslot::WORLD_BEGIN + rof2_slot.Slot; + } + + else if (rof2_slot.Slot == invslot::SLOT_INVALID) { + ServerSlot = EQEmu::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE; + } + + break; + } + case invtype::typeLimbo: { + if (rof2_slot.Slot >= invslot::SLOT_BEGIN && rof2_slot.Slot < invtype::LIMBO_SIZE) { + ServerSlot = EQEmu::invslot::slotCursor; + } + + break; + } + case invtype::typeTribute: { + if (rof2_slot.Slot >= invslot::SLOT_BEGIN && rof2_slot.Slot < invtype::TRIBUTE_SIZE) { + ServerSlot = EQEmu::invslot::TRIBUTE_BEGIN + rof2_slot.Slot; + } + + break; + } + case invtype::typeGuildTribute: { + if (rof2_slot.Slot >= invslot::SLOT_BEGIN && rof2_slot.Slot < invtype::GUILD_TRIBUTE_SIZE) { + ServerSlot = EQEmu::invslot::GUILD_TRIBUTE_BEGIN + rof2_slot.Slot; + } + + break; + } + case invtype::typeCorpse: { + if (rof2_slot.Slot >= invslot::CORPSE_BEGIN && rof2_slot.Slot <= invslot::CORPSE_END) { + ServerSlot = rof2_slot.Slot; + } + + break; + } + default: { + + break; + } + } + + Log(Logs::Detail, Logs::Netcode, "Convert RoF2 Typeless Slot [%i, %i, %i] (implied type: %i, unk1: %i) to Server Slot %i", + rof2_slot.Slot, rof2_slot.SubIndex, rof2_slot.AugIndex, rof2_type, rof2_slot.Unknown01, ServerSlot); + + return ServerSlot; } - static inline void ServerToRoF2SayLink(std::string& rof2SayLink, const std::string& serverSayLink) + static inline void ServerToRoF2SayLink(std::string &rof2_saylink, const std::string &server_saylink) { - if ((constants::SAY_LINK_BODY_SIZE == EQEmu::constants::SAY_LINK_BODY_SIZE) || (serverSayLink.find('\x12') == std::string::npos)) { - rof2SayLink = serverSayLink; + if ((constants::SAY_LINK_BODY_SIZE == EQEmu::constants::SAY_LINK_BODY_SIZE) || (server_saylink.find('\x12') == std::string::npos)) { + rof2_saylink = server_saylink; return; } - auto segments = SplitString(serverSayLink, '\x12'); + auto segments = SplitString(server_saylink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { if (segments[segment_iter].length() <= EQEmu::constants::SAY_LINK_BODY_SIZE) { - rof2SayLink.append(segments[segment_iter]); + rof2_saylink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -6145,29 +6297,29 @@ namespace RoF2 // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) // Diff: - rof2SayLink.push_back('\x12'); - rof2SayLink.append(segments[segment_iter]); - rof2SayLink.push_back('\x12'); + rof2_saylink.push_back('\x12'); + rof2_saylink.append(segments[segment_iter]); + rof2_saylink.push_back('\x12'); } else { - rof2SayLink.append(segments[segment_iter]); + rof2_saylink.append(segments[segment_iter]); } } } - static inline void RoF2ToServerSayLink(std::string& serverSayLink, const std::string& rof2SayLink) + static inline void RoF2ToServerSayLink(std::string &server_saylink, const std::string &rof2_saylink) { - if ((EQEmu::constants::SAY_LINK_BODY_SIZE == constants::SAY_LINK_BODY_SIZE) || (rof2SayLink.find('\x12') == std::string::npos)) { - serverSayLink = rof2SayLink; + if ((EQEmu::constants::SAY_LINK_BODY_SIZE == constants::SAY_LINK_BODY_SIZE) || (rof2_saylink.find('\x12') == std::string::npos)) { + server_saylink = rof2_saylink; return; } - auto segments = SplitString(rof2SayLink, '\x12'); + auto segments = SplitString(rof2_saylink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { if (segments[segment_iter].length() <= constants::SAY_LINK_BODY_SIZE) { - serverSayLink.append(segments[segment_iter]); + server_saylink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -6177,12 +6329,12 @@ namespace RoF2 // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) // Diff: - serverSayLink.push_back('\x12'); - serverSayLink.append(segments[segment_iter]); - serverSayLink.push_back('\x12'); + server_saylink.push_back('\x12'); + server_saylink.append(segments[segment_iter]); + server_saylink.push_back('\x12'); } else { - serverSayLink.append(segments[segment_iter]); + server_saylink.append(segments[segment_iter]); } } } diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 772dcc178..112312afd 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -46,18 +46,18 @@ namespace SoD void SerializeItem(EQEmu::OutBuffer& ob, const EQEmu::ItemInstance *inst, int16 slot_id, uint8 depth); // server to client inventory location converters - static inline uint32 ServerToSoDSlot(uint32 ServerSlot); - static inline uint32 ServerToSoDCorpseSlot(uint32 serverCorpseSlot); + static inline uint32 ServerToSoDSlot(uint32 server_slot); + static inline uint32 ServerToSoDCorpseSlot(uint32 server_corpse_slot); // client to server inventory location converters - static inline uint32 SoDToServerSlot(uint32 sodSlot); - static inline uint32 SoDToServerCorpseSlot(uint32 sodCorpseSlot); + static inline uint32 SoDToServerSlot(uint32 sod_slot); + static inline uint32 SoDToServerCorpseSlot(uint32 sod_corpse_slot); // server to client say link converter - static inline void ServerToSoDSayLink(std::string& sodSayLink, const std::string& serverSayLink); + static inline void ServerToSoDSayLink(std::string &sod_saylink, const std::string &server_saylink); // client to server say link converter - static inline void SoDToServerSayLink(std::string& serverSayLink, const std::string& sodSayLink); + static inline void SoDToServerSayLink(std::string &server_saylink, const std::string &sod_saylink); static inline CastingSlot ServerToSoDCastingSlot(EQEmu::CastingSlot slot); static inline EQEmu::CastingSlot SoDToServerCastingSlot(CastingSlot slot); @@ -400,7 +400,7 @@ namespace SoD for (int index = 0; index < item_count; ++index, ++eq) { SerializeItem(ob, (const EQEmu::ItemInstance*)eq->inst, eq->slot_id, 0); if (ob.tellp() == last_pos) - Log(Logs::General, Logs::Netcode, "[STRUCTS] Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + Log(Logs::General, Logs::Netcode, "SoD::ENCODE(OP_CharInventory) Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); last_pos = ob.tellp(); } @@ -464,7 +464,12 @@ namespace SoD FINISH_ENCODE(); } - ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } + ENCODE(OP_DeleteCharge) + { + Log(Logs::Moderate, Logs::Netcode, "SoD::ENCODE(OP_DeleteCharge)"); + + ENCODE_FORWARD(OP_MoveItem); + } ENCODE(OP_DeleteItem) { @@ -1064,7 +1069,7 @@ namespace SoD SerializeItem(ob, (const EQEmu::ItemInstance*)int_struct->inst, int_struct->slot_id, 0); if (ob.tellp() == last_pos) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Serialization failed on item slot %d.", int_struct->slot_id); + Log(Logs::General, Logs::Netcode, "SoD::ENCODE(OP_ItemPacket) Serialization failed on item slot %d.", int_struct->slot_id); delete in; return; } @@ -1125,6 +1130,8 @@ namespace SoD ENCODE_LENGTH_EXACT(LootingItem_Struct); SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); + Log(Logs::Moderate, Logs::Netcode, "SoD::ENCODE(OP_LootItem)"); + OUT(lootee); OUT(looter); eq->slot_id = ServerToSoDCorpseSlot(emu->slot_id); @@ -1286,6 +1293,8 @@ namespace SoD ENCODE_LENGTH_EXACT(MoveItem_Struct); SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); + Log(Logs::Moderate, Logs::Netcode, "SoD::ENCODE(OP_MoveItem)"); + eq->from_slot = ServerToSoDSlot(emu->from_slot); eq->to_slot = ServerToSoDSlot(emu->to_slot); OUT(number_in_stack); @@ -1524,7 +1533,7 @@ namespace SoD // OUT(unknown06160[4]); - // Copy bandoliers where server and client indexes converge + // Copy bandoliers where server and client indices converge for (r = 0; r < EQEmu::profile::BANDOLIERS_SIZE && r < profile::BANDOLIERS_SIZE; ++r) { OUT_str(bandoliers[r].Name); for (uint32 k = 0; k < profile::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true @@ -1533,7 +1542,7 @@ namespace SoD OUT_str(bandoliers[r].Items[k].Name); } } - // Nullify bandoliers where server and client indexes diverge, with a client bias + // Nullify bandoliers where server and client indices diverge, with a client bias for (r = EQEmu::profile::BANDOLIERS_SIZE; r < profile::BANDOLIERS_SIZE; ++r) { eq->bandoliers[r].Name[0] = '\0'; for (uint32 k = 0; k < profile::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true @@ -1545,13 +1554,13 @@ namespace SoD // OUT(unknown07444[5120]); - // Copy potion belt where server and client indexes converge + // Copy potion belt where server and client indices converge for (r = 0; r < EQEmu::profile::POTION_BELT_SIZE && r < profile::POTION_BELT_SIZE; ++r) { OUT(potionbelt.Items[r].ID); OUT(potionbelt.Items[r].Icon); OUT_str(potionbelt.Items[r].Name); } - // Nullify potion belt where server and client indexes diverge, with a client bias + // Nullify potion belt where server and client indices diverge, with a client bias for (r = EQEmu::profile::POTION_BELT_SIZE; r < profile::POTION_BELT_SIZE; ++r) { eq->potionbelt.Items[r].ID = 0; eq->potionbelt.Items[r].Icon = 0; @@ -3140,6 +3149,8 @@ namespace SoD DECODE_LENGTH_EXACT(structs::LootingItem_Struct); SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); + Log(Logs::Moderate, Logs::Netcode, "SoD::DECODE(OP_LootItem)"); + IN(lootee); IN(looter); emu->slot_id = SoDToServerCorpseSlot(eq->slot_id); @@ -3153,7 +3164,7 @@ namespace SoD DECODE_LENGTH_EXACT(structs::MoveItem_Struct); SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); - Log(Logs::General, Logs::Netcode, "[SoD] Moved item from %u to %u", eq->from_slot, eq->to_slot); + Log(Logs::Moderate, Logs::Netcode, "SoD::DECODE(OP_MoveItem)"); emu->from_slot = SoDToServerSlot(eq->from_slot); emu->to_slot = SoDToServerSlot(eq->to_slot); @@ -3474,7 +3485,7 @@ namespace SoD ibs.nodrop = item->NoDrop; ibs.attune = item->Attuneable; ibs.size = item->Size; - ibs.slots = SwapBits21And22(item->Slots); + ibs.slots = item->Slots; ibs.price = item->Price; ibs.icon = item->Icon; ibs.unknown1 = 1; @@ -3737,94 +3748,216 @@ namespace SoD ob.write((const char*)&subitem_count, sizeof(uint32)); - for (uint32 index = EQEmu::invbag::SLOT_BEGIN; index <= EQEmu::invbag::SLOT_END; ++index) { - EQEmu::ItemInstance* sub = inst->GetItem(index); - if (!sub) - continue; + // moved outside of loop since it is not modified within that scope + int16 SubSlotNumber = EQEmu::invbag::SLOT_INVALID; - int SubSlotNumber = INVALID_INDEX; - if (slot_id_in >= EQEmu::invslot::GENERAL_BEGIN && slot_id_in <= EQEmu::invslot::GENERAL_END) - SubSlotNumber = (((slot_id_in + 3) * EQEmu::invbag::SLOT_COUNT) + index + 1); - else if (slot_id_in >= EQEmu::invslot::BANK_BEGIN && slot_id_in <= EQEmu::invslot::BANK_END) - SubSlotNumber = (((slot_id_in - EQEmu::invslot::BANK_BEGIN) * EQEmu::invbag::SLOT_COUNT) + EQEmu::invbag::BANK_BAGS_BEGIN + index); - else if (slot_id_in >= EQEmu::invslot::SHARED_BANK_BEGIN && slot_id_in <= EQEmu::invslot::SHARED_BANK_END) - SubSlotNumber = (((slot_id_in - EQEmu::invslot::SHARED_BANK_BEGIN) * EQEmu::invbag::SLOT_COUNT) + EQEmu::invbag::SHARED_BANK_BAGS_BEGIN + index); - else - SubSlotNumber = slot_id_in; + if (slot_id_in <= EQEmu::invslot::slotGeneral8 && slot_id_in >= EQEmu::invslot::GENERAL_BEGIN) + SubSlotNumber = EQEmu::invbag::GENERAL_BAGS_BEGIN + ((slot_id_in - EQEmu::invslot::GENERAL_BEGIN) * EQEmu::invbag::SLOT_COUNT); + else if (slot_id_in <= EQEmu::invslot::GENERAL_END && slot_id_in >= EQEmu::invslot::slotGeneral9) + SubSlotNumber = EQEmu::invbag::SLOT_INVALID; + else if (slot_id_in == EQEmu::invslot::slotCursor) + SubSlotNumber = EQEmu::invbag::CURSOR_BAG_BEGIN; + else if (slot_id_in <= EQEmu::invslot::BANK_END && slot_id_in >= EQEmu::invslot::BANK_BEGIN) + SubSlotNumber = EQEmu::invbag::BANK_BAGS_BEGIN + ((slot_id_in - EQEmu::invslot::BANK_BEGIN) * EQEmu::invbag::SLOT_COUNT); + else if (slot_id_in <= EQEmu::invslot::SHARED_BANK_END && slot_id_in >= EQEmu::invslot::SHARED_BANK_BEGIN) + SubSlotNumber = EQEmu::invbag::SHARED_BANK_BAGS_BEGIN + ((slot_id_in - EQEmu::invslot::SHARED_BANK_BEGIN) * EQEmu::invbag::SLOT_COUNT); + else + SubSlotNumber = slot_id_in; // not sure if this is the best way to handle this..leaving for now - ob.write((const char*)&index, sizeof(uint32)); + if (SubSlotNumber != EQEmu::invbag::SLOT_INVALID) { + for (uint32 index = EQEmu::invbag::SLOT_BEGIN; index <= EQEmu::invbag::SLOT_END; ++index) { + EQEmu::ItemInstance* sub = inst->GetItem(index); + if (!sub) + continue; - SerializeItem(ob, sub, SubSlotNumber, (depth + 1)); - ++subitem_count; + ob.write((const char*)&index, sizeof(uint32)); + + SerializeItem(ob, sub, SubSlotNumber, (depth + 1)); + ++subitem_count; + } + + if (subitem_count) + ob.overwrite(count_pos, (const char*)&subitem_count, sizeof(uint32)); } - - if (subitem_count) - ob.overwrite(count_pos, (const char*)&subitem_count, sizeof(uint32)); } static inline uint32 ServerToSoDSlot(uint32 serverSlot) { - uint32 SoDSlot = 0; + uint32 SoDSlot = invslot::SLOT_INVALID; - if (serverSlot >= EQEmu::invslot::slotAmmo && serverSlot <= 53) // Cursor/Ammo/Power Source and Normal Inventory Slots - SoDSlot = serverSlot + 1; - else if (serverSlot >= EQEmu::invbag::GENERAL_BAGS_BEGIN && serverSlot <= EQEmu::invbag::CURSOR_BAG_END) - SoDSlot = serverSlot + 11; - else if (serverSlot >= EQEmu::invbag::BANK_BAGS_BEGIN && serverSlot <= EQEmu::invbag::BANK_BAGS_END) - SoDSlot = serverSlot + 1; - else if (serverSlot >= EQEmu::invbag::SHARED_BANK_BAGS_BEGIN && serverSlot <= EQEmu::invbag::SHARED_BANK_BAGS_END) - SoDSlot = serverSlot + 1; - else if (serverSlot == EQEmu::invslot::SLOT_POWER_SOURCE) - SoDSlot = invslot::slotPowerSource; - else + if (serverSlot <= EQEmu::invslot::slotGeneral8) { SoDSlot = serverSlot; + } + + else if (serverSlot <= EQEmu::invslot::CORPSE_END && serverSlot >= EQEmu::invslot::slotCursor) { + SoDSlot = serverSlot - 2; + } + + else if (serverSlot <= EQEmu::invbag::GENERAL_BAGS_8_END && serverSlot >= EQEmu::invbag::GENERAL_BAGS_BEGIN) { + SoDSlot = serverSlot + 11; + } + + else if (serverSlot <= EQEmu::invbag::CURSOR_BAG_END && serverSlot >= EQEmu::invbag::CURSOR_BAG_BEGIN) { + SoDSlot = serverSlot - 9; + } + + else if (serverSlot <= EQEmu::invslot::TRIBUTE_END && serverSlot >= EQEmu::invslot::TRIBUTE_BEGIN) { + SoDSlot = serverSlot; + } + + else if (serverSlot <= EQEmu::invslot::GUILD_TRIBUTE_END && serverSlot >= EQEmu::invslot::GUILD_TRIBUTE_BEGIN) { + SoDSlot = serverSlot; + } + + else if (serverSlot == EQEmu::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE) { + SoDSlot = serverSlot; + } + + else if (serverSlot <= EQEmu::invslot::BANK_END && serverSlot >= EQEmu::invslot::BANK_BEGIN) { + SoDSlot = serverSlot; + } + + else if (serverSlot <= EQEmu::invbag::BANK_BAGS_END && serverSlot >= EQEmu::invbag::BANK_BAGS_BEGIN) { + SoDSlot = serverSlot + 1; + } + + else if (serverSlot <= EQEmu::invslot::SHARED_BANK_END && serverSlot >= EQEmu::invslot::SHARED_BANK_BEGIN) { + SoDSlot = serverSlot; + } + + else if (serverSlot <= EQEmu::invbag::SHARED_BANK_BAGS_END && serverSlot >= EQEmu::invbag::SHARED_BANK_BAGS_BEGIN) { + SoDSlot = serverSlot + 1; + } + + else if (serverSlot <= EQEmu::invslot::TRADE_END && serverSlot >= EQEmu::invslot::TRADE_BEGIN) { + SoDSlot = serverSlot; + } + + else if (serverSlot <= EQEmu::invbag::TRADE_BAGS_END && serverSlot >= EQEmu::invbag::TRADE_BAGS_BEGIN) { + SoDSlot = serverSlot; + } + + else if (serverSlot <= EQEmu::invslot::WORLD_END && serverSlot >= EQEmu::invslot::WORLD_BEGIN) { + SoDSlot = serverSlot; + } + + Log(Logs::Detail, Logs::Netcode, "Convert Server Slot %i to SoD Slot %i", serverSlot, SoDSlot); + return SoDSlot; } - static inline uint32 ServerToSoDCorpseSlot(uint32 serverCorpseSlot) + static inline uint32 ServerToSoDCorpseSlot(uint32 server_corpse_slot) { - //uint32 SoDCorpse; - return (serverCorpseSlot + 1); + uint32 SoDSlot = invslot::SLOT_INVALID; + + if (server_corpse_slot <= EQEmu::invslot::slotGeneral8 && server_corpse_slot >= EQEmu::invslot::slotGeneral1) { + SoDSlot = server_corpse_slot; + } + + else if (server_corpse_slot <= EQEmu::invslot::CORPSE_END && server_corpse_slot >= EQEmu::invslot::slotCursor) { + SoDSlot = server_corpse_slot - 2; + } + + Log(Logs::Detail, Logs::Netcode, "Convert Server Corpse Slot %i to SoD Corpse Slot %i", server_corpse_slot, SoDSlot); + + return SoDSlot; } - static inline uint32 SoDToServerSlot(uint32 sodSlot) + static inline uint32 SoDToServerSlot(uint32 sod_slot) { - uint32 ServerSlot = 0; + uint32 server_slot = EQEmu::invslot::SLOT_INVALID; - if (sodSlot >= invslot::slotAmmo && sodSlot <= invslot::CORPSE_END) // Cursor/Ammo/Power Source and Normal Inventory Slots - ServerSlot = sodSlot - 1; - else if (sodSlot >= invbag::GENERAL_BAGS_BEGIN && sodSlot <= invbag::CURSOR_BAG_END) - ServerSlot = sodSlot - 11; - else if (sodSlot >= invbag::BANK_BAGS_BEGIN && sodSlot <= invbag::BANK_BAGS_END) - ServerSlot = sodSlot - 1; - else if (sodSlot >= invbag::SHARED_BANK_BAGS_BEGIN && sodSlot <= invbag::SHARED_BANK_BAGS_END) - ServerSlot = sodSlot - 1; - else if (sodSlot == invslot::slotPowerSource) - ServerSlot = EQEmu::invslot::SLOT_POWER_SOURCE; - else - ServerSlot = sodSlot; - return ServerSlot; + if (sod_slot <= invslot::slotGeneral8) { + server_slot = sod_slot; + } + + else if (sod_slot <= invslot::CORPSE_END && sod_slot >= invslot::slotCursor) { + server_slot = sod_slot + 2; + } + + else if (sod_slot <= invbag::GENERAL_BAGS_END && sod_slot >= invbag::GENERAL_BAGS_BEGIN) { + server_slot = sod_slot - 11; + } + + else if (sod_slot <= invbag::CURSOR_BAG_END && sod_slot >= invbag::CURSOR_BAG_BEGIN) { + server_slot = sod_slot + 9; + } + + else if (sod_slot <= invslot::TRIBUTE_END && sod_slot >= invslot::TRIBUTE_BEGIN) { + server_slot = sod_slot; + } + + else if (sod_slot <= invslot::GUILD_TRIBUTE_END && sod_slot >= invslot::GUILD_TRIBUTE_BEGIN) { + server_slot = sod_slot; + } + + else if (sod_slot == invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE) { + server_slot = sod_slot; + } + + else if (sod_slot <= invslot::BANK_END && sod_slot >= invslot::BANK_BEGIN) { + server_slot = sod_slot; + } + + else if (sod_slot <= invbag::BANK_BAGS_END && sod_slot >= invbag::BANK_BAGS_BEGIN) { + server_slot = sod_slot - 1; + } + + else if (sod_slot <= invslot::SHARED_BANK_END && sod_slot >= invslot::SHARED_BANK_BEGIN) { + server_slot = sod_slot; + } + + else if (sod_slot <= invbag::SHARED_BANK_BAGS_END && sod_slot >= invbag::SHARED_BANK_BAGS_BEGIN) { + server_slot = sod_slot - 1; + } + + else if (sod_slot <= invslot::TRADE_END && sod_slot >= invslot::TRADE_BEGIN) { + server_slot = sod_slot; + } + + else if (sod_slot <= invbag::TRADE_BAGS_END && sod_slot >= invbag::TRADE_BAGS_BEGIN) { + server_slot = sod_slot; + } + + else if (sod_slot <= invslot::WORLD_END && sod_slot >= invslot::WORLD_BEGIN) { + server_slot = sod_slot; + } + + Log(Logs::Detail, Logs::Netcode, "Convert SoD Slot %i to Server Slot %i", sod_slot, server_slot); + + return server_slot; } - static inline uint32 SoDToServerCorpseSlot(uint32 sodCorpseSlot) + static inline uint32 SoDToServerCorpseSlot(uint32 sod_corpse_slot) { - //uint32 ServerCorpse; - return (sodCorpseSlot - 1); + uint32 server_slot = EQEmu::invslot::SLOT_INVALID; + + if (sod_corpse_slot <= invslot::slotGeneral8 && sod_corpse_slot >= invslot::slotGeneral1) { + server_slot = sod_corpse_slot; + } + + else if (sod_corpse_slot <= invslot::CORPSE_END && sod_corpse_slot >= invslot::slotCursor) { + server_slot = sod_corpse_slot + 2; + } + + Log(Logs::Detail, Logs::Netcode, "Convert SoD Corpse Slot %i to Server Corpse Slot %i", sod_corpse_slot, server_slot); + + return server_slot; } - static inline void ServerToSoDSayLink(std::string& sodSayLink, const std::string& serverSayLink) + static inline void ServerToSoDSayLink(std::string &sod_saylink, const std::string &server_saylink) { - if ((constants::SAY_LINK_BODY_SIZE == EQEmu::constants::SAY_LINK_BODY_SIZE) || (serverSayLink.find('\x12') == std::string::npos)) { - sodSayLink = serverSayLink; + if ((constants::SAY_LINK_BODY_SIZE == EQEmu::constants::SAY_LINK_BODY_SIZE) || (server_saylink.find('\x12') == std::string::npos)) { + sod_saylink = server_saylink; return; } - auto segments = SplitString(serverSayLink, '\x12'); + auto segments = SplitString(server_saylink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { if (segments[segment_iter].length() <= EQEmu::constants::SAY_LINK_BODY_SIZE) { - sodSayLink.append(segments[segment_iter]); + sod_saylink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -3834,37 +3967,37 @@ namespace SoD // SoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (50) // Diff: ^^^^^ ^ - sodSayLink.push_back('\x12'); - sodSayLink.append(segments[segment_iter].substr(0, 31)); - sodSayLink.append(segments[segment_iter].substr(36, 5)); + sod_saylink.push_back('\x12'); + sod_saylink.append(segments[segment_iter].substr(0, 31)); + sod_saylink.append(segments[segment_iter].substr(36, 5)); if (segments[segment_iter][41] == '0') - sodSayLink.push_back(segments[segment_iter][42]); + sod_saylink.push_back(segments[segment_iter][42]); else - sodSayLink.push_back('F'); + sod_saylink.push_back('F'); - sodSayLink.append(segments[segment_iter].substr(43)); - sodSayLink.push_back('\x12'); + sod_saylink.append(segments[segment_iter].substr(43)); + sod_saylink.push_back('\x12'); } else { - sodSayLink.append(segments[segment_iter]); + sod_saylink.append(segments[segment_iter]); } } } - static inline void SoDToServerSayLink(std::string& serverSayLink, const std::string& sodSayLink) + static inline void SoDToServerSayLink(std::string &server_saylink, const std::string &sod_saylink) { - if ((EQEmu::constants::SAY_LINK_BODY_SIZE == constants::SAY_LINK_BODY_SIZE) || (sodSayLink.find('\x12') == std::string::npos)) { - serverSayLink = sodSayLink; + if ((EQEmu::constants::SAY_LINK_BODY_SIZE == constants::SAY_LINK_BODY_SIZE) || (sod_saylink.find('\x12') == std::string::npos)) { + server_saylink = sod_saylink; return; } - auto segments = SplitString(sodSayLink, '\x12'); + auto segments = SplitString(sod_saylink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { if (segments[segment_iter].length() <= constants::SAY_LINK_BODY_SIZE) { - serverSayLink.append(segments[segment_iter]); + server_saylink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -3874,16 +4007,16 @@ namespace SoD // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) // Diff: ^^^^^ ^ - serverSayLink.push_back('\x12'); - serverSayLink.append(segments[segment_iter].substr(0, 31)); - serverSayLink.append("00000"); - serverSayLink.append(segments[segment_iter].substr(31, 5)); - serverSayLink.push_back('0'); - serverSayLink.append(segments[segment_iter].substr(36)); - serverSayLink.push_back('\x12'); + server_saylink.push_back('\x12'); + server_saylink.append(segments[segment_iter].substr(0, 31)); + server_saylink.append("00000"); + server_saylink.append(segments[segment_iter].substr(31, 5)); + server_saylink.push_back('0'); + server_saylink.append(segments[segment_iter].substr(36)); + server_saylink.push_back('\x12'); } else { - serverSayLink.append(segments[segment_iter]); + server_saylink.append(segments[segment_iter]); } } } diff --git a/common/patches/sod_limits.h b/common/patches/sod_limits.h index 555425cc1..82381355e 100644 --- a/common/patches/sod_limits.h +++ b/common/patches/sod_limits.h @@ -150,6 +150,8 @@ namespace SoD const int16 SLOT_INVALID = IINVALID; const int16 SLOT_BEGIN = INULL; + const int16 SLOT_TRADESKILL_EXPERIMENT_COMBINE = 1000; + const int16 POSSESSIONS_BEGIN = slotCharm; const int16 POSSESSIONS_END = slotCursor; const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1; diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 85f326edb..52af1244f 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -46,21 +46,21 @@ namespace SoF void SerializeItem(EQEmu::OutBuffer& ob, const EQEmu::ItemInstance *inst, int16 slot_id, uint8 depth); // server to client inventory location converters - static inline uint32 ServerToSoFSlot(uint32 serverSlot); - static inline uint32 ServerToSoFCorpseSlot(uint32 serverCorpseSlot); + static inline uint32 ServerToSoFSlot(uint32 server_slot); + static inline uint32 ServerToSoFCorpseSlot(uint32 server_corpse_slot); // client to server inventory location converters - static inline uint32 SoFToServerSlot(uint32 sofSlot); - static inline uint32 SoFToServerCorpseSlot(uint32 sofCorpseSlot); + static inline uint32 SoFToServerSlot(uint32 sof_slot); + static inline uint32 SoFToServerCorpseSlot(uint32 sof_corpse_slot); // server to client say link converter - static inline void ServerToSoFSayLink(std::string& sofSayLink, const std::string& serverSayLink); + static inline void ServerToSoFSayLink(std::string &sof_saylink, const std::string &server_saylink); // client to server say link converter - static inline void SoFToServerSayLink(std::string& serverSayLink, const std::string& sofSayLink); + static inline void SoFToServerSayLink(std::string &server_saylink, const std::string &sof_saylink); static inline CastingSlot ServerToSoFCastingSlot(EQEmu::CastingSlot slot); - static inline EQEmu::CastingSlot SoFToServerCastingSlot(CastingSlot slot, uint32 itemlocation); + static inline EQEmu::CastingSlot SoFToServerCastingSlot(CastingSlot slot, uint32 item_location); static inline int ServerToSoFBuffSlot(int index); static inline int SoFToServerBuffSlot(int index); @@ -103,8 +103,6 @@ namespace SoF signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - Log(Logs::General, Logs::Netcode, "[IDENTIFY] Registered patch %s", name); } @@ -382,7 +380,7 @@ namespace SoF for (int index = 0; index < item_count; ++index, ++eq) { SerializeItem(ob, (const EQEmu::ItemInstance*)eq->inst, eq->slot_id, 0); if (ob.tellp() == last_pos) - Log(Logs::General, Logs::Netcode, "[STRUCTS] Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + Log(Logs::General, Logs::Netcode, "SoF::ENCODE(OP_CharInventory) Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); last_pos = ob.tellp(); } @@ -445,7 +443,12 @@ namespace SoF FINISH_ENCODE(); } - ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } + ENCODE(OP_DeleteCharge) + { + Log(Logs::Moderate, Logs::Netcode, "SoF::ENCODE(OP_DeleteCharge)"); + + ENCODE_FORWARD(OP_MoveItem); + } ENCODE(OP_DeleteItem) { @@ -861,7 +864,7 @@ namespace SoF SerializeItem(ob, (const EQEmu::ItemInstance*)int_struct->inst, int_struct->slot_id, 0); if (ob.tellp() == last_pos) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Serialization failed on item slot %d.", int_struct->slot_id); + Log(Logs::General, Logs::Netcode, "SoF::ENCODE(OP_ItemPacket) Serialization failed on item slot %d.", int_struct->slot_id); delete in; return; } @@ -922,6 +925,8 @@ namespace SoF ENCODE_LENGTH_EXACT(LootingItem_Struct); SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); + Log(Logs::Moderate, Logs::Netcode, "SoF::ENCODE(OP_LootItem)"); + OUT(lootee); OUT(looter); eq->slot_id = ServerToSoFCorpseSlot(emu->slot_id); @@ -965,6 +970,8 @@ namespace SoF ENCODE_LENGTH_EXACT(MoveItem_Struct); SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); + Log(Logs::Moderate, Logs::Netcode, "SoF::ENCODE(OP_MoveItem)"); + eq->from_slot = ServerToSoFSlot(emu->from_slot); eq->to_slot = ServerToSoFSlot(emu->to_slot); OUT(number_in_stack); @@ -1190,7 +1197,7 @@ namespace SoF // OUT(unknown06160[4]); - // Copy bandoliers where server and client indexes converge + // Copy bandoliers where server and client indices converge for (r = 0; r < EQEmu::profile::BANDOLIERS_SIZE && r < profile::BANDOLIERS_SIZE; ++r) { OUT_str(bandoliers[r].Name); for (uint32 k = 0; k < profile::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true @@ -1199,7 +1206,7 @@ namespace SoF OUT_str(bandoliers[r].Items[k].Name); } } - // Nullify bandoliers where server and client indexes diverge, with a client bias + // Nullify bandoliers where server and client indices diverge, with a client bias for (r = EQEmu::profile::BANDOLIERS_SIZE; r < profile::BANDOLIERS_SIZE; ++r) { eq->bandoliers[r].Name[0] = '\0'; for (uint32 k = 0; k < profile::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true @@ -1211,13 +1218,13 @@ namespace SoF // OUT(unknown07444[5120]); - // Copy potion belt where server and client indexes converge + // Copy potion belt where server and client indices converge for (r = 0; r < EQEmu::profile::POTION_BELT_SIZE && r < profile::POTION_BELT_SIZE; ++r) { OUT(potionbelt.Items[r].ID); OUT(potionbelt.Items[r].Icon); OUT_str(potionbelt.Items[r].Name); } - // Nullify potion belt where server and client indexes diverge, with a client bias + // Nullify potion belt where server and client indices diverge, with a client bias for (r = EQEmu::profile::POTION_BELT_SIZE; r < profile::POTION_BELT_SIZE; ++r) { eq->potionbelt.Items[r].ID = 0; eq->potionbelt.Items[r].Icon = 0; @@ -2548,6 +2555,8 @@ namespace SoF DECODE_LENGTH_EXACT(structs::LootingItem_Struct); SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); + Log(Logs::Moderate, Logs::Netcode, "SoF::DECODE(OP_LootItem)"); + IN(lootee); IN(looter); emu->slot_id = SoFToServerCorpseSlot(eq->slot_id); @@ -2561,7 +2570,7 @@ namespace SoF DECODE_LENGTH_EXACT(structs::MoveItem_Struct); SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); - Log(Logs::General, Logs::Netcode, "[SoF] Moved item from %u to %u", eq->from_slot, eq->to_slot); + Log(Logs::Moderate, Logs::Netcode, "SoF::DECODE(OP_MoveItem)"); emu->from_slot = SoFToServerSlot(eq->from_slot); emu->to_slot = SoFToServerSlot(eq->to_slot); @@ -2867,7 +2876,7 @@ namespace SoF ibs.nodrop = item->NoDrop; ibs.attune = item->Attuneable; ibs.size = item->Size; - ibs.slots = SwapBits21And22(item->Slots); + ibs.slots = item->Slots; ibs.price = item->Price; ibs.icon = item->Icon; ibs.unknown1 = 1; @@ -3129,96 +3138,224 @@ namespace SoF ob.write((const char*)&subitem_count, sizeof(uint32)); - for (uint32 index = EQEmu::invbag::SLOT_BEGIN; index <= EQEmu::invbag::SLOT_END; ++index) { - EQEmu::ItemInstance* sub = inst->GetItem(index); - if (!sub) - continue; + // moved outside of loop since it is not modified within that scope + int16 SubSlotNumber = EQEmu::invbag::SLOT_INVALID; - int SubSlotNumber = INVALID_INDEX; - if (slot_id_in >= EQEmu::invslot::GENERAL_BEGIN && slot_id_in <= EQEmu::invslot::GENERAL_END) - SubSlotNumber = (((slot_id_in + 3) * EQEmu::invbag::SLOT_COUNT) + index + 1); - else if (slot_id_in >= EQEmu::invslot::BANK_BEGIN && slot_id_in <= EQEmu::invslot::BANK_END) - SubSlotNumber = (((slot_id_in - EQEmu::invslot::BANK_BEGIN) * EQEmu::invbag::SLOT_COUNT) + EQEmu::invbag::BANK_BAGS_BEGIN + index); - else if (slot_id_in >= EQEmu::invslot::SHARED_BANK_BEGIN && slot_id_in <= EQEmu::invslot::SHARED_BANK_END) - SubSlotNumber = (((slot_id_in - EQEmu::invslot::SHARED_BANK_BEGIN) * EQEmu::invbag::SLOT_COUNT) + EQEmu::invbag::SHARED_BANK_BAGS_BEGIN + index); - else - SubSlotNumber = slot_id_in; + if (slot_id_in <= EQEmu::invslot::slotGeneral8 && slot_id_in >= EQEmu::invslot::GENERAL_BEGIN) + SubSlotNumber = EQEmu::invbag::GENERAL_BAGS_BEGIN + ((slot_id_in - EQEmu::invslot::GENERAL_BEGIN) * EQEmu::invbag::SLOT_COUNT); + else if (slot_id_in <= EQEmu::invslot::GENERAL_END && slot_id_in >= EQEmu::invslot::slotGeneral9) + SubSlotNumber = EQEmu::invbag::SLOT_INVALID; + else if (slot_id_in == EQEmu::invslot::slotCursor) + SubSlotNumber = EQEmu::invbag::CURSOR_BAG_BEGIN; + else if (slot_id_in <= EQEmu::invslot::BANK_END && slot_id_in >= EQEmu::invslot::BANK_BEGIN) + SubSlotNumber = EQEmu::invbag::BANK_BAGS_BEGIN + ((slot_id_in - EQEmu::invslot::BANK_BEGIN) * EQEmu::invbag::SLOT_COUNT); + else if (slot_id_in <= EQEmu::invslot::SHARED_BANK_END && slot_id_in >= EQEmu::invslot::SHARED_BANK_BEGIN) + SubSlotNumber = EQEmu::invbag::SHARED_BANK_BAGS_BEGIN + ((slot_id_in - EQEmu::invslot::SHARED_BANK_BEGIN) * EQEmu::invbag::SLOT_COUNT); + else + SubSlotNumber = slot_id_in; // not sure if this is the best way to handle this..leaving for now - ob.write((const char*)&index, sizeof(uint32)); + if (SubSlotNumber != EQEmu::invbag::SLOT_INVALID) { + for (uint32 index = EQEmu::invbag::SLOT_BEGIN; index <= EQEmu::invbag::SLOT_END; ++index) { + EQEmu::ItemInstance* sub = inst->GetItem(index); + if (!sub) + continue; - SerializeItem(ob, sub, SubSlotNumber, (depth + 1)); - ++subitem_count; + ob.write((const char*)&index, sizeof(uint32)); + + SerializeItem(ob, sub, SubSlotNumber, (depth + 1)); + ++subitem_count; + } + + if (subitem_count) + ob.overwrite(count_pos, (const char*)&subitem_count, sizeof(uint32)); } - - if (subitem_count) - ob.overwrite(count_pos, (const char*)&subitem_count, sizeof(uint32)); } - static inline uint32 ServerToSoFSlot(uint32 serverSlot) + static inline uint32 ServerToSoFSlot(uint32 server_slot) { - uint32 SoFSlot = 0; + uint32 sof_slot = invslot::SLOT_INVALID; - if (serverSlot >= EQEmu::invslot::slotAmmo && serverSlot <= 53) // Cursor/Ammo/Power Source and Normal Inventory Slots - SoFSlot = serverSlot + 1; - else if (serverSlot >= EQEmu::invbag::GENERAL_BAGS_BEGIN && serverSlot <= EQEmu::invbag::CURSOR_BAG_END) - SoFSlot = serverSlot + 11; - else if (serverSlot >= EQEmu::invbag::BANK_BAGS_BEGIN && serverSlot <= EQEmu::invbag::BANK_BAGS_END) - SoFSlot = serverSlot + 1; - else if (serverSlot >= EQEmu::invbag::SHARED_BANK_BAGS_BEGIN && serverSlot <= EQEmu::invbag::SHARED_BANK_BAGS_END) - SoFSlot = serverSlot + 1; - else if (serverSlot == EQEmu::invslot::SLOT_POWER_SOURCE) - SoFSlot = invslot::slotPowerSource; - else - SoFSlot = serverSlot; + if (server_slot <= EQEmu::invslot::slotGeneral8) { + sof_slot = server_slot; + } + + else if (server_slot <= EQEmu::invslot::CORPSE_END && server_slot >= EQEmu::invslot::slotCursor) { + sof_slot = server_slot - 2; + } + + else if (server_slot <= EQEmu::invbag::GENERAL_BAGS_8_END && server_slot >= EQEmu::invbag::GENERAL_BAGS_BEGIN) { + sof_slot = server_slot + 11; + } + + else if (server_slot <= EQEmu::invbag::CURSOR_BAG_END && server_slot >= EQEmu::invbag::CURSOR_BAG_BEGIN) { + sof_slot = server_slot - 9; + } + + else if (server_slot <= EQEmu::invslot::TRIBUTE_END && server_slot >= EQEmu::invslot::TRIBUTE_BEGIN) { + sof_slot = server_slot; + } + + else if (server_slot <= EQEmu::invslot::GUILD_TRIBUTE_END && server_slot >= EQEmu::invslot::GUILD_TRIBUTE_BEGIN) { + sof_slot = server_slot; + } + + else if (server_slot == EQEmu::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE) { + sof_slot = server_slot; + } + + else if (server_slot <= EQEmu::invslot::BANK_END && server_slot >= EQEmu::invslot::BANK_BEGIN) { + sof_slot = server_slot; + } + + else if (server_slot <= EQEmu::invbag::BANK_BAGS_END && server_slot >= EQEmu::invbag::BANK_BAGS_BEGIN) { + sof_slot = server_slot + 1; + } + + else if (server_slot <= EQEmu::invslot::SHARED_BANK_END && server_slot >= EQEmu::invslot::SHARED_BANK_BEGIN) { + sof_slot = server_slot; + } + + else if (server_slot <= EQEmu::invbag::SHARED_BANK_BAGS_END && server_slot >= EQEmu::invbag::SHARED_BANK_BAGS_BEGIN) { + sof_slot = server_slot + 1; + } + + else if (server_slot <= EQEmu::invslot::TRADE_END && server_slot >= EQEmu::invslot::TRADE_BEGIN) { + sof_slot = server_slot; + } + + else if (server_slot <= EQEmu::invbag::TRADE_BAGS_END && server_slot >= EQEmu::invbag::TRADE_BAGS_BEGIN) { + sof_slot = server_slot; + } + + else if (server_slot <= EQEmu::invslot::WORLD_END && server_slot >= EQEmu::invslot::WORLD_BEGIN) { + sof_slot = server_slot; + } + + Log(Logs::Detail, Logs::Netcode, "Convert Server Slot %i to SoF Slot %i", server_slot, sof_slot); + + return sof_slot; + } + + static inline uint32 ServerToSoFCorpseSlot(uint32 server_corpse_slot) + { + uint32 SoFSlot = invslot::SLOT_INVALID; + + if (server_corpse_slot <= EQEmu::invslot::slotGeneral8 && server_corpse_slot >= EQEmu::invslot::slotGeneral1) { + SoFSlot = server_corpse_slot; + } + + else if (server_corpse_slot <= EQEmu::invslot::CORPSE_END && server_corpse_slot >= EQEmu::invslot::slotCursor) { + SoFSlot = server_corpse_slot - 2; + } + + Log(Logs::Detail, + Logs::Netcode, + "Convert Server Corpse Slot %i to SoF Corpse Slot %i", + server_corpse_slot, + SoFSlot); return SoFSlot; } - static inline uint32 ServerToSoFCorpseSlot(uint32 serverCorpseSlot) + static inline uint32 SoFToServerSlot(uint32 sof_slot) { - //uint32 SoFCorpse; - return (serverCorpseSlot + 1); + uint32 server_slot = EQEmu::invslot::SLOT_INVALID; + + if (sof_slot <= invslot::slotGeneral8) { + server_slot = sof_slot; + } + + else if (sof_slot <= invslot::CORPSE_END && sof_slot >= invslot::slotCursor) { + server_slot = sof_slot + 2; + } + + else if (sof_slot <= invbag::GENERAL_BAGS_END && sof_slot >= invbag::GENERAL_BAGS_BEGIN) { + server_slot = sof_slot - 11; + } + + else if (sof_slot <= invbag::CURSOR_BAG_END && sof_slot >= invbag::CURSOR_BAG_BEGIN) { + server_slot = sof_slot + 9; + } + + else if (sof_slot <= invslot::TRIBUTE_END && sof_slot >= invslot::TRIBUTE_BEGIN) { + server_slot = sof_slot; + } + + else if (sof_slot <= invslot::GUILD_TRIBUTE_END && sof_slot >= invslot::GUILD_TRIBUTE_BEGIN) { + server_slot = sof_slot; + } + + else if (sof_slot == invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE) { + server_slot = sof_slot; + } + + else if (sof_slot <= invslot::BANK_END && sof_slot >= invslot::BANK_BEGIN) { + server_slot = sof_slot; + } + + else if (sof_slot <= invbag::BANK_BAGS_END && sof_slot >= invbag::BANK_BAGS_BEGIN) { + server_slot = sof_slot - 1; + } + + else if (sof_slot <= invslot::SHARED_BANK_END && sof_slot >= invslot::SHARED_BANK_BEGIN) { + server_slot = sof_slot; + } + + else if (sof_slot <= invbag::SHARED_BANK_BAGS_END && sof_slot >= invbag::SHARED_BANK_BAGS_BEGIN) { + server_slot = sof_slot - 1; + } + + else if (sof_slot <= invslot::TRADE_END && sof_slot >= invslot::TRADE_BEGIN) { + server_slot = sof_slot; + } + + else if (sof_slot <= invbag::TRADE_BAGS_END && sof_slot >= invbag::TRADE_BAGS_BEGIN) { + server_slot = sof_slot; + } + + else if (sof_slot <= invslot::WORLD_END && sof_slot >= invslot::WORLD_BEGIN) { + server_slot = sof_slot; + } + + Log(Logs::Detail, Logs::Netcode, "Convert SoF Slot %i to Server Slot %i", sof_slot, server_slot); + + return server_slot; } - static inline uint32 SoFToServerSlot(uint32 sofSlot) + static inline uint32 SoFToServerCorpseSlot(uint32 sof_corpse_slot) { - uint32 ServerSlot = 0; + uint32 server_slot = EQEmu::invslot::SLOT_INVALID; - if (sofSlot >= invslot::slotAmmo && sofSlot <= invslot::CORPSE_END) // Cursor/Ammo/Power Source and Normal Inventory Slots - ServerSlot = sofSlot - 1; - else if (sofSlot >= invbag::GENERAL_BAGS_BEGIN && sofSlot <= invbag::CURSOR_BAG_END) - ServerSlot = sofSlot - 11; - else if (sofSlot >= invbag::BANK_BAGS_BEGIN && sofSlot <= invbag::BANK_BAGS_END) - ServerSlot = sofSlot - 1; - else if (sofSlot >= invbag::SHARED_BANK_BAGS_BEGIN && sofSlot <= invbag::SHARED_BANK_BAGS_END) - ServerSlot = sofSlot - 1; - else if (sofSlot == invslot::slotPowerSource) - ServerSlot = EQEmu::invslot::SLOT_POWER_SOURCE; - else - ServerSlot = sofSlot; + if (sof_corpse_slot <= invslot::slotGeneral8 && sof_corpse_slot >= invslot::slotGeneral1) { + server_slot = sof_corpse_slot; + } - return ServerSlot; + else if (sof_corpse_slot <= invslot::CORPSE_END && sof_corpse_slot >= invslot::slotCursor) { + server_slot = sof_corpse_slot + 2; + } + + Log(Logs::Detail, + Logs::Netcode, + "Convert SoF Corpse Slot %i to Server Corpse Slot %i", + sof_corpse_slot, + server_slot); + + return server_slot; } - static inline uint32 SoFToServerCorpseSlot(uint32 sofCorpseSlot) + static inline void ServerToSoFSayLink(std::string &sof_saylink, const std::string &server_saylink) { - //uint32 ServerCorpse; - return (sofCorpseSlot - 1); - } - - static inline void ServerToSoFSayLink(std::string& sofSayLink, const std::string& serverSayLink) - { - if ((constants::SAY_LINK_BODY_SIZE == EQEmu::constants::SAY_LINK_BODY_SIZE) || (serverSayLink.find('\x12') == std::string::npos)) { - sofSayLink = serverSayLink; + if ((constants::SAY_LINK_BODY_SIZE == EQEmu::constants::SAY_LINK_BODY_SIZE) || (server_saylink.find('\x12') == std::string::npos)) { + sof_saylink = server_saylink; return; } - auto segments = SplitString(serverSayLink, '\x12'); + auto segments = SplitString(server_saylink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { if (segments[segment_iter].length() <= EQEmu::constants::SAY_LINK_BODY_SIZE) { - sofSayLink.append(segments[segment_iter]); + sof_saylink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -3228,37 +3365,37 @@ namespace SoF // SoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (50) // Diff: ^^^^^ ^ - sofSayLink.push_back('\x12'); - sofSayLink.append(segments[segment_iter].substr(0, 31)); - sofSayLink.append(segments[segment_iter].substr(36, 5)); + sof_saylink.push_back('\x12'); + sof_saylink.append(segments[segment_iter].substr(0, 31)); + sof_saylink.append(segments[segment_iter].substr(36, 5)); if (segments[segment_iter][41] == '0') - sofSayLink.push_back(segments[segment_iter][42]); + sof_saylink.push_back(segments[segment_iter][42]); else - sofSayLink.push_back('F'); + sof_saylink.push_back('F'); - sofSayLink.append(segments[segment_iter].substr(43)); - sofSayLink.push_back('\x12'); + sof_saylink.append(segments[segment_iter].substr(43)); + sof_saylink.push_back('\x12'); } else { - sofSayLink.append(segments[segment_iter]); + sof_saylink.append(segments[segment_iter]); } } } - static inline void SoFToServerSayLink(std::string& serverSayLink, const std::string& sofSayLink) + static inline void SoFToServerSayLink(std::string &server_saylink, const std::string &sof_saylink) { - if ((EQEmu::constants::SAY_LINK_BODY_SIZE == constants::SAY_LINK_BODY_SIZE) || (sofSayLink.find('\x12') == std::string::npos)) { - serverSayLink = sofSayLink; + if ((EQEmu::constants::SAY_LINK_BODY_SIZE == constants::SAY_LINK_BODY_SIZE) || (sof_saylink.find('\x12') == std::string::npos)) { + server_saylink = sof_saylink; return; } - auto segments = SplitString(sofSayLink, '\x12'); + auto segments = SplitString(sof_saylink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { if (segments[segment_iter].length() <= constants::SAY_LINK_BODY_SIZE) { - serverSayLink.append(segments[segment_iter]); + server_saylink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -3268,98 +3405,95 @@ namespace SoF // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) // Diff: ^^^^^ ^ - serverSayLink.push_back('\x12'); - serverSayLink.append(segments[segment_iter].substr(0, 31)); - serverSayLink.append("00000"); - serverSayLink.append(segments[segment_iter].substr(31, 5)); - serverSayLink.push_back('0'); - serverSayLink.append(segments[segment_iter].substr(36)); - serverSayLink.push_back('\x12'); + server_saylink.push_back('\x12'); + server_saylink.append(segments[segment_iter].substr(0, 31)); + server_saylink.append("00000"); + server_saylink.append(segments[segment_iter].substr(31, 5)); + server_saylink.push_back('0'); + server_saylink.append(segments[segment_iter].substr(36)); + server_saylink.push_back('\x12'); } else { - serverSayLink.append(segments[segment_iter]); + server_saylink.append(segments[segment_iter]); } } } - static inline CastingSlot ServerToSoFCastingSlot(EQEmu::CastingSlot slot) - { + static inline CastingSlot ServerToSoFCastingSlot(EQEmu::CastingSlot slot) { switch (slot) { - case EQEmu::CastingSlot::Gem1: - return CastingSlot::Gem1; - case EQEmu::CastingSlot::Gem2: - return CastingSlot::Gem2; - case EQEmu::CastingSlot::Gem3: - return CastingSlot::Gem3; - case EQEmu::CastingSlot::Gem4: - return CastingSlot::Gem4; - case EQEmu::CastingSlot::Gem5: - return CastingSlot::Gem5; - case EQEmu::CastingSlot::Gem6: - return CastingSlot::Gem6; - case EQEmu::CastingSlot::Gem7: - return CastingSlot::Gem7; - case EQEmu::CastingSlot::Gem8: - return CastingSlot::Gem8; - case EQEmu::CastingSlot::Gem9: - return CastingSlot::Gem9; - case EQEmu::CastingSlot::Item: - return CastingSlot::Item; - case EQEmu::CastingSlot::PotionBelt: - return CastingSlot::PotionBelt; - case EQEmu::CastingSlot::Discipline: - return CastingSlot::Discipline; - case EQEmu::CastingSlot::AltAbility: - return CastingSlot::AltAbility; - default: // we shouldn't have any issues with other slots ... just return something - return CastingSlot::Discipline; + case EQEmu::CastingSlot::Gem1: + return CastingSlot::Gem1; + case EQEmu::CastingSlot::Gem2: + return CastingSlot::Gem2; + case EQEmu::CastingSlot::Gem3: + return CastingSlot::Gem3; + case EQEmu::CastingSlot::Gem4: + return CastingSlot::Gem4; + case EQEmu::CastingSlot::Gem5: + return CastingSlot::Gem5; + case EQEmu::CastingSlot::Gem6: + return CastingSlot::Gem6; + case EQEmu::CastingSlot::Gem7: + return CastingSlot::Gem7; + case EQEmu::CastingSlot::Gem8: + return CastingSlot::Gem8; + case EQEmu::CastingSlot::Gem9: + return CastingSlot::Gem9; + case EQEmu::CastingSlot::Item: + return CastingSlot::Item; + case EQEmu::CastingSlot::PotionBelt: + return CastingSlot::PotionBelt; + case EQEmu::CastingSlot::Discipline: + return CastingSlot::Discipline; + case EQEmu::CastingSlot::AltAbility: + return CastingSlot::AltAbility; + default: // we shouldn't have any issues with other slots ... just return something + return CastingSlot::Discipline; } } - static inline EQEmu::CastingSlot SoFToServerCastingSlot(CastingSlot slot, uint32 itemlocation) - { + static inline EQEmu::CastingSlot SoFToServerCastingSlot(CastingSlot slot, uint32 item_location) { switch (slot) { - case CastingSlot::Gem1: - return EQEmu::CastingSlot::Gem1; - case CastingSlot::Gem2: - return EQEmu::CastingSlot::Gem2; - case CastingSlot::Gem3: - return EQEmu::CastingSlot::Gem3; - case CastingSlot::Gem4: - return EQEmu::CastingSlot::Gem4; - case CastingSlot::Gem5: - return EQEmu::CastingSlot::Gem5; - case CastingSlot::Gem6: - return EQEmu::CastingSlot::Gem6; - case CastingSlot::Gem7: - return EQEmu::CastingSlot::Gem7; - case CastingSlot::Gem8: - return EQEmu::CastingSlot::Gem8; - case CastingSlot::Gem9: - return EQEmu::CastingSlot::Gem9; - case CastingSlot::Ability: - return EQEmu::CastingSlot::Ability; - // Tit uses 10 for item and discipline casting, but items have a valid location - case CastingSlot::Item: - if (itemlocation == INVALID_INDEX) + case CastingSlot::Gem1: + return EQEmu::CastingSlot::Gem1; + case CastingSlot::Gem2: + return EQEmu::CastingSlot::Gem2; + case CastingSlot::Gem3: + return EQEmu::CastingSlot::Gem3; + case CastingSlot::Gem4: + return EQEmu::CastingSlot::Gem4; + case CastingSlot::Gem5: + return EQEmu::CastingSlot::Gem5; + case CastingSlot::Gem6: + return EQEmu::CastingSlot::Gem6; + case CastingSlot::Gem7: + return EQEmu::CastingSlot::Gem7; + case CastingSlot::Gem8: + return EQEmu::CastingSlot::Gem8; + case CastingSlot::Gem9: + return EQEmu::CastingSlot::Gem9; + case CastingSlot::Ability: + return EQEmu::CastingSlot::Ability; + // Tit uses 10 for item and discipline casting, but items have a valid location + case CastingSlot::Item: + if (item_location == INVALID_INDEX) + return EQEmu::CastingSlot::Discipline; + else + return EQEmu::CastingSlot::Item; + case CastingSlot::PotionBelt: + return EQEmu::CastingSlot::PotionBelt; + case CastingSlot::AltAbility: + return EQEmu::CastingSlot::AltAbility; + default: // we shouldn't have any issues with other slots ... just return something return EQEmu::CastingSlot::Discipline; - else - return EQEmu::CastingSlot::Item; - case CastingSlot::PotionBelt: - return EQEmu::CastingSlot::PotionBelt; - case CastingSlot::AltAbility: - return EQEmu::CastingSlot::AltAbility; - default: // we shouldn't have any issues with other slots ... just return something - return EQEmu::CastingSlot::Discipline; } } - static inline int ServerToSoFBuffSlot(int index) - { + static inline int ServerToSoFBuffSlot(int index) { // we're a disc if (index >= EQEmu::constants::LongBuffs + EQEmu::constants::ShortBuffs) return index - EQEmu::constants::LongBuffs - EQEmu::constants::ShortBuffs + - constants::LongBuffs + constants::ShortBuffs; + constants::LongBuffs + constants::ShortBuffs; // we're a song if (index >= EQEmu::constants::LongBuffs) return index - EQEmu::constants::LongBuffs + constants::LongBuffs; @@ -3379,4 +3513,4 @@ namespace SoF // we're a normal buff return index; // as long as we guard against bad slots server side, we should be fine } -} /*SoF*/ +} diff --git a/common/patches/sof_limits.h b/common/patches/sof_limits.h index fdc688370..ab5a32807 100644 --- a/common/patches/sof_limits.h +++ b/common/patches/sof_limits.h @@ -150,6 +150,8 @@ namespace SoF const int16 SLOT_INVALID = IINVALID; const int16 SLOT_BEGIN = INULL; + const int16 SLOT_TRADESKILL_EXPERIMENT_COMBINE = 1000; + const int16 POSSESSIONS_BEGIN = slotCharm; const int16 POSSESSIONS_END = slotCursor; const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1; diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index 339cbb85d..2a1345ad0 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -45,21 +45,21 @@ namespace Titanium void SerializeItem(EQEmu::OutBuffer& ob, const EQEmu::ItemInstance *inst, int16 slot_id_in, uint8 depth); // server to client inventory location converters - static inline int16 ServerToTitaniumSlot(uint32 serverSlot); - static inline int16 ServerToTitaniumCorpseSlot(uint32 serverCorpseSlot); + static inline int16 ServerToTitaniumSlot(uint32 server_slot); + static inline int16 ServerToTitaniumCorpseSlot(uint32 server_corpse_slot); // client to server inventory location converters - static inline uint32 TitaniumToServerSlot(int16 titaniumSlot); - static inline uint32 TitaniumToServerCorpseSlot(int16 titaniumCorpseSlot); + static inline uint32 TitaniumToServerSlot(int16 titanium_slot); + static inline uint32 TitaniumToServerCorpseSlot(int16 titanium_corpse_slot); // server to client say link converter - static inline void ServerToTitaniumSayLink(std::string& titaniumSayLink, const std::string& serverSayLink); + static inline void ServerToTitaniumSayLink(std::string &titanium_saylink, const std::string &server_saylink); // client to server say link converter - static inline void TitaniumToServerSayLink(std::string& serverSayLink, const std::string& titaniumSayLink); + static inline void TitaniumToServerSayLink(std::string &server_saylink, const std::string &titanium_saylink); static inline CastingSlot ServerToTitaniumCastingSlot(EQEmu::CastingSlot slot); - static inline EQEmu::CastingSlot TitaniumToServerCastingSlot(CastingSlot slot, uint32 itemlocation); + static inline EQEmu::CastingSlot TitaniumToServerCastingSlot(CastingSlot slot, uint32 item_location); static inline int ServerToTitaniumBuffSlot(int index); static inline int TitaniumToServerBuffSlot(int index); @@ -335,9 +335,9 @@ namespace Titanium EQEmu::OutBuffer::pos_type last_pos = ob.tellp(); for (int r = 0; r < itemcount; r++, eq++) { - SerializeItem(ob, (const EQEmu::ItemInstance*)eq->inst, eq->slot_id, 0); + SerializeItem(ob, (const EQEmu::ItemInstance*)eq->inst, ServerToTitaniumSlot(eq->slot_id), 0); if (ob.tellp() == last_pos) - Log(Logs::General, Logs::Netcode, "[STRUCTS] Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + Log(Logs::General, Logs::Netcode, "Titanium::ENCODE(OP_CharInventory) Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); last_pos = ob.tellp(); } @@ -367,7 +367,12 @@ namespace Titanium FINISH_ENCODE(); } - ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } + ENCODE(OP_DeleteCharge) + { + Log(Logs::Moderate, Logs::Netcode, "Titanium::ENCODE(OP_DeleteCharge)"); + + ENCODE_FORWARD(OP_MoveItem); + } ENCODE(OP_DeleteItem) { @@ -772,21 +777,15 @@ namespace Titanium OUT(TargetID); OUT(playerid); - int r; - for (r = 0; r <= 20; r++) { - strn0cpy(eq->itemnames[r], emu->itemnames[r], sizeof(eq->itemnames[r])); + for (int i = EQEmu::invslot::slotCharm; i <= EQEmu::invslot::slotWaist; ++i) { + strn0cpy(eq->itemnames[i], emu->itemnames[i], sizeof(eq->itemnames[i])); + OUT(itemicons[i]); } - // move arrow item down to last element in titanium array - strn0cpy(eq->itemnames[21], emu->itemnames[22], sizeof(eq->itemnames[21])); + // move ammo down to last element in titanium array + strn0cpy(eq->itemnames[invslot::slotAmmo], emu->itemnames[EQEmu::invslot::slotAmmo], sizeof(eq->itemnames[invslot::slotAmmo])); + eq->itemicons[invslot::slotAmmo] = emu->itemicons[EQEmu::invslot::slotAmmo]; - int k; - for (k = 0; k <= 20; k++) { - OUT(itemicons[k]); - } - - // move arrow icon down to last element in titanium array - eq->itemicons[21] = emu->itemicons[22]; strn0cpy(eq->text, emu->text, sizeof(eq->text)); FINISH_ENCODE(); @@ -821,9 +820,9 @@ namespace Titanium ob.write((const char*)__emu_buffer, 4); - SerializeItem(ob, (const EQEmu::ItemInstance*)int_struct->inst, int_struct->slot_id, 0); + SerializeItem(ob, (const EQEmu::ItemInstance*)int_struct->inst, ServerToTitaniumSlot(int_struct->slot_id), 0); if (ob.tellp() == last_pos) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Serialization failed on item slot %d.", int_struct->slot_id); + Log(Logs::General, Logs::Netcode, "Titanium::ENCODE(OP_ItemPacket) Serialization failed on item slot %d.", int_struct->slot_id); delete in; return; } @@ -874,6 +873,8 @@ namespace Titanium ENCODE_LENGTH_EXACT(LootingItem_Struct); SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); + Log(Logs::Moderate, Logs::Netcode, "Titanium::ENCODE(OP_LootItem)"); + OUT(lootee); OUT(looter); eq->slot_id = ServerToTitaniumCorpseSlot(emu->slot_id); @@ -903,6 +904,8 @@ namespace Titanium ENCODE_LENGTH_EXACT(MoveItem_Struct); SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); + Log(Logs::Moderate, Logs::Netcode, "Titanium::ENCODE(OP_MoveItem)"); + eq->from_slot = ServerToTitaniumSlot(emu->from_slot); eq->to_slot = ServerToTitaniumSlot(emu->to_slot); OUT(number_in_stack); @@ -1050,7 +1053,7 @@ namespace Titanium // OUT(unknown06160[4]); - // Copy bandoliers where server and client indexes converge + // Copy bandoliers where server and client indices converge for (r = 0; r < EQEmu::profile::BANDOLIERS_SIZE && r < profile::BANDOLIERS_SIZE; ++r) { OUT_str(bandoliers[r].Name); for (uint32 k = 0; k < profile::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true @@ -1059,7 +1062,7 @@ namespace Titanium OUT_str(bandoliers[r].Items[k].Name); } } - // Nullify bandoliers where server and client indexes diverge, with a client bias + // Nullify bandoliers where server and client indices diverge, with a client bias for (r = EQEmu::profile::BANDOLIERS_SIZE; r < profile::BANDOLIERS_SIZE; ++r) { eq->bandoliers[r].Name[0] = '\0'; for (uint32 k = 0; k < profile::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true @@ -1071,13 +1074,13 @@ namespace Titanium // OUT(unknown07444[5120]); - // Copy potion belt where server and client indexes converge + // Copy potion belt where server and client indices converge for (r = 0; r < EQEmu::profile::POTION_BELT_SIZE && r < profile::POTION_BELT_SIZE; ++r) { OUT(potionbelt.Items[r].ID); OUT(potionbelt.Items[r].Icon); OUT_str(potionbelt.Items[r].Name); } - // Nullify potion belt where server and client indexes diverge, with a client bias + // Nullify potion belt where server and client indices diverge, with a client bias for (r = EQEmu::profile::POTION_BELT_SIZE; r < profile::POTION_BELT_SIZE; ++r) { eq->potionbelt.Items[r].ID = 0; eq->potionbelt.Items[r].Icon = 0; @@ -1936,23 +1939,18 @@ namespace Titanium IN(TargetID); IN(playerid); - int r; - for (r = 0; r <= 20; r++) { - strn0cpy(emu->itemnames[r], eq->itemnames[r], sizeof(emu->itemnames[r])); + for (int i = invslot::slotCharm; i <= invslot::slotWaist; ++i) { + strn0cpy(emu->itemnames[i], eq->itemnames[i], sizeof(emu->itemnames[i])); + IN(itemicons[i]); } - // move arrow item up to last element in server array - strn0cpy(emu->itemnames[21], "", sizeof(emu->itemnames[21])); - strn0cpy(emu->itemnames[22], eq->itemnames[21], sizeof(emu->itemnames[22])); + // move ammo up to last element in server array + strn0cpy(emu->itemnames[EQEmu::invslot::slotAmmo], eq->itemnames[invslot::slotAmmo], sizeof(emu->itemnames[EQEmu::invslot::slotAmmo])); + emu->itemicons[EQEmu::invslot::slotAmmo] = eq->itemicons[invslot::slotAmmo]; - int k; - for (k = 0; k <= 20; k++) { - IN(itemicons[k]); - } - - // move arrow icon up to last element in server array - emu->itemicons[21] = 0xFFFFFFFF; - emu->itemicons[22] = eq->itemicons[21]; + // nullify power source element in server array + strn0cpy(emu->itemnames[EQEmu::invslot::slotPowerSource], "", sizeof(emu->itemnames[EQEmu::invslot::slotPowerSource])); + emu->itemicons[EQEmu::invslot::slotPowerSource] = 0xFFFFFFFF; strn0cpy(emu->text, eq->text, sizeof(emu->text)); @@ -2020,6 +2018,8 @@ namespace Titanium DECODE_LENGTH_EXACT(structs::LootingItem_Struct); SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); + Log(Logs::Moderate, Logs::Netcode, "Titanium::DECODE(OP_LootItem)"); + IN(lootee); IN(looter); emu->slot_id = TitaniumToServerCorpseSlot(eq->slot_id); @@ -2033,7 +2033,7 @@ namespace Titanium DECODE_LENGTH_EXACT(structs::MoveItem_Struct); SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); - Log(Logs::General, Logs::Netcode, "[Titanium] Moved item from %u to %u", eq->from_slot, eq->to_slot); + Log(Logs::Moderate, Logs::Netcode, "Titanium::DECODE(OP_MoveItem)"); emu->from_slot = TitaniumToServerSlot(eq->from_slot); emu->to_slot = TitaniumToServerSlot(eq->to_slot); @@ -2225,13 +2225,16 @@ namespace Titanium } // file scope helper methods - void SerializeItem(EQEmu::OutBuffer& ob, const EQEmu::ItemInstance *inst, int16 slot_id_in, uint8 depth) - { - const char* protection = "\\\\\\\\\\"; - const EQEmu::ItemData* item = inst->GetUnscaledItem(); + void SerializeItem(EQEmu::OutBuffer& ob, const EQEmu::ItemInstance *inst, int16 slot_id_in, uint8 depth) { + const char *protection = "\\\\\\\\\\"; + const EQEmu::ItemData *item = inst->GetUnscaledItem(); + + ob << StringFormat( + "%.*s%s", + (depth ? (depth - 1) : 0), + protection, + (depth ? "\"" : "")); // For leading quotes (and protection) if a subitem; - ob << StringFormat("%.*s%s", (depth ? (depth - 1) : 0), protection, (depth ? "\"" : "")); // For leading quotes (and protection) if a subitem; - // Instance data ob << itoa((inst->IsStackable() ? inst->GetCharges() : 0)); // stack count ob << '|' << itoa(0); // unknown @@ -2239,9 +2242,11 @@ namespace Titanium ob << '|' << itoa(inst->GetPrice()); // merchant price ob << '|' << itoa((!inst->GetMerchantSlot() ? 1 : inst->GetMerchantCount())); // inst count/merchant count ob << '|' << itoa((inst->IsScaling() ? (inst->GetExp() / 100) : 0)); // inst experience - ob << '|' << itoa((!inst->GetMerchantSlot() ? inst->GetSerialNumber() : inst->GetMerchantSlot())); // merchant serial number + ob << '|' << itoa((!inst->GetMerchantSlot() ? inst->GetSerialNumber() + : inst->GetMerchantSlot())); // merchant serial number ob << '|' << itoa(inst->GetRecastTimestamp()); // recast timestamp - ob << '|' << itoa(((inst->IsStackable() ? ((inst->GetItem()->ItemType == EQEmu::item::ItemTypePotion) ? 1 : 0) : inst->GetCharges()))); // charge count + ob << '|' << itoa(((inst->IsStackable() ? ((inst->GetItem()->ItemType == EQEmu::item::ItemTypePotion) ? 1 : 0) + : inst->GetCharges()))); // charge count ob << '|' << itoa((inst->IsAttuned() ? 1 : 0)); // inst attuned ob << '|' << itoa(0); // unknown ob << '|'; @@ -2259,7 +2264,7 @@ namespace Titanium ob << '|' << itoa(item->NoRent); ob << '|' << itoa(item->NoDrop); ob << '|' << itoa(item->Size); - ob << '|' << itoa(item->Slots); + ob << '|' << itoa(Catch22(SwapBits21And22(item->Slots))); ob << '|' << itoa(item->Price); ob << '|' << itoa(item->Icon); ob << '|' << "0"; @@ -2449,62 +2454,208 @@ namespace Titanium for (int index = EQEmu::invbag::SLOT_BEGIN; index <= invbag::SLOT_END; ++index) { ob << '|'; - EQEmu::ItemInstance* sub = inst->GetItem(index); + EQEmu::ItemInstance *sub = inst->GetItem(index); if (!sub) continue; - + SerializeItem(ob, sub, 0, (depth + 1)); } - ob << StringFormat("%.*s%s", (depth ? (depth - 1) : 0), protection, (depth ? "\"" : "")); // For trailing quotes (and protection) if a subitem; + ob << StringFormat( + "%.*s%s", + (depth ? (depth - 1) : 0), + protection, + (depth ? "\"" : "")); // For trailing quotes (and protection) if a subitem; if (!depth) ob.write("\0", 1); } - static inline int16 ServerToTitaniumSlot(uint32 serverSlot) - { - //int16 TitaniumSlot; - if (serverSlot == INVALID_INDEX) - return INVALID_INDEX; + static inline int16 ServerToTitaniumSlot(uint32 server_slot) { + int16 titanium_slot = invslot::SLOT_INVALID; - return serverSlot; // deprecated + if (server_slot <= EQEmu::invslot::slotWaist) { + titanium_slot = server_slot; + } + else if (server_slot == EQEmu::invslot::slotAmmo) { + titanium_slot = server_slot - 1; + } + else if (server_slot <= EQEmu::invslot::slotGeneral8 && server_slot >= EQEmu::invslot::slotGeneral1) { + titanium_slot = server_slot - 1; + } + else if (server_slot <= (EQEmu::invslot::POSSESSIONS_COUNT + EQEmu::invslot::slotWaist) && + server_slot >= EQEmu::invslot::slotCursor) { + titanium_slot = server_slot - 3; + } + else if (server_slot == (EQEmu::invslot::POSSESSIONS_COUNT + EQEmu::invslot::slotAmmo)) { + titanium_slot = server_slot - 4; + } + else if (server_slot <= EQEmu::invbag::GENERAL_BAGS_8_END && + server_slot >= EQEmu::invbag::GENERAL_BAGS_BEGIN) { + titanium_slot = server_slot; + } + else if (server_slot <= EQEmu::invbag::CURSOR_BAG_END && server_slot >= EQEmu::invbag::CURSOR_BAG_BEGIN) { + titanium_slot = server_slot - 20; + } + else if (server_slot <= EQEmu::invslot::TRIBUTE_END && server_slot >= EQEmu::invslot::TRIBUTE_BEGIN) { + titanium_slot = server_slot; + } + else if (server_slot <= EQEmu::invslot::GUILD_TRIBUTE_END && + server_slot >= EQEmu::invslot::GUILD_TRIBUTE_BEGIN) { + titanium_slot = server_slot; + } + else if (server_slot == EQEmu::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE) { + titanium_slot = server_slot; + } + else if (server_slot <= EQEmu::invslot::BANK_END && server_slot >= EQEmu::invslot::BANK_BEGIN) { + titanium_slot = server_slot; + } + else if (server_slot <= EQEmu::invbag::BANK_BAGS_16_END && server_slot >= EQEmu::invbag::BANK_BAGS_BEGIN) { + titanium_slot = server_slot; + } + else if (server_slot <= EQEmu::invslot::SHARED_BANK_END && server_slot >= EQEmu::invslot::SHARED_BANK_BEGIN) { + titanium_slot = server_slot; + } + else if (server_slot <= EQEmu::invbag::SHARED_BANK_BAGS_END && + server_slot >= EQEmu::invbag::SHARED_BANK_BAGS_BEGIN) { + titanium_slot = server_slot; + } + else if (server_slot <= EQEmu::invslot::TRADE_END && server_slot >= EQEmu::invslot::TRADE_BEGIN) { + titanium_slot = server_slot; + } + else if (server_slot <= EQEmu::invbag::TRADE_BAGS_END && server_slot >= EQEmu::invbag::TRADE_BAGS_BEGIN) { + titanium_slot = server_slot; + } + else if (server_slot <= EQEmu::invslot::WORLD_END && server_slot >= EQEmu::invslot::WORLD_BEGIN) { + titanium_slot = server_slot; + } + + Log(Logs::Detail, Logs::Netcode, "Convert Server Slot %i to Titanium Slot %i", server_slot, titanium_slot); + + return titanium_slot; } - static inline int16 ServerToTitaniumCorpseSlot(uint32 serverCorpseSlot) - { - //int16 TitaniumCorpse; - return serverCorpseSlot; + + static inline int16 ServerToTitaniumCorpseSlot(uint32 server_corpse_slot) { + int16 titanium_slot = invslot::SLOT_INVALID; + + if (server_corpse_slot <= EQEmu::invslot::slotGeneral8 && server_corpse_slot >= EQEmu::invslot::slotGeneral1) { + titanium_slot = server_corpse_slot - 1; + } + + else if (server_corpse_slot <= (EQEmu::invslot::POSSESSIONS_COUNT + EQEmu::invslot::slotWaist) && + server_corpse_slot >= EQEmu::invslot::slotCursor) { + titanium_slot = server_corpse_slot - 3; + } + + else if (server_corpse_slot == (EQEmu::invslot::POSSESSIONS_COUNT + EQEmu::invslot::slotAmmo)) { + titanium_slot = server_corpse_slot - 4; + } + + Log(Logs::Detail, + Logs::Netcode, + "Convert Server Corpse Slot %i to Titanium Corpse Slot %i", + server_corpse_slot, + titanium_slot); + + return titanium_slot; } - static inline uint32 TitaniumToServerSlot(int16 titaniumSlot) - { - //uint32 ServerSlot; - if (titaniumSlot == INVALID_INDEX) - return INVALID_INDEX; + static inline uint32 TitaniumToServerSlot(int16 titanium_slot) { + uint32 server_slot = EQEmu::invslot::SLOT_INVALID; - return titaniumSlot; // deprecated + if (titanium_slot <= invslot::slotWaist) { + server_slot = titanium_slot; + } + else if (titanium_slot == invslot::slotAmmo) { + server_slot = titanium_slot + 1; + } + else if (titanium_slot <= invslot::slotGeneral8 && titanium_slot >= invslot::slotGeneral1) { + server_slot = titanium_slot + 1; + } + else if (titanium_slot <= (invslot::POSSESSIONS_COUNT + invslot::slotWaist) && + titanium_slot >= invslot::slotCursor) { + server_slot = titanium_slot + 3; + } + else if (titanium_slot == (invslot::POSSESSIONS_COUNT + invslot::slotAmmo)) { + server_slot = titanium_slot + 4; + } + else if (titanium_slot <= invbag::GENERAL_BAGS_END && titanium_slot >= invbag::GENERAL_BAGS_BEGIN) { + server_slot = titanium_slot; + } + else if (titanium_slot <= invbag::CURSOR_BAG_END && titanium_slot >= invbag::CURSOR_BAG_BEGIN) { + server_slot = titanium_slot + 20; + } + else if (titanium_slot <= invslot::TRIBUTE_END && titanium_slot >= invslot::TRIBUTE_BEGIN) { + server_slot = titanium_slot; + } + else if (titanium_slot == invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE) { + server_slot = titanium_slot; + } + else if (titanium_slot <= invslot::GUILD_TRIBUTE_END && titanium_slot >= invslot::GUILD_TRIBUTE_BEGIN) { + server_slot = titanium_slot; + } + else if (titanium_slot <= invslot::BANK_END && titanium_slot >= invslot::BANK_BEGIN) { + server_slot = titanium_slot; + } + else if (titanium_slot <= invbag::BANK_BAGS_END && titanium_slot >= invbag::BANK_BAGS_BEGIN) { + server_slot = titanium_slot; + } + else if (titanium_slot <= invslot::SHARED_BANK_END && titanium_slot >= invslot::SHARED_BANK_BEGIN) { + server_slot = titanium_slot; + } + else if (titanium_slot <= invbag::SHARED_BANK_BAGS_END && titanium_slot >= invbag::SHARED_BANK_BAGS_BEGIN) { + server_slot = titanium_slot; + } + else if (titanium_slot <= invslot::TRADE_END && titanium_slot >= invslot::TRADE_BEGIN) { + server_slot = titanium_slot; + } + else if (titanium_slot <= invbag::TRADE_BAGS_END && titanium_slot >= invbag::TRADE_BAGS_BEGIN) { + server_slot = titanium_slot; + } + else if (titanium_slot <= invslot::WORLD_END && titanium_slot >= invslot::WORLD_BEGIN) { + server_slot = titanium_slot; + } + + Log(Logs::Detail, Logs::Netcode, "Convert Titanium Slot %i to Server Slot %i", titanium_slot, server_slot); + + return server_slot; } - static inline uint32 TitaniumToServerCorpseSlot(int16 titaniumCorpseSlot) + static inline uint32 TitaniumToServerCorpseSlot(int16 titanium_corpse_slot) { - //uint32 ServerCorpse; - return titaniumCorpseSlot; + uint32 server_slot = EQEmu::invslot::SLOT_INVALID; + + if (titanium_corpse_slot <= invslot::slotGeneral8 && titanium_corpse_slot >= invslot::slotGeneral1) { + server_slot = titanium_corpse_slot + 1; + } + + else if (titanium_corpse_slot <= (invslot::POSSESSIONS_COUNT + invslot::slotWaist) && titanium_corpse_slot >= invslot::slotCursor) { + server_slot = titanium_corpse_slot + 3; + } + + else if (titanium_corpse_slot == (invslot::POSSESSIONS_COUNT + invslot::slotAmmo)) { + server_slot = titanium_corpse_slot + 4; + } + + Log(Logs::Detail, Logs::Netcode, "Convert Titanium Corpse Slot %i to Server Corpse Slot %i", titanium_corpse_slot, server_slot); + + return server_slot; } - static inline void ServerToTitaniumSayLink(std::string& titaniumSayLink, const std::string& serverSayLink) + static inline void ServerToTitaniumSayLink(std::string &titanium_saylink, const std::string &server_saylink) { - if ((constants::SAY_LINK_BODY_SIZE == EQEmu::constants::SAY_LINK_BODY_SIZE) || (serverSayLink.find('\x12') == std::string::npos)) { - titaniumSayLink = serverSayLink; + if ((constants::SAY_LINK_BODY_SIZE == EQEmu::constants::SAY_LINK_BODY_SIZE) || (server_saylink.find('\x12') == std::string::npos)) { + titanium_saylink = server_saylink; return; } - auto segments = SplitString(serverSayLink, '\x12'); + auto segments = SplitString(server_saylink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { if (segments[segment_iter].length() <= EQEmu::constants::SAY_LINK_BODY_SIZE) { - titaniumSayLink.append(segments[segment_iter]); + titanium_saylink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -2514,37 +2665,37 @@ namespace Titanium // 6.2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXXXXX (45) // Diff: ^^^^^ ^ ^^^^^ - titaniumSayLink.push_back('\x12'); - titaniumSayLink.append(segments[segment_iter].substr(0, 31)); - titaniumSayLink.append(segments[segment_iter].substr(36, 5)); + titanium_saylink.push_back('\x12'); + titanium_saylink.append(segments[segment_iter].substr(0, 31)); + titanium_saylink.append(segments[segment_iter].substr(36, 5)); if (segments[segment_iter][41] == '0') - titaniumSayLink.push_back(segments[segment_iter][42]); + titanium_saylink.push_back(segments[segment_iter][42]); else - titaniumSayLink.push_back('F'); + titanium_saylink.push_back('F'); - titaniumSayLink.append(segments[segment_iter].substr(48)); - titaniumSayLink.push_back('\x12'); + titanium_saylink.append(segments[segment_iter].substr(48)); + titanium_saylink.push_back('\x12'); } else { - titaniumSayLink.append(segments[segment_iter]); + titanium_saylink.append(segments[segment_iter]); } } } - static inline void TitaniumToServerSayLink(std::string& serverSayLink, const std::string& titaniumSayLink) + static inline void TitaniumToServerSayLink(std::string &server_saylink, const std::string &titanium_saylink) { - if ((EQEmu::constants::SAY_LINK_BODY_SIZE == constants::SAY_LINK_BODY_SIZE) || (titaniumSayLink.find('\x12') == std::string::npos)) { - serverSayLink = titaniumSayLink; + if ((EQEmu::constants::SAY_LINK_BODY_SIZE == constants::SAY_LINK_BODY_SIZE) || (titanium_saylink.find('\x12') == std::string::npos)) { + server_saylink = titanium_saylink; return; } - auto segments = SplitString(titaniumSayLink, '\x12'); + auto segments = SplitString(titanium_saylink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { if (segments[segment_iter].length() <= constants::SAY_LINK_BODY_SIZE) { - serverSayLink.append(segments[segment_iter]); + server_saylink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -2554,91 +2705,89 @@ namespace Titanium // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) // Diff: ^^^^^ ^ ^^^^^ - serverSayLink.push_back('\x12'); - serverSayLink.append(segments[segment_iter].substr(0, 31)); - serverSayLink.append("00000"); - serverSayLink.append(segments[segment_iter].substr(31, 5)); - serverSayLink.push_back('0'); - serverSayLink.push_back(segments[segment_iter][36]); - serverSayLink.append("00000"); - serverSayLink.append(segments[segment_iter].substr(37)); - serverSayLink.push_back('\x12'); + server_saylink.push_back('\x12'); + server_saylink.append(segments[segment_iter].substr(0, 31)); + server_saylink.append("00000"); + server_saylink.append(segments[segment_iter].substr(31, 5)); + server_saylink.push_back('0'); + server_saylink.push_back(segments[segment_iter][36]); + server_saylink.append("00000"); + server_saylink.append(segments[segment_iter].substr(37)); + server_saylink.push_back('\x12'); } else { - serverSayLink.append(segments[segment_iter]); + server_saylink.append(segments[segment_iter]); } } } - static inline CastingSlot ServerToTitaniumCastingSlot(EQEmu::CastingSlot slot) - { + static inline CastingSlot ServerToTitaniumCastingSlot(EQEmu::CastingSlot slot) { switch (slot) { - case EQEmu::CastingSlot::Gem1: - return CastingSlot::Gem1; - case EQEmu::CastingSlot::Gem2: - return CastingSlot::Gem2; - case EQEmu::CastingSlot::Gem3: - return CastingSlot::Gem3; - case EQEmu::CastingSlot::Gem4: - return CastingSlot::Gem4; - case EQEmu::CastingSlot::Gem5: - return CastingSlot::Gem5; - case EQEmu::CastingSlot::Gem6: - return CastingSlot::Gem6; - case EQEmu::CastingSlot::Gem7: - return CastingSlot::Gem7; - case EQEmu::CastingSlot::Gem8: - return CastingSlot::Gem8; - case EQEmu::CastingSlot::Gem9: - return CastingSlot::Gem9; - case EQEmu::CastingSlot::Item: - return CastingSlot::Item; - case EQEmu::CastingSlot::PotionBelt: - return CastingSlot::PotionBelt; - case EQEmu::CastingSlot::Discipline: - return CastingSlot::Discipline; - case EQEmu::CastingSlot::AltAbility: - return CastingSlot::AltAbility; - default: // we shouldn't have any issues with other slots ... just return something - return CastingSlot::Discipline; + case EQEmu::CastingSlot::Gem1: + return CastingSlot::Gem1; + case EQEmu::CastingSlot::Gem2: + return CastingSlot::Gem2; + case EQEmu::CastingSlot::Gem3: + return CastingSlot::Gem3; + case EQEmu::CastingSlot::Gem4: + return CastingSlot::Gem4; + case EQEmu::CastingSlot::Gem5: + return CastingSlot::Gem5; + case EQEmu::CastingSlot::Gem6: + return CastingSlot::Gem6; + case EQEmu::CastingSlot::Gem7: + return CastingSlot::Gem7; + case EQEmu::CastingSlot::Gem8: + return CastingSlot::Gem8; + case EQEmu::CastingSlot::Gem9: + return CastingSlot::Gem9; + case EQEmu::CastingSlot::Item: + return CastingSlot::Item; + case EQEmu::CastingSlot::PotionBelt: + return CastingSlot::PotionBelt; + case EQEmu::CastingSlot::Discipline: + return CastingSlot::Discipline; + case EQEmu::CastingSlot::AltAbility: + return CastingSlot::AltAbility; + default: // we shouldn't have any issues with other slots ... just return something + return CastingSlot::Discipline; } } - static inline EQEmu::CastingSlot TitaniumToServerCastingSlot(CastingSlot slot, uint32 itemlocation) - { + static inline EQEmu::CastingSlot TitaniumToServerCastingSlot(CastingSlot slot, uint32 item_location) { switch (slot) { - case CastingSlot::Gem1: - return EQEmu::CastingSlot::Gem1; - case CastingSlot::Gem2: - return EQEmu::CastingSlot::Gem2; - case CastingSlot::Gem3: - return EQEmu::CastingSlot::Gem3; - case CastingSlot::Gem4: - return EQEmu::CastingSlot::Gem4; - case CastingSlot::Gem5: - return EQEmu::CastingSlot::Gem5; - case CastingSlot::Gem6: - return EQEmu::CastingSlot::Gem6; - case CastingSlot::Gem7: - return EQEmu::CastingSlot::Gem7; - case CastingSlot::Gem8: - return EQEmu::CastingSlot::Gem8; - case CastingSlot::Gem9: - return EQEmu::CastingSlot::Gem9; - case CastingSlot::Ability: - return EQEmu::CastingSlot::Ability; - // Tit uses 10 for item and discipline casting, but items have a valid location - case CastingSlot::Item: - if (itemlocation == INVALID_INDEX) + case CastingSlot::Gem1: + return EQEmu::CastingSlot::Gem1; + case CastingSlot::Gem2: + return EQEmu::CastingSlot::Gem2; + case CastingSlot::Gem3: + return EQEmu::CastingSlot::Gem3; + case CastingSlot::Gem4: + return EQEmu::CastingSlot::Gem4; + case CastingSlot::Gem5: + return EQEmu::CastingSlot::Gem5; + case CastingSlot::Gem6: + return EQEmu::CastingSlot::Gem6; + case CastingSlot::Gem7: + return EQEmu::CastingSlot::Gem7; + case CastingSlot::Gem8: + return EQEmu::CastingSlot::Gem8; + case CastingSlot::Gem9: + return EQEmu::CastingSlot::Gem9; + case CastingSlot::Ability: + return EQEmu::CastingSlot::Ability; + // Tit uses 10 for item and discipline casting, but items have a valid location + case CastingSlot::Item: + if (item_location == INVALID_INDEX) + return EQEmu::CastingSlot::Discipline; + else + return EQEmu::CastingSlot::Item; + case CastingSlot::PotionBelt: + return EQEmu::CastingSlot::PotionBelt; + case CastingSlot::AltAbility: + return EQEmu::CastingSlot::AltAbility; + default: // we shouldn't have any issues with other slots ... just return something return EQEmu::CastingSlot::Discipline; - else - return EQEmu::CastingSlot::Item; - case CastingSlot::PotionBelt: - return EQEmu::CastingSlot::PotionBelt; - case CastingSlot::AltAbility: - return EQEmu::CastingSlot::AltAbility; - default: // we shouldn't have any issues with other slots ... just return something - return EQEmu::CastingSlot::Discipline; } } diff --git a/common/patches/titanium_limits.h b/common/patches/titanium_limits.h index cd69c21e1..33fb3bdc5 100644 --- a/common/patches/titanium_limits.h +++ b/common/patches/titanium_limits.h @@ -149,6 +149,8 @@ namespace Titanium const int16 SLOT_INVALID = IINVALID; const int16 SLOT_BEGIN = INULL; + const int16 SLOT_TRADESKILL_EXPERIMENT_COMBINE = 1000; + const int16 POSSESSIONS_BEGIN = slotCharm; const int16 POSSESSIONS_END = slotCursor; const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1; diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index 840c6fcd7..b199197a2 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -520,7 +520,7 @@ namespace UF for (int index = 0; index < item_count; ++index, ++eq) { SerializeItem(ob, (const EQEmu::ItemInstance*)eq->inst, eq->slot_id, 0); if (ob.tellp() == last_pos) - Log(Logs::General, Logs::Netcode, "[STRUCTS] Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + Log(Logs::General, Logs::Netcode, "UF::ENCODE(OP_CharInventory) Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); last_pos = ob.tellp(); } @@ -584,7 +584,12 @@ namespace UF FINISH_ENCODE(); } - ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } + ENCODE(OP_DeleteCharge) + { + Log(Logs::Moderate, Logs::Netcode, "UF::ENCODE(OP_DeleteCharge)"); + + ENCODE_FORWARD(OP_MoveItem); + } ENCODE(OP_DeleteItem) { @@ -1272,7 +1277,7 @@ namespace UF SerializeItem(ob, (const EQEmu::ItemInstance*)int_struct->inst, int_struct->slot_id, 0); if (ob.tellp() == last_pos) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Serialization failed on item slot %d.", int_struct->slot_id); + Log(Logs::General, Logs::Netcode, "UF::ENCODE(OP_ItemPacket) Serialization failed on item slot %d.", int_struct->slot_id); delete in; return; } @@ -1345,6 +1350,8 @@ namespace UF ENCODE_LENGTH_EXACT(LootingItem_Struct); SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); + Log(Logs::Moderate, Logs::Netcode, "UF::ENCODE(OP_LootItem)"); + OUT(lootee); OUT(looter); eq->slot_id = ServerToUFCorpseSlot(emu->slot_id); @@ -1510,6 +1517,8 @@ namespace UF ENCODE_LENGTH_EXACT(MoveItem_Struct); SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); + Log(Logs::Moderate, Logs::Netcode, "UF::ENCODE(OP_MoveItem)"); + eq->from_slot = ServerToUFSlot(emu->from_slot); eq->to_slot = ServerToUFSlot(emu->to_slot); OUT(number_in_stack); @@ -1769,7 +1778,7 @@ namespace UF // OUT(unknown06160[4]); - // Copy bandoliers where server and client indexes converge + // Copy bandoliers where server and client indices converge for (r = 0; r < EQEmu::profile::BANDOLIERS_SIZE && r < profile::BANDOLIERS_SIZE; ++r) { OUT_str(bandoliers[r].Name); for (uint32 k = 0; k < profile::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true @@ -1778,7 +1787,7 @@ namespace UF OUT_str(bandoliers[r].Items[k].Name); } } - // Nullify bandoliers where server and client indexes diverge, with a client bias + // Nullify bandoliers where server and client indices diverge, with a client bias for (r = EQEmu::profile::BANDOLIERS_SIZE; r < profile::BANDOLIERS_SIZE; ++r) { eq->bandoliers[r].Name[0] = '\0'; for (uint32 k = 0; k < profile::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true @@ -1790,13 +1799,13 @@ namespace UF // OUT(unknown07444[5120]); - // Copy potion belt where server and client indexes converge + // Copy potion belt where server and client indices converge for (r = 0; r < EQEmu::profile::POTION_BELT_SIZE && r < profile::POTION_BELT_SIZE; ++r) { OUT(potionbelt.Items[r].ID); OUT(potionbelt.Items[r].Icon); OUT_str(potionbelt.Items[r].Name); } - // Nullify potion belt where server and client indexes diverge, with a client bias + // Nullify potion belt where server and client indices diverge, with a client bias for (r = EQEmu::profile::POTION_BELT_SIZE; r < profile::POTION_BELT_SIZE; ++r) { eq->potionbelt.Items[r].ID = 0; eq->potionbelt.Items[r].Icon = 0; @@ -3496,6 +3505,8 @@ namespace UF DECODE_LENGTH_EXACT(structs::LootingItem_Struct); SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); + Log(Logs::Moderate, Logs::Netcode, "UF::DECODE(OP_LootItem)"); + IN(lootee); IN(looter); emu->slot_id = UFToServerCorpseSlot(eq->slot_id); @@ -3509,7 +3520,7 @@ namespace UF DECODE_LENGTH_EXACT(structs::MoveItem_Struct); SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); - Log(Logs::General, Logs::Netcode, "[UF] Moved item from %u to %u", eq->from_slot, eq->to_slot); + Log(Logs::Moderate, Logs::Netcode, "UF::DECODE(OP_MoveItem)"); emu->from_slot = UFToServerSlot(eq->from_slot); emu->to_slot = UFToServerSlot(eq->to_slot); @@ -3810,7 +3821,7 @@ namespace UF ibs.nodrop = item->NoDrop; ibs.attune = item->Attuneable; ibs.size = item->Size; - ibs.slots = SwapBits21And22(item->Slots); + ibs.slots = item->Slots; ibs.price = item->Price; ibs.icon = item->Icon; ibs.unknown1 = 1; @@ -4096,81 +4107,201 @@ namespace UF ob.write((const char*)&subitem_count, sizeof(uint32)); - for (uint32 index = EQEmu::invbag::SLOT_BEGIN; index <= EQEmu::invbag::SLOT_END; ++index) { - EQEmu::ItemInstance* sub = inst->GetItem(index); - if (!sub) - continue; + // moved outside of loop since it is not modified within that scope + int16 SubSlotNumber = EQEmu::invbag::SLOT_INVALID; - int SubSlotNumber = INVALID_INDEX; - if (slot_id_in >= EQEmu::invslot::GENERAL_BEGIN && slot_id_in <= EQEmu::invslot::GENERAL_END) - SubSlotNumber = (((slot_id_in + 3) * EQEmu::invbag::SLOT_COUNT) + index + 1); - else if (slot_id_in >= EQEmu::invslot::BANK_BEGIN && slot_id_in <= EQEmu::invslot::BANK_END) - SubSlotNumber = (((slot_id_in - EQEmu::invslot::BANK_BEGIN) * EQEmu::invbag::SLOT_COUNT) + EQEmu::invbag::BANK_BAGS_BEGIN + index); - else if (slot_id_in >= EQEmu::invslot::SHARED_BANK_BEGIN && slot_id_in <= EQEmu::invslot::SHARED_BANK_END) - SubSlotNumber = (((slot_id_in - EQEmu::invslot::SHARED_BANK_BEGIN) * EQEmu::invbag::SLOT_COUNT) + EQEmu::invbag::SHARED_BANK_BAGS_BEGIN + index); - else - SubSlotNumber = slot_id_in; + if (slot_id_in <= EQEmu::invslot::slotGeneral8 && slot_id_in >= EQEmu::invslot::GENERAL_BEGIN) + SubSlotNumber = EQEmu::invbag::GENERAL_BAGS_BEGIN + ((slot_id_in - EQEmu::invslot::GENERAL_BEGIN) * EQEmu::invbag::SLOT_COUNT); + else if (slot_id_in <= EQEmu::invslot::GENERAL_END && slot_id_in >= EQEmu::invslot::slotGeneral9) + SubSlotNumber = EQEmu::invbag::SLOT_INVALID; + else if (slot_id_in == EQEmu::invslot::slotCursor) + SubSlotNumber = EQEmu::invbag::CURSOR_BAG_BEGIN; + else if (slot_id_in <= EQEmu::invslot::BANK_END && slot_id_in >= EQEmu::invslot::BANK_BEGIN) + SubSlotNumber = EQEmu::invbag::BANK_BAGS_BEGIN + ((slot_id_in - EQEmu::invslot::BANK_BEGIN) * EQEmu::invbag::SLOT_COUNT); + else if (slot_id_in <= EQEmu::invslot::SHARED_BANK_END && slot_id_in >= EQEmu::invslot::SHARED_BANK_BEGIN) + SubSlotNumber = EQEmu::invbag::SHARED_BANK_BAGS_BEGIN + ((slot_id_in - EQEmu::invslot::SHARED_BANK_BEGIN) * EQEmu::invbag::SLOT_COUNT); + else + SubSlotNumber = slot_id_in; // not sure if this is the best way to handle this..leaving for now - ob.write((const char*)&index, sizeof(uint32)); + if (SubSlotNumber != EQEmu::invbag::SLOT_INVALID) { + for (uint32 index = EQEmu::invbag::SLOT_BEGIN; index <= EQEmu::invbag::SLOT_END; ++index) { + EQEmu::ItemInstance* sub = inst->GetItem(index); + if (!sub) + continue; - SerializeItem(ob, sub, SubSlotNumber, (depth + 1)); - ++subitem_count; + ob.write((const char*)&index, sizeof(uint32)); + + SerializeItem(ob, sub, SubSlotNumber, (depth + 1)); + ++subitem_count; + } + + if (subitem_count) + ob.overwrite(count_pos, (const char*)&subitem_count, sizeof(uint32)); } - - if (subitem_count) - ob.overwrite(count_pos, (const char*)&subitem_count, sizeof(uint32)); } static inline uint32 ServerToUFSlot(uint32 serverSlot) { - uint32 UnderfootSlot = 0; + uint32 UFSlot = invslot::SLOT_INVALID; - if (serverSlot >= EQEmu::invslot::slotAmmo && serverSlot <= 53) // Cursor/Ammo/Power Source and Normal Inventory Slots - UnderfootSlot = serverSlot + 1; - else if (serverSlot >= EQEmu::invbag::GENERAL_BAGS_BEGIN && serverSlot <= EQEmu::invbag::CURSOR_BAG_END) - UnderfootSlot = serverSlot + 11; - else if (serverSlot >= EQEmu::invbag::BANK_BAGS_BEGIN && serverSlot <= EQEmu::invbag::BANK_BAGS_END) - UnderfootSlot = serverSlot + 1; - else if (serverSlot >= EQEmu::invbag::SHARED_BANK_BAGS_BEGIN && serverSlot <= EQEmu::invbag::SHARED_BANK_BAGS_END) - UnderfootSlot = serverSlot + 1; - else if (serverSlot == EQEmu::invslot::SLOT_POWER_SOURCE) - UnderfootSlot = invslot::slotPowerSource; - else - UnderfootSlot = serverSlot; + if (serverSlot <= EQEmu::invslot::slotGeneral8) { + UFSlot = serverSlot; + } - return UnderfootSlot; + else if (serverSlot <= EQEmu::invslot::CORPSE_END && serverSlot >= EQEmu::invslot::slotCursor) { + UFSlot = serverSlot - 2; + } + + else if (serverSlot <= EQEmu::invbag::GENERAL_BAGS_8_END && serverSlot >= EQEmu::invbag::GENERAL_BAGS_BEGIN) { + UFSlot = serverSlot + 11; + } + + else if (serverSlot <= EQEmu::invbag::CURSOR_BAG_END && serverSlot >= EQEmu::invbag::CURSOR_BAG_BEGIN) { + UFSlot = serverSlot - 9; + } + + else if (serverSlot <= EQEmu::invslot::TRIBUTE_END && serverSlot >= EQEmu::invslot::TRIBUTE_BEGIN) { + UFSlot = serverSlot; + } + + else if (serverSlot <= EQEmu::invslot::GUILD_TRIBUTE_END && serverSlot >= EQEmu::invslot::GUILD_TRIBUTE_BEGIN) { + UFSlot = serverSlot; + } + + else if (serverSlot == EQEmu::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE) { + UFSlot = serverSlot; + } + + else if (serverSlot <= EQEmu::invslot::BANK_END && serverSlot >= EQEmu::invslot::BANK_BEGIN) { + UFSlot = serverSlot; + } + + else if (serverSlot <= EQEmu::invbag::BANK_BAGS_END && serverSlot >= EQEmu::invbag::BANK_BAGS_BEGIN) { + UFSlot = serverSlot + 1; + } + + else if (serverSlot <= EQEmu::invslot::SHARED_BANK_END && serverSlot >= EQEmu::invslot::SHARED_BANK_BEGIN) { + UFSlot = serverSlot; + } + + else if (serverSlot <= EQEmu::invbag::SHARED_BANK_BAGS_END && serverSlot >= EQEmu::invbag::SHARED_BANK_BAGS_BEGIN) { + UFSlot = serverSlot + 1; + } + + else if (serverSlot <= EQEmu::invslot::TRADE_END && serverSlot >= EQEmu::invslot::TRADE_BEGIN) { + UFSlot = serverSlot; + } + + else if (serverSlot <= EQEmu::invbag::TRADE_BAGS_END && serverSlot >= EQEmu::invbag::TRADE_BAGS_BEGIN) { + UFSlot = serverSlot; + } + + else if (serverSlot <= EQEmu::invslot::WORLD_END && serverSlot >= EQEmu::invslot::WORLD_BEGIN) { + UFSlot = serverSlot; + } + + Log(Logs::Detail, Logs::Netcode, "Convert Server Slot %i to UF Slot %i", serverSlot, UFSlot); + + return UFSlot; } static inline uint32 ServerToUFCorpseSlot(uint32 serverCorpseSlot) { - //uint32 UnderfootCorpse; - return (serverCorpseSlot + 1); + uint32 UFSlot = invslot::SLOT_INVALID; + + if (serverCorpseSlot <= EQEmu::invslot::slotGeneral8 && serverCorpseSlot >= EQEmu::invslot::slotGeneral1) { + UFSlot = serverCorpseSlot; + } + + else if (serverCorpseSlot <= EQEmu::invslot::CORPSE_END && serverCorpseSlot >= EQEmu::invslot::slotCursor) { + UFSlot = serverCorpseSlot - 2; + } + + Log(Logs::Detail, Logs::Netcode, "Convert Server Corpse Slot %i to UF Corpse Slot %i", serverCorpseSlot, UFSlot); + + return UFSlot; } static inline uint32 UFToServerSlot(uint32 ufSlot) { - uint32 ServerSlot = 0; + uint32 ServerSlot = EQEmu::invslot::SLOT_INVALID; - if (ufSlot >= invslot::slotAmmo && ufSlot <= invslot::CORPSE_END) // Cursor/Ammo/Power Source and Normal Inventory Slots - ServerSlot = ufSlot - 1; - else if (ufSlot >= invbag::GENERAL_BAGS_BEGIN && ufSlot <= invbag::CURSOR_BAG_END) - ServerSlot = ufSlot - 11; - else if (ufSlot >= invbag::BANK_BAGS_BEGIN && ufSlot <= invbag::BANK_BAGS_END) - ServerSlot = ufSlot - 1; - else if (ufSlot >= invbag::SHARED_BANK_BAGS_BEGIN && ufSlot <= invbag::SHARED_BANK_BAGS_END) - ServerSlot = ufSlot - 1; - else if (ufSlot == invslot::slotPowerSource) - ServerSlot = EQEmu::invslot::SLOT_POWER_SOURCE; - else + if (ufSlot <= invslot::slotGeneral8) { ServerSlot = ufSlot; + } + + else if (ufSlot <= invslot::CORPSE_END && ufSlot >= invslot::slotCursor) { + ServerSlot = ufSlot + 2; + } + + else if (ufSlot <= invbag::GENERAL_BAGS_END && ufSlot >= invbag::GENERAL_BAGS_BEGIN) { + ServerSlot = ufSlot - 11; + } + + else if (ufSlot <= invbag::CURSOR_BAG_END && ufSlot >= invbag::CURSOR_BAG_BEGIN) { + ServerSlot = ufSlot + 9; + } + + else if (ufSlot <= invslot::TRIBUTE_END && ufSlot >= invslot::TRIBUTE_BEGIN) { + ServerSlot = ufSlot; + } + + else if (ufSlot <= invslot::GUILD_TRIBUTE_END && ufSlot >= invslot::GUILD_TRIBUTE_BEGIN) { + ServerSlot = ufSlot; + } + + else if (ufSlot == invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE) { + ServerSlot = ufSlot; + } + + else if (ufSlot <= invslot::BANK_END && ufSlot >= invslot::BANK_BEGIN) { + ServerSlot = ufSlot; + } + + else if (ufSlot <= invbag::BANK_BAGS_END && ufSlot >= invbag::BANK_BAGS_BEGIN) { + ServerSlot = ufSlot - 1; + } + + else if (ufSlot <= invslot::SHARED_BANK_END && ufSlot >= invslot::SHARED_BANK_BEGIN) { + ServerSlot = ufSlot; + } + + else if (ufSlot <= invbag::SHARED_BANK_BAGS_END && ufSlot >= invbag::SHARED_BANK_BAGS_BEGIN) { + ServerSlot = ufSlot - 1; + } + + else if (ufSlot <= invslot::TRADE_END && ufSlot >= invslot::TRADE_BEGIN) { + ServerSlot = ufSlot; + } + + else if (ufSlot <= invbag::TRADE_BAGS_END && ufSlot >= invbag::TRADE_BAGS_BEGIN) { + ServerSlot = ufSlot; + } + + else if (ufSlot <= invslot::WORLD_END && ufSlot >= invslot::WORLD_BEGIN) { + ServerSlot = ufSlot; + } + + Log(Logs::Detail, Logs::Netcode, "Convert UF Slot %i to Server Slot %i", ufSlot, ServerSlot); return ServerSlot; } static inline uint32 UFToServerCorpseSlot(uint32 ufCorpseSlot) { - //uint32 ServerCorpse; - return (ufCorpseSlot - 1); + uint32 ServerSlot = EQEmu::invslot::SLOT_INVALID; + + if (ufCorpseSlot <= invslot::slotGeneral8 && ufCorpseSlot >= invslot::slotGeneral1) { + ServerSlot = ufCorpseSlot; + } + + else if (ufCorpseSlot <= invslot::CORPSE_END && ufCorpseSlot >= invslot::slotCursor) { + ServerSlot = ufCorpseSlot + 2; + } + + Log(Logs::Detail, Logs::Netcode, "Convert UF Corpse Slot %i to Server Corpse Slot %i", ufCorpseSlot, ServerSlot); + + return ServerSlot; } static inline void ServerToUFSayLink(std::string& ufSayLink, const std::string& serverSayLink) diff --git a/common/patches/uf_limits.h b/common/patches/uf_limits.h index 52924f94d..ef00b912e 100644 --- a/common/patches/uf_limits.h +++ b/common/patches/uf_limits.h @@ -150,6 +150,8 @@ namespace UF const int16 SLOT_INVALID = IINVALID; const int16 SLOT_BEGIN = INULL; + const int16 SLOT_TRADESKILL_EXPERIMENT_COMBINE = 1000; + const int16 POSSESSIONS_BEGIN = slotCharm; const int16 POSSESSIONS_END = slotCursor; const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1; diff --git a/common/ruletypes.h b/common/ruletypes.h index 0f8420aa1..5c6ff6d3a 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -389,6 +389,7 @@ RULE_INT(Spells, AI_PursueDetrimentalChance, 90) // Chance while chasing target RULE_INT(Spells, AI_IdleNoSpellMinRecast, 6000) // AI spell recast time(MS) check when no spell is cast while idle. (min time in random) RULE_INT(Spells, AI_IdleNoSpellMaxRecast, 60000) // AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random) RULE_INT(Spells, AI_IdleBeneficialChance, 100) // Chance while idle to do a beneficial spell on self or others. +RULE_INT(Spells, AI_HealHPPct, 50) // HP Pct NPCs will start heals at (in and out of combat) if spell's max_hp is not set RULE_BOOL(Spells, SHDProcIDOffByOne, true) // pre June 2009 SHD spell procs were off by 1, they stopped doing this in June 2009 (so UF+ spell files need this false) RULE_BOOL(Spells, Jun182014HundredHandsRevamp, false) // this should be true for if you import a spell file newer than June 18, 2014 RULE_BOOL(Spells, SwarmPetTargetLock, false) // Use old method of swarm pets target locking till target dies then despawning. @@ -551,7 +552,8 @@ RULE_REAL(Aggro, TunnelVisionAggroMod, 0.75) //people not currently the top hate RULE_INT(Aggro, MaxScalingProcAggro, 400) // Set to -1 for no limit. Maxmimum amount of aggro that HP scaling SPA effect in a proc will add. RULE_INT(Aggro, IntAggroThreshold, 75) // Int <= this will aggro regardless of level difference. RULE_BOOL(Aggro, AllowTickPulling, false) // tick pulling is an exploit in an NPC's call for help fixed sometime in 2006 on live -RULE_BOOL(Aggro, UseLevelAggro, true) // Level 18+ and Undead will aggro regardless of level difference. (this will disabled Rule:IntAggroThreshold if set to true) +RULE_INT(Aggro, MinAggroLevel, 18) // For use with UseLevelAggro +RULE_BOOL(Aggro, UseLevelAggro, true) // MinAggroLevel rule value+ and Undead will aggro regardless of level difference. (this will disabled Rule:IntAggroThreshold if set to true) RULE_INT(Aggro, ClientAggroCheckInterval, 6) // Interval in which clients actually check for aggro - in seconds RULE_REAL(Aggro, PetAttackRange, 40000.0) // max squared range /pet attack works at default is 200 RULE_BOOL(Aggro, NPCAggroMaxDistanceEnabled, true) /* If enabled, NPC's will drop aggro beyond 600 units or what is defined at the zone level */ diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 00fdaea96..9e0cd5f7e 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -547,9 +547,12 @@ bool SharedDatabase::GetSharedBank(uint32 id, EQEmu::InventoryProfile *inv, bool return true; } -// Overloaded: Retrieve character inventory based on character id +// Overloaded: Retrieve character inventory based on character id (zone entry) bool SharedDatabase::GetInventory(uint32 char_id, EQEmu::InventoryProfile *inv) { + if (!char_id || !inv) + return false; + // Retrieve character inventory std::string query = StringFormat("SELECT slotid, itemid, charges, color, augslot1, augslot2, augslot3, augslot4, augslot5, " @@ -566,8 +569,40 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQEmu::InventoryProfile *inv) auto timestamps = GetItemRecastTimestamps(char_id); + auto cv_conflict = false; + auto pmask = inv->GetLookup()->PossessionsBitmask; + auto bank_size = inv->GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank]; + for (auto row = results.begin(); row != results.end(); ++row) { int16 slot_id = atoi(row[0]); + + if (slot_id <= EQEmu::invslot::POSSESSIONS_END && slot_id >= EQEmu::invslot::POSSESSIONS_BEGIN) { // Titanium thru UF check + if ((((uint64)1 << slot_id) & pmask) == 0) { + cv_conflict = true; + continue; + } + } + else if (slot_id <= EQEmu::invbag::GENERAL_BAGS_END && slot_id >= EQEmu::invbag::GENERAL_BAGS_BEGIN) { // Titanium thru UF check + auto parent_slot = EQEmu::invslot::GENERAL_BEGIN + ((slot_id - EQEmu::invbag::GENERAL_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT); + if ((((uint64)1 << parent_slot) & pmask) == 0) { + cv_conflict = true; + continue; + } + } + else if (slot_id <= EQEmu::invslot::BANK_END && slot_id >= EQEmu::invslot::BANK_BEGIN) { // Titanium check + if ((slot_id - EQEmu::invslot::BANK_BEGIN) >= bank_size) { + cv_conflict = true; + continue; + } + } + else if (slot_id <= EQEmu::invbag::BANK_BAGS_END && slot_id >= EQEmu::invbag::BANK_BAGS_BEGIN) { // Titanium check + auto parent_index = ((slot_id - EQEmu::invbag::BANK_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT); + if (parent_index < EQEmu::invslot::SLOT_BEGIN || parent_index >= bank_size) { + cv_conflict = true; + continue; + } + } + uint32 item_id = atoi(row[1]); uint16 charges = atoi(row[2]); uint32 color = atoul(row[3]); @@ -633,10 +668,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQEmu::InventoryProfile *inv) inst->SetOrnamentationIDFile(ornament_idfile); inst->SetOrnamentHeroModel(item->HerosForgeModel); - if (instnodrop || - (((slot_id >= EQEmu::invslot::EQUIPMENT_BEGIN && slot_id <= EQEmu::invslot::EQUIPMENT_END) || - slot_id == EQEmu::invslot::SLOT_POWER_SOURCE) && - inst->GetItem()->Attuneable)) + if (instnodrop || (inst->GetItem()->Attuneable && slot_id >= EQEmu::invslot::EQUIPMENT_BEGIN && slot_id <= EQEmu::invslot::EQUIPMENT_END)) inst->SetAttuned(true); if (color > 0) @@ -684,13 +716,22 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQEmu::InventoryProfile *inv) char_id, item_id, slot_id); } } + + if (cv_conflict) { + char char_name[64] = ""; + GetCharName(char_id, char_name); + Log(Logs::Moderate, Logs::Client_Login, + "ClientVersion conflict during inventory load at zone entry for '%s' (charid: %u, inver: %s)", + char_name, char_id, EQEmu::versions::MobVersionName(inv->InventoryVersion()) + ); + } // Retrieve shared inventory return GetSharedBank(char_id, inv, true); } -// Overloaded: Retrieve character inventory based on account_id and character name -bool SharedDatabase::GetInventory(uint32 account_id, char *name, EQEmu::InventoryProfile *inv) +// Overloaded: Retrieve character inventory based on account_id and character name (char select) +bool SharedDatabase::GetInventory(uint32 account_id, char *name, EQEmu::InventoryProfile *inv) // deprecated { // Retrieve character inventory std::string query = diff --git a/common/shareddb.h b/common/shareddb.h index bbd8dc9a0..f6b1879df 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -89,7 +89,7 @@ class SharedDatabase : public Database int32 GetSharedPlatinum(uint32 account_id); bool SetSharedPlatinum(uint32 account_id, int32 amount_to_add); bool GetInventory(uint32 char_id, EQEmu::InventoryProfile* inv); - bool GetInventory(uint32 account_id, char* name, EQEmu::InventoryProfile* inv); + bool GetInventory(uint32 account_id, char* name, EQEmu::InventoryProfile* inv); // deprecated std::map GetItemRecastTimestamps(uint32 char_id); uint32 GetItemRecastTimestamp(uint32 char_id, uint32 recast_type); void ClearOldRecastTimestamps(uint32 char_id); diff --git a/common/version.h b/common/version.h index a7e066ab1..4caca5b38 100644 --- a/common/version.h +++ b/common/version.h @@ -30,9 +30,9 @@ Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9127 +#define CURRENT_BINARY_DATABASE_VERSION 9129 #ifdef BOTS - #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9019 + #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9021 #else #define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0 #endif diff --git a/utils/sql/character_table_list.txt b/utils/sql/character_table_list.txt index 4d70051ec..8a47abf67 100644 --- a/utils/sql/character_table_list.txt +++ b/utils/sql/character_table_list.txt @@ -31,6 +31,7 @@ friends guild_members instance_list_player inventory +inventory_snapshots keyring mail player_titlesets diff --git a/utils/sql/data_tables.txt b/utils/sql/data_tables.txt index 76938a8d2..8738c716b 100644 --- a/utils/sql/data_tables.txt +++ b/utils/sql/data_tables.txt @@ -1,4 +1,5 @@ command_settings +inventory_versions launcher rule_sets rule_values diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index e84395a54..dae319641 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -381,6 +381,8 @@ 9125|2018_07_20_task_emote.sql|SHOW COLUMNS FROM `tasks` LIKE 'completion_emote'|empty| 9126|2018_09_07_FastRegen.sql|SHOW COLUMNS FROM `zone` LIKE 'fast_regen_hp'|empty| 9127|2018_09_07_NPCMaxAggroDist.sql|SHOW COLUMNS FROM `zone` LIKE 'npc_max_aggro_dist'|empty| +9128|2018_08_13_inventory_version_update.sql|SHOW TABLES LIKE 'inventory_versions'|empty| +9129|2018_08_13_inventory_update.sql|SELECT * FROM `inventory_versions` WHERE `version` = 2 and `step` = 0|not_empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/bots/bots_db_update_manifest.txt b/utils/sql/git/bots/bots_db_update_manifest.txt index 2bf71af18..acdb20532 100644 --- a/utils/sql/git/bots/bots_db_update_manifest.txt +++ b/utils/sql/git/bots/bots_db_update_manifest.txt @@ -18,6 +18,8 @@ 9017|2017_03_26_bots_spells_id_fix_for_saved_shadowknight_bots.sql|SELECT * FROM `bot_data` WHERE `class` = '5' AND `spells_id` = '3004'|not_empty| 9018|2018_02_02_Bot_Spells_Min_Max_HP.sql|SHOW COLUMNS FROM `bot_spells_entries` LIKE 'min_hp'|empty| 9019|2018_04_12_bots_stop_melee_level.sql|SHOW COLUMNS FROM `bot_data` LIKE 'stop_melee_level'|empty| +9020|2018_08_13_bots_inventory_update.sql|SELECT * FROM `inventory_versions` WHERE `version` = 2 and `bot_step` = 0|not_empty| +9021|2018_10_09_bots_owner_options.sql|SHOW TABLES LIKE 'bot_owner_options'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/bots/required/2018_08_13_bots_inventory_update.sql b/utils/sql/git/bots/required/2018_08_13_bots_inventory_update.sql new file mode 100644 index 000000000..94ca630c2 --- /dev/null +++ b/utils/sql/git/bots/required/2018_08_13_bots_inventory_update.sql @@ -0,0 +1,5 @@ +-- update `bot_inventories` slots +UPDATE `bot_inventories` SET `slot_id` = 22 WHERE `slot_id` = 21; -- adjust ammo slot +UPDATE `bot_inventories` SET `slot_id` = 21 WHERE `slot_id` = 9999; -- adjust powersource slot + +UPDATE `inventory_versions` SET `bot_step` = 1 WHERE `version` = 2; \ No newline at end of file diff --git a/utils/sql/git/bots/required/2018_10_09_bots_owner_options.sql b/utils/sql/git/bots/required/2018_10_09_bots_owner_options.sql new file mode 100644 index 000000000..ec1a7bebe --- /dev/null +++ b/utils/sql/git/bots/required/2018_10_09_bots_owner_options.sql @@ -0,0 +1,11 @@ +DROP TABLE IF EXISTS `bot_owner_options`; + +CREATE TABLE `bot_owner_options` ( + `owner_id` INT(11) UNSIGNED NOT NULL, + `death_marquee` SMALLINT(3) UNSIGNED NULL DEFAULT '0', + PRIMARY KEY (`owner_id`) +) +COLLATE='latin1_swedish_ci' +ENGINE=MyISAM; + +INSERT INTO `bot_command_settings`(`bot_command`, `access`, `aliases`) VALUES ('owneroption', '0', 'oo'); diff --git a/utils/sql/git/required/2018_08_13_inventory_update.sql b/utils/sql/git/required/2018_08_13_inventory_update.sql new file mode 100644 index 000000000..e02bc3ae0 --- /dev/null +++ b/utils/sql/git/required/2018_08_13_inventory_update.sql @@ -0,0 +1,72 @@ +-- create inventory v1 backup +SELECT @pre_timestamp := UNIX_TIMESTAMP(NOW()); +INSERT INTO `inventory_snapshots_v1_bak` + (`time_index`,`charid`,`slotid`,`itemid`,`charges`,`color`,`augslot1`,`augslot2`,`augslot3`,`augslot4`, + `augslot5`,`augslot6`,`instnodrop`,`custom_data`,`ornamenticon`,`ornamentidfile`,`ornament_hero_model`) +SELECT + @pre_timestamp, `charid`, `slotid`, `itemid`, `charges`, `color`, `augslot1`, `augslot2`, `augslot3`, `augslot4`, + `augslot5`,`augslot6`,`instnodrop`,`custom_data`,`ornamenticon`,`ornamentidfile`,`ornament_hero_model` +FROM `inventory`; + + +-- update equipable slots in `items` table +SELECT 'pre-transform count..', +(SELECT COUNT(id) FROM `items` WHERE `slots` & (3 << 21)) total, +(SELECT COUNT(id) FROM `items` WHERE `slots` & (1 << 21)) bit21, +(SELECT COUNT(id) FROM `items` WHERE `slots` & (1 << 22)) bit22; + +UPDATE `items` SET `slots` = (`slots` ^ (3 << 21)) WHERE (`slots` & (3 << 21)) IN ((1 << 21), (1 << 22)); -- transform + +SELECT 'post-transform count..', +(SELECT COUNT(id) FROM `items` WHERE `slots` & (3 << 21)) total, +(SELECT COUNT(id) FROM `items` WHERE `slots` & (1 << 21)) bit21, +(SELECT COUNT(id) FROM `items` WHERE `slots` & (1 << 22)) bit22; + + +-- update `inventory` slots +UPDATE `inventory` SET `slotid` = 33 WHERE `slotid` = 30; -- adjust cursor +UPDATE `inventory` SET `slotid` = (`slotid` + 20) WHERE `slotid` >= 331 AND `slotid` <= 340; -- adjust cursor bags +UPDATE `inventory` SET `slotid` = 30 WHERE `slotid` = 29; -- adjust general8 slot +UPDATE `inventory` SET `slotid` = 29 WHERE `slotid` = 28; -- adjust general7 slot +UPDATE `inventory` SET `slotid` = 28 WHERE `slotid` = 27; -- adjust general6 slot +UPDATE `inventory` SET `slotid` = 27 WHERE `slotid` = 26; -- adjust general5 slot +UPDATE `inventory` SET `slotid` = 26 WHERE `slotid` = 25; -- adjust general4 slot +UPDATE `inventory` SET `slotid` = 25 WHERE `slotid` = 24; -- adjust general3 slot +UPDATE `inventory` SET `slotid` = 24 WHERE `slotid` = 23; -- adjust general2 slot +UPDATE `inventory` SET `slotid` = 23 WHERE `slotid` = 22; -- adjust general1 slot +-- current general bags remain the same +UPDATE `inventory` SET `slotid` = 22 WHERE `slotid` = 21; -- adjust ammo slot +UPDATE `inventory` SET `slotid` = 21 WHERE `slotid` = 9999; -- adjust powersource slot + + +-- update `character_corpse_items` slots +UPDATE `character_corpse_items` SET `equip_slot` = 33 WHERE `equip_slot` = 30; -- adjust cursor +UPDATE `character_corpse_items` SET `equip_slot` = (`equip_slot` + 20) WHERE `equip_slot` >= 331 AND `equip_slot` <= 340; -- adjust cursor bags +UPDATE `character_corpse_items` SET `equip_slot` = 30 WHERE `equip_slot` = 29; -- adjust general8 slot +UPDATE `character_corpse_items` SET `equip_slot` = 29 WHERE `equip_slot` = 28; -- adjust general7 slot +UPDATE `character_corpse_items` SET `equip_slot` = 28 WHERE `equip_slot` = 27; -- adjust general6 slot +UPDATE `character_corpse_items` SET `equip_slot` = 27 WHERE `equip_slot` = 26; -- adjust general5 slot +UPDATE `character_corpse_items` SET `equip_slot` = 26 WHERE `equip_slot` = 25; -- adjust general4 slot +UPDATE `character_corpse_items` SET `equip_slot` = 25 WHERE `equip_slot` = 24; -- adjust general3 slot +UPDATE `character_corpse_items` SET `equip_slot` = 24 WHERE `equip_slot` = 23; -- adjust general2 slot +UPDATE `character_corpse_items` SET `equip_slot` = 23 WHERE `equip_slot` = 22; -- adjust general1 slot +-- current general bags remain the same +UPDATE `character_corpse_items` SET `equip_slot` = 22 WHERE `equip_slot` = 21; -- adjust ammo slot +UPDATE `character_corpse_items` SET `equip_slot` = 21 WHERE `equip_slot` = 9999; -- adjust powersource slot + + +-- update `character_pet_inventory` slots +UPDATE `character_pet_inventory` SET `slot` = 22 WHERE `slot` = 21; -- adjust ammo slot + +UPDATE `inventory_versions` SET `step` = 1 WHERE `version` = 2; + + +-- create initial inventory v2 snapshots +SELECT @post_timestamp := UNIX_TIMESTAMP(NOW()); +INSERT INTO `inventory_snapshots` + (`time_index`,`charid`,`slotid`,`itemid`,`charges`,`color`,`augslot1`,`augslot2`,`augslot3`,`augslot4`, + `augslot5`,`augslot6`,`instnodrop`,`custom_data`,`ornamenticon`,`ornamentidfile`,`ornament_hero_model`) +SELECT + @post_timestamp, `charid`, `slotid`, `itemid`, `charges`, `color`, `augslot1`, `augslot2`, `augslot3`, `augslot4`, + `augslot5`,`augslot6`,`instnodrop`,`custom_data`,`ornamenticon`,`ornamentidfile`,`ornament_hero_model` +FROM `inventory`; diff --git a/utils/sql/git/required/2018_08_13_inventory_version_update.sql b/utils/sql/git/required/2018_08_13_inventory_version_update.sql new file mode 100644 index 000000000..f8fc33162 --- /dev/null +++ b/utils/sql/git/required/2018_08_13_inventory_version_update.sql @@ -0,0 +1,61 @@ +DROP TABLE IF EXISTS `inventory_version`; +DROP TABLE IF EXISTS `inventory_snapshots`; + + +CREATE TABLE `inventory_versions` ( + `version` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `step` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `bot_step` INT(11) UNSIGNED NOT NULL DEFAULT '0' +) +COLLATE='latin1_swedish_ci' +ENGINE=MyISAM; + +INSERT INTO `inventory_versions` VALUES (2, 0, 0); + + +CREATE TABLE `inventory_snapshots` ( + `time_index` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `charid` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `slotid` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT '0', + `itemid` INT(11) UNSIGNED NULL DEFAULT '0', + `charges` SMALLINT(3) UNSIGNED NULL DEFAULT '0', + `color` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `augslot1` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT '0', + `augslot2` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT '0', + `augslot3` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT '0', + `augslot4` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT '0', + `augslot5` MEDIUMINT(7) UNSIGNED NULL DEFAULT '0', + `augslot6` MEDIUMINT(7) NOT NULL DEFAULT '0', + `instnodrop` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0', + `custom_data` TEXT NULL, + `ornamenticon` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `ornamentidfile` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `ornament_hero_model` INT(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`time_index`, `charid`, `slotid`) +) +COLLATE='latin1_swedish_ci' +ENGINE=InnoDB; + + +CREATE TABLE `inventory_snapshots_v1_bak` ( + `time_index` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `charid` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `slotid` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT '0', + `itemid` INT(11) UNSIGNED NULL DEFAULT '0', + `charges` SMALLINT(3) UNSIGNED NULL DEFAULT '0', + `color` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `augslot1` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT '0', + `augslot2` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT '0', + `augslot3` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT '0', + `augslot4` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT '0', + `augslot5` MEDIUMINT(7) UNSIGNED NULL DEFAULT '0', + `augslot6` MEDIUMINT(7) NOT NULL DEFAULT '0', + `instnodrop` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0', + `custom_data` TEXT NULL, + `ornamenticon` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `ornamentidfile` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `ornament_hero_model` INT(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`time_index`, `charid`, `slotid`) +) +COLLATE='latin1_swedish_ci' +ENGINE=InnoDB; diff --git a/utils/sql/user_tables.txt b/utils/sql/user_tables.txt index a210aa381..941e30c47 100644 --- a/utils/sql/user_tables.txt +++ b/utils/sql/user_tables.txt @@ -52,6 +52,7 @@ guild_members hackers instance_list_player inventory +inventory_snapshots item_tick keyring launcher_zones diff --git a/world/client.cpp b/world/client.cpp index 0b04373a1..9b04f5cc5 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -254,7 +254,7 @@ void Client::SendMembership() { mc->entries[1] = 0xffffffff; // Max Level Restriction mc->entries[2] = 0xffffffff; // Max Char Slots per Account (not used by client?) mc->entries[3] = 0xffffffff; // 1 for Silver - mc->entries[4] = 8; // Main Inventory Size (0xffffffff on Live for Gold, but limiting to 8 until 10 is supported) + mc->entries[4] = 0xffffffff; // Main Inventory Size (0xffffffff on Live for Gold, but limiting to 8 until 10 is supported) mc->entries[5] = 0xffffffff; // Max Platinum per level mc->entries[6] = 1; // 0 for Silver mc->entries[7] = 1; // 0 for Silver @@ -1444,6 +1444,7 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) PlayerProfile_Struct pp; ExtendedProfile_Struct ext; EQEmu::InventoryProfile inv; + inv.SetInventoryVersion(EQEmu::versions::ConvertClientVersionBitToClientVersion(m_ClientVersionBit)); time_t bday = time(nullptr); char startzone[50]={0}; uint32 i; diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 67b0eab38..830f0bfc3 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -97,6 +97,7 @@ void WorldDatabase::GetCharSelectInfo(uint32 accountID, EQApplicationPacket **ou CharacterSelectEntry_Struct *cse = (CharacterSelectEntry_Struct *)buff_ptr; PlayerProfile_Struct pp; EQEmu::InventoryProfile inv; + inv.SetInventoryVersion(client_version); uint32 character_id = (uint32)atoi(row[0]); uint8 has_home = 0; uint8 has_bind = 0; @@ -243,7 +244,7 @@ void WorldDatabase::GetCharSelectInfo(uint32 accountID, EQApplicationPacket **ou /* Load Inventory */ // If we ensure that the material data is updated appropriately, we can do away with inventory loads - if (GetInventory(accountID, cse->Name, &inv)) { + if (GetCharSelInventory(accountID, cse->Name, &inv)) { const EQEmu::ItemData* item = nullptr; const EQEmu::ItemInstance* inst = nullptr; int16 invslot = 0; @@ -590,3 +591,143 @@ bool WorldDatabase::LoadCharacterCreateCombos() return true; } + +// this is a slightly modified version of SharedDatabase::GetInventory(...) for character select use-only +bool WorldDatabase::GetCharSelInventory(uint32 account_id, char *name, EQEmu::InventoryProfile *inv) +{ + if (!account_id || !name || !inv) + return false; + + std::string query = StringFormat( + "SELECT" + " slotid," + " itemid," + " charges," + " color," + " augslot1," + " augslot2," + " augslot3," + " augslot4," + " augslot5," + " augslot6," + " instnodrop," + " custom_data," + " ornamenticon," + " ornamentidfile," + " ornament_hero_model " + "FROM" + " inventory " + "INNER JOIN" + " character_data ch " + "ON" + " ch.id = charid " + "WHERE" + " ch.name = '%s' " + "AND" + " ch.account_id = %i " + "AND" + " slotid >= %i " + "AND" + " slotid <= %i", + name, + account_id, + EQEmu::invslot::slotHead, + EQEmu::invslot::slotFeet + ); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + for (auto row = results.begin(); row != results.end(); ++row) { + int16 slot_id = atoi(row[0]); + + switch (slot_id) { + case EQEmu::invslot::slotFace: + case EQEmu::invslot::slotEar2: + case EQEmu::invslot::slotNeck: + case EQEmu::invslot::slotShoulders: + case EQEmu::invslot::slotBack: + case EQEmu::invslot::slotFinger1: + case EQEmu::invslot::slotFinger2: + continue; + default: + break; + } + + uint32 item_id = atoi(row[1]); + int8 charges = atoi(row[2]); + uint32 color = atoul(row[3]); + + uint32 aug[EQEmu::invaug::SOCKET_COUNT]; + aug[0] = (uint32)atoi(row[4]); + aug[1] = (uint32)atoi(row[5]); + aug[2] = (uint32)atoi(row[6]); + aug[3] = (uint32)atoi(row[7]); + aug[4] = (uint32)atoi(row[8]); + aug[5] = (uint32)atoi(row[9]); + + bool instnodrop = ((row[10] && (uint16)atoi(row[10])) ? true : false); + uint32 ornament_icon = (uint32)atoul(row[12]); + uint32 ornament_idfile = (uint32)atoul(row[13]); + uint32 ornament_hero_model = (uint32)atoul(row[14]); + + const EQEmu::ItemData *item = GetItem(item_id); + if (!item) + continue; + + EQEmu::ItemInstance *inst = CreateBaseItem(item, charges); + + if (inst == nullptr) + continue; + + inst->SetAttuned(instnodrop); + + if (row[11]) { + std::string data_str(row[11]); + std::string idAsString; + std::string value; + bool use_id = true; + + for (int i = 0; i < data_str.length(); ++i) { + if (data_str[i] == '^') { + if (!use_id) { + inst->SetCustomData(idAsString, value); + idAsString.clear(); + value.clear(); + } + + use_id = !use_id; + continue; + } + + char v = data_str[i]; + if (use_id) + idAsString.push_back(v); + else + value.push_back(v); + } + } + + inst->SetOrnamentIcon(ornament_icon); + inst->SetOrnamentationIDFile(ornament_idfile); + inst->SetOrnamentHeroModel(item->HerosForgeModel); + + if (color > 0) + inst->SetColor(color); + + inst->SetCharges(charges); + + if (item->IsClassCommon()) { + for (int i = EQEmu::invaug::SOCKET_BEGIN; i <= EQEmu::invaug::SOCKET_END; i++) { + if (aug[i]) + inst->PutAugment(this, i, aug[i]); + } + } + + inv->PutItem(slot_id, *inst); + + safe_delete(inst); + } + + return true; +} diff --git a/world/worlddb.h b/world/worlddb.h index 2afd69920..036c0dc40 100644 --- a/world/worlddb.h +++ b/world/worlddb.h @@ -41,6 +41,8 @@ public: private: void SetTitaniumDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc); void SetSoFDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc); + + bool GetCharSelInventory(uint32 account_id, char* name, EQEmu::InventoryProfile* inv); }; extern WorldDatabase database; diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 30e3b9e0f..0b61662fc 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -107,16 +107,10 @@ void NPC::DescribeAggro(Client *towho, Mob *mob, bool verbose) { float iAggroRange = GetAggroRange(); float t1, t2, t3; - t1 = mob->GetX() - GetX(); - t2 = mob->GetY() - GetY(); - t3 = mob->GetZ() - GetZ(); - //Cheap ABS() - if(t1 < 0) - t1 = 0 - t1; - if(t2 < 0) - t2 = 0 - t2; - if(t3 < 0) - t3 = 0 - t3; + t1 = std::abs(mob->GetX() - GetX()); + t2 = std::abs(mob->GetY() - GetY()); + t3 = std::abs(mob->GetZ() - GetZ()); + if(( t1 > iAggroRange) || ( t2 > iAggroRange) || ( t3 > iAggroRange) ) { @@ -158,7 +152,7 @@ void NPC::DescribeAggro(Client *towho, Mob *mob, bool verbose) { if (RuleB(Aggro, UseLevelAggro)) { - if (GetLevel() < 18 && mob->GetLevelCon(GetLevel()) == CON_GRAY && GetBodyType() != 3) + if (GetLevel() < RuleI(Aggro, MinAggroLevel) && mob->GetLevelCon(GetLevel()) == CON_GRAY && GetBodyType() != 3) { towho->Message(0, "...%s is red to me (basically)", mob->GetName(), dist2, iAggroRange2); return; @@ -271,16 +265,10 @@ bool Mob::CheckWillAggro(Mob *mob) { // Image: I moved this up by itself above faction and distance checks because if one of these return true, theres no reason to go through the other information float t1, t2, t3; - t1 = mob->GetX() - GetX(); - t2 = mob->GetY() - GetY(); - t3 = mob->GetZ() - GetZ(); - //Cheap ABS() - if(t1 < 0) - t1 = 0 - t1; - if(t2 < 0) - t2 = 0 - t2; - if(t3 < 0) - t3 = 0 - t3; + t1 = std::abs(mob->GetX() - GetX()); + t2 = std::abs(mob->GetY() - GetY()); + t3 = std::abs(mob->GetZ() - GetZ()); + if(( t1 > iAggroRange) || ( t2 > iAggroRange) || ( t3 > iAggroRange) @@ -336,7 +324,7 @@ bool Mob::CheckWillAggro(Mob *mob) { ( //old InZone check taken care of above by !mob->CastToClient()->Connected() ( - ( GetLevel() >= 18 ) + ( GetLevel() >= RuleI(Aggro, MinAggroLevel)) ||(GetBodyType() == 3) ||( mob->IsClient() && mob->CastToClient()->IsSitting() ) ||( mob->GetLevelCon(GetLevel()) != CON_GRAY) @@ -526,7 +514,7 @@ void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) { Log(Logs::General, Logs::None, "AIYellForHelp(\"%s\",\"%s\") %s attacking %s Dist %f Z %f", sender->GetName(), attacker->GetName(), mob->GetName(), attacker->GetName(), DistanceSquared(mob->GetPosition(), - sender->GetPosition()), fabs(sender->GetZ()+mob->GetZ())); + sender->GetPosition()), std::abs(sender->GetZ()+mob->GetZ())); #endif mob->AddToHateList(attacker, 25, 0, false); sender->AddAssistCap(); diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index b6de679d1..e71c6e0d5 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -179,14 +179,6 @@ void Client::CalcItemBonuses(StatBonuses* newbon) { SetTwoHanderEquipped(true); } - //Power Source Slot - if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF) - { - const EQEmu::ItemInstance* inst = m_inv[EQEmu::invslot::SLOT_POWER_SOURCE]; - if(inst) - AddItemBonuses(inst, newbon); - } - //tribute items for (i = EQEmu::invslot::TRIBUTE_BEGIN; i <= EQEmu::invslot::TRIBUTE_END; i++) { const EQEmu::ItemInstance* inst = m_inv[i]; @@ -3261,7 +3253,7 @@ void NPC::CalcItemBonuses(StatBonuses *newbon) { if(newbon){ - for (int i = EQEmu::invslot::EQUIPMENT_BEGIN; i <= EQEmu::invslot::EQUIPMENT_END; i++){ + for (int i = EQEmu::invslot::BONUS_BEGIN; i <= EQEmu::invslot::BONUS_STAT_END; i++){ const EQEmu::ItemData *cur = database.GetItem(equipment[i]); if(cur){ //basic stats @@ -3353,13 +3345,6 @@ void Client::CalcItemScale() { if (CalcItemScale(EQEmu::invslot::TRIBUTE_BEGIN, EQEmu::invslot::TRIBUTE_END)) // (< 405) changed = true; - //Power Source Slot - if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF) - { - if (CalcItemScale(EQEmu::invslot::SLOT_POWER_SOURCE, EQEmu::invslot::SLOT_POWER_SOURCE)) - changed = true; - } - if(changed) { CalcBonuses(); @@ -3447,13 +3432,6 @@ void Client::DoItemEnterZone() { if (DoItemEnterZone(EQEmu::invslot::TRIBUTE_BEGIN, EQEmu::invslot::TRIBUTE_END)) // (< 405) changed = true; - //Power Source Slot - if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF) - { - if (DoItemEnterZone(EQEmu::invslot::SLOT_POWER_SOURCE, EQEmu::invslot::SLOT_POWER_SOURCE)) - changed = true; - } - if(changed) { CalcBonuses(); @@ -3486,7 +3464,7 @@ bool Client::DoItemEnterZone(uint32 slot_x, uint32 slot_y) { uint16 oldexp = inst->GetExp(); parse->EventItem(EVENT_ITEM_ENTER_ZONE, this, inst, nullptr, "", 0); - if (i <= EQEmu::invslot::slotAmmo || i == EQEmu::invslot::SLOT_POWER_SOURCE) { + if (i <= EQEmu::invslot::EQUIPMENT_END) { parse->EventItem(EVENT_EQUIP_ITEM, this, inst, nullptr, "", i); } @@ -3496,7 +3474,7 @@ bool Client::DoItemEnterZone(uint32 slot_x, uint32 slot_y) { update_slot = true; } } else { - if (i <= EQEmu::invslot::slotAmmo || i == EQEmu::invslot::SLOT_POWER_SOURCE) { + if (i <= EQEmu::invslot::EQUIPMENT_END) { parse->EventItem(EVENT_EQUIP_ITEM, this, inst, nullptr, "", i); } diff --git a/zone/bot.cpp b/zone/bot.cpp index c30f08e08..23163d1de 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -38,6 +38,8 @@ Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, nullptr, glm this->_botOwnerCharacterID = 0; } + m_inv.SetInventoryVersion(EQEmu::versions::MobVersion::Bot); + _guildRank = 0; _guildId = 0; _lastTotalPlayTime = 0; @@ -110,6 +112,8 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to auto bot_owner = GetBotOwner(); + m_inv.SetInventoryVersion(EQEmu::versions::MobVersion::Bot); + _guildRank = 0; _guildId = 0; _lastTotalPlayTime = totalPlayTime; @@ -1850,6 +1854,13 @@ bool Bot::Process() { SendHPUpdate(); if(HasPet()) GetPet()->SendHPUpdate(); + + // hack fix until percentage changes can be implemented + auto g = GetGroup(); + if (g) { + g->SendManaPacketFrom(this); + g->SendEndurancePacketFrom(this); + } } if(GetAppearance() == eaDead && GetHP() > 0) @@ -2261,10 +2272,15 @@ void Bot::AI_Process() { } if (find_target) { - if (IsRooted()) + if (IsRooted()) { SetTarget(hate_list.GetClosestEntOnHateList(this)); - else - SetTarget(hate_list.GetEntWithMostHateOnList(this)); + } + else { + // This will keep bots on target for now..but, future updates will allow for rooting/stunning + SetTarget(hate_list.GetEscapingEntOnHateList(leash_owner, BOT_LEASH_DISTANCE)); + if (!GetTarget()) + SetTarget(hate_list.GetEntWithMostHateOnList(this)); + } } TEST_TARGET(); @@ -2471,6 +2487,8 @@ void Bot::AI_Process() { ChangeBotArcherWeapons(IsBotArcher()); } + // all of this needs review... + if (IsBotArcher() && atArcheryRange) atCombatRange = true; else if (caster_distance_max && tar_distance <= caster_distance_max) @@ -2567,6 +2585,10 @@ void Bot::AI_Process() { } } + if (!IsBotNonSpellFighter() && AI_EngagedCastCheck()) { + return; + } + // Up to this point, GetTarget() has been safe to dereference since the initial // TEST_TARGET() call. Due to the chance of the target dying and our pointer // being nullified, we need to test it before dereferencing to avoid crashes @@ -2576,7 +2598,7 @@ void Bot::AI_Process() { if (GetTarget()->GetHPRatio() <= 99.0f) BotRangedAttack(tar); } - else if (!IsBotArcher() && (!(IsBotCaster() && GetLevel() >= RuleI(Bots, CasterStopMeleeLevel)))) { + else if (!IsBotArcher() && (IsBotNonSpellFighter() || GetLevel() < GetStopMeleeLevel())) { // we can't fight if we don't have a target, are stun/mezzed or dead.. // Stop attacking if the target is enraged TEST_TARGET(); @@ -3643,15 +3665,13 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli ClientReturn(const ItemInstance* item, int16 from, const char* name = "") : returnItemInstance(item), fromBotSlot(from), toClientSlot(invslot::SLOT_INVALID), adjustStackSize(0), failedItemName(name) { } }; - static const int16 proxyPowerSource = 22; - - static const int16 bot_equip_order[(invslot::CORPSE_BEGIN + 1)] = { + static const int16 bot_equip_order[invslot::EQUIPMENT_COUNT] = { invslot::slotCharm, invslot::slotEar1, invslot::slotHead, invslot::slotFace, invslot::slotEar2, invslot::slotNeck, invslot::slotShoulders, invslot::slotArms, invslot::slotBack, invslot::slotWrist1, invslot::slotWrist2, invslot::slotRange, invslot::slotHands, invslot::slotPrimary, invslot::slotSecondary, invslot::slotFinger1, invslot::slotFinger2, invslot::slotChest, invslot::slotLegs, invslot::slotFeet, - invslot::slotWaist, invslot::slotAmmo, proxyPowerSource // invslot::SLOT_POWER_SOURCE + invslot::slotWaist, invslot::slotPowerSource, invslot::slotAmmo }; enum { stageStackable = 0, stageEmpty, stageReplaceable }; @@ -3787,9 +3807,7 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli //} if (stage_loop != stageReplaceable) { - if ((index == proxyPowerSource) && m_inv[invslot::SLOT_POWER_SOURCE]) - continue; - else if (m_inv[index]) + if (m_inv[index]) continue; } @@ -3838,18 +3856,10 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli } } - if (index == proxyPowerSource) { - trade_iterator.toBotSlot = invslot::SLOT_POWER_SOURCE; + trade_iterator.toBotSlot = index; - if (m_inv[invslot::SLOT_POWER_SOURCE]) - client_return.push_back(ClientReturn(m_inv[invslot::SLOT_POWER_SOURCE], invslot::SLOT_POWER_SOURCE)); - } - else { - trade_iterator.toBotSlot = index; - - if (m_inv[index]) - client_return.push_back(ClientReturn(m_inv[index], index)); - } + if (m_inv[index]) + client_return.push_back(ClientReturn(m_inv[index], index)); break; } @@ -3878,6 +3888,8 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli client->ResetTrade(); return; } + // non-failing checks above are causing this to trigger (i.e., !ItemClassCommon and !IsEquipable{race, class, min_level}) + // this process is hindered by not having bots use the inventory trade method (TODO: implement bot inventory use) if (client->CheckLoreConflict(return_instance->GetItem())) { client->Message(CC_Yellow, "You already have lore equipment matching the item '%s' - Trade Canceled!", return_instance->GetItem()->Name); client->ResetTrade(); @@ -3981,8 +3993,17 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli m_inv.PutItem(trade_iterator.toBotSlot, *trade_iterator.tradeItemInstance); this->BotAddEquipItem(trade_iterator.toBotSlot, (trade_iterator.tradeItemInstance ? trade_iterator.tradeItemInstance->GetID() : 0)); + trade_iterator.tradeItemInstance = nullptr; // actual deletion occurs in client delete below + client->DeleteItemInInventory(trade_iterator.fromClientSlot, 0, (trade_iterator.fromClientSlot == EQEmu::invslot::slotCursor)); - trade_iterator.tradeItemInstance = nullptr; + + // database currently has unattuned item saved in inventory..it will be attuned on next bot load + // this prevents unattuned item returns in the mean time (TODO: re-work process) + if (trade_iterator.toBotSlot >= invslot::EQUIPMENT_BEGIN && trade_iterator.toBotSlot <= invslot::EQUIPMENT_END) { + auto attune_item = m_inv.GetItem(trade_iterator.toBotSlot); + if (attune_item && attune_item->GetItem()->Attuneable) + attune_item->SetAttuned(true); + } } // trade messages @@ -4006,6 +4027,15 @@ bool Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, EQEmu::skills::Sk return false; Save(); + + Mob *my_owner = GetBotOwner(); + if (my_owner && my_owner->IsClient() && my_owner->CastToClient()->GetBotOptionDeathMarquee()) { + if (killerMob) + my_owner->CastToClient()->SendMarqueeMessage(CC_Yellow, 510, 0, 1000, 3000, StringFormat("%s has been slain by %s", GetCleanName(), killerMob->GetCleanName())); + else + my_owner->CastToClient()->SendMarqueeMessage(CC_Yellow, 510, 0, 1000, 3000, StringFormat("%s has been slain", GetCleanName())); + } + Mob *give_exp = hate_list.GetDamageTopOnHateList(this); Client *give_exp_client = nullptr; if(give_exp && give_exp->IsClient()) @@ -4634,6 +4664,7 @@ int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { int32 focus_max = 0; int32 focus_max_real = 0; //item focus + // are focus effects the same as bonus? (slotAmmo-excluded) for (int x = EQEmu::invslot::EQUIPMENT_BEGIN; x <= EQEmu::invslot::EQUIPMENT_END; x++) { TempItem = nullptr; EQEmu::ItemInstance* ins = GetBotItem(x); @@ -7589,10 +7620,7 @@ void Bot::ProcessBotInspectionRequest(Bot* inspectedBot, Client* client) { const EQEmu::ItemData* item = nullptr; const EQEmu::ItemInstance* inst = nullptr; - // Modded to display power source items (will only show up on SoF+ client inspect windows though.) - // I don't think bots are currently coded to use them..but, you'll have to use '#bot inventory list' - // to see them on a Titanium client when/if they are activated. - for (int16 L = EQEmu::invslot::EQUIPMENT_BEGIN; L <= EQEmu::invslot::slotWaist; L++) { + for (int16 L = EQEmu::invslot::EQUIPMENT_BEGIN; L <= EQEmu::invslot::EQUIPMENT_END; L++) { inst = inspectedBot->GetBotItem(L); if(inst) { @@ -7601,33 +7629,15 @@ void Bot::ProcessBotInspectionRequest(Bot* inspectedBot, Client* client) { strcpy(insr->itemnames[L], item->Name); insr->itemicons[L] = item->Icon; } - else + else { + insr->itemnames[L][0] = '\0'; insr->itemicons[L] = 0xFFFFFFFF; + } } - } - - inst = inspectedBot->GetBotItem(EQEmu::invslot::SLOT_POWER_SOURCE); - - if(inst) { - item = inst->GetItem(); - if(item) { - strcpy(insr->itemnames[SoF::invslot::slotPowerSource], item->Name); - insr->itemicons[SoF::invslot::slotPowerSource] = item->Icon; + else { + insr->itemnames[L][0] = '\0'; + insr->itemicons[L] = 0xFFFFFFFF; } - else - insr->itemicons[SoF::invslot::slotPowerSource] = 0xFFFFFFFF; - } - - inst = inspectedBot->GetBotItem(EQEmu::invslot::slotAmmo); - - if(inst) { - item = inst->GetItem(); - if(item) { - strcpy(insr->itemnames[SoF::invslot::slotAmmo], item->Name); - insr->itemicons[SoF::invslot::slotAmmo] = item->Icon; - } - else - insr->itemicons[SoF::invslot::slotAmmo] = 0xFFFFFFFF; } strcpy(insr->text, inspectedBot->GetInspectMessage().text); @@ -7640,8 +7650,8 @@ void Bot::CalcItemBonuses(StatBonuses* newbon) { const EQEmu::ItemData* itemtmp = nullptr; - for (int i = EQEmu::invslot::EQUIPMENT_BEGIN; i <= (EQEmu::invslot::EQUIPMENT_END + 1); ++i) { - const EQEmu::ItemInstance* item = GetBotItem((i == 22 ? 9999 : i)); + for (int i = EQEmu::invslot::BONUS_BEGIN; i <= EQEmu::invslot::BONUS_STAT_END; ++i) { + const EQEmu::ItemInstance* item = GetBotItem(i); if(item) { AddItemBonuses(item, newbon); } diff --git a/zone/bot.h b/zone/bot.h index 0c48b6483..d80ac5ad6 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -80,33 +80,33 @@ static const std::string bot_stance_name[BOT_STANCE_COUNT] = { static const char* GetBotStanceName(int stance_id) { return bot_stance_name[VALIDBOTSTANCE(stance_id)].c_str(); } -#define VALIDBOTEQUIPSLOT(x) ((x >= EQEmu::invslot::EQUIPMENT_BEGIN && x <= EQEmu::invslot::EQUIPMENT_END) ? (x) : ((x == EQEmu::invslot::SLOT_POWER_SOURCE) ? (22) : (23))) +#define VALIDBOTEQUIPSLOT(x) ((x >= EQEmu::invslot::EQUIPMENT_BEGIN && x <= EQEmu::invslot::EQUIPMENT_END) ? (x) : (EQEmu::invslot::EQUIPMENT_COUNT)) -static std::string bot_equip_slot_name[EQEmu::invslot::EQUIPMENT_COUNT + 2] = +static const std::string bot_equip_slot_name[EQEmu::invslot::EQUIPMENT_COUNT + 1] = { - "Charm", // MainCharm - "Left Ear", // MainEar1 - "Head", // MainHead - "Face", // MainFace - "Right Ear", // MainEar2 - "Neck", // MainNeck - "Shoulders", // MainShoulders - "Arms", // MainArms - "Back", // MainBack - "Left Wrist", // MainWrist1 - "Right Wrist", // MainWrist2 - "Range", // MainRange - "Hands", // MainHands - "Primary Hand", // MainPrimary - "Secondary Hand", // MainSecondary - "Left Finger", // MainFinger1 - "Right Finger", // MainFinger2 - "Chest", // MainChest - "Legs", // MainLegs - "Feet", // MainFeet - "Waist", // MainWaist - "Ammo", // MainAmmo - "Power Source", // 22 (MainPowerSource = 9999) + "Charm", // slotCharm + "Ear 1", // slotEar1 + "Head", // slotHead + "Face", // slotFace + "Ear 2", // slotEar2 + "Neck", // slotNeck + "Shoulders", // slotShoulders + "Arms", // slotArms + "Back", // slotBack + "Wrist 1", // slotWrist1 + "Wrist 2", // slotWrist2 + "Range", // slotRange + "Hands", // slotHands + "Primary", // slotPrimary + "Secondary", // slotSecondary + "Finger 1", // slotFinger1 + "Finger 2", // slotFinger2 + "Chest", // slotChest + "Legs", // slotLegs + "Feet", // slotFeet + "Waist", // slotWaist + "Power Source", // slotPowerSource + "Ammo", // slotAmmo "Unknown" }; @@ -534,6 +534,9 @@ public: bool IsBotCaster() { return IsCasterClass(GetClass()); } bool IsBotINTCaster() { return IsINTCasterClass(GetClass()); } bool IsBotWISCaster() { return IsWISCasterClass(GetClass()); } + bool IsBotSpellFighter() { return IsSpellFighterClass(GetClass()); } + bool IsBotFighter() { return IsFighterClass(GetClass()); } + bool IsBotNonSpellFighter() { return IsNonSpellFighterClass(GetClass()); } bool CanHeal(); int GetRawACNoShield(int &shield_ac); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 08b157ff0..fe5dcd69f 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1399,6 +1399,7 @@ int bot_command_init(void) bot_command_add("lull", "Orders a bot to cast a pacification spell", 0, bot_command_lull) || bot_command_add("mesmerize", "Orders a bot to cast a mesmerization spell", 0, bot_command_mesmerize) || bot_command_add("movementspeed", "Orders a bot to cast a movement speed enhancement spell", 0, bot_command_movement_speed) || + bot_command_add("owneroption", "Sets options available to bot owners", 0, bot_command_owner_option) || bot_command_add("pet", "Lists the available bot pet [subcommands]", 0, bot_command_pet) || bot_command_add("petremove", "Orders a bot to remove its pet", 0, bot_subcommand_pet_remove) || bot_command_add("petsettype", "Orders a Magician bot to use a specified pet type", 0, bot_subcommand_pet_set_type) || @@ -3437,6 +3438,25 @@ void bot_command_movement_speed(Client *c, const Seperator *sep) helper_no_available_bots(c, my_bot); } +void bot_command_owner_option(Client *c, const Seperator *sep) +{ + if (helper_is_help_or_usage(sep->arg[1])) { + c->Message(m_usage, "usage: %s [deathmarquee]", sep->arg[0]); + return; + } + + std::string owner_option = sep->arg[1]; + + if (!owner_option.compare("deathmarquee")) { + c->SetBotOptionDeathMarquee(!c->GetBotOptionDeathMarquee()); + c->Message(m_action, "Bot death marquee is now %s.", (c->GetBotOptionDeathMarquee() == true ? "enabled" : "disabled")); + botdb.SaveOwnerOptionDeathMarquee(c->CharacterID(), c->GetBotOptionDeathMarquee()); + } + else { + c->Message(m_fail, "Owner option '%s' is not recognized.", owner_option.c_str()); + } +} + void bot_command_pet(Client *c, const Seperator *sep) { /* VS2012 code - begin */ @@ -7194,13 +7214,13 @@ void bot_subcommand_inventory_list(Client *c, const Seperator *sep) linker.SetLinkType(EQEmu::saylink::SayLinkItemInst); uint32 inventory_count = 0; - for (int i = EQEmu::invslot::EQUIPMENT_BEGIN; i <= (EQEmu::invslot::EQUIPMENT_END + 1); ++i) { + for (int i = EQEmu::invslot::EQUIPMENT_BEGIN; i <= EQEmu::invslot::EQUIPMENT_END; ++i) { if ((i == EQEmu::invslot::slotSecondary) && is2Hweapon) continue; - inst = my_bot->CastToBot()->GetBotItem(i == 22 ? EQEmu::invslot::SLOT_POWER_SOURCE : i); + inst = my_bot->CastToBot()->GetBotItem(i); if (!inst || !inst->GetItem()) { - c->Message(m_message, "I need something for my %s (slot %i)", GetBotEquipSlotName(i), (i == 22 ? EQEmu::invslot::SLOT_POWER_SOURCE : i)); + c->Message(m_message, "I need something for my %s (slot %i)", GetBotEquipSlotName(i), i); continue; } @@ -7210,7 +7230,7 @@ void bot_subcommand_inventory_list(Client *c, const Seperator *sep) } linker.SetItemInst(inst); - c->Message(m_message, "Using %s in my %s (slot %i)", linker.GenerateLink().c_str(), GetBotEquipSlotName(i), (i == 22 ? EQEmu::invslot::SLOT_POWER_SOURCE : i)); + c->Message(m_message, "Using %s in my %s (slot %i)", linker.GenerateLink().c_str(), GetBotEquipSlotName(i), i); ++inventory_count; } @@ -7249,8 +7269,8 @@ void bot_subcommand_inventory_remove(Client *c, const Seperator *sep) } int slotId = atoi(sep->arg[1]); - if (!sep->IsNumber(1) || ((slotId > EQEmu::invslot::EQUIPMENT_END || slotId < EQEmu::invslot::EQUIPMENT_BEGIN) && slotId != EQEmu::invslot::SLOT_POWER_SOURCE)) { - c->Message(m_fail, "Valid slots are 0-21 or 9999"); + if (!sep->IsNumber(1) || (slotId > EQEmu::invslot::EQUIPMENT_END || slotId < EQEmu::invslot::EQUIPMENT_BEGIN)) { + c->Message(m_fail, "Valid slots are 0-22"); return; } @@ -7311,7 +7331,7 @@ void bot_subcommand_inventory_remove(Client *c, const Seperator *sep) case EQEmu::invslot::slotFinger2: case EQEmu::invslot::slotChest: case EQEmu::invslot::slotWaist: - case EQEmu::invslot::SLOT_POWER_SOURCE: + case EQEmu::invslot::slotPowerSource: case EQEmu::invslot::slotAmmo: c->Message(m_message, "My %s is %s unequipped", GetBotEquipSlotName(slotId), ((itm) ? ("now") : ("already"))); break; @@ -7356,14 +7376,14 @@ void bot_subcommand_inventory_window(Client *c, const Seperator *sep) //EQEmu::SayLinkEngine linker; //linker.SetLinkType(EQEmu::saylink::SayLinkItemInst); - for (int i = EQEmu::invslot::EQUIPMENT_BEGIN; i <= (EQEmu::invslot::EQUIPMENT_END + 1); ++i) { + for (int i = EQEmu::invslot::EQUIPMENT_BEGIN; i <= EQEmu::invslot::EQUIPMENT_END; ++i) { const EQEmu::ItemData* item = nullptr; - const EQEmu::ItemInstance* inst = my_bot->CastToBot()->GetBotItem(i == 22 ? EQEmu::invslot::SLOT_POWER_SOURCE : i); + const EQEmu::ItemInstance* inst = my_bot->CastToBot()->GetBotItem(i); if (inst) item = inst->GetItem(); window_text.append(""); - window_text.append(GetBotEquipSlotName(i == 22 ? EQEmu::invslot::SLOT_POWER_SOURCE : i)); + window_text.append(GetBotEquipSlotName(i)); window_text.append(": "); if (item) { //window_text.append(""); diff --git a/zone/bot_command.h b/zone/bot_command.h index f2c706cac..603852b38 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -575,6 +575,7 @@ void bot_command_levitation(Client *c, const Seperator *sep); void bot_command_lull(Client *c, const Seperator *sep); void bot_command_mesmerize(Client *c, const Seperator *sep); void bot_command_movement_speed(Client *c, const Seperator *sep); +void bot_command_owner_option(Client *c, const Seperator *sep); void bot_command_pet(Client *c, const Seperator *sep); void bot_command_pick_lock(Client *c, const Seperator *sep); void bot_command_pull(Client *c, const Seperator *sep); diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index a5e50fdec..17bc51487 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -25,6 +25,7 @@ #include "bot_database.h" #include "bot.h" +#include "client.h" BotDatabase botdb; @@ -1138,7 +1139,7 @@ bool BotDatabase::LoadItems(const uint32 bot_id, EQEmu::InventoryProfile& invent for (auto row = results.begin(); row != results.end(); ++row) { int16 slot_id = atoi(row[0]); - if ((slot_id < EQEmu::invslot::EQUIPMENT_BEGIN || slot_id > EQEmu::invslot::EQUIPMENT_END) && slot_id != EQEmu::invslot::SLOT_POWER_SOURCE) + if (slot_id < EQEmu::invslot::EQUIPMENT_BEGIN || slot_id > EQEmu::invslot::EQUIPMENT_END) continue; uint32 item_id = atoi(row[1]); @@ -1173,7 +1174,7 @@ bool BotDatabase::LoadItems(const uint32 bot_id, EQEmu::InventoryProfile& invent if (item_inst->GetItem()->Attuneable) { if (atoi(row[4])) item_inst->SetAttuned(true); - else if (((slot_id >= EQEmu::invslot::EQUIPMENT_BEGIN) && (slot_id <= EQEmu::invslot::EQUIPMENT_END) || slot_id == EQEmu::invslot::SLOT_POWER_SOURCE)) + else if (slot_id >= EQEmu::invslot::EQUIPMENT_BEGIN && slot_id <= EQEmu::invslot::EQUIPMENT_END) item_inst->SetAttuned(true); } @@ -1241,7 +1242,7 @@ bool BotDatabase::LoadItemBySlot(Bot* bot_inst) bool BotDatabase::LoadItemBySlot(const uint32 bot_id, const uint32 slot_id, uint32& item_id) { - if (!bot_id || (slot_id > EQEmu::invslot::EQUIPMENT_END && slot_id != EQEmu::invslot::SLOT_POWER_SOURCE)) + if (!bot_id || slot_id > EQEmu::invslot::EQUIPMENT_END) return false; query = StringFormat("SELECT `item_id` FROM `bot_inventories` WHERE `bot_id` = '%i' AND `slot_id` = '%i' LIMIT 1", bot_id, slot_id); @@ -1259,7 +1260,7 @@ bool BotDatabase::LoadItemBySlot(const uint32 bot_id, const uint32 slot_id, uint bool BotDatabase::SaveItemBySlot(Bot* bot_inst, const uint32 slot_id, const EQEmu::ItemInstance* item_inst) { - if (!bot_inst || !bot_inst->GetBotID() || (slot_id > EQEmu::invslot::EQUIPMENT_END && slot_id != EQEmu::invslot::SLOT_POWER_SOURCE)) + if (!bot_inst || !bot_inst->GetBotID() || slot_id > EQEmu::invslot::EQUIPMENT_END) return false; if (!DeleteItemBySlot(bot_inst->GetBotID(), slot_id)) @@ -1343,7 +1344,7 @@ bool BotDatabase::SaveItemBySlot(Bot* bot_inst, const uint32 slot_id, const EQEm bool BotDatabase::DeleteItemBySlot(const uint32 bot_id, const uint32 slot_id) { - if (!bot_id || (slot_id > EQEmu::invslot::EQUIPMENT_END && slot_id != EQEmu::invslot::SLOT_POWER_SOURCE)) + if (!bot_id || slot_id > EQEmu::invslot::EQUIPMENT_END) return false; query = StringFormat("DELETE FROM `bot_inventories` WHERE `bot_id` = '%u' AND `slot_id` = '%u'", bot_id, slot_id); @@ -1382,7 +1383,7 @@ bool BotDatabase::SaveEquipmentColor(const uint32 bot_id, const int16 slot_id, c return false; bool all_flag = (slot_id == -2); - if ((slot_id < EQEmu::invslot::EQUIPMENT_BEGIN || slot_id > EQEmu::invslot::EQUIPMENT_END) && slot_id != EQEmu::invslot::SLOT_POWER_SOURCE && !all_flag) + if ((slot_id < EQEmu::invslot::EQUIPMENT_BEGIN || slot_id > EQEmu::invslot::EQUIPMENT_END) && !all_flag) return false; std::string where_clause; @@ -1659,7 +1660,7 @@ bool BotDatabase::LoadPetItems(const uint32 bot_id, uint32* pet_items) return true; int item_index = EQEmu::invslot::EQUIPMENT_BEGIN; - for (auto row = results.begin(); row != results.end() && item_index <= EQEmu::invslot::EQUIPMENT_END; ++row) { + for (auto row = results.begin(); row != results.end() && (item_index >= EQEmu::invslot::EQUIPMENT_BEGIN && item_index <= EQEmu::invslot::EQUIPMENT_END); ++row) { pet_items[item_index] = atoi(row[0]); ++item_index; } @@ -1683,7 +1684,7 @@ bool BotDatabase::SavePetItems(const uint32 bot_id, const uint32* pet_items, boo if (!saved_pet_index) return true; - for (int item_index = EQEmu::invslot::SLOT_BEGIN; item_index <= EQEmu::invslot::EQUIPMENT_END; ++item_index) { + for (int item_index = EQEmu::invslot::EQUIPMENT_BEGIN; item_index <= EQEmu::invslot::EQUIPMENT_END; ++item_index) { if (!pet_items[item_index]) continue; @@ -2185,6 +2186,51 @@ bool BotDatabase::SaveStopMeleeLevel(const uint32 owner_id, const uint32 bot_id, return true; } +bool BotDatabase::LoadOwnerOptions(Client *owner) +{ + if (!owner || !owner->CharacterID()) + return false; + + query = StringFormat( + "SELECT `death_marquee` FROM `bot_owner_options`" + " WHERE `owner_id` = '%u'", + owner->CharacterID() + ); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) { + query = StringFormat("REPLACE INTO `bot_owner_options` (`owner_id`) VALUES ('%u')", owner->CharacterID()); + results = QueryDatabase(query); + + return false; + } + + auto row = results.begin(); + owner->SetBotOptionDeathMarquee((atoi(row[0]) != 0)); + + return true; +} + +bool BotDatabase::SaveOwnerOptionDeathMarquee(const uint32 owner_id, const bool flag) +{ + if (!owner_id) + return false; + + query = StringFormat( + "UPDATE `bot_owner_options`" + " SET `death_marquee` = '%u'" + " WHERE `owner_id` = '%u'", + (flag == true ? 1 : 0), + owner_id + ); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + return true; +} + /* Bot bot-group functions */ bool BotDatabase::QueryBotGroupExistence(const std::string& group_name, bool& extant_flag) diff --git a/zone/bot_database.h b/zone/bot_database.h index 2361c0cf1..2840c415c 100644 --- a/zone/bot_database.h +++ b/zone/bot_database.h @@ -32,6 +32,7 @@ class Bot; struct BotsAvailableList; +class Client; namespace EQEmu { @@ -145,6 +146,8 @@ public: bool SaveStopMeleeLevel(const uint32 owner_id, const uint32 bot_id, const uint8 sml_value); + bool LoadOwnerOptions(Client *owner); + bool SaveOwnerOptionDeathMarquee(const uint32 owner_id, const bool flag); /* Bot bot-group functions */ bool QueryBotGroupExistence(const std::string& botgroup_name, bool& extant_flag); diff --git a/zone/client.cpp b/zone/client.cpp index a22052415..462c9c679 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -326,6 +326,10 @@ Client::Client(EQStreamInterface* ieqs) temp_pvp = false; is_client_moving = false; +#ifdef BOTS + bot_owner_options = DefaultBotOwnerOptions; +#endif + AI_Init(); } @@ -672,7 +676,7 @@ bool Client::Save(uint8 iCommitNow) { // perform snapshot before SaveCharacterData() so that m_epp will contain the updated time if (RuleB(Character, ActiveInvSnapshots) && time(nullptr) >= GetNextInvSnapshotTime()) { - if (database.SaveCharacterInventorySnapshot(CharacterID())) { + if (database.SaveCharacterInvSnapshot(CharacterID())) { SetNextInvSnapshot(RuleI(Character, InvSnapshotMinIntervalM)); } else { @@ -1898,7 +1902,7 @@ void Client::CheckManaEndUpdate() { else if (group) { group->SendEndurancePacketFrom(this); } - + auto endurance_packet = new EQApplicationPacket(OP_EnduranceUpdate, sizeof(EnduranceUpdate_Struct)); EnduranceUpdate_Struct* endurance_update = (EnduranceUpdate_Struct*)endurance_packet->pBuffer; endurance_update->cur_end = GetEndurance(); @@ -5674,56 +5678,34 @@ void Client::ProcessInspectRequest(Client* requestee, Client* requester) { const EQEmu::ItemData* item = nullptr; const EQEmu::ItemInstance* inst = nullptr; int ornamentationAugtype = RuleI(Character, OrnamentationAugmentType); - for(int16 L = 0; L <= 20; L++) { + for(int16 L = EQEmu::invslot::EQUIPMENT_BEGIN; L <= EQEmu::invslot::EQUIPMENT_END; L++) { inst = requestee->GetInv().GetItem(L); if(inst) { item = inst->GetItem(); if(item) { strcpy(insr->itemnames[L], item->Name); - if (inst && inst->GetOrnamentationAug(ornamentationAugtype)) - { - const EQEmu::ItemData *aug_item = inst->GetOrnamentationAug(ornamentationAugtype)->GetItem(); + + const EQEmu::ItemData *aug_item = nullptr; + if (inst->GetOrnamentationAug(ornamentationAugtype)) + inst->GetOrnamentationAug(ornamentationAugtype)->GetItem(); + + if (aug_item) insr->itemicons[L] = aug_item->Icon; - } - else if (inst && inst->GetOrnamentationIcon()) - { + else if (inst->GetOrnamentationIcon()) insr->itemicons[L] = inst->GetOrnamentationIcon(); - } else - { insr->itemicons[L] = item->Icon; - } } - else + else { + insr->itemnames[L][0] = '\0'; insr->itemicons[L] = 0xFFFFFFFF; + } } - } - - inst = requestee->GetInv().GetItem(EQEmu::invslot::SLOT_POWER_SOURCE); - - if(inst) { - item = inst->GetItem(); - if(item) { - // we shouldn't do this..but, that's the way it's coded atm... - // (this type of action should be handled exclusively in the client translator) - strcpy(insr->itemnames[SoF::invslot::slotPowerSource], item->Name); - insr->itemicons[SoF::invslot::slotPowerSource] = item->Icon; + else { + insr->itemnames[L][0] = '\0'; + insr->itemicons[L] = 0xFFFFFFFF; } - else - insr->itemicons[SoF::invslot::slotPowerSource] = 0xFFFFFFFF; - } - - inst = requestee->GetInv().GetItem(EQEmu::invslot::slotAmmo); - - if(inst) { - item = inst->GetItem(); - if(item) { - strcpy(insr->itemnames[SoF::invslot::slotAmmo], item->Name); - insr->itemicons[SoF::invslot::slotAmmo] = item->Icon; - } - else - insr->itemicons[SoF::invslot::slotAmmo] = 0xFFFFFFFF; } strcpy(insr->text, requestee->GetInspectMessage().text); @@ -7773,9 +7755,9 @@ void Client::SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, ui { //The ole switcheroo if (npc_value[i] > 0) - npc_value[i] = -abs(npc_value[i]); + npc_value[i] = -std::abs(npc_value[i]); else if (npc_value[i] < 0) - npc_value[i] = abs(npc_value[i]); + npc_value[i] = std::abs(npc_value[i]); } // Adjust the amount you can go up or down so the resulting range @@ -8076,13 +8058,8 @@ void Client::TickItemCheck() if(zone->tick_items.empty()) { return; } - //Scan equip slots for items - for (i = EQEmu::invslot::EQUIPMENT_BEGIN; i <= EQEmu::invslot::EQUIPMENT_END; i++) - { - TryItemTick(i); - } - //Scan main inventory + cursor - for (i = EQEmu::invslot::GENERAL_BEGIN; i <= EQEmu::invslot::slotCursor; i++) + //Scan equip, general, cursor slots for items + for (i = EQEmu::invslot::POSSESSIONS_BEGIN; i <= EQEmu::invslot::POSSESSIONS_END; i++) { TryItemTick(i); } @@ -8134,16 +8111,10 @@ void Client::TryItemTick(int slot) void Client::ItemTimerCheck() { int i; - for (i = EQEmu::invslot::EQUIPMENT_BEGIN; i <= EQEmu::invslot::EQUIPMENT_END; i++) + for (i = EQEmu::invslot::POSSESSIONS_BEGIN; i <= EQEmu::invslot::POSSESSIONS_END; i++) { TryItemTimer(i); } - - for (i = EQEmu::invslot::GENERAL_BEGIN; i <= EQEmu::invslot::slotCursor; i++) - { - TryItemTimer(i); - } - for (i = EQEmu::invbag::GENERAL_BAGS_BEGIN; i <= EQEmu::invbag::CURSOR_BAG_END; i++) { TryItemTimer(i); diff --git a/zone/client.h b/zone/client.h index 6279fba56..f9359aa02 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1614,6 +1614,25 @@ private: void InterrogateInventory_(bool errorcheck, Client* requester, int16 head, int16 index, const EQEmu::ItemInstance* inst, const EQEmu::ItemInstance* parent, bool log, bool silent, bool &error, int depth); bool InterrogateInventory_error(int16 head, int16 index, const EQEmu::ItemInstance* inst, const EQEmu::ItemInstance* parent, int depth); + +#ifdef BOTS + struct BotOwnerOptions { + bool death_marquee; + }; + + BotOwnerOptions bot_owner_options; + + const BotOwnerOptions DefaultBotOwnerOptions = { + false // death_marquee + }; + +public: + void SetBotOptionDeathMarquee(bool flag) { bot_owner_options.death_marquee = flag; } + + bool GetBotOptionDeathMarquee() const { return bot_owner_options.death_marquee; } + +private: +#endif }; #endif diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index cb5d7ede6..8d6ec0328 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1496,6 +1496,8 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) } #ifdef BOTS + botdb.LoadOwnerOptions(this); + // TODO: mod below function for loading spawned botgroups Bot::LoadAndSpawnAllZonedBots(this); #endif @@ -2902,8 +2904,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) bool deleteItems = false; if (ClientVersion() >= EQEmu::versions::ClientVersion::RoF) { - if ((in_augment->container_slot < 0 || in_augment->container_slot >= EQEmu::invslot::slotCursor) && - in_augment->container_slot != EQEmu::invslot::SLOT_POWER_SOURCE && + if ((in_augment->container_slot < EQEmu::invslot::EQUIPMENT_BEGIN || in_augment->container_slot > EQEmu::invslot::GENERAL_END) && (in_augment->container_slot < EQEmu::invbag::GENERAL_BAGS_BEGIN || in_augment->container_slot > EQEmu::invbag::GENERAL_BAGS_END)) { Message(13, "The server does not allow augmentation actions from this slot."); @@ -8036,7 +8037,7 @@ void Client::Handle_OP_InspectAnswer(const EQApplicationPacket *app) const EQEmu::ItemData* item = nullptr; int ornamentationAugtype = RuleI(Character, OrnamentationAugmentType); - for (int16 L = EQEmu::invslot::EQUIPMENT_BEGIN; L <= EQEmu::invslot::slotWaist; L++) { + for (int16 L = EQEmu::invslot::EQUIPMENT_BEGIN; L <= EQEmu::invslot::EQUIPMENT_END; L++) { const EQEmu::ItemInstance* inst = GetInv().GetItem(L); item = inst ? inst->GetItem() : nullptr; @@ -8056,16 +8057,6 @@ void Client::Handle_OP_InspectAnswer(const EQApplicationPacket *app) else { insr->itemicons[L] = 0xFFFFFFFF; } } - const EQEmu::ItemInstance* inst = GetInv().GetItem(EQEmu::invslot::slotAmmo); - item = inst ? inst->GetItem() : nullptr; - - if (item) { - // another one..I did these, didn't I!!? - strcpy(insr->itemnames[SoF::invslot::slotAmmo], item->Name); - insr->itemicons[SoF::invslot::slotAmmo] = item->Icon; - } - else { insr->itemicons[SoF::invslot::slotAmmo] = 0xFFFFFFFF; } - InspectMessage_Struct* newmessage = (InspectMessage_Struct*)insr->text; InspectMessage_Struct& playermessage = this->GetInspectMessage(); memcpy(&playermessage, newmessage, sizeof(InspectMessage_Struct)); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index cea1dd49f..87c6d8a49 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -238,6 +238,19 @@ bool Client::Process() { } } + if (RuleB(Character, ActiveInvSnapshots) && time(nullptr) >= GetNextInvSnapshotTime()) { + if (database.SaveCharacterInvSnapshot(CharacterID())) { + SetNextInvSnapshot(RuleI(Character, InvSnapshotMinIntervalM)); + Log(Logs::Moderate, Logs::Inventory, "Successful inventory snapshot taken of %s - setting next interval for %i minute%s.", + GetName(), RuleI(Character, InvSnapshotMinIntervalM), (RuleI(Character, InvSnapshotMinIntervalM) == 1 ? "" : "s")); + } + else { + SetNextInvSnapshot(RuleI(Character, InvSnapshotMinRetryM)); + Log(Logs::Moderate, Logs::Inventory, "Failed to take inventory snapshot of %s - retrying in %i minute%s.", + GetName(), RuleI(Character, InvSnapshotMinRetryM), (RuleI(Character, InvSnapshotMinRetryM) == 1 ? "" : "s")); + } + } + bool may_use_attacks = false; /* Things which prevent us from attacking: @@ -745,19 +758,6 @@ void Client::BulkSendInventoryItems() last_pos = ob.tellp(); } - // PowerSource item - if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF) { - const EQEmu::ItemInstance* inst = m_inv[EQEmu::invslot::SLOT_POWER_SOURCE]; - if (inst) { - inst->Serialize(ob, EQEmu::invslot::SLOT_POWER_SOURCE); - - if (ob.tellp() == last_pos) - Log(Logs::General, Logs::Inventory, "Serialization failed on item slot %d during BulkSendInventoryItems. Item skipped.", EQEmu::invslot::SLOT_POWER_SOURCE); - - last_pos = ob.tellp(); - } - } - // Bank items for (int16 slot_id = EQEmu::invslot::BANK_BEGIN; slot_id <= EQEmu::invslot::BANK_END; slot_id++) { const EQEmu::ItemInstance* inst = m_inv[slot_id]; diff --git a/zone/command.cpp b/zone/command.cpp index 5ea4f5c85..b0bdc35be 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -173,7 +173,6 @@ int command_init(void) command_add("castspell", "[spellid] - Cast a spell", 50, command_castspell) || command_add("chat", "[channel num] [message] - Send a channel message to all zones", 200, command_chat) || command_add("checklos", "- Check for line of sight to your target", 50, command_checklos) || - command_add("clearinvsnapshots", "[use rule] - Clear inventory snapshot history (true - elapsed entries, false - all entries)", 200, command_clearinvsnapshots) || command_add("corpse", "- Manipulate corpses, use with no arguments for help", 50, command_corpse) || command_add("corpsefix", "Attempts to bring corpses from underneath the ground within close proximity of the player", 0, command_corpsefix) || command_add("crashtest", "- Crash the zoneserver", 255, command_crashtest) || @@ -240,7 +239,7 @@ int command_init(void) command_add("instance", "- Modify Instances", 200, command_instance) || command_add("interrogateinv", "- use [help] argument for available options", 0, command_interrogateinv) || command_add("interrupt", "[message id] [color] - Interrupt your casting. Arguments are optional.", 50, command_interrupt) || - command_add("invsnapshot", "- Takes an inventory snapshot of your current target", 80, command_invsnapshot) || + command_add("invsnapshot", "- Manipulates inventory snapshots for your current target", 80, command_invsnapshot) || command_add("invul", "[on/off] - Turn player target's or your invulnerable flag on or off", 80, command_invul) || command_add("ipban", "[IP address] - Ban IP by character name", 200, command_ipban) || command_add("iplookup", "[charname] - Look up IP address of charname", 200, command_iplookup) || @@ -2809,52 +2808,6 @@ void command_peekinv(Client *c, const Seperator *sep) } } - if ((scopeBit & peekEquip) && (targetClient->ClientVersion() >= EQEmu::versions::ClientVersion::SoF)) { - inst_main = targetClient->GetInv().GetItem(EQEmu::invslot::SLOT_POWER_SOURCE); - if (inst_main) { - itemsFound = true; - item_data = inst_main->GetItem(); - } - else { - item_data = nullptr; - } - - linker.SetItemInst(inst_main); - - c->Message( - (item_data == nullptr), - "%sSlot: %i, Item: %i (%s), Charges: %i", - scope_prefix[scopeIndex], - EQEmu::invslot::SLOT_POWER_SOURCE, - ((item_data == nullptr) ? 0 : item_data->ID), - linker.GenerateLink().c_str(), - ((inst_main == nullptr) ? 0 : inst_main->GetCharges()) - ); - - if (inst_main && inst_main->IsClassCommon()) { - for (uint8 indexAug = EQEmu::invaug::SOCKET_BEGIN; indexAug <= EQEmu::invaug::SOCKET_END; ++indexAug) { - inst_aug = inst_main->GetItem(indexAug); - if (!inst_aug) // extant only - continue; - - item_data = inst_aug->GetItem(); - linker.SetItemInst(inst_aug); - - c->Message( - (item_data == nullptr), - ".%sAugSlot: %i (Slot #%i, Aug idx #%i), Item: %i (%s), Charges: %i", - scope_prefix[scopeIndex], - INVALID_INDEX, - EQEmu::invslot::SLOT_POWER_SOURCE, - indexAug, - ((item_data == nullptr) ? 0 : item_data->ID), - linker.GenerateLink().c_str(), - ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) - ); - } - } - } - if (scopeBit & peekLimbo) { int limboIndex = 0; for (auto it = targetClient->GetInv().cursor_cbegin(); (it != targetClient->GetInv().cursor_cend()); ++it, ++limboIndex) { @@ -3031,31 +2984,344 @@ void command_interrogateinv(Client *c, const Seperator *sep) void command_invsnapshot(Client *c, const Seperator *sep) { - auto t = c->GetTarget(); - if (!t || !t->IsClient()) { - c->Message(0, "Target must be a client"); + if (!c) + return; + + if (sep->argnum == 0 || strcmp(sep->arg[1], "help") == 0) { + std::string window_title = "Inventory Snapshot Argument Help Menu"; + + std::string window_text = + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ""; + + if (c->Admin() >= commandInvSnapshot) + window_text.append( + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ); + + window_text.append( + "
Usage:#invsnapshot arguments
(required optional)
helpthis menu
capturetakes snapshot of character inventory
gcountreturns global snapshot count
gclear
now
delete all snapshots - rule
delete all snapshots - now
countreturns character snapshot count
clear
now
delete character snapshots - rule
delete character snapshots - now
list
count
lists entry ids for current character
limits to count
parsetstmpdisplays slots and items in snapshot
comparetstmpcompares inventory against snapshot
restoretstmprestores slots and items in snapshot
" + ); + + c->SendPopupToClient(window_title.c_str(), window_text.c_str()); + return; } - if (database.SaveCharacterInventorySnapshot(((Client*)t)->CharacterID())) { - c->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinIntervalM)); - c->Message(0, "Successful inventory snapshot taken of %s", t->GetName()); - } - else { - c->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinRetryM)); - c->Message(0, "Failed to take inventory snapshot of %s", t->GetName()); - } -} + if (c->Admin() >= commandInvSnapshot) { // global arguments -void command_clearinvsnapshots(Client *c, const Seperator *sep) -{ - if (strcmp(sep->arg[1], "false") == 0) { - database.ClearInvSnapshots(false); - c->Message(0, "Inventory snapshots cleared using current time"); + if (strcmp(sep->arg[1], "gcount") == 0) { + auto is_count = database.CountInvSnapshots(); + c->Message(0, "There %s %i inventory snapshot%s.", (is_count == 1 ? "is" : "are"), is_count, (is_count == 1 ? "" : "s")); + + return; + } + + if (strcmp(sep->arg[1], "gclear") == 0) { + if (strcmp(sep->arg[2], "now") == 0) { + database.ClearInvSnapshots(true); + c->Message(0, "Inventory snapshots cleared using current time."); + } + else { + database.ClearInvSnapshots(); + c->Message(0, "Inventory snapshots cleared using RuleI(Character, InvSnapshotHistoryD) (%i day%s).", + RuleI(Character, InvSnapshotHistoryD), (RuleI(Character, InvSnapshotHistoryD) == 1 ? "" : "s")); + } + + return; + } } - else { - database.ClearInvSnapshots(); - c->Message(0, "Inventory snapshots cleared using RuleI(Character, InvSnapshotHistoryD) (%i days)", RuleI(Character, InvSnapshotHistoryD)); + + if (!c->GetTarget() || !c->GetTarget()->IsClient()) { + c->Message(0, "Target must be a client."); + return; + } + + auto tc = (Client*)c->GetTarget(); + + if (strcmp(sep->arg[1], "capture") == 0) { + if (database.SaveCharacterInvSnapshot(tc->CharacterID())) { + tc->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinIntervalM)); + c->Message(0, "Successful inventory snapshot taken of %s - setting next interval for %i minute%s.", + tc->GetName(), RuleI(Character, InvSnapshotMinIntervalM), (RuleI(Character, InvSnapshotMinIntervalM) == 1 ? "" : "s")); + } + else { + tc->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinRetryM)); + c->Message(0, "Failed to take inventory snapshot of %s - retrying in %i minute%s.", + tc->GetName(), RuleI(Character, InvSnapshotMinRetryM), (RuleI(Character, InvSnapshotMinRetryM) == 1 ? "" : "s")); + } + + return; + } + + if (c->Admin() >= commandInvSnapshot) { + if (strcmp(sep->arg[1], "count") == 0) { + auto is_count = database.CountCharacterInvSnapshots(tc->CharacterID()); + c->Message(0, "%s (id: %u) has %i inventory snapshot%s.", tc->GetName(), tc->CharacterID(), is_count, (is_count == 1 ? "" : "s")); + + return; + } + + if (strcmp(sep->arg[1], "clear") == 0) { + if (strcmp(sep->arg[2], "now") == 0) { + database.ClearCharacterInvSnapshots(tc->CharacterID(), true); + c->Message(0, "%s\'s (id: %u) inventory snapshots cleared using current time.", tc->GetName(), tc->CharacterID()); + } + else { + database.ClearCharacterInvSnapshots(tc->CharacterID()); + c->Message(0, "%s\'s (id: %u) inventory snapshots cleared using RuleI(Character, InvSnapshotHistoryD) (%i day%s).", + tc->GetName(), tc->CharacterID(), RuleI(Character, InvSnapshotHistoryD), (RuleI(Character, InvSnapshotHistoryD) == 1 ? "" : "s")); + } + + return; + } + + if (strcmp(sep->arg[1], "list") == 0) { + std::list> is_list; + database.ListCharacterInvSnapshots(tc->CharacterID(), is_list); + + if (is_list.empty()) { + c->Message(0, "No inventory snapshots for %s (id: %u)", tc->GetName(), tc->CharacterID()); + return; + } + + auto list_count = 0; + if (sep->IsNumber(2)) + list_count = atoi(sep->arg[2]); + if (list_count < 1 || list_count > is_list.size()) + list_count = is_list.size(); + + std::string window_title = StringFormat("Snapshots for %s", tc->GetName()); + + std::string window_text = + "" + "" + "" + "" + ""; + + for (auto iter : is_list) { + if (!list_count) + break; + + window_text.append(StringFormat( + "" + "" + "" + "", + iter.first, + iter.second + )); + + --list_count; + } + + window_text.append( + "
TimestampEntry Count
%u%i
" + ); + + c->SendPopupToClient(window_title.c_str(), window_text.c_str()); + + return; + } + + if (strcmp(sep->arg[1], "parse") == 0) { + if (!sep->IsNumber(2)) { + c->Message(0, "A timestamp is required to use this option."); + return; + } + + uint32 timestamp = atoul(sep->arg[2]); + + if (!database.ValidateCharacterInvSnapshotTimestamp(tc->CharacterID(), timestamp)) { + c->Message(0, "No inventory snapshots for %s (id: %u) exist at %u.", tc->GetName(), tc->CharacterID(), timestamp); + return; + } + + std::list> parse_list; + database.ParseCharacterInvSnapshot(tc->CharacterID(), timestamp, parse_list); + + std::string window_title = StringFormat("Snapshot Parse for %s @ %u", tc->GetName(), timestamp); + + std::string window_text = "Slot: ItemID - Description
"; + + for (auto iter : parse_list) { + auto item_data = database.GetItem(iter.second); + std::string window_line = StringFormat("%i: %u - %s
", iter.first, iter.second, (item_data ? item_data->Name : "[error]")); + + if (window_text.length() + window_line.length() < 4095) { + window_text.append(window_line); + } + else { + c->Message(0, "Too many snapshot entries to list..."); + break; + } + } + + c->SendPopupToClient(window_title.c_str(), window_text.c_str()); + + return; + } + + if (strcmp(sep->arg[1], "compare") == 0) { + if (!sep->IsNumber(2)) { + c->Message(0, "A timestamp is required to use this option."); + return; + } + + uint32 timestamp = atoul(sep->arg[2]); + + if (!database.ValidateCharacterInvSnapshotTimestamp(tc->CharacterID(), timestamp)) { + c->Message(0, "No inventory snapshots for %s (id: %u) exist at %u.", tc->GetName(), tc->CharacterID(), timestamp); + return; + } + + std::list> inv_compare_list; + database.DivergeCharacterInventoryFromInvSnapshot(tc->CharacterID(), timestamp, inv_compare_list); + + std::list> iss_compare_list; + database.DivergeCharacterInvSnapshotFromInventory(tc->CharacterID(), timestamp, iss_compare_list); + + std::string window_title = StringFormat("Snapshot Comparison for %s @ %u", tc->GetName(), timestamp); + + std::string window_text = "Slot: (action) Snapshot -> Inventory
"; + + auto inv_iter = inv_compare_list.begin(); + auto iss_iter = iss_compare_list.begin(); + + while (true) { + std::string window_line; + + if (inv_iter == inv_compare_list.end() && iss_iter == iss_compare_list.end()) { + break; + } + else if (inv_iter != inv_compare_list.end() && iss_iter == iss_compare_list.end()) { + window_line = StringFormat("%i: (delete) [empty] -> %u
", inv_iter->first, inv_iter->second); + ++inv_iter; + } + else if (inv_iter == inv_compare_list.end() && iss_iter != iss_compare_list.end()) { + window_line = StringFormat("%i: (insert) %u -> [empty]
", iss_iter->first, iss_iter->second); + ++iss_iter; + } + else { + if (inv_iter->first < iss_iter->first) { + window_line = StringFormat("%i: (delete) [empty] -> %u
", inv_iter->first, inv_iter->second); + ++inv_iter; + } + else if (inv_iter->first > iss_iter->first) { + window_line = StringFormat("%i: (insert) %u -> [empty]
", iss_iter->first, iss_iter->second); + ++iss_iter; + } + else { + window_line = StringFormat("%i: (replace) %u -> %u
", iss_iter->first, iss_iter->second, inv_iter->second); + ++inv_iter; + ++iss_iter; + } + } + + if (window_text.length() + window_line.length() < 4095) { + window_text.append(window_line); + } + else { + c->Message(0, "Too many comparison entries to list..."); + break; + } + } + + c->SendPopupToClient(window_title.c_str(), window_text.c_str()); + + return; + } + + if (strcmp(sep->arg[1], "restore") == 0) { + if (!sep->IsNumber(2)) { + c->Message(0, "A timestamp is required to use this option."); + return; + } + + uint32 timestamp = atoul(sep->arg[2]); + + if (!database.ValidateCharacterInvSnapshotTimestamp(tc->CharacterID(), timestamp)) { + c->Message(0, "No inventory snapshots for %s (id: %u) exist at %u.", tc->GetName(), tc->CharacterID(), timestamp); + return; + } + + if (database.SaveCharacterInvSnapshot(tc->CharacterID())) { + tc->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinIntervalM)); + } + else { + c->Message(13, "Failed to take pre-restore inventory snapshot of %s (id: %u).", + tc->GetName(), tc->CharacterID()); + return; + } + + if (database.RestoreCharacterInvSnapshot(tc->CharacterID(), timestamp)) { + // cannot delete all valid item slots from client..so, we worldkick + tc->WorldKick(); // self restores update before the 'kick' is processed + + c->Message(0, "Successfully applied snapshot %u to %s's (id: %u) inventory.", + timestamp, tc->GetName(), tc->CharacterID()); + } + else { + c->Message(13, "Failed to apply snapshot %u to %s's (id: %u) inventory.", + timestamp, tc->GetName(), tc->CharacterID()); + } + + return; + } } } @@ -3365,7 +3631,7 @@ void command_listpetition(Client *c, const Seperator *sep) void command_equipitem(Client *c, const Seperator *sep) { uint32 slot_id = atoi(sep->arg[1]); - if (sep->IsNumber(1) && ((slot_id >= EQEmu::invslot::EQUIPMENT_BEGIN) && (slot_id <= EQEmu::invslot::EQUIPMENT_END) || (slot_id == EQEmu::invslot::SLOT_POWER_SOURCE))) { + if (sep->IsNumber(1) && (slot_id >= EQEmu::invslot::EQUIPMENT_BEGIN && slot_id <= EQEmu::invslot::EQUIPMENT_END)) { const EQEmu::ItemInstance* from_inst = c->GetInv().GetItem(EQEmu::invslot::slotCursor); const EQEmu::ItemInstance* to_inst = c->GetInv().GetItem(slot_id); // added (desync issue when forcing stack to stack) bool partialmove = false; diff --git a/zone/common.h b/zone/common.h index 030ad716a..6d78dc46f 100644 --- a/zone/common.h +++ b/zone/common.h @@ -261,6 +261,17 @@ enum class LootResponse : uint8 { LootAll = 6 // SoD+ }; +enum class LootRequestType : uint8 { + Forbidden = 0, + GMPeek, + GMAllowed, + Self, + AllowedPVE, + AllowedPVPAll, + AllowedPVPSingle, // can make this 'AllowedPVPVariable' and allow values between 1 and EQEmu::invtype::POSSESSIONS_SIZE + AllowedPVPDefined, +}; + //this is our internal representation of the BUFF struct, can put whatever we want in it struct Buffs_Struct { uint16 spellid; diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 586789f0f..c31a9a5d2 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -202,6 +202,8 @@ Corpse::Corpse(NPC* in_npc, ItemList* in_itemlist, uint32 in_npctypeid, const NP UpdateEquipmentLight(); UpdateActiveLight(); + + loot_request_type = LootRequestType::Forbidden; } Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( @@ -322,15 +324,11 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( // to go into the regular slots on the player, out of bags std::list removed_list; + // ideally, we would start at invslot::slotGeneral1 and progress to invslot::slotCursor.. + // ..then regress and process invslot::EQUIPMENT_BEGIN through invslot::EQUIPMENT_END... + // without additional work to database loading of player corpses, this order is not + // currently preserved and a re-work of this processing loop is not warranted. for (i = EQEmu::invslot::POSSESSIONS_BEGIN; i <= EQEmu::invslot::POSSESSIONS_END; ++i) { - if (i == EQEmu::invslot::slotAmmo && client->ClientVersion() >= EQEmu::versions::ClientVersion::SoF) { - item = client->GetInv().GetItem(EQEmu::invslot::SLOT_POWER_SOURCE); - if (item != nullptr) { - if (!client->IsBecomeNPC() || (client->IsBecomeNPC() && !item->GetItem()->NoRent)) - MoveItemToCorpse(client, item, EQEmu::invslot::SLOT_POWER_SOURCE, removed_list); - } - } - item = client->GetInv().GetItem(i); if (item == nullptr) { continue; } @@ -340,25 +338,25 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( database.TransactionBegin(); - // I have an untested process that avoids this snarl up when all possessions inventory is removed..but this isn't broke + // this should not be modified to include the entire range of invtype::TYPE_POSSESSIONS slots by default.. + // ..due to the possibility of 'hidden' items from client version bias..or, possibly, soul-bound items (WoW?) if (!removed_list.empty()) { - std::stringstream ss(""); - ss << "DELETE FROM inventory WHERE charid=" << client->CharacterID(); - ss << " AND ("; std::list::const_iterator iter = removed_list.begin(); - bool first = true; - while (iter != removed_list.end()) { - if (first) { - first = false; - } - else { - ss << " OR "; - } - ss << "slotid=" << (*iter); + + if (iter != removed_list.end()) { + std::stringstream ss(""); + ss << "DELETE FROM `inventory` WHERE `charid` = " << client->CharacterID(); + ss << " AND `slotid` IN (" << (*iter); ++iter; + + while (iter != removed_list.end()) { + ss << ", " << (*iter); + ++iter; + } + ss << ")"; + + database.QueryDatabase(ss.str().c_str()); } - ss << ")"; - database.QueryDatabase(ss.str().c_str()); } auto start = client->GetInv().cursor_cbegin(); @@ -382,6 +380,8 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( UpdateEquipmentLight(); UpdateActiveLight(); + loot_request_type = LootRequestType::Forbidden; + IsRezzed(false); Save(); } @@ -524,6 +524,8 @@ EQEmu::TintProfile(), UpdateEquipmentLight(); UpdateActiveLight(); + + loot_request_type = LootRequestType::Forbidden; } Corpse::~Corpse() { @@ -873,13 +875,16 @@ void Corpse::AllowPlayerLoot(Mob *them, uint8 slot) { } void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* app) { + if (!client) + return; + // Added 12/08. Started compressing loot struct on live. if(player_corpse_depop) { SendLootReqErrorPacket(client, LootResponse::SomeoneElse); return; } - if(IsPlayerCorpse() && corpse_db_id == 0) { + if(IsPlayerCorpse() && !corpse_db_id) { // really should try to resave in this case // SendLootReqErrorPacket(client, 0); client->Message(13, "Warning: Corpse's dbid = 0! Corpse will not survive zone shutdown!"); std::cout << "Error: PlayerCorpse::MakeLootRequestPackets: dbid = 0!" << std::endl; @@ -892,165 +897,195 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a return; } - if(being_looted_by == 0) + if(!being_looted_by || (being_looted_by != 0xFFFFFFFF && !entity_list.GetID(being_looted_by))) being_looted_by = 0xFFFFFFFF; - if(this->being_looted_by != 0xFFFFFFFF) { - // lets double check.... - Entity* looter = entity_list.GetID(this->being_looted_by); - if(looter == nullptr) - this->being_looted_by = 0xFFFFFFFF; - } - - uint8 Loot_Request_Type = 1; - bool loot_coin = false; - std::string tmp; - if(database.GetVariable("LootCoin", tmp)) - loot_coin = tmp[0] == 1 && tmp[1] == '\0'; - if (DistanceSquaredNoZ(client->GetPosition(), m_Position) > 625) { SendLootReqErrorPacket(client, LootResponse::TooFar); - // not sure if we need to send the packet back in this case? Didn't before! - // Will just return for now return; } - else if (this->being_looted_by != 0xFFFFFFFF && this->being_looted_by != client->GetID()) { + + if (being_looted_by != 0xFFFFFFFF && being_looted_by != client->GetID()) { SendLootReqErrorPacket(client, LootResponse::SomeoneElse); - Loot_Request_Type = 0; - } - else if (IsPlayerCorpse() && char_id == client->CharacterID()) { - Loot_Request_Type = 2; - } - else if ((IsNPCCorpse() || become_npc) && CanPlayerLoot(client->CharacterID())) { - Loot_Request_Type = 2; - } - else if (GetPlayerKillItem() == -1 && CanPlayerLoot(client->CharacterID())) { /* PVP loot all items, variable cash */ - Loot_Request_Type = 3; - } - else if (GetPlayerKillItem() == 1 && CanPlayerLoot(client->CharacterID())) { /* PVP loot 1 item, variable cash */ - Loot_Request_Type = 4; - } - else if (GetPlayerKillItem() > 1 && CanPlayerLoot(client->CharacterID())) { /* PVP loot 1 set item, variable cash */ - Loot_Request_Type = 5; + return; } - if (Loot_Request_Type == 1) { - if (client->Admin() < 100 || !client->GetGM()) { - SendLootReqErrorPacket(client, LootResponse::NotAtThisTime); + // all loot session disqualifiers should occur before this point as not to interfere with any current looter + loot_request_type = LootRequestType::Forbidden; + + // loot_request_type is scoped to class Corpse and reset on a per-loot session basis + if (client->GetGM()) { + if (client->Admin() >= 100) + loot_request_type = LootRequestType::GMAllowed; + else + loot_request_type = LootRequestType::GMPeek; + } + else { + if (IsPlayerCorpse()) { + if (char_id == client->CharacterID()) { + loot_request_type = LootRequestType::Self; + } + else if (CanPlayerLoot(client->CharacterID())) { + if (GetPlayerKillItem() == -1) + loot_request_type = LootRequestType::AllowedPVPAll; + else if (GetPlayerKillItem() == 1) + loot_request_type = LootRequestType::AllowedPVPSingle; + else if (GetPlayerKillItem() > 1) + loot_request_type = LootRequestType::AllowedPVPDefined; + } } + else if ((IsNPCCorpse() || become_npc) && CanPlayerLoot(client->CharacterID())) { + loot_request_type = LootRequestType::AllowedPVE; + } + } - if(Loot_Request_Type >= 2 || (Loot_Request_Type == 1 && client->Admin() >= 100 && client->GetGM())) { - client->CommonBreakInvisible(); // we should be "all good" so lets break invis now instead of earlier before all error checking is done - this->being_looted_by = client->GetID(); + Log(Logs::Moderate, Logs::Inventory, "MakeLootRequestPackets() LootRequestType %u for %s", loot_request_type, client->GetName()); + + if (loot_request_type == LootRequestType::Forbidden) { + SendLootReqErrorPacket(client, LootResponse::NotAtThisTime); + return; + } + + being_looted_by = client->GetID(); + client->CommonBreakInvisible(); // we should be "all good" so lets break invis now instead of earlier before all error checking is done + + // process coin + bool loot_coin = false; + std::string tmp; + if (database.GetVariable("LootCoin", tmp)) + loot_coin = (tmp[0] == 1 && tmp[1] == '\0'); + + if (loot_request_type == LootRequestType::GMPeek || loot_request_type == LootRequestType::GMAllowed) { + client->Message(15, "This corpse contains %u platinum, %u gold, %u silver and %u copper.", + GetPlatinum(), GetGold(), GetSilver(), GetCopper()); + auto outapp = new EQApplicationPacket(OP_MoneyOnCorpse, sizeof(moneyOnCorpseStruct)); - moneyOnCorpseStruct* d = (moneyOnCorpseStruct*) outapp->pBuffer; + moneyOnCorpseStruct* d = (moneyOnCorpseStruct*)outapp->pBuffer; - d->response = static_cast(LootResponse::Normal); - d->unknown1 = 0x42; - d->unknown2 = 0xef; + d->response = static_cast(LootResponse::Normal); + d->unknown1 = 0x42; + d->unknown2 = 0xef; - /* Dont take the coin off if it's a gm peeking at the corpse */ - if(Loot_Request_Type == 2 || (Loot_Request_Type >= 3 && loot_coin)) { - if(!IsPlayerCorpse() && client->IsGrouped() && client->AutoSplitEnabled() && client->GetGroup()) { - d->copper = 0; - d->silver = 0; - d->gold = 0; - d->platinum = 0; - Group *cgroup = client->GetGroup(); - cgroup->SplitMoney(GetCopper(), GetSilver(), GetGold(), GetPlatinum(), client); - } - else { - d->copper = this->GetCopper(); - d->silver = this->GetSilver(); - d->gold = this->GetGold(); - d->platinum = this->GetPlatinum(); - client->AddMoneyToPP(GetCopper(), GetSilver(), GetGold(), GetPlatinum(), false); - } + d->copper = 0; + d->silver = 0; + d->gold = 0; + d->platinum = 0; - RemoveCash(); - Save(); - } - - auto timestamps = database.GetItemRecastTimestamps(client->CharacterID()); outapp->priority = 6; client->QueuePacket(outapp); + safe_delete(outapp); - if(Loot_Request_Type == 5) { - int pkitem = GetPlayerKillItem(); - const EQEmu::ItemData* item = database.GetItem(pkitem); - EQEmu::ItemInstance* inst = database.CreateItem(item, item->MaxCharges); - if(inst) { - if (item->RecastDelay) - inst->SetRecastTimestamp(timestamps.count(item->RecastType) ? timestamps.at(item->RecastType) : 0); - client->SendItemPacket(EQEmu::invslot::CORPSE_BEGIN, inst, ItemPacketLoot); - safe_delete(inst); - } - else { client->Message(13, "Could not find item number %i to send!!", GetPlayerKillItem()); } + } + else { + auto outapp = new EQApplicationPacket(OP_MoneyOnCorpse, sizeof(moneyOnCorpseStruct)); + moneyOnCorpseStruct* d = (moneyOnCorpseStruct*)outapp->pBuffer; - client->QueuePacket(app); - return; + d->response = static_cast(LootResponse::Normal); + d->unknown1 = 0x42; + d->unknown2 = 0xef; + + Group* cgroup = client->GetGroup(); + + // this can be reworked into a switch and/or massaged to include specialized pve loot rules based on 'LootRequestType' + if (!IsPlayerCorpse() && client->IsGrouped() && client->AutoSplitEnabled() && cgroup) { + d->copper = 0; + d->silver = 0; + d->gold = 0; + d->platinum = 0; + cgroup->SplitMoney(GetCopper(), GetSilver(), GetGold(), GetPlatinum(), client); + } + else { + d->copper = GetCopper(); + d->silver = GetSilver(); + d->gold = GetGold(); + d->platinum = GetPlatinum(); + client->AddMoneyToPP(GetCopper(), GetSilver(), GetGold(), GetPlatinum(), false); } - int i = 0; - const EQEmu::ItemData* item = nullptr; - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); + RemoveCash(); + Save(); - int corpselootlimit = EQEmu::inventory::Lookup(EQEmu::versions::ConvertClientVersionToMobVersion(client->ClientVersion()))->InventoryTypeSize[EQEmu::invtype::typeCorpse]; + outapp->priority = 6; + client->QueuePacket(outapp); - for(; cur != end; ++cur) { - ServerLootItem_Struct* item_data = *cur; - item_data->lootslot = 0xFFFF; + safe_delete(outapp); + } - // Dont display the item if it's in a bag + // process items + auto timestamps = database.GetItemRecastTimestamps(client->CharacterID()); - // Added cursor queue slots to corpse item visibility list. Nothing else should be making it to corpse. - if (!IsPlayerCorpse() || item_data->equip_slot <= EQEmu::invslot::slotCursor || item_data->equip_slot == EQEmu::invslot::SLOT_POWER_SOURCE || Loot_Request_Type >= 3 || - (item_data->equip_slot >= 8000 && item_data->equip_slot <= 8999)) { - if(i < corpselootlimit) { - item = database.GetItem(item_data->item_id); - if(client && item) { - EQEmu::ItemInstance* inst = database.CreateItem(item, item_data->charges, item_data->aug_1, item_data->aug_2, item_data->aug_3, item_data->aug_4, item_data->aug_5, item_data->aug_6, item_data->attuned); - if(inst) { - if (item->RecastDelay) - inst->SetRecastTimestamp(timestamps.count(item->RecastType) ? timestamps.at(item->RecastType) : 0); - // SlotGeneral1 is the corpse inventory start offset for Ti(EMu) - CORPSE_END = SlotGeneral1 + SlotCursor - client->SendItemPacket(i + EQEmu::invslot::CORPSE_BEGIN, inst, ItemPacketLoot); - safe_delete(inst); - } + if (loot_request_type == LootRequestType::AllowedPVPDefined) { + auto pkitemid = GetPlayerKillItem(); + auto pkitem = database.GetItem(pkitemid); + auto pkinst = database.CreateItem(pkitem, pkitem->MaxCharges); - item_data->lootslot = i; - } - } + if (pkinst) { + if (pkitem->RecastDelay) + pkinst->SetRecastTimestamp(timestamps.count(pkitem->RecastType) ? timestamps.at(pkitem->RecastType) : 0); - i++; - } + Log(Logs::Detail, Logs::Inventory, "MakeLootRequestPackets() Slot %u, Item '%s'", EQEmu::invslot::CORPSE_BEGIN, pkitem->Name); + + client->SendItemPacket(EQEmu::invslot::CORPSE_BEGIN, pkinst, ItemPacketLoot); + safe_delete(pkinst); + } + else { + Log(Logs::General, Logs::Inventory, "MakeLootRequestPackets() PlayerKillItem %i not found", pkitemid); + + client->Message(CC_Red, "PlayerKillItem (id: %i) could not be found!", pkitemid); } - if(IsPlayerCorpse() && (char_id == client->CharacterID() || client->GetGM())) { - if(i > corpselootlimit) { - client->Message(15, "*** This corpse contains more items than can be displayed! ***"); - client->Message(0, "Remove items and re-loot corpse to access remaining inventory."); - client->Message(0, "(%s contains %i additional %s.)", GetName(), (i - corpselootlimit), (i - corpselootlimit) == 1 ? "item" : "items"); - } + client->QueuePacket(app); + return; + } - if(IsPlayerCorpse() && i == 0 && itemlist.size() > 0) { // somehow, player corpse contains items, but client doesn't see them... - client->Message(13, "This corpse contains items that are inaccessable!"); - client->Message(15, "Contact a GM for item replacement, if necessary."); - client->Message(15, "BUGGED CORPSE [DBID: %i, Name: %s, Item Count: %i]", GetCorpseDBID(), GetName(), itemlist.size()); + auto loot_slot = EQEmu::invslot::CORPSE_BEGIN; + auto corpse_mask = client->GetInv().GetLookup()->CorpseBitmask; + + for (auto item_data : itemlist) { + // every loot session must either set all items' lootslots to 'invslot::SLOT_INVALID' + // or to a valid enumerated client-versioned corpse slot (lootslot is not equip_slot) + item_data->lootslot = 0xFFFF; + + // align server and client corpse slot mappings so translators can function properly + while (loot_slot <= EQEmu::invslot::CORPSE_END && (((uint64)1 << loot_slot) & corpse_mask) == 0) + ++loot_slot; + + if (loot_slot > EQEmu::invslot::CORPSE_END) + continue; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end; ++cur) { - ServerLootItem_Struct* item_data = *cur; - item = database.GetItem(item_data->item_id); - Log(Logs::General, Logs::None, "Corpse Looting: %s was not sent to client loot window (corpse_dbid: %i, charname: %s(%s))", item->Name, GetCorpseDBID(), client->GetName(), client->GetGM() ? "GM" : "Owner"); - client->Message(0, "Inaccessable Corpse Item: %s", item->Name); - } - } + if (IsPlayerCorpse()) { + if (loot_request_type == LootRequestType::AllowedPVPSingle && loot_slot != EQEmu::invslot::CORPSE_BEGIN) + continue; + + if (item_data->equip_slot < EQEmu::invslot::POSSESSIONS_BEGIN || item_data->equip_slot > EQEmu::invslot::POSSESSIONS_END) + continue; } + + const auto *item = database.GetItem(item_data->item_id); + auto inst = database.CreateItem( + item, + item_data->charges, + item_data->aug_1, + item_data->aug_2, + item_data->aug_3, + item_data->aug_4, + item_data->aug_5, + item_data->aug_6, + item_data->attuned + ); + if (!inst) + continue; + + if (item->RecastDelay) + inst->SetRecastTimestamp(timestamps.count(item->RecastType) ? timestamps.at(item->RecastType) : 0); + + Log(Logs::Moderate, Logs::Inventory, "MakeLootRequestPackets() Slot %i, Item '%s'", loot_slot, item->Name); + + client->SendItemPacket(loot_slot, inst, ItemPacketLoot); + safe_delete(inst); + + item_data->lootslot = loot_slot++; } // Disgrace: Client seems to require that we send the packet back... @@ -1064,8 +1099,22 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a void Corpse::LootItem(Client *client, const EQApplicationPacket *app) { + if (!client) + return; + auto lootitem = (LootingItem_Struct *)app->pBuffer; + Log(Logs::Moderate, Logs::Inventory, "LootItem() LootRequestType %u, Slot %u for %s", loot_request_type, lootitem->slot_id, client->GetName()); + + if (loot_request_type < LootRequestType::GMAllowed) { // LootRequestType::Forbidden and LootRequestType::GMPeek + client->QueuePacket(app); + SendEndLootErrorPacket(client); + // unlock corpse for others + if (IsBeingLootedBy(client)) + ResetLooter(); + return; + } + if (!loot_cooldown_timer.Check()) { client->QueuePacket(app); SendEndLootErrorPacket(client); @@ -1093,7 +1142,7 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) } if (IsPlayerCorpse() && !CanPlayerLoot(client->CharacterID()) && !become_npc && - (char_id != client->CharacterID() && client->Admin() < 150)) { + (char_id != client->CharacterID() && client->Admin() < 150)) { client->Message(13, "Error: This is a player corpse and you dont own it."); client->QueuePacket(app); SendEndLootErrorPacket(client); @@ -1108,7 +1157,7 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) } if (IsPlayerCorpse() && (char_id != client->CharacterID()) && CanPlayerLoot(client->CharacterID()) && - GetPlayerKillItem() == 0) { + GetPlayerKillItem() == 0) { client->Message(13, "Error: You cannot loot any more items from this corpse."); client->QueuePacket(app); SendEndLootErrorPacket(client); @@ -1123,12 +1172,13 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) memset(bag_item_data, 0, sizeof(bag_item_data)); if (GetPlayerKillItem() > 1) { item = database.GetItem(GetPlayerKillItem()); - } else if (GetPlayerKillItem() == -1 || GetPlayerKillItem() == 1) { + } + else if (GetPlayerKillItem() == -1 || GetPlayerKillItem() == 1) { item_data = - GetItem(lootitem->slot_id - - EQEmu::invslot::CORPSE_BEGIN); // dont allow them to loot entire bags of items as pvp reward - } else { - item_data = GetItem(lootitem->slot_id - EQEmu::invslot::CORPSE_BEGIN, bag_item_data); + GetItem(lootitem->slot_id); // dont allow them to loot entire bags of items as pvp reward + } + else { + item_data = GetItem(lootitem->slot_id, bag_item_data); } if (GetPlayerKillItem() <= 1 && item_data != 0) { @@ -1138,9 +1188,10 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) if (item != 0) { if (item_data) { inst = database.CreateItem(item, item_data ? item_data->charges : 0, item_data->aug_1, - item_data->aug_2, item_data->aug_3, item_data->aug_4, - item_data->aug_5, item_data->aug_6, item_data->attuned); - } else { + item_data->aug_2, item_data->aug_3, item_data->aug_4, + item_data->aug_5, item_data->aug_6, item_data->attuned); + } + else { inst = database.CreateItem(item); } } @@ -1175,7 +1226,7 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) char q_corpse_name[64]; strcpy(q_corpse_name, corpse_name); snprintf(buf, 87, "%d %d %s", inst->GetItem()->ID, inst->GetCharges(), - EntityList::RemoveNumbers(q_corpse_name)); + EntityList::RemoveNumbers(q_corpse_name)); buf[87] = '\0'; std::vector args; args.push_back(inst); @@ -1187,6 +1238,8 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) delete inst; return; } + + // do we want this to have a fail option too? parse->EventItem(EVENT_LOOT, client, inst, this, buf, 0); @@ -1211,7 +1264,8 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) if (lootitem->auto_loot > 0) { if (!client->AutoPutLootInInventory(*inst, true, true, bag_item_data)) client->PutLootInInventory(EQEmu::invslot::slotCursor, *inst, bag_item_data); - } else { + } + else { client->PutLootInInventory(EQEmu::invslot::slotCursor, *inst, bag_item_data); } @@ -1222,9 +1276,9 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) /* Remove it from Corpse */ if (item_data) { /* Delete needs to be before RemoveItem because its deletes the pointer for - * item_data/bag_item_data */ + * item_data/bag_item_data */ database.DeleteItemOffCharacterCorpse(this->corpse_db_id, item_data->equip_slot, - item_data->item_id); + item_data->item_id); /* Delete Item Instance */ RemoveItem(item_data->lootslot); } @@ -1234,10 +1288,10 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) for (int i = EQEmu::invbag::SLOT_BEGIN; i <= EQEmu::invbag::SLOT_END; i++) { if (bag_item_data[i]) { /* Delete needs to be before RemoveItem because its deletes the pointer for - * item_data/bag_item_data */ + * item_data/bag_item_data */ database.DeleteItemOffCharacterCorpse(this->corpse_db_id, - bag_item_data[i]->equip_slot, - bag_item_data[i]->item_id); + bag_item_data[i]->equip_slot, + bag_item_data[i]->item_id); /* Delete Item Instance */ RemoveItem(bag_item_data[i]); } @@ -1261,16 +1315,18 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) Group *g = client->GetGroup(); if (g != nullptr) { g->GroupMessage_StringID(client, MT_LootMessages, OTHER_LOOTED_MESSAGE, - client->GetName(), linker.Link().c_str()); - } else { + client->GetName(), linker.Link().c_str()); + } + else { Raid *r = client->GetRaid(); if (r != nullptr) { r->RaidMessage_StringID(client, MT_LootMessages, OTHER_LOOTED_MESSAGE, - client->GetName(), linker.Link().c_str()); + client->GetName(), linker.Link().c_str()); } } } - } else { + } + else { SendEndLootErrorPacket(client); safe_delete(inst); return; @@ -1278,7 +1334,8 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) if (IsPlayerCorpse()) { client->SendItemLink(inst); - } else { + } + else { client->SendItemLink(inst, true); } @@ -1457,7 +1514,7 @@ void Corpse::UpdateEquipmentLight() m_Light.Level[EQEmu::lightsource::LightEquipment] = 0; for (auto iter = itemlist.begin(); iter != itemlist.end(); ++iter) { - if (((*iter)->equip_slot < EQEmu::invslot::EQUIPMENT_BEGIN || (*iter)->equip_slot > EQEmu::invslot::EQUIPMENT_END) && (*iter)->equip_slot != EQEmu::invslot::SLOT_POWER_SOURCE) { continue; } + if ((*iter)->equip_slot < EQEmu::invslot::EQUIPMENT_BEGIN || (*iter)->equip_slot > EQEmu::invslot::EQUIPMENT_END) { continue; } if ((*iter)->equip_slot == EQEmu::invslot::slotAmmo) { continue; } auto item = database.GetItem((*iter)->item_id); diff --git a/zone/corpse.h b/zone/corpse.h index 84bce15fa..ff323e791 100644 --- a/zone/corpse.h +++ b/zone/corpse.h @@ -116,7 +116,7 @@ class Corpse : public Mob { inline void Lock() { is_locked = true; } inline void UnLock() { is_locked = false; } inline bool IsLocked() { return is_locked; } - inline void ResetLooter() { being_looted_by = 0xFFFFFFFF; } + inline void ResetLooter() { being_looted_by = 0xFFFFFFFF; loot_request_type = LootRequestType::Forbidden; } inline bool IsBeingLooted() { return (being_looted_by != 0xFFFFFFFF); } inline bool IsBeingLootedBy(Client *c) { return being_looted_by == c->GetID(); } @@ -161,6 +161,7 @@ private: Timer loot_cooldown_timer; /* Delay between loot actions on the corpse entity */ EQEmu::TintProfile item_tint; + LootRequestType loot_request_type; }; #endif diff --git a/zone/embparser.cpp b/zone/embparser.cpp index 80f6ace11..c267bd857 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -1128,9 +1128,9 @@ void PerlembParser::ExportItemVariables(std::string &package_name, Mob *mob) { std::string hashname = package_name + std::string("::oncursor"); perl->eval(std::string("%").append(hashname).append(" = ();").c_str()); char *hi_decl = nullptr; - int itemid = mob->CastToClient()->GetItemIDAt(30); + int itemid = mob->CastToClient()->GetItemIDAt(EQEmu::invslot::slotCursor); if(!HASITEM_ISNULLITEM(itemid)) { - MakeAnyLenString(&hi_decl, "push (@{$%s{%d}},%d);",hashname.c_str(), itemid, 30); + MakeAnyLenString(&hi_decl, "push (@{$%s{%d}},%d);",hashname.c_str(), itemid, EQEmu::invslot::slotCursor); perl->eval(hi_decl); safe_delete_array(hi_decl); } diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index e1f2c1c12..589f1146f 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -33,6 +33,8 @@ #include "zone.h" #include "data_bucket.h" +#include + extern Zone *zone; extern QueryServ *QServ; @@ -348,6 +350,83 @@ XS(XS__incstat) { XSRETURN_EMPTY; } +XS(XS__getinventoryslotid); +XS(XS__getinventoryslotid) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getinventoryslotid(string identifier)"); + + int16 RETVAL; + dXSTARG; + + std::string identifier = (Const_char *)SvPV_nolen(ST(0)); + for (std::string::size_type i = 0; i < identifier.length(); ++i) + identifier[i] = std::tolower(identifier[i]); + + if (identifier == "invalid") RETVAL = EQEmu::invslot::SLOT_INVALID; + else if (identifier == "cursor") RETVAL = EQEmu::invslot::slotCursor; + else if (identifier == "possessions.begin") RETVAL = EQEmu::invslot::POSSESSIONS_BEGIN; + else if (identifier == "possessions.end") RETVAL = EQEmu::invslot::POSSESSIONS_END; + else if (identifier == "bank.begin") RETVAL = EQEmu::invslot::BANK_BEGIN; + else if (identifier == "bank.end") RETVAL = EQEmu::invslot::BANK_END; + else if (identifier == "sharedbank.begin") RETVAL = EQEmu::invslot::SHARED_BANK_BEGIN; + else if (identifier == "sharedbank.end") RETVAL = EQEmu::invslot::SHARED_BANK_END; + else if (identifier == "generalbags.begin") RETVAL = EQEmu::invbag::GENERAL_BAGS_BEGIN; + else if (identifier == "generalbags.end") RETVAL = EQEmu::invbag::GENERAL_BAGS_END; + else if (identifier == "cursorbag.begin") RETVAL = EQEmu::invbag::CURSOR_BAG_BEGIN; + else if (identifier == "cursorbag.end") RETVAL = EQEmu::invbag::CURSOR_BAG_END; + else if (identifier == "bankbags.begin") RETVAL = EQEmu::invbag::BANK_BAGS_BEGIN; + else if (identifier == "bankbags.end") RETVAL = EQEmu::invbag::BANK_BAGS_END; + else if (identifier == "sharedbankbags.begin") RETVAL = EQEmu::invbag::SHARED_BANK_BAGS_BEGIN; + else if (identifier == "sharedbankbags.end") RETVAL = EQEmu::invbag::SHARED_BANK_BAGS_END; + else if (identifier == "bagslot.begin") RETVAL = EQEmu::invbag::SLOT_BEGIN; + else if (identifier == "bagslot.end") RETVAL = EQEmu::invbag::SLOT_END; + else if (identifier == "augsocket.begin") RETVAL = EQEmu::invaug::SOCKET_BEGIN; + else if (identifier == "augsocket.end") RETVAL = EQEmu::invaug::SOCKET_END; + else if (identifier == "equipment.begin") RETVAL = EQEmu::invslot::EQUIPMENT_BEGIN; + else if (identifier == "equipment.end") RETVAL = EQEmu::invslot::EQUIPMENT_END; + else if (identifier == "general.begin") RETVAL = EQEmu::invslot::GENERAL_BEGIN; + else if (identifier == "general.end") RETVAL = EQEmu::invslot::GENERAL_END; + else if (identifier == "charm") RETVAL = EQEmu::invslot::slotCharm; + else if (identifier == "ear1") RETVAL = EQEmu::invslot::slotEar1; + else if (identifier == "head") RETVAL = EQEmu::invslot::slotHead; + else if (identifier == "face") RETVAL = EQEmu::invslot::slotFace; + else if (identifier == "ear2") RETVAL = EQEmu::invslot::slotEar2; + else if (identifier == "neck") RETVAL = EQEmu::invslot::slotNeck; + else if (identifier == "shoulders") RETVAL = EQEmu::invslot::slotShoulders; + else if (identifier == "arms") RETVAL = EQEmu::invslot::slotArms; + else if (identifier == "back") RETVAL = EQEmu::invslot::slotBack; + else if (identifier == "wrist1") RETVAL = EQEmu::invslot::slotWrist1; + else if (identifier == "wrist2") RETVAL = EQEmu::invslot::slotWrist2; + else if (identifier == "range") RETVAL = EQEmu::invslot::slotRange; + else if (identifier == "hands") RETVAL = EQEmu::invslot::slotHands; + else if (identifier == "primary") RETVAL = EQEmu::invslot::slotPrimary; + else if (identifier == "secondary") RETVAL = EQEmu::invslot::slotSecondary; + else if (identifier == "finger1") RETVAL = EQEmu::invslot::slotFinger1; + else if (identifier == "finger2") RETVAL = EQEmu::invslot::slotFinger2; + else if (identifier == "chest") RETVAL = EQEmu::invslot::slotChest; + else if (identifier == "legs") RETVAL = EQEmu::invslot::slotLegs; + else if (identifier == "feet") RETVAL = EQEmu::invslot::slotFeet; + else if (identifier == "waist") RETVAL = EQEmu::invslot::slotWaist; + else if (identifier == "powersource") RETVAL = EQEmu::invslot::slotPowerSource; + else if (identifier == "ammo") RETVAL = EQEmu::invslot::slotAmmo; + else if (identifier == "general1") RETVAL = EQEmu::invslot::slotGeneral1; + else if (identifier == "general2") RETVAL = EQEmu::invslot::slotGeneral2; + else if (identifier == "general3") RETVAL = EQEmu::invslot::slotGeneral3; + else if (identifier == "general4") RETVAL = EQEmu::invslot::slotGeneral4; + else if (identifier == "general5") RETVAL = EQEmu::invslot::slotGeneral5; + else if (identifier == "general6") RETVAL = EQEmu::invslot::slotGeneral6; + else if (identifier == "general7") RETVAL = EQEmu::invslot::slotGeneral7; + else if (identifier == "general8") RETVAL = EQEmu::invslot::slotGeneral8; + else if (identifier == "general9") RETVAL = EQEmu::invslot::slotGeneral9; + else if (identifier == "general10") RETVAL = EQEmu::invslot::slotGeneral10; + else RETVAL = EQEmu::invslot::SLOT_INVALID; + + XSprePUSH; PUSHu((IV)RETVAL); + + XSRETURN(1); +} + XS(XS__castspell); XS(XS__castspell) { dXSARGS; @@ -3672,6 +3751,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "follow"), XS__follow, file); newXS(strcpy(buf, "forcedoorclose"), XS__forcedoorclose, file); newXS(strcpy(buf, "forcedooropen"), XS__forcedooropen, file); + newXS(strcpy(buf, "getinventoryslotid"), XS__getinventoryslotid, file); newXS(strcpy(buf, "getItemName"), XS_qc_getItemName, file); newXS(strcpy(buf, "get_spawn_condition"), XS__get_spawn_condition, file); newXS(strcpy(buf, "getguildnamebyid"), XS__getguildnamebyid, file); diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index 5649c0da4..3f1fbf892 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -538,6 +538,69 @@ Mob *HateList::GetRandomEntOnHateList() return (*iterator)->entity_on_hatelist; } +Mob *HateList::GetEscapingEntOnHateList() { + // function is still in design stage + + for (auto iter : list) { + if (!iter->entity_on_hatelist) + continue; + + if (!iter->entity_on_hatelist->IsFeared()) + continue; + + if (iter->entity_on_hatelist->IsRooted()) + continue; + if (iter->entity_on_hatelist->IsMezzed()) + continue; + if (iter->entity_on_hatelist->IsStunned()) + continue; + + return iter->entity_on_hatelist; + } + + return nullptr; +} + +Mob *HateList::GetEscapingEntOnHateList(Mob *center, float range, bool first) { + // function is still in design stage + + if (!center) + return nullptr; + + Mob *escaping_mob = nullptr; + float mob_distance = 0.0f; + + for (auto iter : list) { + if (!iter->entity_on_hatelist) + continue; + + if (!iter->entity_on_hatelist->IsFeared()) + continue; + + if (iter->entity_on_hatelist->IsRooted()) + continue; + if (iter->entity_on_hatelist->IsMezzed()) + continue; + if (iter->entity_on_hatelist->IsStunned()) + continue; + + float distance_test = DistanceSquared(center->GetPosition(), iter->entity_on_hatelist->GetPosition()); + + if (range > 0.0f && distance_test > range) + continue; + + if (first) + return iter->entity_on_hatelist; + + if (distance_test > mob_distance) { + escaping_mob = iter->entity_on_hatelist; + mob_distance = distance_test; + } + } + + return escaping_mob; +} + int32 HateList::GetEntHateAmount(Mob *in_entity, bool damage) { struct_HateList *entity; diff --git a/zone/hate_list.h b/zone/hate_list.h index 0ac1840ac..96d8ed067 100644 --- a/zone/hate_list.h +++ b/zone/hate_list.h @@ -46,6 +46,8 @@ public: Mob *GetEntWithMostHateOnList(Mob *center, Mob *skip = nullptr); Mob *GetRandomEntOnHateList(); Mob *GetEntWithMostHateOnList(); + Mob *GetEscapingEntOnHateList(); // returns first eligble entity + Mob *GetEscapingEntOnHateList(Mob *center, float range = 0.0f, bool first = false); bool IsEntOnHateList(Mob *mob); bool IsHateListEmpty(); diff --git a/zone/inventory.cpp b/zone/inventory.cpp index d4604852d..24fa86b30 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -44,23 +44,9 @@ uint32 Client::NukeItem(uint32 itemnum, uint8 where_to_check) { x++; } - DeleteItemInInventory(i, 0, true); + DeleteItemInInventory(i, 0, ((((uint64)1 << i) & GetInv().GetLookup()->PossessionsBitmask) != 0)); } } - - if (GetItemIDAt(EQEmu::invslot::SLOT_POWER_SOURCE) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(EQEmu::invslot::SLOT_POWER_SOURCE) != INVALID_ID)) { - cur = m_inv.GetItem(EQEmu::invslot::SLOT_POWER_SOURCE); - if(cur && cur->GetItem()->Stackable) { - x += cur->GetCharges(); - } else { - x++; - } - - if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF) - DeleteItemInInventory(EQEmu::invslot::SLOT_POWER_SOURCE, 0, true); - else - DeleteItemInInventory(EQEmu::invslot::SLOT_POWER_SOURCE, 0, false); // Prevents Titanium crash - } } if(where_to_check & invWhereCursor) { @@ -99,7 +85,7 @@ uint32 Client::NukeItem(uint32 itemnum, uint8 where_to_check) { x++; } - DeleteItemInInventory(i, 0, true); + DeleteItemInInventory(i, 0, ((((uint64)1 << i) & GetInv().GetLookup()->PossessionsBitmask) != 0)); } } @@ -112,7 +98,7 @@ uint32 Client::NukeItem(uint32 itemnum, uint8 where_to_check) { x++; } - DeleteItemInInventory(i, 0, true); + DeleteItemInInventory(i, 0, ((((uint64)1 << (EQEmu::invslot::GENERAL_BEGIN + ((i - EQEmu::invbag::GENERAL_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT))) & GetInv().GetLookup()->PossessionsBitmask) == 0)); } } } @@ -127,7 +113,7 @@ uint32 Client::NukeItem(uint32 itemnum, uint8 where_to_check) { x++; } - DeleteItemInInventory(i, 0, true); + DeleteItemInInventory(i, 0, ((i - EQEmu::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank])); } } @@ -140,7 +126,7 @@ uint32 Client::NukeItem(uint32 itemnum, uint8 where_to_check) { x++; } - DeleteItemInInventory(i, 0, true); + DeleteItemInInventory(i, 0, (((i - EQEmu::invbag::BANK_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT) >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank])); } } } @@ -554,8 +540,8 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, inst->SetOrnamentHeroModel(ornament_hero_model); // check to see if item is usable in requested slot - if (enforceusable && (((to_slot >= EQEmu::invslot::slotCharm) && (to_slot <= EQEmu::invslot::slotAmmo)) || (to_slot == EQEmu::invslot::SLOT_POWER_SOURCE))) { - uint32 slottest = (to_slot == EQEmu::invslot::SLOT_POWER_SOURCE) ? 22 : to_slot; // can't change '22' just yet... + if (enforceusable && (to_slot >= EQEmu::invslot::EQUIPMENT_BEGIN && to_slot <= EQEmu::invslot::EQUIPMENT_END)) { + uint32 slottest = to_slot; if(!(slots & ((uint32)1 << slottest))) { Message(0, "This item is not equipable at slot %u - moving to cursor.", to_slot); @@ -785,6 +771,25 @@ void Client::DropInst(const EQEmu::ItemInstance* inst) // Returns a slot's item ID (returns INVALID_ID if not found) int32 Client::GetItemIDAt(int16 slot_id) { + if (slot_id <= EQEmu::invslot::POSSESSIONS_END && slot_id >= EQEmu::invslot::POSSESSIONS_BEGIN) { + if ((((uint64)1 << slot_id) & GetInv().GetLookup()->PossessionsBitmask) == 0) + return INVALID_ID; + } + else if (slot_id <= EQEmu::invbag::GENERAL_BAGS_END && slot_id >= EQEmu::invbag::GENERAL_BAGS_BEGIN) { + auto temp_slot = EQEmu::invslot::GENERAL_BEGIN + ((slot_id - EQEmu::invbag::GENERAL_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT); + if ((((uint64)1 << temp_slot) & GetInv().GetLookup()->PossessionsBitmask) == 0) + return INVALID_ID; + } + else if (slot_id <= EQEmu::invslot::BANK_END && slot_id >= EQEmu::invslot::BANK_BEGIN) { + if ((slot_id - EQEmu::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank]) + return INVALID_ID; + } + else if (slot_id <= EQEmu::invbag::BANK_BAGS_END && slot_id >= EQEmu::invbag::BANK_BAGS_BEGIN) { + auto temp_slot = (slot_id - EQEmu::invbag::BANK_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT; + if (temp_slot >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank]) + return INVALID_ID; + } + const EQEmu::ItemInstance* inst = m_inv[slot_id]; if (inst) return inst->GetItem()->ID; @@ -796,6 +801,25 @@ int32 Client::GetItemIDAt(int16 slot_id) { // Returns an augment's ID that's in an item (returns INVALID_ID if not found) // Pass in the slot ID of the item and which augslot you want to check (0-5) int32 Client::GetAugmentIDAt(int16 slot_id, uint8 augslot) { + if (slot_id <= EQEmu::invslot::POSSESSIONS_END && slot_id >= EQEmu::invslot::POSSESSIONS_BEGIN) { + if ((((uint64)1 << slot_id) & GetInv().GetLookup()->PossessionsBitmask) == 0) + return INVALID_ID; + } + else if (slot_id <= EQEmu::invbag::GENERAL_BAGS_END && slot_id >= EQEmu::invbag::GENERAL_BAGS_BEGIN) { + auto temp_slot = EQEmu::invslot::GENERAL_BEGIN + ((slot_id - EQEmu::invbag::GENERAL_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT); + if ((((uint64)1 << temp_slot) & GetInv().GetLookup()->PossessionsBitmask) == 0) + return INVALID_ID; + } + else if (slot_id <= EQEmu::invslot::BANK_END && slot_id >= EQEmu::invslot::BANK_BEGIN) { + if ((slot_id - EQEmu::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank]) + return INVALID_ID; + } + else if (slot_id <= EQEmu::invbag::BANK_BAGS_END && slot_id >= EQEmu::invbag::BANK_BAGS_BEGIN) { + auto temp_slot = (slot_id - EQEmu::invbag::BANK_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT; + if (temp_slot >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank]) + return INVALID_ID; + } + const EQEmu::ItemInstance* inst = m_inv[slot_id]; if (inst && inst->GetAugmentItemID(augslot)) { return inst->GetAugmentItemID(augslot); @@ -1032,7 +1056,7 @@ void Client::PutLootInInventory(int16 slot_id, const EQEmu::ItemInstance &inst, } if (bag_item_data) { - for (int index = 0; index < EQEmu::invbag::SLOT_COUNT; ++index) { + for (int index = EQEmu::invbag::SLOT_BEGIN; index <= EQEmu::invbag::SLOT_END; ++index) { if (bag_item_data[index] == nullptr) continue; @@ -1072,12 +1096,15 @@ void Client::PutLootInInventory(int16 slot_id, const EQEmu::ItemInstance &inst, CalcBonuses(); } -bool Client::TryStacking(EQEmu::ItemInstance* item, uint8 type, bool try_worn, bool try_cursor){ +bool Client::TryStacking(EQEmu::ItemInstance* item, uint8 type, bool try_worn, bool try_cursor) { if(!item || !item->IsStackable() || item->GetCharges()>=item->GetItem()->StackSize) return false; int16 i; uint32 item_id = item->GetItem()->ID; for (i = EQEmu::invslot::GENERAL_BEGIN; i <= EQEmu::invslot::GENERAL_END; i++) { + if (((uint64)1 << i) & GetInv().GetLookup()->PossessionsBitmask == 0) + continue; + EQEmu::ItemInstance* tmp_inst = m_inv.GetItem(i); if(tmp_inst && tmp_inst->GetItem()->ID == item_id && tmp_inst->GetCharges() < tmp_inst->GetItem()->StackSize){ MoveItemCharges(*item, i, type); @@ -1089,6 +1116,9 @@ bool Client::TryStacking(EQEmu::ItemInstance* item, uint8 type, bool try_worn, b } } for (i = EQEmu::invslot::GENERAL_BEGIN; i <= EQEmu::invslot::GENERAL_END; i++) { + if (((uint64)1 << i) & GetInv().GetLookup()->PossessionsBitmask == 0) + continue; + for (uint8 j = EQEmu::invbag::SLOT_BEGIN; j <= EQEmu::invbag::SLOT_END; j++) { uint16 slotid = EQEmu::InventoryProfile::CalcSlotId(i, j); EQEmu::ItemInstance* tmp_inst = m_inv.GetItem(slotid); @@ -1113,15 +1143,9 @@ bool Client::AutoPutLootInInventory(EQEmu::ItemInstance& inst, bool try_worn, bo { // #1: Try to auto equip if (try_worn && inst.IsEquipable(GetBaseRace(), GetClass()) && inst.GetItem()->ReqLevel <= level && (!inst.GetItem()->Attuneable || inst.IsAttuned()) && inst.GetItem()->ItemType != EQEmu::item::ItemTypeAugmentation) { - // too messy as-is... - for (int16 i = EQEmu::invslot::EQUIPMENT_BEGIN; i < EQEmu::invslot::SLOT_POWER_SOURCE; i++) { // originally (i < 22) - if (i == EQEmu::invslot::GENERAL_BEGIN) { - // added power source check for SoF+ clients - if (this->ClientVersion() >= EQEmu::versions::ClientVersion::SoF) - i = EQEmu::invslot::SLOT_POWER_SOURCE; - else - break; - } + for (int16 i = EQEmu::invslot::EQUIPMENT_BEGIN; i <= EQEmu::invslot::EQUIPMENT_END; i++) { + if (((uint64)1 << i) & GetInv().GetLookup()->PossessionsBitmask == 0) + continue; if (!m_inv[i]) { if (i == EQEmu::invslot::slotPrimary && inst.IsWeapon()) { // If item is primary slot weapon @@ -1442,23 +1466,34 @@ void Client::SendLootItemInPacket(const EQEmu::ItemInstance* inst, int16 slot_id } bool Client::IsValidSlot(uint32 slot) { - if ((slot == (uint32)INVALID_INDEX) || - (slot >= EQEmu::invslot::POSSESSIONS_BEGIN && slot <= EQEmu::invslot::POSSESSIONS_END) || - (slot >= EQEmu::invbag::GENERAL_BAGS_BEGIN && slot <= EQEmu::invbag::CURSOR_BAG_END) || - (slot >= EQEmu::invslot::TRIBUTE_BEGIN && slot <= EQEmu::invslot::TRIBUTE_END) || - (slot >= EQEmu::invslot::BANK_BEGIN && slot <= EQEmu::invslot::BANK_END) || - (slot >= EQEmu::invbag::BANK_BAGS_BEGIN && slot <= EQEmu::invbag::BANK_BAGS_END) || - (slot >= EQEmu::invslot::SHARED_BANK_BEGIN && slot <= EQEmu::invslot::SHARED_BANK_END) || - (slot >= EQEmu::invbag::SHARED_BANK_BAGS_BEGIN && slot <= EQEmu::invbag::SHARED_BANK_BAGS_END) || - (slot >= EQEmu::invslot::TRADE_BEGIN && slot <= EQEmu::invslot::TRADE_END) || - (slot >= EQEmu::invslot::WORLD_BEGIN && slot <= EQEmu::invslot::WORLD_END) || - (slot == EQEmu::invslot::SLOT_POWER_SOURCE) - ) { + if (slot <= EQEmu::invslot::POSSESSIONS_END && slot >= EQEmu::invslot::POSSESSIONS_BEGIN) { + return ((((uint64)1 << slot) & GetInv().GetLookup()->PossessionsBitmask) != 0); + } + else if (slot <= EQEmu::invbag::GENERAL_BAGS_END && slot >= EQEmu::invbag::GENERAL_BAGS_BEGIN) { + auto temp_slot = EQEmu::invslot::GENERAL_BEGIN + ((slot - EQEmu::invbag::GENERAL_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT); + return ((((uint64)1 << temp_slot) & GetInv().GetLookup()->PossessionsBitmask) != 0); + } + else if (slot <= EQEmu::invslot::BANK_END && slot >= EQEmu::invslot::BANK_BEGIN) { + return ((slot - EQEmu::invslot::BANK_BEGIN) < GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank]); + } + else if (slot <= EQEmu::invbag::BANK_BAGS_END && slot >= EQEmu::invbag::BANK_BAGS_BEGIN) { + auto temp_slot = (slot - EQEmu::invbag::BANK_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT; + return (temp_slot < GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank]); + } + else if ( + (slot == (uint32)INVALID_INDEX) || + (slot == (uint32)EQEmu::invslot::slotCursor) || + (slot <= EQEmu::invbag::CURSOR_BAG_END && slot >= EQEmu::invbag::CURSOR_BAG_BEGIN) || + (slot <= EQEmu::invslot::TRIBUTE_END && slot >= EQEmu::invslot::TRIBUTE_BEGIN) || + (slot <= EQEmu::invslot::SHARED_BANK_END && slot >= EQEmu::invslot::SHARED_BANK_BEGIN) || + (slot <= EQEmu::invbag::SHARED_BANK_BAGS_END && slot >= EQEmu::invbag::SHARED_BANK_BAGS_BEGIN) || + (slot <= EQEmu::invslot::TRADE_END && slot >= EQEmu::invslot::TRADE_BEGIN) || + (slot <= EQEmu::invslot::WORLD_END && slot >= EQEmu::invslot::WORLD_BEGIN) + ) { return true; } - else { - return false; - } + + return false; } bool Client::IsBankSlot(uint32 slot) @@ -1550,10 +1585,14 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { return true; // Item deletion } } - if (auto_attack && (move_in->from_slot == EQEmu::invslot::slotPrimary || move_in->from_slot == EQEmu::invslot::slotSecondary || move_in->from_slot == EQEmu::invslot::slotRange)) - SetAttackTimer(); - else if (auto_attack && (move_in->to_slot == EQEmu::invslot::slotPrimary || move_in->to_slot == EQEmu::invslot::slotSecondary || move_in->to_slot == EQEmu::invslot::slotRange)) - SetAttackTimer(); + + if (auto_attack) { + if (move_in->from_slot == EQEmu::invslot::slotPrimary || move_in->from_slot == EQEmu::invslot::slotSecondary || move_in->from_slot == EQEmu::invslot::slotRange) + SetAttackTimer(); + else if (move_in->to_slot == EQEmu::invslot::slotPrimary || move_in->to_slot == EQEmu::invslot::slotSecondary || move_in->to_slot == EQEmu::invslot::slotRange) + SetAttackTimer(); + } + // Step 1: Variables int16 src_slot_id = (int16)move_in->from_slot; int16 dst_slot_id = (int16)move_in->to_slot; @@ -1599,13 +1638,11 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { uint32 srcbagid =0; uint32 dstbagid = 0; - //if (src_slot_id >= 250 && src_slot_id < 330) { if (src_slot_id >= EQEmu::invbag::GENERAL_BAGS_BEGIN && src_slot_id <= EQEmu::invbag::GENERAL_BAGS_END) { srcbag = m_inv.GetItem(((int)(src_slot_id / 10)) - 3); if (srcbag) srcbagid = srcbag->GetItem()->ID; } - //if (dst_slot_id >= 250 && dst_slot_id < 330) { if (dst_slot_id >= EQEmu::invbag::GENERAL_BAGS_BEGIN && dst_slot_id <= EQEmu::invbag::GENERAL_BAGS_END) { dstbag = m_inv.GetItem(((int)(dst_slot_id / 10)) - 3); if (dstbag) @@ -1877,7 +1914,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { } else { // Not dealing with charges - just do direct swap - if (src_inst && (dst_slot_id <= EQEmu::invslot::EQUIPMENT_END || dst_slot_id == EQEmu::invslot::SLOT_POWER_SOURCE) && dst_slot_id >= EQEmu::invslot::EQUIPMENT_BEGIN) { + if (src_inst && (dst_slot_id <= EQEmu::invslot::EQUIPMENT_END) && dst_slot_id >= EQEmu::invslot::EQUIPMENT_BEGIN) { if (src_inst->GetItem()->Attuneable) { src_inst->SetAttuned(true); } @@ -1909,7 +1946,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { Log(Logs::Detail, Logs::Inventory, "Moving entire item from slot %d to slot %d", src_slot_id, dst_slot_id); - if (src_slot_id <= EQEmu::invslot::EQUIPMENT_END || src_slot_id == EQEmu::invslot::SLOT_POWER_SOURCE) { + if (src_slot_id <= EQEmu::invslot::EQUIPMENT_END) { if(src_inst) { parse->EventItem(EVENT_UNEQUIP_ITEM, this, src_inst, nullptr, "", src_slot_id); } @@ -1919,7 +1956,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { } } - if (dst_slot_id <= EQEmu::invslot::EQUIPMENT_END || dst_slot_id == EQEmu::invslot::SLOT_POWER_SOURCE) { + if (dst_slot_id <= EQEmu::invslot::EQUIPMENT_END) { if(dst_inst) { parse->EventItem(EVENT_UNEQUIP_ITEM, this, dst_inst, nullptr, "", dst_slot_id); } @@ -1973,7 +2010,7 @@ void Client::SwapItemResync(MoveItem_Struct* move_slots) { Log(Logs::Detail, Logs::Inventory, "Inventory desyncronization. (charname: %s, source: %i, destination: %i)", GetName(), move_slots->from_slot, move_slots->to_slot); Message(15, "Inventory Desyncronization detected: Resending slot data..."); - if ((move_slots->from_slot >= EQEmu::invslot::EQUIPMENT_BEGIN && move_slots->from_slot <= EQEmu::invbag::CURSOR_BAG_END) || move_slots->from_slot == EQEmu::invslot::SLOT_POWER_SOURCE) { + if (move_slots->from_slot >= EQEmu::invslot::EQUIPMENT_BEGIN && move_slots->from_slot <= EQEmu::invbag::CURSOR_BAG_END) { int16 resync_slot = (EQEmu::InventoryProfile::CalcSlotId(move_slots->from_slot) == INVALID_INDEX) ? move_slots->from_slot : EQEmu::InventoryProfile::CalcSlotId(move_slots->from_slot); if (IsValidSlot(resync_slot) && resync_slot != INVALID_INDEX) { // This prevents the client from crashing when closing any 'phantom' bags @@ -2016,7 +2053,7 @@ void Client::SwapItemResync(MoveItem_Struct* move_slots) { else { Message(13, "Could not resyncronize source slot %i.", move_slots->from_slot); } } - if ((move_slots->to_slot >= EQEmu::invslot::EQUIPMENT_BEGIN && move_slots->to_slot <= EQEmu::invbag::CURSOR_BAG_END) || move_slots->to_slot == EQEmu::invslot::SLOT_POWER_SOURCE) { + if (move_slots->to_slot >= EQEmu::invslot::EQUIPMENT_BEGIN && move_slots->to_slot <= EQEmu::invbag::CURSOR_BAG_END) { int16 resync_slot = (EQEmu::InventoryProfile::CalcSlotId(move_slots->to_slot) == INVALID_INDEX) ? move_slots->to_slot : EQEmu::InventoryProfile::CalcSlotId(move_slots->to_slot); if (IsValidSlot(resync_slot) && resync_slot != INVALID_INDEX) { const EQEmu::ItemData* token_struct = database.GetItem(22292); // 'Copper Coin' @@ -2251,47 +2288,118 @@ bool Client::DecreaseByID(uint32 type, uint8 amt) { EQEmu::ItemInstance* ins = nullptr; int x; int num = 0; - for(x = EQEmu::invslot::EQUIPMENT_BEGIN; x <= EQEmu::invbag::GENERAL_BAGS_END; x++) - { - if (x == EQEmu::invslot::slotCursor + 1) - x = EQEmu::invbag::GENERAL_BAGS_BEGIN; + + for (x = EQEmu::invslot::POSSESSIONS_BEGIN; x <= EQEmu::invslot::POSSESSIONS_END; ++x) { + if (num >= amt) + break; + if (((uint64)1 << x) & GetInv().GetLookup()->PossessionsBitmask == 0) + continue; + TempItem = nullptr; ins = GetInv().GetItem(x); if (ins) TempItem = ins->GetItem(); if (TempItem && TempItem->ID == type) - { num += ins->GetCharges(); - if (num >= amt) - break; - } } + + for (x = EQEmu::invbag::GENERAL_BAGS_BEGIN; x <= EQEmu::invbag::GENERAL_BAGS_END; ++x) { + if (num >= amt) + break; + if ((((uint64)1 << (EQEmu::invslot::GENERAL_BEGIN + ((x - EQEmu::invbag::GENERAL_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT))) & GetInv().GetLookup()->PossessionsBitmask) == 0) + continue; + + TempItem = nullptr; + ins = GetInv().GetItem(x); + if (ins) + TempItem = ins->GetItem(); + if (TempItem && TempItem->ID == type) + num += ins->GetCharges(); + } + + for (x = EQEmu::invbag::CURSOR_BAG_BEGIN; x <= EQEmu::invbag::CURSOR_BAG_END; ++x) { + if (num >= amt) + break; + + TempItem = nullptr; + ins = GetInv().GetItem(x); + if (ins) + TempItem = ins->GetItem(); + if (TempItem && TempItem->ID == type) + num += ins->GetCharges(); + } + if (num < amt) return false; - for(x = EQEmu::invslot::EQUIPMENT_BEGIN; x <= EQEmu::invbag::GENERAL_BAGS_END; x++) // should this be CURSOR_BAG_END? - { - if (x == EQEmu::invslot::slotCursor + 1) - x = EQEmu::invbag::GENERAL_BAGS_BEGIN; + + + for (x = EQEmu::invslot::POSSESSIONS_BEGIN; x <= EQEmu::invslot::POSSESSIONS_END; ++x) { + if (amt < 1) + break; + if (((uint64)1 << x) & GetInv().GetLookup()->PossessionsBitmask == 0) + continue; + TempItem = nullptr; ins = GetInv().GetItem(x); if (ins) TempItem = ins->GetItem(); - if (TempItem && TempItem->ID == type) - { - if (ins->GetCharges() < amt) - { - amt -= ins->GetCharges(); - DeleteItemInInventory(x,amt,true); - } - else - { - DeleteItemInInventory(x,amt,true); - amt = 0; - } - if (amt < 1) - break; + if (TempItem && TempItem->ID != type) + continue; + + if (ins->GetCharges() < amt) { + amt -= ins->GetCharges(); + DeleteItemInInventory(x, amt, true); + } + else { + DeleteItemInInventory(x, amt, true); + amt = 0; } } + + for (x = EQEmu::invbag::GENERAL_BAGS_BEGIN; x <= EQEmu::invbag::GENERAL_BAGS_END; ++x) { + if (amt < 1) + break; + if ((((uint64)1 << (EQEmu::invslot::GENERAL_BEGIN + ((x - EQEmu::invbag::GENERAL_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT))) & GetInv().GetLookup()->PossessionsBitmask) == 0) + continue; + + TempItem = nullptr; + ins = GetInv().GetItem(x); + if (ins) + TempItem = ins->GetItem(); + if (TempItem && TempItem->ID != type) + continue; + + if (ins->GetCharges() < amt) { + amt -= ins->GetCharges(); + DeleteItemInInventory(x, amt, true); + } + else { + DeleteItemInInventory(x, amt, true); + amt = 0; + } + } + + for (x = EQEmu::invbag::CURSOR_BAG_BEGIN; x <= EQEmu::invbag::CURSOR_BAG_END; ++x) { + if (amt < 1) + break; + + TempItem = nullptr; + ins = GetInv().GetItem(x); + if (ins) + TempItem = ins->GetItem(); + if (TempItem && TempItem->ID != type) + continue; + + if (ins->GetCharges() < amt) { + amt -= ins->GetCharges(); + DeleteItemInInventory(x, amt, true); + } + else { + DeleteItemInInventory(x, amt, true); + amt = 0; + } + } + return true; } @@ -2368,6 +2476,9 @@ static bool CopyBagContents(EQEmu::ItemInstance* new_bag, const EQEmu::ItemInsta void Client::DisenchantSummonedBags(bool client_update) { for (auto slot_id = EQEmu::invslot::GENERAL_BEGIN; slot_id <= EQEmu::invslot::GENERAL_END; ++slot_id) { + if (((uint64)1 << slot_id) & GetInv().GetLookup()->PossessionsBitmask == 0) + continue; // not useable this session - will be disenchanted once player logs in on client that doesn't exclude affected slots + auto inst = m_inv[slot_id]; if (!inst) { continue; } if (!IsSummonedBagID(inst->GetItem()->ID)) { continue; } @@ -2389,6 +2500,9 @@ void Client::DisenchantSummonedBags(bool client_update) } for (auto slot_id = EQEmu::invslot::BANK_BEGIN; slot_id <= EQEmu::invslot::BANK_END; ++slot_id) { + if ((slot_id - EQEmu::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank]) + continue; + auto inst = m_inv[slot_id]; if (!inst) { continue; } if (!IsSummonedBagID(inst->GetItem()->ID)) { continue; } @@ -2479,6 +2593,9 @@ void Client::DisenchantSummonedBags(bool client_update) void Client::RemoveNoRent(bool client_update) { for (auto slot_id = EQEmu::invslot::EQUIPMENT_BEGIN; slot_id <= EQEmu::invslot::EQUIPMENT_END; ++slot_id) { + if (((uint64)1 << slot_id) & GetInv().GetLookup()->PossessionsBitmask == 0) + continue; + auto inst = m_inv[slot_id]; if(inst && !inst->GetItem()->NoRent) { Log(Logs::Detail, Logs::Inventory, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); @@ -2487,6 +2604,9 @@ void Client::RemoveNoRent(bool client_update) } for (auto slot_id = EQEmu::invslot::GENERAL_BEGIN; slot_id <= EQEmu::invslot::GENERAL_END; ++slot_id) { + if (((uint64)1 << slot_id) & GetInv().GetLookup()->PossessionsBitmask == 0) + continue; + auto inst = m_inv[slot_id]; if (inst && !inst->GetItem()->NoRent) { Log(Logs::Detail, Logs::Inventory, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); @@ -2494,15 +2614,11 @@ void Client::RemoveNoRent(bool client_update) } } - if (m_inv[EQEmu::invslot::SLOT_POWER_SOURCE]) { - auto inst = m_inv[EQEmu::invslot::SLOT_POWER_SOURCE]; - if (inst && !inst->GetItem()->NoRent) { - Log(Logs::Detail, Logs::Inventory, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, EQEmu::invslot::SLOT_POWER_SOURCE); - DeleteItemInInventory(EQEmu::invslot::SLOT_POWER_SOURCE, 0, (ClientVersion() >= EQEmu::versions::ClientVersion::SoF) ? client_update : false); // Ti slot non-existent - } - } - for (auto slot_id = EQEmu::invbag::GENERAL_BAGS_BEGIN; slot_id <= EQEmu::invbag::CURSOR_BAG_END; ++slot_id) { + auto temp_slot = EQEmu::invslot::GENERAL_BEGIN + ((slot_id - EQEmu::invbag::GENERAL_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT); + if ((((uint64)1 << temp_slot) & GetInv().GetLookup()->PossessionsBitmask) == 0) + continue; + auto inst = m_inv[slot_id]; if(inst && !inst->GetItem()->NoRent) { Log(Logs::Detail, Logs::Inventory, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); @@ -2511,6 +2627,9 @@ void Client::RemoveNoRent(bool client_update) } for (auto slot_id = EQEmu::invslot::BANK_BEGIN; slot_id <= EQEmu::invslot::BANK_END; ++slot_id) { + if ((slot_id - EQEmu::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank]) + continue; + auto inst = m_inv[slot_id]; if(inst && !inst->GetItem()->NoRent) { Log(Logs::Detail, Logs::Inventory, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); @@ -2519,6 +2638,10 @@ void Client::RemoveNoRent(bool client_update) } for (auto slot_id = EQEmu::invbag::BANK_BAGS_BEGIN; slot_id <= EQEmu::invbag::BANK_BAGS_END; ++slot_id) { + auto temp_slot = (slot_id - EQEmu::invbag::BANK_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT; + if (temp_slot >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank]) + continue; + auto inst = m_inv[slot_id]; if(inst && !inst->GetItem()->NoRent) { Log(Logs::Detail, Logs::Inventory, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); @@ -2573,6 +2696,9 @@ void Client::RemoveNoRent(bool client_update) void Client::RemoveDuplicateLore(bool client_update) { for (auto slot_id = EQEmu::invslot::EQUIPMENT_BEGIN; slot_id <= EQEmu::invslot::EQUIPMENT_END; ++slot_id) { + if (((uint64)1 << slot_id) & GetInv().GetLookup()->PossessionsBitmask == 0) + continue; + auto inst = m_inv.PopItem(slot_id); if (inst == nullptr) { continue; } if(CheckLoreConflict(inst->GetItem())) { @@ -2586,6 +2712,9 @@ void Client::RemoveDuplicateLore(bool client_update) } for (auto slot_id = EQEmu::invslot::GENERAL_BEGIN; slot_id <= EQEmu::invslot::GENERAL_END; ++slot_id) { + if (((uint64)1 << slot_id) & GetInv().GetLookup()->PossessionsBitmask == 0) + continue; + auto inst = m_inv.PopItem(slot_id); if (inst == nullptr) { continue; } if (CheckLoreConflict(inst->GetItem())) { @@ -2598,21 +2727,11 @@ void Client::RemoveDuplicateLore(bool client_update) safe_delete(inst); } - if (m_inv[EQEmu::invslot::SLOT_POWER_SOURCE]) { - auto inst = m_inv.PopItem(EQEmu::invslot::SLOT_POWER_SOURCE); - if (inst) { - if (CheckLoreConflict(inst->GetItem())) { - Log(Logs::Detail, Logs::Inventory, "Lore Duplication Error: Deleting %s from slot %i", inst->GetItem()->Name, EQEmu::invslot::SLOT_POWER_SOURCE); - database.SaveInventory(character_id, nullptr, EQEmu::invslot::SLOT_POWER_SOURCE); - } - else { - m_inv.PutItem(EQEmu::invslot::SLOT_POWER_SOURCE, *inst); - } - safe_delete(inst); - } - } - for (auto slot_id = EQEmu::invbag::GENERAL_BAGS_BEGIN; slot_id <= EQEmu::invbag::CURSOR_BAG_END; ++slot_id) { + auto temp_slot = EQEmu::invslot::GENERAL_BEGIN + ((slot_id - EQEmu::invbag::GENERAL_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT); + if ((((uint64)1 << temp_slot) & GetInv().GetLookup()->PossessionsBitmask) == 0) + continue; + auto inst = m_inv.PopItem(slot_id); if (inst == nullptr) { continue; } if(CheckLoreConflict(inst->GetItem())) { @@ -2626,6 +2745,9 @@ void Client::RemoveDuplicateLore(bool client_update) } for (auto slot_id = EQEmu::invslot::BANK_BEGIN; slot_id <= EQEmu::invslot::BANK_END; ++slot_id) { + if ((slot_id - EQEmu::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank]) + continue; + auto inst = m_inv.PopItem(slot_id); if (inst == nullptr) { continue; } if(CheckLoreConflict(inst->GetItem())) { @@ -2639,6 +2761,10 @@ void Client::RemoveDuplicateLore(bool client_update) } for (auto slot_id = EQEmu::invbag::BANK_BAGS_BEGIN; slot_id <= EQEmu::invbag::BANK_BAGS_END; ++slot_id) { + auto temp_slot = (slot_id - EQEmu::invbag::BANK_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT; + if (temp_slot >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank]) + continue; + auto inst = m_inv.PopItem(slot_id); if (inst == nullptr) { continue; } if(CheckLoreConflict(inst->GetItem())) { @@ -2711,15 +2837,21 @@ void Client::MoveSlotNotAllowed(bool client_update) } } - if (m_inv[EQEmu::invslot::SLOT_POWER_SOURCE] && !m_inv[EQEmu::invslot::SLOT_POWER_SOURCE]->IsSlotAllowed(EQEmu::invslot::SLOT_POWER_SOURCE)) { - auto inst = m_inv.PopItem(EQEmu::invslot::SLOT_POWER_SOURCE); - bool is_arrow = (inst->GetItem()->ItemType == EQEmu::item::ItemTypeArrow) ? true : false; - int16 free_slot_id = m_inv.FindFreeSlot(inst->IsClassBag(), true, inst->GetItem()->Size, is_arrow); - Log(Logs::Detail, Logs::Inventory, "Slot Assignment Error: Moving %s from slot %i to %i", inst->GetItem()->Name, EQEmu::invslot::SLOT_POWER_SOURCE, free_slot_id); - PutItemInInventory(free_slot_id, *inst, (ClientVersion() >= EQEmu::versions::ClientVersion::SoF) ? client_update : false); - database.SaveInventory(character_id, nullptr, EQEmu::invslot::SLOT_POWER_SOURCE); - safe_delete(inst); - } + // added this check to move any client-based excluded slots + //for (auto slot_id = EQEmu::invslot::POSSESSIONS_BEGIN; slot_id <= EQEmu::invslot::POSSESSIONS_END; ++slot_id) { + // if (((uint64)1 << slot_id) & GetInv().GetLookup()->PossessionsBitmask != 0) + // continue; + + // if (m_inv[slot_id]) { // this is currently dangerous for bag-based movements since limbo does not save bag slots + // auto inst = m_inv.PopItem(slot_id); + // bool is_arrow = (inst->GetItem()->ItemType == EQEmu::item::ItemTypeArrow) ? true : false; + // int16 free_slot_id = m_inv.FindFreeSlot(inst->IsClassBag(), true, inst->GetItem()->Size, is_arrow); + // Log(Logs::Detail, Logs::Inventory, "Slot Assignment Error: Moving %s from slot %i to %i", inst->GetItem()->Name, slot_id, free_slot_id); + // PutItemInInventory(free_slot_id, *inst, client_update); + // database.SaveInventory(character_id, nullptr, slot_id); + // safe_delete(inst); + // } + //} // No need to check inventory, cursor, bank or shared bank since they allow max item size and containers // Code can be added to check item size vs. container size, but it is left to attrition for now. @@ -2785,7 +2917,26 @@ void Client::SendItemPacket(int16 slot_id, const EQEmu::ItemInstance* inst, Item if (!inst) return; - // Serialize item into |-delimited string + if (slot_id <= EQEmu::invslot::POSSESSIONS_END && slot_id >= EQEmu::invslot::POSSESSIONS_BEGIN) { + if ((((uint64)1 << slot_id) & GetInv().GetLookup()->PossessionsBitmask) == 0) + return; + } + else if (slot_id <= EQEmu::invbag::GENERAL_BAGS_END && slot_id >= EQEmu::invbag::GENERAL_BAGS_BEGIN) { + auto temp_slot = EQEmu::invslot::GENERAL_BEGIN + ((slot_id - EQEmu::invbag::GENERAL_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT); + if ((((uint64)1 << temp_slot) & GetInv().GetLookup()->PossessionsBitmask) == 0) + return; + } + else if (slot_id <= EQEmu::invslot::BANK_END && slot_id >= EQEmu::invslot::BANK_BEGIN) { + if ((slot_id - EQEmu::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank]) + return; + } + else if (slot_id <= EQEmu::invbag::BANK_BAGS_END && slot_id >= EQEmu::invbag::BANK_BAGS_BEGIN) { + auto temp_slot = (slot_id - EQEmu::invbag::BANK_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT; + if (temp_slot >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank]) + return; + } + + // Serialize item into |-delimited string (Titanium- uses '|' delimiter .. newer clients use pure data serialization) std::string packet = inst->Serialize(slot_id); EmuOpcode opcode = OP_Unknown; @@ -3069,6 +3220,8 @@ bool Client::MoveItemToInventory(EQEmu::ItemInstance *ItemToReturn, bool UpdateC if(ItemToReturn->IsStackable()) { for (int16 i = EQEmu::invslot::GENERAL_BEGIN; i <= EQEmu::invslot::slotCursor; i++) { // changed slot max to 30 from 29. client will stack into slot 30 (bags too) before moving. + if (((uint64)1 << i) & GetInv().GetLookup()->PossessionsBitmask == 0) + continue; EQEmu::ItemInstance* InvItem = m_inv.GetItem(i); @@ -3128,6 +3281,8 @@ bool Client::MoveItemToInventory(EQEmu::ItemInstance *ItemToReturn, bool UpdateC // We have tried stacking items, now just try and find an empty slot. for (int16 i = EQEmu::invslot::GENERAL_BEGIN; i <= EQEmu::invslot::slotCursor; i++) { // changed slot max to 30 from 29. client will move into slot 30 (bags too) before pushing onto cursor. + if (((uint64)1 << i) & GetInv().GetLookup()->PossessionsBitmask == 0) + continue; EQEmu::ItemInstance* InvItem = m_inv.GetItem(i); @@ -3188,7 +3343,7 @@ bool Client::InterrogateInventory(Client* requester, bool log, bool silent, bool std::map instmap; // build reference map - for (int16 index = EQEmu::invslot::EQUIPMENT_BEGIN; index <= EQEmu::invslot::POSSESSIONS_END; ++index) { + for (int16 index = EQEmu::invslot::POSSESSIONS_BEGIN; index <= EQEmu::invslot::POSSESSIONS_END; ++index) { auto inst = m_inv[index]; if (inst == nullptr) { continue; } instmap[index] = inst; @@ -3232,9 +3387,6 @@ bool Client::InterrogateInventory(Client* requester, bool log, bool silent, bool instmap[8000 + limbo] = *cursor_itr; } - if (m_inv[EQEmu::invslot::SLOT_POWER_SOURCE]) - instmap[EQEmu::invslot::SLOT_POWER_SOURCE] = m_inv[EQEmu::invslot::SLOT_POWER_SOURCE]; - // call InterrogateInventory_ for error check for (auto instmap_itr = instmap.begin(); (instmap_itr != instmap.end()) && (!error); ++instmap_itr) { InterrogateInventory_(true, requester, instmap_itr->first, INVALID_INDEX, instmap_itr->second, nullptr, log, silent, error, 0); @@ -3339,8 +3491,7 @@ bool Client::InterrogateInventory_error(int16 head, int16 index, const EQEmu::It (head >= EQEmu::invslot::EQUIPMENT_BEGIN && head <= EQEmu::invslot::EQUIPMENT_END) || (head >= EQEmu::invslot::TRIBUTE_BEGIN && head <= EQEmu::invslot::TRIBUTE_END) || (head >= EQEmu::invslot::WORLD_BEGIN && head <= EQEmu::invslot::WORLD_END) || - (head >= 8000 && head <= 8101) || - (head == EQEmu::invslot::SLOT_POWER_SOURCE)) { + (head >= 8000 && head <= 8101)) { switch (depth) { case 0: // requirement: inst is extant diff --git a/zone/loottables.cpp b/zone/loottables.cpp index 70a7e2bc4..e7c9624f1 100644 --- a/zone/loottables.cpp +++ b/zone/loottables.cpp @@ -323,7 +323,7 @@ void NPC::AddLootDrop(const EQEmu::ItemData *item2, ItemList* itemlist, int16 ch // @merth: IDFile size has been increased, this needs to change uint16 emat; if(item2->Material <= 0 - || item2->Slots & (1 << EQEmu::invslot::slotPrimary | 1 << EQEmu::invslot::slotSecondary)) { + || (item2->Slots & ((1 << EQEmu::invslot::slotPrimary) | (1 << EQEmu::invslot::slotSecondary)))) { memset(newid, 0, sizeof(newid)); for(int i=0;i<7;i++){ if (!isalpha(item2->IDFile[i])){ diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index f65e6f2ad..5979a3959 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -1870,27 +1870,22 @@ luabind::scope lua_register_slot() { luabind::value("Face", static_cast(EQEmu::invslot::slotFace)), luabind::value("Ear2", static_cast(EQEmu::invslot::slotEar2)), luabind::value("Neck", static_cast(EQEmu::invslot::slotNeck)), - luabind::value("Shoulder", static_cast(EQEmu::invslot::slotShoulders)), // deprecated luabind::value("Shoulders", static_cast(EQEmu::invslot::slotShoulders)), luabind::value("Arms", static_cast(EQEmu::invslot::slotArms)), luabind::value("Back", static_cast(EQEmu::invslot::slotBack)), - luabind::value("Bracer1", static_cast(EQEmu::invslot::slotWrist1)), // deprecated luabind::value("Wrist1", static_cast(EQEmu::invslot::slotWrist1)), - luabind::value("Bracer2", static_cast(EQEmu::invslot::slotWrist2)), // deprecated luabind::value("Wrist2", static_cast(EQEmu::invslot::slotWrist2)), luabind::value("Range", static_cast(EQEmu::invslot::slotRange)), luabind::value("Hands", static_cast(EQEmu::invslot::slotHands)), luabind::value("Primary", static_cast(EQEmu::invslot::slotPrimary)), luabind::value("Secondary", static_cast(EQEmu::invslot::slotSecondary)), - luabind::value("Ring1", static_cast(EQEmu::invslot::slotFinger1)), // deprecated luabind::value("Finger1", static_cast(EQEmu::invslot::slotFinger1)), - luabind::value("Ring2", static_cast(EQEmu::invslot::slotFinger2)), // deprecated luabind::value("Finger2", static_cast(EQEmu::invslot::slotFinger2)), luabind::value("Chest", static_cast(EQEmu::invslot::slotChest)), luabind::value("Legs", static_cast(EQEmu::invslot::slotLegs)), luabind::value("Feet", static_cast(EQEmu::invslot::slotFeet)), luabind::value("Waist", static_cast(EQEmu::invslot::slotWaist)), - luabind::value("PowerSource", static_cast(EQEmu::invslot::SLOT_POWER_SOURCE)), + luabind::value("PowerSource", static_cast(EQEmu::invslot::slotPowerSource)), luabind::value("Ammo", static_cast(EQEmu::invslot::slotAmmo)), luabind::value("General1", static_cast(EQEmu::invslot::slotGeneral1)), luabind::value("General2", static_cast(EQEmu::invslot::slotGeneral2)), @@ -1900,15 +1895,43 @@ luabind::scope lua_register_slot() { luabind::value("General6", static_cast(EQEmu::invslot::slotGeneral6)), luabind::value("General7", static_cast(EQEmu::invslot::slotGeneral7)), luabind::value("General8", static_cast(EQEmu::invslot::slotGeneral8)), + luabind::value("General9", static_cast(EQEmu::invslot::slotGeneral9)), + luabind::value("General10", static_cast(EQEmu::invslot::slotGeneral10)), luabind::value("Cursor", static_cast(EQEmu::invslot::slotCursor)), - luabind::value("PersonalBegin", static_cast(EQEmu::invslot::GENERAL_BEGIN)), // deprecated + luabind::value("PossessionsBegin", static_cast(EQEmu::invslot::POSSESSIONS_BEGIN)), + luabind::value("PossessionsEnd", static_cast(EQEmu::invslot::POSSESSIONS_END)), + luabind::value("EquipmentBegin", static_cast(EQEmu::invslot::EQUIPMENT_BEGIN)), + luabind::value("EquipmentEnd", static_cast(EQEmu::invslot::EQUIPMENT_END)), luabind::value("GeneralBegin", static_cast(EQEmu::invslot::GENERAL_BEGIN)), - luabind::value("PersonalEnd", static_cast(EQEmu::invslot::GENERAL_END)), // deprecated luabind::value("GeneralEnd", static_cast(EQEmu::invslot::GENERAL_END)), - luabind::value("CursorEnd", 0xFFFE), // deprecated - luabind::value("Tradeskill", static_cast(EQEmu::legacy::SLOT_TRADESKILL)), // deprecated - luabind::value("Augment", static_cast(EQEmu::legacy::SLOT_AUGMENT)), // deprecated - luabind::value("Invalid", INVALID_INDEX) + luabind::value("GeneralBagsBegin", static_cast(EQEmu::invbag::GENERAL_BAGS_BEGIN)), + luabind::value("GeneralBagsEnd", static_cast(EQEmu::invbag::GENERAL_BAGS_END)), + luabind::value("CursorBagBegin", static_cast(EQEmu::invbag::CURSOR_BAG_BEGIN)), + luabind::value("CursorBagEnd", static_cast(EQEmu::invbag::CURSOR_BAG_END)), + luabind::value("Tradeskill", static_cast(EQEmu::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE)), + luabind::value("Augment", static_cast(EQEmu::invslot::SLOT_AUGMENT_GENERIC_RETURN)), // will be revised out + luabind::value("BankBegin", static_cast(EQEmu::invslot::BANK_BEGIN)), + luabind::value("BankEnd", static_cast(EQEmu::invslot::BANK_END)), + luabind::value("BankBagsBegin", static_cast(EQEmu::invbag::BANK_BAGS_BEGIN)), + luabind::value("BankBagsEnd", static_cast(EQEmu::invbag::BANK_BAGS_END)), + luabind::value("SharedBankBegin", static_cast(EQEmu::invslot::SHARED_BANK_BEGIN)), + luabind::value("SharedBankEnd", static_cast(EQEmu::invslot::SHARED_BANK_END)), + luabind::value("SharedBankBagsBegin", static_cast(EQEmu::invbag::SHARED_BANK_BAGS_BEGIN)), + luabind::value("SharedBankBagsEnd", static_cast(EQEmu::invbag::SHARED_BANK_BAGS_END)), + luabind::value("BagSlotBegin", static_cast(EQEmu::invbag::SLOT_BEGIN)), + luabind::value("BagSlotEnd", static_cast(EQEmu::invbag::SLOT_END)), + luabind::value("AugSocketBegin", static_cast(EQEmu::invaug::SOCKET_BEGIN)), + luabind::value("AugSocketEnd", static_cast(EQEmu::invaug::SOCKET_END)), + luabind::value("Invalid", static_cast(EQEmu::invslot::SLOT_INVALID)), + + luabind::value("Shoulder", static_cast(EQEmu::invslot::slotShoulders)), // deprecated + luabind::value("Bracer1", static_cast(EQEmu::invslot::slotWrist1)), // deprecated + luabind::value("Bracer2", static_cast(EQEmu::invslot::slotWrist2)), // deprecated + luabind::value("Ring1", static_cast(EQEmu::invslot::slotFinger1)), // deprecated + luabind::value("Ring2", static_cast(EQEmu::invslot::slotFinger2)), // deprecated + luabind::value("PersonalBegin", static_cast(EQEmu::invslot::GENERAL_BEGIN)), // deprecated + luabind::value("PersonalEnd", static_cast(EQEmu::invslot::GENERAL_END)), // deprecated + luabind::value("CursorEnd", 0xFFFE) // deprecated (not in use..and never valid vis-a-vis client behavior) ]; } @@ -1919,16 +1942,17 @@ luabind::scope lua_register_material() { luabind::value("Head", static_cast(EQEmu::textures::armorHead)), luabind::value("Chest", static_cast(EQEmu::textures::armorChest)), luabind::value("Arms", static_cast(EQEmu::textures::armorArms)), - luabind::value("Bracer", static_cast(EQEmu::textures::armorWrist)), // deprecated luabind::value("Wrist", static_cast(EQEmu::textures::armorWrist)), luabind::value("Hands", static_cast(EQEmu::textures::armorHands)), luabind::value("Legs", static_cast(EQEmu::textures::armorLegs)), luabind::value("Feet", static_cast(EQEmu::textures::armorFeet)), luabind::value("Primary", static_cast(EQEmu::textures::weaponPrimary)), luabind::value("Secondary", static_cast(EQEmu::textures::weaponSecondary)), - luabind::value("Max", static_cast(EQEmu::textures::materialCount)), // deprecated luabind::value("Count", static_cast(EQEmu::textures::materialCount)), - luabind::value("Invalid", static_cast(EQEmu::textures::materialInvalid)) + luabind::value("Invalid", static_cast(EQEmu::textures::materialInvalid)), + + luabind::value("Bracer", static_cast(EQEmu::textures::armorWrist)), // deprecated + luabind::value("Max", static_cast(EQEmu::textures::materialCount)) // deprecated ]; } diff --git a/zone/lua_inventory.cpp b/zone/lua_inventory.cpp index d29bb435b..dd4e1cc6b 100644 --- a/zone/lua_inventory.cpp +++ b/zone/lua_inventory.cpp @@ -38,10 +38,10 @@ int Lua_Inventory::PushCursor(Lua_ItemInst item) { return self->PushCursor(*inst); } -bool Lua_Inventory::SwapItem(int slot_a, int slot_b) { +bool Lua_Inventory::SwapItem(int source_slot, int destination_slot) { Lua_Safe_Call_Bool(); EQEmu::InventoryProfile::SwapItemFailState fail_state = EQEmu::InventoryProfile::swapInvalid; - return self->SwapItem(slot_a, slot_b, fail_state); + return self->SwapItem(source_slot, destination_slot, fail_state); } bool Lua_Inventory::DeleteItem(int slot_id) { diff --git a/zone/lua_inventory.h b/zone/lua_inventory.h index 1e391b4da..272e142de 100644 --- a/zone/lua_inventory.h +++ b/zone/lua_inventory.h @@ -39,7 +39,7 @@ public: Lua_ItemInst GetItem(int slot_id, int bag_slot); int PutItem(int slot_id, Lua_ItemInst item); int PushCursor(Lua_ItemInst item); - bool SwapItem(int slot_a, int slot_b); + bool SwapItem(int source_slot, int destination_slot); bool DeleteItem(int slot_id); bool DeleteItem(int slot_id, int quantity); bool CheckNoDrop(int slot_id); diff --git a/zone/merc.cpp b/zone/merc.cpp index b03042f5a..c7bd12765 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -217,22 +217,16 @@ void Merc::CalcItemBonuses(StatBonuses* newbon) { unsigned int i; //should not include 21 (SLOT_AMMO) - for (i = 0; i < EQEmu::invslot::slotAmmo; i++) { - if(equipment[i] == 0) + for (i = EQEmu::invslot::BONUS_BEGIN; i <= EQEmu::invslot::BONUS_STAT_END; i++) { + if (i == EQEmu::invslot::slotAmmo) + continue; + if (equipment[i] == 0) continue; const EQEmu::ItemData * itm = database.GetItem(equipment[i]); - if(itm) + if (itm) AddItemBonuses(itm, newbon); } - //Power Source Slot - /*if (GetClientVersion() >= EQClientSoF) - { - const EQEmu::ItemInstance* inst = m_inv[MainPowerSource]; - if(inst) - AddItemBonuses(inst, newbon); - }*/ - // Caps if(newbon->HPRegen > CalcHPRegenCap()) newbon->HPRegen = CalcHPRegenCap(); diff --git a/zone/mob.cpp b/zone/mob.cpp index 99ef689fe..71f805d86 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -249,7 +249,7 @@ Mob::Mob(const char* in_name, max_mana = 0; hp_regen = in_hp_regen; mana_regen = in_mana_regen; - oocregen = RuleI(NPC, OOCRegen); //default Out of Combat Regen + ooc_regen = RuleI(NPC, OOCRegen); //default Out of Combat Regen maxlevel = in_maxlevel; scalerate = in_scalerate; invisible = false; @@ -1297,14 +1297,17 @@ void Mob::CreateHPPacket(EQApplicationPacket* app) } } -// sends hp update of this mob to people who might care -void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= false*/) -{ - - /* If our HP is different from last HP update call - let's update ourself */ +void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= false*/) { + + /** + * If our HP is different from last HP update call - let's update selves + */ if (IsClient()) { if (cur_hp != last_hp || force_update_all) { - /* This is to prevent excessive packet sending under trains/fast combat */ + + /** + * This is to prevent excessive packet sending under trains/fast combat + */ if (this->CastToClient()->hp_self_update_throttle_timer.Check() || force_update_all) { Log(Logs::General, Logs::HP_Update, "Mob::SendHPUpdate :: Update HP of self (%s) HP: %i last: %i skip_self: %s", @@ -1316,11 +1319,12 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal if (!skip_self || this->CastToClient()->ClientVersion() >= EQEmu::versions::ClientVersion::SoD) { auto client_packet = new EQApplicationPacket(OP_HPUpdate, sizeof(SpawnHPUpdate_Struct)); - SpawnHPUpdate_Struct* hp_packet_client = (SpawnHPUpdate_Struct*)client_packet->pBuffer; - hp_packet_client->cur_hp = CastToClient()->GetHP() - itembonuses.HP; + SpawnHPUpdate_Struct *hp_packet_client = (SpawnHPUpdate_Struct *) client_packet->pBuffer; + + hp_packet_client->cur_hp = CastToClient()->GetHP() - itembonuses.HP; hp_packet_client->spawn_id = GetID(); - hp_packet_client->max_hp = CastToClient()->GetMaxHP() - itembonuses.HP; + hp_packet_client->max_hp = CastToClient()->GetMaxHP() - itembonuses.HP; CastToClient()->QueuePacket(client_packet); @@ -1329,7 +1333,9 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal ResetHPUpdateTimer(); } - /* Used to check if HP has changed to update self next round */ + /** + * Used to check if HP has changed to update self next round + */ last_hp = cur_hp; } } @@ -1337,7 +1343,12 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal int8 current_hp_percent = (max_hp == 0 ? 0 : static_cast(cur_hp * 100 / max_hp)); - Log(Logs::General, Logs::HP_Update, "Mob::SendHPUpdate :: SendHPUpdate %s HP is %i last %i", this->GetCleanName(), current_hp_percent, last_hp_percent); + Log(Logs::General, + Logs::HP_Update, + "Mob::SendHPUpdate :: SendHPUpdate %s HP is %i last %i", + this->GetCleanName(), + current_hp_percent, + last_hp_percent); if (current_hp_percent == last_hp_percent && !force_update_all) { Log(Logs::General, Logs::HP_Update, "Mob::SendHPUpdate :: Same HP - skipping update"); @@ -1346,8 +1357,9 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal } else { - if (IsClient() && RuleB(Character, MarqueeHPUpdates)) + if (IsClient() && RuleB(Character, MarqueeHPUpdates)) { this->CastToClient()->SendHPUpdateMarquee(); + } Log(Logs::General, Logs::HP_Update, "Mob::SendHPUpdate :: HP Changed - Send update"); @@ -1355,52 +1367,69 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal } EQApplicationPacket hp_packet; - Group *group = nullptr; + Group *group = nullptr; CreateHPPacket(&hp_packet); - /* Update those who have us targeted */ + /** + * Update those who have us targeted + */ entity_list.QueueClientsByTarget(this, &hp_packet, false, 0, false, true, EQEmu::versions::bit_AllClients); - /* Update those who have us on x-target */ + /** + * Update those who have us on x-target + */ entity_list.QueueClientsByXTarget(this, &hp_packet, false); - /* Update groups using Group LAA health name tag counter */ + /** + * Update groups using Group LAA health name tag counter + */ entity_list.QueueToGroupsForNPCHealthAA(this, &hp_packet); - /* Update group */ - if(IsGrouped()) { + /** + * Group + */ + if (IsGrouped()) { group = entity_list.GetGroupByMob(this); - if(group) + if (group) { group->SendHPPacketsFrom(this); + } } - /* Update Raid */ - if(IsClient()){ + /** + * Raid + */ + if (IsClient()) { Raid *raid = entity_list.GetRaidByClient(CastToClient()); - if (raid) + if (raid) { raid->SendHPManaEndPacketsFrom(this); + } } - /* Pet - Update master - group and raid if exists */ - if(GetOwner() && GetOwner()->IsClient()) { + /** + * Pet + */ + if (GetOwner() && GetOwner()->IsClient()) { GetOwner()->CastToClient()->QueuePacket(&hp_packet, false); group = entity_list.GetGroupByClient(GetOwner()->CastToClient()); - if(group) + if (group) { group->SendHPPacketsFrom(this); + } Raid *raid = entity_list.GetRaidByClient(GetOwner()->CastToClient()); - if(raid) + if (raid) { raid->SendHPManaEndPacketsFrom(this); + } } - /* Send to pet */ - if(GetPet() && GetPet()->IsClient()) { + if (GetPet() && GetPet()->IsClient()) { GetPet()->CastToClient()->QueuePacket(&hp_packet, false); } - /* Destructible objects */ + /** + * Destructible objects + */ if (IsNPC() && IsDestructibleObject()) { if (GetHPRatio() > 74) { if (GetAppearance() != eaStanding) { diff --git a/zone/mob.h b/zone/mob.h index f6e110353..cd8cc4185 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -420,7 +420,7 @@ public: bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None) = 0; inline virtual void SetHP(int32 hp) { if (hp >= max_hp) cur_hp = max_hp; else cur_hp = hp;} bool ChangeHP(Mob* other, int32 amount, uint16 spell_id = 0, int8 buffslot = -1, bool iBuffTic = false); - inline void SetOOCRegen(int32 newoocregen) {oocregen = newoocregen;} + inline void SetOOCRegen(int32 newoocregen) {ooc_regen = newoocregen;} virtual void Heal(); virtual void HealDamage(uint32 ammount, Mob* caster = nullptr, uint16 spell_id = SPELL_UNKNOWN); virtual void SetMaxHP() { cur_hp = max_hp; } @@ -1216,7 +1216,7 @@ protected: int32 max_mana; int32 hp_regen; int32 mana_regen; - int32 oocregen; + int32 ooc_regen; uint8 maxlevel; uint32 scalerate; Buffs_Struct *buffs; diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 12360f4e7..19c418f10 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -97,10 +97,11 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes, bool bInnates continue; } - if (AIspells[i].min_hp != 0 && GetIntHPRatio() < AIspells[i].min_hp) + // we reuse these fields for heal overrides + if (AIspells[i].type != SpellType_Heal && AIspells[i].min_hp != 0 && GetIntHPRatio() < AIspells[i].min_hp) continue; - if (AIspells[i].max_hp != 0 && GetIntHPRatio() > AIspells[i].max_hp) + if (AIspells[i].type != SpellType_Heal && AIspells[i].max_hp != 0 && GetIntHPRatio() > AIspells[i].max_hp) continue; if (iSpellTypes & AIspells[i].type) { @@ -137,9 +138,13 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes, bool bInnates && tar->DontHealMeBefore() < Timer::GetCurrentTime() && !(tar->IsPet() && tar->GetOwner()->IsClient()) //no buffing PC's pets ) { - uint8 hpr = (uint8)tar->GetHPRatio(); - if(hpr <= 35 || (!IsEngaged() && hpr <= 50) || (tar->IsClient() && hpr <= 99)) { + auto hp_ratio = tar->GetIntHPRatio(); + + int min_hp = AIspells[i].min_hp; // well 0 is default, so no special case here + int max_hp = AIspells[i].max_hp ? AIspells[i].max_hp : RuleI(Spells, AI_HealHPPct); + + if (EQEmu::ValueWithin(hp_ratio, min_hp, max_hp) || (tar->IsClient() && hp_ratio <= 99)) { // not sure about client bit, leaving it uint32 tempTime = 0; AIDoSpellCast(i, tar, mana_cost, &tempTime); tar->SetDontHealMeBefore(tempTime); diff --git a/zone/npc.cpp b/zone/npc.cpp index a0e510a43..05db28e1d 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -667,77 +667,78 @@ bool NPC::Process() parse->EventNPC(EVENT_TICK, this, nullptr, "", 0); BuffProcess(); - if (currently_fleeing) + if (currently_fleeing) { ProcessFlee(); - - uint32 sitting_bonus = 0; - uint32 petbonus = 0; - uint32 bestregen = 0; - int32 dbregen = GetNPCHPRegen(); - - if (GetAppearance() == eaSitting) - sitting_bonus += 3; - - int32 OOCRegen = 0; - if (oocregen > 0) { //should pull from Mob class - OOCRegen += GetMaxHP() * oocregen / 100; } - // Fixing NPC regen.NPCs should regen to full during - // a set duration, not based on their HPs.Increase NPC's HPs by - // % of total HPs / tick. - // - // If oocregen set in db, apply to pets as well. - // This allows the obscene #s for pets in the db to be tweaked - // while maintaining a decent ooc regen. + uint32 npc_sitting_regen_bonus = 0; + uint32 pet_regen_bonus = 0; + uint32 npc_regen = 0; + int32 npc_hp_regen = GetNPCHPRegen(); - bestregen = std::max(dbregen,OOCRegen); + if (GetAppearance() == eaSitting) { + npc_sitting_regen_bonus += 3; + } + + int32 ooc_regen_calc = 0; + if (ooc_regen > 0) { //should pull from Mob class + ooc_regen_calc += GetMaxHP() * ooc_regen / 100; + } + + /** + * Use max value between two values + */ + npc_regen = std::max(npc_hp_regen, ooc_regen_calc); if ((GetHP() < GetMaxHP()) && !IsPet()) { - if (!IsEngaged()) - SetHP(GetHP() + bestregen + sitting_bonus); - else - SetHP(GetHP() + dbregen); + if (!IsEngaged()) { + SetHP(GetHP() + npc_regen + npc_sitting_regen_bonus); + } + else { + SetHP(GetHP() + npc_hp_regen); + } } else if (GetHP() < GetMaxHP() && GetOwnerID() != 0) { if (!IsEngaged()) { - if (oocregen > 0) { - petbonus = std::max(OOCRegen,dbregen); + if (ooc_regen > 0) { + pet_regen_bonus = std::max(ooc_regen_calc, npc_hp_regen); } else { - petbonus = dbregen + (GetLevel() / 5); + pet_regen_bonus = npc_hp_regen + (GetLevel() / 5); } - SetHP(GetHP() + sitting_bonus + petbonus); + SetHP(GetHP() + npc_sitting_regen_bonus + pet_regen_bonus); } - else - SetHP(GetHP() + dbregen); + else { + SetHP(GetHP() + npc_hp_regen); + } + + } + else { + SetHP(GetHP() + npc_hp_regen + npc_sitting_regen_bonus); } - else - SetHP(GetHP() + dbregen + sitting_bonus); if (GetMana() < GetMaxMana()) { - SetMana(GetMana() + mana_regen + sitting_bonus); + SetMana(GetMana() + mana_regen + npc_sitting_regen_bonus); } + SendHPUpdate(); - if (zone->adv_data && !p_depop) - { - ServerZoneAdventureDataReply_Struct* ds = (ServerZoneAdventureDataReply_Struct*)zone->adv_data; - if (ds->type == Adventure_Rescue && ds->data_id == GetNPCTypeID()) - { + if (zone->adv_data && !p_depop) { + ServerZoneAdventureDataReply_Struct *ds = (ServerZoneAdventureDataReply_Struct *) zone->adv_data; + if (ds->type == Adventure_Rescue && ds->data_id == GetNPCTypeID()) { Mob *o = GetOwner(); - if (o && o->IsClient()) - { + if (o && o->IsClient()) { float x_diff = ds->dest_x - GetX(); float y_diff = ds->dest_y - GetY(); float z_diff = ds->dest_z - GetZ(); - float dist = ((x_diff * x_diff) + (y_diff * y_diff) + (z_diff * z_diff)); - if (dist < RuleR(Adventure, DistanceForRescueComplete)) - { + float dist = ((x_diff * x_diff) + (y_diff * y_diff) + (z_diff * z_diff)); + if (dist < RuleR(Adventure, DistanceForRescueComplete)) { zone->DoAdventureCountIncrease(); - Say("You don't know what this means to me. Thank you so much for finding and saving me from" - " this wretched place. I'll find my way from here."); + Say( + "You don't know what this means to me. Thank you so much for finding and saving me from" + " this wretched place. I'll find my way from here." + ); Depop(); } } diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 47d5dd89a..74c95f5d2 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -3026,7 +3026,7 @@ XS(XS_Client_SummonItem) { dXSARGS; if (items < 2 || items > 10) Perl_croak(aTHX_ - "Usage: Client::SummonItem(THIS, uint32 item_id, [int16 charges = -1], [bool attune = false], [uint32 aug1 = 0], [uint32 aug2 = 0], [uint32 aug3 = 0], [uint32 aug4 = 0], [uint32 aug5 = 0], [uint16 slot_id = 30])"); + "Usage: Client::SummonItem(THIS, uint32 item_id, [int16 charges = -1], [bool attune = false], [uint32 aug1 = 0], [uint32 aug2 = 0], [uint32 aug3 = 0], [uint32 aug4 = 0], [uint32 aug5 = 0], [uint16 slot_id = cursor])"); { Client *THIS; uint32 item_id = (uint32) SvUV(ST(1)); @@ -3037,7 +3037,7 @@ XS(XS_Client_SummonItem) { uint32 aug3 = 0; uint32 aug4 = 0; uint32 aug5 = 0; - uint16 slot_id = 30; + uint16 slot_id = EQEmu::invslot::slotCursor; if (sv_derived_from(ST(0), "Client")) { IV tmp = SvIV((SV *) SvRV(ST(0))); diff --git a/zone/pets.cpp b/zone/pets.cpp index a436b0097..bf2f0ad3a 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -527,7 +527,7 @@ void NPC::GetPetState(SpellBuff_Struct *pet_buffs, uint32 *items, char *name) { memcpy(items, equipment, sizeof(uint32) * EQEmu::invslot::EQUIPMENT_COUNT); //save their buffs. - for (int i=0; i < GetPetMaxTotalSlots(); i++) { + for (int i=EQEmu::invslot::EQUIPMENT_BEGIN; i < GetPetMaxTotalSlots(); i++) { if (buffs[i].spellid != SPELL_UNKNOWN) { pet_buffs[i].spellid = buffs[i].spellid; pet_buffs[i].effect_type = i+1; diff --git a/zone/spells.cpp b/zone/spells.cpp index 0b705717d..d4c42d624 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -281,7 +281,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, return(false); } } - if (itm && (itm->GetItem()->Click.Type == EQEmu::item::ItemEffectEquipClick) && !(item_slot <= EQEmu::invslot::slotAmmo || item_slot == EQEmu::invslot::SLOT_POWER_SOURCE)){ + if (itm && (itm->GetItem()->Click.Type == EQEmu::item::ItemEffectEquipClick) && item_slot > EQEmu::invslot::EQUIPMENT_END){ if (CastToClient()->ClientVersion() < EQEmu::versions::ClientVersion::SoF) { // They are attempting to cast a must equip clicky without having it equipped Log(Logs::General, Logs::Error, "HACKER: %s (account: %s) attempted to click an equip-only effect on item %s (id: %d) without equiping it!", CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID); diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index a6e0dbd34..72cb51795 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -264,7 +264,7 @@ void Object::HandleCombine(Client* user, const NewCombine_Struct* in_combine, Ob uint32 some_id = 0; bool worldcontainer=false; - if (in_combine->container_slot == EQEmu::legacy::SLOT_TRADESKILL) { + if (in_combine->container_slot == EQEmu::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE) { if(!worldo) { user->Message(13, "Error: Server is not aware of the tradeskill container you are attempting to use"); return; diff --git a/zone/trading.cpp b/zone/trading.cpp index d3456fbf2..8c07db9fc 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -1202,7 +1202,7 @@ void Client::SendSingleTraderItem(uint32 CharID, int SerialNumber) { EQEmu::ItemInstance* inst= database.LoadSingleTraderItem(CharID, SerialNumber); if(inst) { - SendItemPacket(30, inst, ItemPacketMerchant); // MainCursor? + SendItemPacket(EQEmu::invslot::slotCursor, inst, ItemPacketMerchant); // MainCursor? safe_delete(inst); } @@ -1233,7 +1233,7 @@ void Client::BulkSendTraderInventory(uint32 char_id) { } inst->SetPrice(TraderItems->ItemCost[i]); - SendItemPacket(30, inst, ItemPacketMerchant); // MainCursor? + SendItemPacket(EQEmu::invslot::slotCursor, inst, ItemPacketMerchant); // MainCursor? safe_delete(inst); } else @@ -2072,7 +2072,7 @@ static void UpdateTraderCustomerItemsAdded(uint32 CustomerID, TraderCharges_Stru Log(Logs::Detail, Logs::Trading, "Sending price update for %s, Serial No. %i with %i charges", item->Name, gis->SerialNumber[i], gis->Charges[i]); - Customer->SendItemPacket(30, inst, ItemPacketMerchant); // MainCursor? + Customer->SendItemPacket(EQEmu::invslot::slotCursor, inst, ItemPacketMerchant); // MainCursor? } } @@ -2156,7 +2156,7 @@ static void UpdateTraderCustomerPriceChanged(uint32 CustomerID, TraderCharges_St Log(Logs::Detail, Logs::Trading, "Sending price update for %s, Serial No. %i with %i charges", item->Name, gis->SerialNumber[i], gis->Charges[i]); - Customer->SendItemPacket(30, inst, ItemPacketMerchant); // MainCursor?? + Customer->SendItemPacket(EQEmu::invslot::slotCursor, inst, ItemPacketMerchant); // MainCursor?? } safe_delete(inst); } diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index cced38aac..027225243 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1180,7 +1180,7 @@ bool ZoneDatabase::LoadCharacterData(uint32 character_id, PlayerProfile_Struct* m_epp->aa_effects = atoi(row[r]); r++; // "`e_aa_effects`, " m_epp->perAA = atoi(row[r]); r++; // "`e_percent_to_aa`, " m_epp->expended_aa = atoi(row[r]); r++; // "`e_expended_aa_spent`, " - m_epp->last_invsnapshot_time = atoi(row[r]); r++; // "`e_last_invsnapshot` " + m_epp->last_invsnapshot_time = atoul(row[r]); r++; // "`e_last_invsnapshot` " m_epp->next_invsnapshot_time = m_epp->last_invsnapshot_time + (RuleI(Character, InvSnapshotMinIntervalM) * 60); } return true; @@ -1576,56 +1576,6 @@ bool ZoneDatabase::SaveCharacterLeadershipAA(uint32 character_id, PlayerProfile_ return true; } -bool ZoneDatabase::SaveCharacterInventorySnapshot(uint32 character_id){ - uint32 time_index = time(nullptr); - std::string query = StringFormat( - "INSERT INTO inventory_snapshots (" - " time_index," - " charid," - " slotid," - " itemid," - " charges," - " color," - " augslot1," - " augslot2," - " augslot3," - " augslot4," - " augslot5," - " augslot6," - " instnodrop," - " custom_data," - " ornamenticon," - " ornamentidfile," - " ornament_hero_model" - ")" - " SELECT" - " %u," - " charid," - " slotid," - " itemid," - " charges," - " color," - " augslot1," - " augslot2," - " augslot3," - " augslot4," - " augslot5," - " augslot6," - " instnodrop," - " custom_data," - " ornamenticon," - " ornamentidfile," - " ornament_hero_model" - " FROM inventory" - " WHERE charid = %u", - time_index, - character_id - ); - auto results = database.QueryDatabase(query); - Log(Logs::General, Logs::None, "ZoneDatabase::SaveCharacterInventorySnapshot %i (%s)", character_id, (results.Success() ? "pass" : "fail")); - return results.Success(); -} - bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp){ /* If this is ever zero - the client hasn't fully loaded and potentially crashed during zone */ @@ -2052,6 +2002,344 @@ bool ZoneDatabase::NoRentExpired(const char* name){ return (seconds>1800); } +bool ZoneDatabase::SaveCharacterInvSnapshot(uint32 character_id) { + uint32 time_index = time(nullptr); + std::string query = StringFormat( + "INSERT " + "INTO" + " `inventory_snapshots` " + "(`time_index`," + " `charid`," + " `slotid`," + " `itemid`," + " `charges`," + " `color`," + " `augslot1`," + " `augslot2`," + " `augslot3`," + " `augslot4`," + " `augslot5`," + " `augslot6`," + " `instnodrop`," + " `custom_data`," + " `ornamenticon`," + " `ornamentidfile`," + " `ornament_hero_model`" + ") " + "SELECT" + " %u," + " `charid`," + " `slotid`," + " `itemid`," + " `charges`," + " `color`," + " `augslot1`," + " `augslot2`," + " `augslot3`," + " `augslot4`," + " `augslot5`," + " `augslot6`," + " `instnodrop`," + " `custom_data`," + " `ornamenticon`," + " `ornamentidfile`," + " `ornament_hero_model` " + "FROM" + " `inventory` " + "WHERE" + " `charid` = %u", + time_index, + character_id + ); + auto results = database.QueryDatabase(query); + Log(Logs::Moderate, Logs::Inventory, "ZoneDatabase::SaveCharacterInventorySnapshot %i (%s)", character_id, (results.Success() ? "pass" : "fail")); + return results.Success(); +} + +int ZoneDatabase::CountCharacterInvSnapshots(uint32 character_id) { + std::string query = StringFormat( + "SELECT" + " COUNT(*) " + "FROM " + "(" + "SELECT * FROM" + " `inventory_snapshots` a " + "WHERE" + " `charid` = %u " + "GROUP BY" + " `time_index`" + ") b", + character_id + ); + auto results = QueryDatabase(query); + + if (!results.Success()) + return -1; + + auto row = results.begin(); + + int64 count = atoll(row[0]); + if (count > 2147483647) + return -2; + if (count < 0) + return -3; + + return count; +} + +void ZoneDatabase::ClearCharacterInvSnapshots(uint32 character_id, bool from_now) { + uint32 del_time = time(nullptr); + if (!from_now) { del_time -= RuleI(Character, InvSnapshotHistoryD) * 86400; } + + std::string query = StringFormat( + "DELETE " + "FROM" + " `inventory_snapshots` " + "WHERE" + " `charid` = %u " + "AND" + " `time_index` <= %lu", + character_id, + (unsigned long)del_time + ); + QueryDatabase(query); +} + +void ZoneDatabase::ListCharacterInvSnapshots(uint32 character_id, std::list> &is_list) { + std::string query = StringFormat( + "SELECT" + " `time_index`," + " COUNT(*) " + "FROM" + " `inventory_snapshots` " + "WHERE" + " `charid` = %u " + "GROUP BY" + " `time_index` " + "ORDER BY" + " `time_index` " + "DESC", + character_id + ); + auto results = QueryDatabase(query); + + if (!results.Success()) + return; + + for (auto row : results) + is_list.push_back(std::pair(atoul(row[0]), atoi(row[1]))); +} + +bool ZoneDatabase::ValidateCharacterInvSnapshotTimestamp(uint32 character_id, uint32 timestamp) { + if (!character_id || !timestamp) + return false; + + std::string query = StringFormat( + "SELECT" + " * " + "FROM" + " `inventory_snapshots` " + "WHERE" + " `charid` = %u " + "AND" + " `time_index` = %u " + "LIMIT 1", + character_id, + timestamp + ); + auto results = QueryDatabase(query); + + if (!results.Success() || results.RowCount() == 0) + return false; + + return true; +} + +void ZoneDatabase::ParseCharacterInvSnapshot(uint32 character_id, uint32 timestamp, std::list> &parse_list) { + std::string query = StringFormat( + "SELECT" + " `slotid`," + " `itemid` " + "FROM" + " `inventory_snapshots` " + "WHERE" + " `charid` = %u " + "AND" + " `time_index` = %u " + "ORDER BY" + " `slotid`", + character_id, + timestamp + ); + auto results = QueryDatabase(query); + + if (!results.Success()) + return; + + for (auto row : results) + parse_list.push_back(std::pair(atoi(row[0]), atoul(row[1]))); +} + +void ZoneDatabase::DivergeCharacterInvSnapshotFromInventory(uint32 character_id, uint32 timestamp, std::list> &compare_list) { + std::string query = StringFormat( + "SELECT" + " slotid," + " itemid " + "FROM" + " `inventory_snapshots` " + "WHERE" + " `time_index` = %u " + "AND" + " `charid` = %u " + "AND" + " `slotid` NOT IN " + "(" + "SELECT" + " a.`slotid` " + "FROM" + " `inventory_snapshots` a " + "JOIN" + " `inventory` b " + "USING" + " (`slotid`, `itemid`) " + "WHERE" + " a.`time_index` = %u " + "AND" + " a.`charid` = %u " + "AND" + " b.`charid` = %u" + ")", + timestamp, + character_id, + timestamp, + character_id, + character_id + ); + auto results = QueryDatabase(query); + + if (!results.Success()) + return; + + for (auto row : results) + compare_list.push_back(std::pair(atoi(row[0]), atoul(row[1]))); +} + +void ZoneDatabase::DivergeCharacterInventoryFromInvSnapshot(uint32 character_id, uint32 timestamp, std::list> &compare_list) { + std::string query = StringFormat( + "SELECT" + " `slotid`," + " `itemid` " + "FROM" + " `inventory` " + "WHERE" + " `charid` = %u " + "AND" + " `slotid` NOT IN " + "(" + "SELECT" + " a.`slotid` " + "FROM" + " `inventory` a " + "JOIN" + " `inventory_snapshots` b " + "USING" + " (`slotid`, `itemid`) " + "WHERE" + " b.`time_index` = %u " + "AND" + " b.`charid` = %u " + "AND" + " a.`charid` = %u" + ")", + character_id, + timestamp, + character_id, + character_id + ); + auto results = QueryDatabase(query); + + if (!results.Success()) + return; + + for (auto row : results) + compare_list.push_back(std::pair(atoi(row[0]), atoul(row[1]))); +} + +bool ZoneDatabase::RestoreCharacterInvSnapshot(uint32 character_id, uint32 timestamp) { + // we should know what we're doing by the time we call this function..but, + // this is to prevent inventory deletions where no timestamp entries exists + if (!ValidateCharacterInvSnapshotTimestamp(character_id, timestamp)) { + Log(Logs::General, Logs::Error, "ZoneDatabase::RestoreCharacterInvSnapshot() called for id: %u without valid snapshot entries @ %u", character_id, timestamp); + return false; + } + + std::string query = StringFormat( + "DELETE " + "FROM" + " `inventory` " + "WHERE" + " `charid` = %u", + character_id + ); + auto results = database.QueryDatabase(query); + if (!results.Success()) + return false; + + query = StringFormat( + "INSERT " + "INTO" + " `inventory` " + "(`charid`," + " `slotid`," + " `itemid`," + " `charges`," + " `color`," + " `augslot1`," + " `augslot2`," + " `augslot3`," + " `augslot4`," + " `augslot5`," + " `augslot6`," + " `instnodrop`," + " `custom_data`," + " `ornamenticon`," + " `ornamentidfile`," + " `ornament_hero_model`" + ") " + "SELECT" + " `charid`," + " `slotid`," + " `itemid`," + " `charges`," + " `color`," + " `augslot1`," + " `augslot2`," + " `augslot3`," + " `augslot4`," + " `augslot5`," + " `augslot6`," + " `instnodrop`," + " `custom_data`," + " `ornamenticon`," + " `ornamentidfile`," + " `ornament_hero_model` " + "FROM" + " `inventory_snapshots` " + "WHERE" + " `charid` = %u " + "AND" + " `time_index` = %u", + character_id, + timestamp + ); + results = database.QueryDatabase(query); + + Log(Logs::General, Logs::Inventory, "ZoneDatabase::RestoreCharacterInvSnapshot() %s snapshot for %u @ %u", + (results.Success() ? "restored" : "failed to restore"), character_id, timestamp); + + return results.Success(); +} + const NPCType* ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load /*= false*/) { const NPCType *npc = nullptr; diff --git a/zone/zonedb.h b/zone/zonedb.h index 8943f6f01..78f78ab83 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -315,7 +315,6 @@ public: bool SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name); bool SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon); bool SaveCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp); - bool SaveCharacterInventorySnapshot(uint32 character_id); /* Character Data Deletes */ bool DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); @@ -328,7 +327,16 @@ public: /* Character Inventory */ bool NoRentExpired(const char* name); - + bool SaveCharacterInvSnapshot(uint32 character_id); + int CountCharacterInvSnapshots(uint32 character_id); + void ClearCharacterInvSnapshots(uint32 character_id, bool from_now = false); + void ListCharacterInvSnapshots(uint32 character_id, std::list> &is_list); + bool ValidateCharacterInvSnapshotTimestamp(uint32 character_id, uint32 timestamp); + void ParseCharacterInvSnapshot(uint32 character_id, uint32 timestamp, std::list> &parse_list); + void DivergeCharacterInvSnapshotFromInventory(uint32 character_id, uint32 timestamp, std::list> &compare_list); + void DivergeCharacterInventoryFromInvSnapshot(uint32 character_id, uint32 timestamp, std::list> &compare_list); + bool RestoreCharacterInvSnapshot(uint32 character_id, uint32 timestamp); + /* Corpses */ bool DeleteItemOffCharacterCorpse(uint32 db_id, uint32 equip_slot, uint32 item_id); uint32 GetCharacterCorpseItemCount(uint32 corpse_id);