From ea1281f3bee4b02c102114fba5a27ac5f0b7f991 Mon Sep 17 00:00:00 2001 From: sruon Date: Mon, 6 Apr 2026 00:27:56 -0600 Subject: [PATCH] Exdata definitions Augments Timers Serialized items Linkshell Co-Authored-By: atom0s --- .../abyssea/sturdypyxis/augmented_item.lua | 20 ++- scripts/globals/magian.lua | 19 ++- scripts/specs/core/Exdata.lua | 71 ++++++++++- scripts/tests/systems/exdata.lua | 119 +++++++++++++++++- src/map/items.h | 2 + src/map/items/CMakeLists.txt | 16 +++ src/map/items/exdata.cpp | 109 +++++++++++++++- src/map/items/exdata.h | 9 ++ src/map/items/exdata/appraisable.h | 36 ++++++ src/map/items/exdata/augment_bundle.cpp | 52 ++++++++ src/map/items/exdata/augment_bundle.h | 54 ++++++++ src/map/items/exdata/augment_mezzotint.cpp | 70 +++++++++++ src/map/items/exdata/augment_mezzotint.h | 56 +++++++++ src/map/items/exdata/augment_standard.cpp | 64 ++++++++++ src/map/items/exdata/augment_standard.h | 48 +++++++ src/map/items/exdata/augment_trial.cpp | 74 +++++++++++ src/map/items/exdata/augment_trial.h | 42 +++++++ src/map/items/exdata/glowing_lamp.h | 2 +- src/map/items/exdata/linkshell.cpp | 70 +++++++++++ src/map/items/exdata/linkshell.h | 49 ++++++++ src/map/items/exdata/perpetual_hourglass.h | 2 +- src/map/items/exdata/serialized.cpp | 44 +++++++ src/map/items/exdata/serialized.h | 47 +++++++ src/map/items/exdata/storage_slip.h | 35 ++++++ src/map/items/exdata/timer_info.cpp | 45 +++++++ src/map/items/exdata/timer_info.h | 43 +++++++ src/map/items/exdata/worn_item.h | 36 ++++++ src/map/items/item.cpp | 13 +- src/map/items/item_equipment.cpp | 46 ++++--- src/map/items/item_equipment.h | 2 - src/map/items/item_furnishing.cpp | 3 +- src/map/items/item_linkshell.cpp | 44 ++++--- src/map/items/item_linkshell.h | 11 +- src/map/items/item_usable.cpp | 10 +- src/map/lua/lua_baseentity.cpp | 102 +++------------ src/map/lua/lua_baseentity.h | 4 +- src/map/lua/lua_item.cpp | 19 +-- .../c2s/0x0c4_group_comlink_active.cpp | 9 +- src/map/packets/c2s/0x0fb_myroom_bankin.cpp | 17 +-- src/map/packets/char_status.cpp | 2 +- src/map/packets/char_update.cpp | 2 +- src/map/packets/s2c/0x023_item_trade_list.cpp | 2 +- .../s2c/0x0c9_equip_inspect_equipment.cpp | 4 +- .../s2c/0x0c9_equip_inspect_general.cpp | 2 +- src/map/utils/charutils.cpp | 16 +-- 45 files changed, 1329 insertions(+), 213 deletions(-) create mode 100644 src/map/items/exdata/appraisable.h create mode 100644 src/map/items/exdata/augment_bundle.cpp create mode 100644 src/map/items/exdata/augment_bundle.h create mode 100644 src/map/items/exdata/augment_mezzotint.cpp create mode 100644 src/map/items/exdata/augment_mezzotint.h create mode 100644 src/map/items/exdata/augment_standard.cpp create mode 100644 src/map/items/exdata/augment_standard.h create mode 100644 src/map/items/exdata/augment_trial.cpp create mode 100644 src/map/items/exdata/augment_trial.h create mode 100644 src/map/items/exdata/linkshell.cpp create mode 100644 src/map/items/exdata/linkshell.h create mode 100644 src/map/items/exdata/serialized.cpp create mode 100644 src/map/items/exdata/serialized.h create mode 100644 src/map/items/exdata/storage_slip.h create mode 100644 src/map/items/exdata/timer_info.cpp create mode 100644 src/map/items/exdata/timer_info.h create mode 100644 src/map/items/exdata/worn_item.h diff --git a/scripts/globals/abyssea/sturdypyxis/augmented_item.lua b/scripts/globals/abyssea/sturdypyxis/augmented_item.lua index 0048f14a55a..aa90578b535 100644 --- a/scripts/globals/abyssea/sturdypyxis/augmented_item.lua +++ b/scripts/globals/abyssea/sturdypyxis/augmented_item.lua @@ -585,7 +585,15 @@ local function GiveAugItem(player, npc, slot) return 0 elseif player:getFreeSlotsCount() > 0 then if GetAugItemID(npc, 1) ~= 0 then - player:addItem(item1, 1, item1aug1, item1aug1val, item1aug2, item1aug2val) + player:addItem({ + id = item1, + exdata = + { + augmentKind = xi.augment.kind.HAS_AUGMENTS, + augmentSubKind = xi.augment.subKind.STANDARD, + augments = { { id = item1aug1, value = item1aug1val }, { id = item1aug2, value = item1aug2val } }, + }, + }) xi.pyxis.messageChest(player, zones[zoneId].text.OBTAINS_ITEM, item1, 0, 0, 0) npc:setLocalVar('ITEM1ID', 0) end @@ -601,7 +609,15 @@ local function GiveAugItem(player, npc, slot) return 0 elseif player:getFreeSlotsCount() > 0 then if GetAugItemID(npc, 2) ~= 0 then - player:addItem(item2, 1, item2aug1, item2aug1val, item2aug2, item2aug2val) + player:addItem({ + id = item2, + exdata = + { + augmentKind = xi.augment.kind.HAS_AUGMENTS, + augmentSubKind = xi.augment.subKind.STANDARD, + augments = { { id = item2aug1, value = item2aug1val }, { id = item2aug2, value = item2aug2val } }, + }, + }) xi.pyxis.messageChest(player, zones[zoneId].text.OBTAINS_ITEM, item2, 0, 0, 0) npc:setLocalVar('ITEM2ID', 0) end diff --git a/scripts/globals/magian.lua b/scripts/globals/magian.lua index 7b4426d5b42..0b16e75f5f1 100644 --- a/scripts/globals/magian.lua +++ b/scripts/globals/magian.lua @@ -309,16 +309,25 @@ end ----------------------------------- local function giveMagianItem(player, itemData, inscribeTrialId) - local itemParameters = { itemData.itemId, 1, 0, 0, 0, 0, 0, 0, 0, 0, inscribeTrialId and inscribeTrialId or 0 } + local exdata = + { + augmentKind = xi.augment.kind.HAS_AUGMENTS, + augmentSubKind = xi.augment.subKind.STANDARD, + } if itemData.itemAugments then - for augIndex, augData in pairs(itemData.itemAugments) do - itemParameters[augIndex * 2 + 1] = augData[1] - itemParameters[augIndex * 2 + 2] = augData[2] + exdata.augments = {} + for _, augData in pairs(itemData.itemAugments) do + table.insert(exdata.augments, { id = augData[1], value = augData[2] }) end end - player:addItem(unpack(itemParameters)) + if inscribeTrialId and inscribeTrialId ~= 0 then + exdata.augmentSubKind = exdata.augmentSubKind + xi.augment.subKind.TRIAL + exdata.trial = { id = inscribeTrialId, completed = false } + end + + player:addItem({ id = itemData.itemId, exdata = exdata }) end xi.magian.giveRequiredItem = function(player, trialId, inscribeTrialId) diff --git a/scripts/specs/core/Exdata.lua b/scripts/specs/core/Exdata.lua index d6c598d445a..ad1f9054c6e 100644 --- a/scripts/specs/core/Exdata.lua +++ b/scripts/specs/core/Exdata.lua @@ -179,4 +179,73 @@ ---@field race? xi.mannequin.type ---@field pose? xi.mannequin.pose ----@alias Exdata ExdataLegionPass|ExdataPerpetualHourglass|ExdataBettingSlip|ExdataAssaultLog|ExdataBrennerBook|ExdataMeebleGrimoire|ExdataHoneymoonTicket|ExdataRaceCertificate|ExdataLotteryTicket|ExdataTabula|ExdataEvolith|ExdataCraftingSet|ExdataGlowingLamp|ExdataChocoboEgg|ExdataChocoboCard|ExdataFish|ExdataEscutcheon|ExdataSoulPlate|ExdataSoulReflector|ExdataWeaponUnlock|ExdataFurniture|ExdataFlowerPot|ExdataMannequin +---@class ExdataAugment +---@field id? integer +---@field value? integer + +---@class ExdataAugmentStandard +---@field augmentKind? xi.augment.kind +---@field augmentSubKind? xi.augment.subKind|integer +---@field augments? ExdataAugment[] # Up to 5 augments +---@field signature? string + +---@class ExdataAugmentTrialInfo +---@field id? integer +---@field completed? boolean + +---@class ExdataAugmentTrial +---@field augmentKind? xi.augment.kind +---@field augmentSubKind? xi.augment.subKind|integer +---@field augments? ExdataAugment[] # Up to 4 augments +---@field trial? ExdataAugmentTrialInfo +---@field signature? string + +---@class ExdataMezzotintAugment +---@field index? xi.mezzotint.augment +---@field value? integer + +---@class ExdataAugmentMezzotint +---@field augmentKind? xi.augment.kind +---@field augmentSubKind? xi.augment.subKind|integer +---@field type? xi.mezzotint.type +---@field rank? integer +---@field accumulatedRP? integer +---@field augments? ExdataMezzotintAugment[] # Up to 3 slots +---@field signature? string + +---@class ExdataAugmentBundle +---@field augmentKind? xi.augment.kind +---@field type? integer +---@field accumulatedRP? integer +---@field rank? integer +---@field maxRankTier? integer +---@field rpCurve? xi.augment.rpCurve +---@field augmentIndex? integer +---@field signature? string + +---@class ExdataLinkshellColor +---@field r? integer +---@field g? integer +---@field b? integer +---@field a? integer + +---@class ExdataLinkshell +---@field groupId? integer +---@field groupKey? integer +---@field color? ExdataLinkshellColor +---@field flag? integer +---@field name? string + +---@class ExdataSerialized +---@field serverIndex? integer # Only retail server IDs render correctly +---@field serialNumber? integer # [1-65535] +---@field signature? string + +---@class ExdataItemTimerInfo +---@field remainingCharges? integer +---@field flags? integer +---@field timeValue1? integer +---@field timeValue2? integer +---@field signature? string + +---@alias Exdata ExdataLegionPass|ExdataPerpetualHourglass|ExdataBettingSlip|ExdataAssaultLog|ExdataBrennerBook|ExdataMeebleGrimoire|ExdataHoneymoonTicket|ExdataRaceCertificate|ExdataLotteryTicket|ExdataTabula|ExdataEvolith|ExdataCraftingSet|ExdataGlowingLamp|ExdataChocoboEgg|ExdataChocoboCard|ExdataFish|ExdataEscutcheon|ExdataSoulPlate|ExdataSoulReflector|ExdataWeaponUnlock|ExdataFurniture|ExdataFlowerPot|ExdataMannequin|ExdataAugmentStandard|ExdataAugmentTrial|ExdataAugmentMezzotint|ExdataAugmentBundle|ExdataLinkshell|ExdataSerialized|ExdataItemTimerInfo diff --git a/scripts/tests/systems/exdata.lua b/scripts/tests/systems/exdata.lua index 76395e24790..5bfbccbf45d 100644 --- a/scripts/tests/systems/exdata.lua +++ b/scripts/tests/systems/exdata.lua @@ -286,7 +286,7 @@ describe('Exdata', function() shape = xi.evolith.shape.DOWN_FILLED, element = xi.evolith.element.FIRE, bonus = 3, - signature = 'TestCrafter', + signature = player:getName(), }) local ex = item:getExData() @@ -294,7 +294,7 @@ describe('Exdata', function() assert(ex.shape == xi.evolith.shape.DOWN_FILLED) assert(ex.element == xi.evolith.element.FIRE) assert(ex.bonus == 3) - assert(ex.signature == 'TestCrafter') + assert(ex.signature == player:getName()) end) it('can get and set Crafting Set exdata', function() @@ -426,7 +426,7 @@ describe('Exdata', function() craftsmanship = 2500, stage = xi.escutcheon.stage.ASPIS, successDownPenalty = 0, - signature = 'TestCrafter', + signature = player:getName(), }) local ex = item:getExData() @@ -435,7 +435,7 @@ describe('Exdata', function() assert(ex.craftsmanship == 2500) assert(ex.stage == xi.escutcheon.stage.ASPIS) assert(ex.successDownPenalty == 0) - assert(ex.signature == 'TestCrafter') + assert(ex.signature == player:getName()) end) it('can get and set Soul Plate exdata', function() @@ -585,6 +585,117 @@ describe('Exdata', function() assert(ex.pose == xi.mannequin.pose.HURRAY) end) + it('can get and set AugmentStandard exdata', function() + local item = player:addItem({ id = xi.item.BRONZE_SWORD, quantity = 1 }) + assert(item) + + item:setExData( + { + augmentKind = xi.augment.kind.HAS_AUGMENTS, + augmentSubKind = xi.augment.subKind.STANDARD, + augments = + { + { id = 1, value = 4 }, + { id = 9, value = 2 }, + }, + signature = player:getName(), + }) + + local ex = item:getExData() + assert(ex.augmentKind == xi.augment.kind.HAS_AUGMENTS) + assert(ex.augmentSubKind == xi.augment.subKind.STANDARD) + assert(ex.augments[1].id == 1) + assert(ex.augments[1].value == 4) + assert(ex.augments[2].id == 9) + assert(ex.augments[2].value == 2) + assert(ex.signature == player:getName()) + end) + + it('can get and set AugmentTrial exdata', function() + local item = player:addItem({ id = xi.item.BRONZE_SWORD, quantity = 1 }) + assert(item) + + item:setExData( + { + augmentKind = xi.augment.kind.HAS_AUGMENTS, + augmentSubKind = xi.augment.subKind.STANDARD + xi.augment.subKind.TRIAL, + augments = + { + { id = 1, value = 4 }, + }, + trial = { id = 100, completed = false }, + }) + + local ex = item:getExData() + assert(ex.trial.id == 100) + assert(ex.trial.completed == false) + assert(ex.augments[1].id == 1) + end) + + it('can get and set AugmentBundle exdata', function() + local item = player:addItem({ id = xi.item.BRONZE_SWORD, quantity = 1 }) + assert(item) + + item:setExData( + { + augmentKind = xi.augment.kind.BUNDLED, + type = 2, + rank = 18, + accumulatedRP = 350, + maxRankTier = 2, + rpCurve = xi.augment.rpCurve.B, + augmentIndex = 63, + }) + + local ex = item:getExData() + assert(ex.augmentKind == xi.augment.kind.BUNDLED) + assert(ex.rank == 18) + assert(ex.accumulatedRP == 350) + assert(ex.augmentIndex == 63) + end) + + it('can get and set AugmentMezzotint exdata', function() + local item = player:addItem({ id = xi.item.BRONZE_SWORD, quantity = 1 }) + assert(item) + + item:setExData( + { + augmentKind = xi.augment.kind.HAS_AUGMENTS, + augmentSubKind = xi.augment.subKind.MEZZOTINT, + type = xi.mezzotint.type.B, + rank = 10, + accumulatedRP = 500, + augments = + { + { index = xi.mezzotint.augment.MP_II, value = 3 }, + { index = xi.mezzotint.augment.ACCURACY, value = 7 }, + }, + }) + + local ex = item:getExData() + assert(ex.rank == 10) + assert(ex.accumulatedRP == 500) + assert(ex.augments[1].index == xi.mezzotint.augment.MP_II) + assert(ex.augments[1].value == 3) + end) + + it('can get and set Serialized exdata', function() + local item = player:addItem({ id = 19320, quantity = 1 }) + assert(item) + + item:setExData( + { + serverIndex = 0x7F, + serialNumber = 500, + signature = player:getName(), + }) + + local ex = item:getExData() + assert(ex.serverIndex == 0x7F) + assert(ex.serialNumber == 500) + assert(ex.signature == player:getName()) + end) + it('unhandled items fall back to raw bytes', function() local item = player:addItem({ id = xi.item.FIRE_CRYSTAL, quantity = 1 }) assert(item) diff --git a/src/map/items.h b/src/map/items.h index 4f2f1598edc..b1afb3324b3 100644 --- a/src/map/items.h +++ b/src/map/items.h @@ -138,6 +138,8 @@ enum ITEMID : uint16 DREAM_BELL_P1 = 18864, LADY_BELL = 18868, LADY_BELL_P1 = 18869, + LU_SHANGS_FISHING_ROD_P1 = 19320, + EBISU_FISHING_ROD_P1 = 19321, MARVELOUS_CHEER = 22283, JOINERS_ASPIS = 26423, CHEFS_SHIELD = 26461, diff --git a/src/map/items/CMakeLists.txt b/src/map/items/CMakeLists.txt index 107ba779c54..0d83c3d71b8 100644 --- a/src/map/items/CMakeLists.txt +++ b/src/map/items/CMakeLists.txt @@ -1,8 +1,17 @@ set(ITEM_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/exdata.cpp ${CMAKE_CURRENT_SOURCE_DIR}/exdata.h + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/appraisable.h ${CMAKE_CURRENT_SOURCE_DIR}/exdata/assault_log.cpp ${CMAKE_CURRENT_SOURCE_DIR}/exdata/assault_log.h + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/augment_bundle.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/augment_bundle.h + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/augment_mezzotint.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/augment_mezzotint.h + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/augment_standard.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/augment_standard.h + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/augment_trial.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/augment_trial.h ${CMAKE_CURRENT_SOURCE_DIR}/exdata/base.h ${CMAKE_CURRENT_SOURCE_DIR}/exdata/betting_slip.cpp ${CMAKE_CURRENT_SOURCE_DIR}/exdata/betting_slip.h @@ -26,6 +35,8 @@ set(ITEM_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/exdata/furniture.h ${CMAKE_CURRENT_SOURCE_DIR}/exdata/glowing_lamp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/exdata/glowing_lamp.h + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/linkshell.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/linkshell.h ${CMAKE_CURRENT_SOURCE_DIR}/exdata/honeymoon_ticket.cpp ${CMAKE_CURRENT_SOURCE_DIR}/exdata/honeymoon_ticket.h ${CMAKE_CURRENT_SOURCE_DIR}/exdata/legion_pass.cpp @@ -40,12 +51,17 @@ set(ITEM_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/exdata/perpetual_hourglass.h ${CMAKE_CURRENT_SOURCE_DIR}/exdata/race_certificate.cpp ${CMAKE_CURRENT_SOURCE_DIR}/exdata/race_certificate.h + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/serialized.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/serialized.h ${CMAKE_CURRENT_SOURCE_DIR}/exdata/soul_plate.cpp ${CMAKE_CURRENT_SOURCE_DIR}/exdata/soul_plate.h ${CMAKE_CURRENT_SOURCE_DIR}/exdata/soul_reflector.cpp ${CMAKE_CURRENT_SOURCE_DIR}/exdata/soul_reflector.h ${CMAKE_CURRENT_SOURCE_DIR}/exdata/tabula.cpp ${CMAKE_CURRENT_SOURCE_DIR}/exdata/tabula.h + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/timer_info.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/timer_info.h + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/worn_item.h ${CMAKE_CURRENT_SOURCE_DIR}/exdata/weapon_unlock.cpp ${CMAKE_CURRENT_SOURCE_DIR}/exdata/weapon_unlock.h ${CMAKE_CURRENT_SOURCE_DIR}/item_currency.cpp diff --git a/src/map/items/exdata.cpp b/src/map/items/exdata.cpp index 407570038e2..95cabc7f4c5 100644 --- a/src/map/items/exdata.cpp +++ b/src/map/items/exdata.cpp @@ -22,6 +22,7 @@ #include "exdata.h" #include "item.h" +#include "item_equipment.h" #include "item_furnishing.h" #include "item_weapon.h" @@ -43,6 +44,11 @@ auto getType(const CItem* item) -> Type const auto itemId = item->getID(); + if (item->isType(ITEM_LINKSHELL)) + { + return Type::Linkshell; + } + if (fishingutils::IsFish(item)) { return Type::Fish; @@ -160,8 +166,13 @@ auto getType(const CItem* item) -> Type return Type::Furniture; } + if (itemId == LU_SHANGS_FISHING_ROD_P1 || itemId == EBISU_FISHING_ROD_P1) + { + return Type::Serialized; + } + // Unlockable weapons use specific exdata - // Must be checked before augments + // Must be checked before charged items and augments if (item->isType(ITEM_WEAPON)) { auto* PWeapon = static_cast(item); @@ -171,6 +182,18 @@ auto getType(const CItem* item) -> Type } } + // Charged items (equipment or usable) use timer exdata + if (item->isSubType(ITEM_CHARGED)) + { + return Type::Usable; + } + + // Equipment with augments + if (item->isType(ITEM_EQUIPMENT)) + { + return Type::Augment; + } + return Type::None; } @@ -248,6 +271,38 @@ auto toTable(const CItem* item, sol::table& table) -> bool case Type::Mannequin: item->exdata().toTable(table); return true; + case Type::Linkshell: + item->exdata().toTable(table); + return true; + case Type::Serialized: + item->exdata().toTable(table); + return true; + case Type::Usable: + item->exdata().toTable(table); + return true; + case Type::Augment: + { + const auto kind = item->exdata().AugmentKind; + const auto subKind = item->exdata().AugmentSubKind; + + if (kind == AugmentKindFlags::Bundled) + { + item->exdata().toTable(table); + } + else if (static_cast(subKind & AugmentSubKindFlags::Mezzotint)) + { + item->exdata().toTable(table); + } + else if (static_cast(subKind & AugmentSubKindFlags::Trial)) + { + item->exdata().toTable(table); + } + else + { + item->exdata().toTable(table); + } + return true; + } default: return false; } @@ -327,6 +382,58 @@ auto fromTable(CItem* item, const sol::table& data) -> bool case Type::Mannequin: item->exdata().fromTable(data); return true; + case Type::Linkshell: + item->exdata().fromTable(data); + return true; + case Type::Serialized: + item->exdata().fromTable(data); + return true; + case Type::Usable: + item->exdata().fromTable(data); + return true; + case Type::Augment: + { + const auto kind = Exdata::get_or(data, "augmentKind", AugmentKindFlags{}); + const auto subKind = Exdata::get_or(data, "augmentSubKind", AugmentSubKindFlags{}); + + if (kind == AugmentKindFlags::Bundled) + { + item->exdata().fromTable(data); + } + else if (static_cast(subKind & AugmentSubKindFlags::Mezzotint)) + { + item->exdata().fromTable(data); + } + else if (static_cast(subKind & AugmentSubKindFlags::Trial)) + { + item->exdata().fromTable(data); + if (auto* PEquip = dynamic_cast(item)) + { + for (uint8 slot = 0; slot < std::size(item->exdata().Augments); ++slot) + { + if (item->exdata().Augments[slot].Id != 0) + { + PEquip->ApplyAugment(slot); + } + } + } + } + else + { + item->exdata().fromTable(data); + if (auto* PEquip = dynamic_cast(item)) + { + for (uint8 slot = 0; slot < std::size(item->exdata().Augments); ++slot) + { + if (item->exdata().Augments[slot].Id != 0) + { + PEquip->ApplyAugment(slot); + } + } + } + } + return true; + } default: return false; } diff --git a/src/map/items/exdata.h b/src/map/items/exdata.h index a50cd41b348..ecb648cf93a 100644 --- a/src/map/items/exdata.h +++ b/src/map/items/exdata.h @@ -25,7 +25,12 @@ #include "exdata/base.h" +#include "exdata/appraisable.h" #include "exdata/assault_log.h" +#include "exdata/augment_bundle.h" +#include "exdata/augment_mezzotint.h" +#include "exdata/augment_standard.h" +#include "exdata/augment_trial.h" #include "exdata/betting_slip.h" #include "exdata/brenner_book.h" #include "exdata/chocobo_card.h" @@ -39,15 +44,19 @@ #include "exdata/glowing_lamp.h" #include "exdata/honeymoon_ticket.h" #include "exdata/legion_pass.h" +#include "exdata/linkshell.h" #include "exdata/lottery_ticket.h" #include "exdata/mannequin.h" #include "exdata/meeble_grimoire.h" #include "exdata/perpetual_hourglass.h" #include "exdata/race_certificate.h" +#include "exdata/serialized.h" #include "exdata/soul_plate.h" #include "exdata/soul_reflector.h" #include "exdata/tabula.h" +#include "exdata/timer_info.h" #include "exdata/weapon_unlock.h" +#include "exdata/worn_item.h" class CItem; diff --git a/src/map/items/exdata/appraisable.h b/src/map/items/exdata/appraisable.h new file mode 100644 index 00000000000..69905702293 --- /dev/null +++ b/src/map/items/exdata/appraisable.h @@ -0,0 +1,36 @@ +/* +=========================================================================== + + Copyright (c) 2026 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#pragma once + +#include "base.h" + +namespace Exdata +{ +#pragma pack(push, 1) +struct Appraisable +{ + uint8_t padding00[22]; + uint8_t AppraisalId; + uint8_t padding01; +}; +#pragma pack(pop) +} // namespace Exdata diff --git a/src/map/items/exdata/augment_bundle.cpp b/src/map/items/exdata/augment_bundle.cpp new file mode 100644 index 00000000000..9402b9e783e --- /dev/null +++ b/src/map/items/exdata/augment_bundle.cpp @@ -0,0 +1,52 @@ +/* +=========================================================================== + + Copyright (c) 2026 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#include "augment_bundle.h" + +void Exdata::AugmentBundle::toTable(sol::table& table) const +{ + table["augmentKind"] = static_cast(this->AugmentKind); + table["augmentSubKind"] = static_cast(this->AugmentSubKind); + table["type"] = static_cast(this->Type); + table["rank"] = static_cast(this->Rank); + table["accumulatedRP"] = static_cast(this->AccumulatedRP); + table["maxRankTier"] = static_cast(this->MaxRankTier); + table["rpCurve"] = static_cast(this->RPCurve); + table["augmentIndex"] = this->AugmentIndex; + table["signature"] = Exdata::decodeSignature(this->Signature); +} + +void Exdata::AugmentBundle::fromTable(const sol::table& data) +{ + this->AugmentKind = AugmentKindFlags::Bundled; + this->AugmentSubKind = AugmentSubKindFlags::Standard | AugmentSubKindFlags::Evolith; + this->Type = Exdata::get_or(data, "type", this->Type); + this->Rank = Exdata::get_or(data, "rank", this->Rank); + this->AccumulatedRP = Exdata::get_or(data, "accumulatedRP", this->AccumulatedRP); + this->MaxRankTier = Exdata::get_or(data, "maxRankTier", this->MaxRankTier); + this->RPCurve = Exdata::get_or(data, "rpCurve", this->RPCurve); + this->AugmentIndex = Exdata::get_or(data, "augmentIndex", this->AugmentIndex); + + if (sol::optional sig = data["signature"]) + { + Exdata::encodeSignature(*sig, this->Signature); + } +} diff --git a/src/map/items/exdata/augment_bundle.h b/src/map/items/exdata/augment_bundle.h new file mode 100644 index 00000000000..bf79cdae65b --- /dev/null +++ b/src/map/items/exdata/augment_bundle.h @@ -0,0 +1,54 @@ +/* +=========================================================================== + + Copyright (c) 2026 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#pragma once + +#include "base.h" + +#include "enums/exdata.h" + +namespace Exdata +{ +#pragma pack(push, 1) +// Bundled augments (Odyssey, JSE necks, Unity belts). +// Augments referenced by DAT table index (see xi.data.augments.bundles). +// MaxRankTier (2 bits): 0=15, 1=20, 2=25, 3=30. +// RPCurve selects the RP-per-rank table (see xi.data.augments.rpCurves). +struct AugmentBundle +{ + AugmentKindFlags AugmentKind; + AugmentSubKindFlags AugmentSubKind; + uint16_t padding00{}; + uint32_t Type : 2; + uint32_t AccumulatedRP : 16; + uint32_t Rank : 5; + uint32_t MaxRankTier : 2; + uint32_t padding01 : 1; + uint32_t RPCurve : 2; // RP per rank client table + uint32_t padding02 : 4; + uint32_t AugmentIndex; + uint8_t Signature[12]; + + void toTable(sol::table& table) const; + void fromTable(const sol::table& data); +}; +#pragma pack(pop) +} // namespace Exdata diff --git a/src/map/items/exdata/augment_mezzotint.cpp b/src/map/items/exdata/augment_mezzotint.cpp new file mode 100644 index 00000000000..41d9115e7d1 --- /dev/null +++ b/src/map/items/exdata/augment_mezzotint.cpp @@ -0,0 +1,70 @@ +/* +=========================================================================== + + Copyright (c) 2026 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#include "augment_mezzotint.h" + +#include "common/lua.h" + +void Exdata::AugmentMezzotint::toTable(sol::table& table) const +{ + table["augmentKind"] = static_cast(this->AugmentKind); + table["augmentSubKind"] = static_cast(this->AugmentSubKind); + table["type"] = static_cast(this->Type); + table["rank"] = static_cast(this->Rank); + table["accumulatedRP"] = this->AccumulatedRP; + + sol::table augments = lua.create_table(); + for (size_t i = 0; i < std::size(this->Augments); ++i) + { + sol::table aug = lua.create_table(); + aug["index"] = this->Augments[i].Index; + aug["value"] = this->Augments[i].Value; + augments[i + 1] = aug; + } + table["augments"] = augments; + table["signature"] = Exdata::decodeSignature(this->Signature); +} + +void Exdata::AugmentMezzotint::fromTable(const sol::table& data) +{ + this->AugmentKind = Exdata::get_or(data, "augmentKind", this->AugmentKind); + this->AugmentSubKind = Exdata::get_or(data, "augmentSubKind", this->AugmentSubKind); + this->Type = Exdata::get_or(data, "type", this->Type); + this->Rank = Exdata::get_or(data, "rank", this->Rank); + this->AccumulatedRP = Exdata::get_or(data, "accumulatedRP", this->AccumulatedRP); + + if (sol::optional augments = data["augments"]) + { + for (size_t i = 0; i < std::size(this->Augments); ++i) + { + if (sol::optional entry = (*augments)[i + 1]) + { + this->Augments[i].Index = Exdata::get_or(*entry, "index", this->Augments[i].Index); + this->Augments[i].Value = Exdata::get_or(*entry, "value", this->Augments[i].Value); + } + } + } + + if (sol::optional sig = data["signature"]) + { + Exdata::encodeSignature(*sig, this->Signature); + } +} diff --git a/src/map/items/exdata/augment_mezzotint.h b/src/map/items/exdata/augment_mezzotint.h new file mode 100644 index 00000000000..b150ff18138 --- /dev/null +++ b/src/map/items/exdata/augment_mezzotint.h @@ -0,0 +1,56 @@ +/* +=========================================================================== + + Copyright (c) 2026 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#pragma once + +#include "base.h" + +#include "enums/exdata.h" + +namespace Exdata +{ +#pragma pack(push, 1) +// Mezzotint augments (Delve, Geas Fete). +// 3 augment slots using client LUT indices (see xi.mezzotint.augment). + +struct AugmentSlot +{ + uint8_t Index; + uint8_t Value; +}; + +struct AugmentMezzotint +{ + AugmentKindFlags AugmentKind; + AugmentSubKindFlags AugmentSubKind; + uint8_t Type : 2; + uint8_t Rank : 5; + uint8_t padding00 : 1; + uint8_t padding01; + uint16_t AccumulatedRP; + AugmentSlot Augments[3]; + uint8_t Signature[12]; + + void toTable(sol::table& table) const; + void fromTable(const sol::table& data); +}; +#pragma pack(pop) +} // namespace Exdata diff --git a/src/map/items/exdata/augment_standard.cpp b/src/map/items/exdata/augment_standard.cpp new file mode 100644 index 00000000000..3bb4a4a0356 --- /dev/null +++ b/src/map/items/exdata/augment_standard.cpp @@ -0,0 +1,64 @@ +/* +=========================================================================== + + Copyright (c) 2026 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#include "augment_standard.h" + +#include "common/lua.h" + +void Exdata::AugmentStandard::toTable(sol::table& table) const +{ + table["augmentKind"] = static_cast(this->AugmentKind); + table["augmentSubKind"] = static_cast(this->AugmentSubKind); + + sol::table augments = lua.create_table(); + for (size_t i = 0; i < std::size(this->Augments); ++i) + { + sol::table aug = lua.create_table(); + aug["id"] = this->Augments[i].Id; + aug["value"] = this->Augments[i].Value; + augments[i + 1] = aug; + } + table["augments"] = augments; + table["signature"] = Exdata::decodeSignature(this->Signature); +} + +void Exdata::AugmentStandard::fromTable(const sol::table& data) +{ + this->AugmentKind = Exdata::get_or(data, "augmentKind", this->AugmentKind); + this->AugmentSubKind = Exdata::get_or(data, "augmentSubKind", this->AugmentSubKind); + + if (sol::optional augments = data["augments"]) + { + for (size_t i = 0; i < std::size(this->Augments); ++i) + { + if (sol::optional entry = (*augments)[i + 1]) + { + this->Augments[i].Id = Exdata::get_or(*entry, "id", this->Augments[i].Id); + this->Augments[i].Value = Exdata::get_or(*entry, "value", this->Augments[i].Value); + } + } + } + + if (sol::optional sig = data["signature"]) + { + Exdata::encodeSignature(*sig, this->Signature); + } +} diff --git a/src/map/items/exdata/augment_standard.h b/src/map/items/exdata/augment_standard.h new file mode 100644 index 00000000000..7ddd4ccef31 --- /dev/null +++ b/src/map/items/exdata/augment_standard.h @@ -0,0 +1,48 @@ +/* +=========================================================================== + + Copyright (c) 2026 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#pragma once + +#include "base.h" + +#include "enums/exdata.h" + +namespace Exdata +{ +#pragma pack(push, 1) +struct Augment +{ + uint16_t Id : 11; + uint16_t Value : 5; +}; + +struct AugmentStandard +{ + AugmentKindFlags AugmentKind; + AugmentSubKindFlags AugmentSubKind; + Augment Augments[5]; + uint8_t Signature[12]; + + void toTable(sol::table& table) const; + void fromTable(const sol::table& data); +}; +#pragma pack(pop) +} // namespace Exdata diff --git a/src/map/items/exdata/augment_trial.cpp b/src/map/items/exdata/augment_trial.cpp new file mode 100644 index 00000000000..a47c3fe36a6 --- /dev/null +++ b/src/map/items/exdata/augment_trial.cpp @@ -0,0 +1,74 @@ +/* +=========================================================================== + + Copyright (c) 2026 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#include "augment_trial.h" + +#include "common/lua.h" + +void Exdata::AugmentTrial::toTable(sol::table& table) const +{ + table["augmentKind"] = static_cast(this->AugmentKind); + table["augmentSubKind"] = static_cast(this->AugmentSubKind); + + sol::table augments = lua.create_table(); + for (size_t i = 0; i < std::size(this->Augments); ++i) + { + sol::table aug = lua.create_table(); + aug["id"] = this->Augments[i].Id; + aug["value"] = this->Augments[i].Value; + augments[i + 1] = aug; + } + table["augments"] = augments; + sol::table trial = lua.create_table(); + trial["id"] = this->TrialId; + trial["completed"] = static_cast(this->Completed); + table["trial"] = trial; + table["signature"] = Exdata::decodeSignature(this->Signature); +} + +void Exdata::AugmentTrial::fromTable(const sol::table& data) +{ + this->AugmentKind = Exdata::get_or(data, "augmentKind", this->AugmentKind); + this->AugmentSubKind = Exdata::get_or(data, "augmentSubKind", this->AugmentSubKind); + + if (sol::optional augments = data["augments"]) + { + for (size_t i = 0; i < std::size(this->Augments); ++i) + { + if (sol::optional entry = (*augments)[i + 1]) + { + this->Augments[i].Id = Exdata::get_or(*entry, "id", this->Augments[i].Id); + this->Augments[i].Value = Exdata::get_or(*entry, "value", this->Augments[i].Value); + } + } + } + + if (sol::optional trialTable = data["trial"]) + { + this->TrialId = Exdata::get_or(*trialTable, "id", this->TrialId); + this->Completed = Exdata::get_or(*trialTable, "completed", this->Completed); + } + + if (sol::optional sig = data["signature"]) + { + Exdata::encodeSignature(*sig, this->Signature); + } +} diff --git a/src/map/items/exdata/augment_trial.h b/src/map/items/exdata/augment_trial.h new file mode 100644 index 00000000000..dec4450d5ab --- /dev/null +++ b/src/map/items/exdata/augment_trial.h @@ -0,0 +1,42 @@ +/* +=========================================================================== + + Copyright (c) 2026 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#pragma once + +#include "augment_standard.h" + +namespace Exdata +{ +#pragma pack(push, 1) +struct AugmentTrial +{ + AugmentKindFlags AugmentKind; + AugmentSubKindFlags AugmentSubKind; + Augment Augments[4]; + uint16_t TrialId : 15; + uint16_t Completed : 1; + uint8_t Signature[12]; + + void toTable(sol::table& table) const; + void fromTable(const sol::table& data); +}; +#pragma pack(pop) +} // namespace Exdata diff --git a/src/map/items/exdata/glowing_lamp.h b/src/map/items/exdata/glowing_lamp.h index 727a50f3a30..646abcba641 100644 --- a/src/map/items/exdata/glowing_lamp.h +++ b/src/map/items/exdata/glowing_lamp.h @@ -32,8 +32,8 @@ struct GlowingLamp uint8_t Flags : 3; uint8_t padding00 : 5; uint8_t padding01[5]; - uint32_t StartTime; uint32_t EndTime; + uint32_t StartTime; uint8_t padding02[8]; void toTable(sol::table& table) const; diff --git a/src/map/items/exdata/linkshell.cpp b/src/map/items/exdata/linkshell.cpp new file mode 100644 index 00000000000..5388fe4f811 --- /dev/null +++ b/src/map/items/exdata/linkshell.cpp @@ -0,0 +1,70 @@ +/* +=========================================================================== + + Copyright (c) 2026 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#include "linkshell.h" + +#include "common/lua.h" +#include "common/utils.h" + +void Exdata::Linkshell::toTable(sol::table& table) const +{ + table["groupId"] = this->GroupId; + table["groupKey"] = this->GroupKey; + + sol::table color = lua.create_table(); + color["r"] = this->Color.R; + color["g"] = this->Color.G; + color["b"] = this->Color.B; + color["a"] = this->Color.A; + table["color"] = color; + + table["flag"] = this->Flag; + + char decoded[21] = {}; + DecodeStringLinkshell(std::string(reinterpret_cast(this->Name), sizeof(this->Name)), decoded); + table["name"] = std::string(decoded); +} + +void Exdata::Linkshell::fromTable(const sol::table& data) +{ + this->GroupId = Exdata::get_or(data, "groupId", this->GroupId); + this->GroupKey = Exdata::get_or(data, "groupKey", this->GroupKey); + + sol::optional color = data["color"]; + if (color) + { + this->Color.R = Exdata::get_or(*color, "r", this->Color.R); + this->Color.G = Exdata::get_or(*color, "g", this->Color.G); + this->Color.B = Exdata::get_or(*color, "b", this->Color.B); + this->Color.A = Exdata::get_or(*color, "a", this->Color.A); + } + + this->Flag = Exdata::get_or(data, "flag", this->Flag); + + sol::optional name = data["name"]; + if (name) + { + std::memset(this->Name, 0, sizeof(this->Name)); + char encoded[LinkshellStringLength] = {}; + EncodeStringLinkshell(*name, encoded); + std::memcpy(this->Name, encoded, sizeof(this->Name)); + } +} diff --git a/src/map/items/exdata/linkshell.h b/src/map/items/exdata/linkshell.h new file mode 100644 index 00000000000..608363b1d32 --- /dev/null +++ b/src/map/items/exdata/linkshell.h @@ -0,0 +1,49 @@ +/* +=========================================================================== + + Copyright (c) 2026 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#pragma once + +#include "base.h" + +namespace Exdata +{ +#pragma pack(push, 1) +struct lscolor_t +{ + uint16_t R : 4; + uint16_t G : 4; + uint16_t B : 4; + uint16_t A : 4; +}; + +struct Linkshell +{ + uint32_t GroupId; + uint16_t GroupKey; + lscolor_t Color; + uint8_t Flag; + uint8_t Name[15]; + + void toTable(sol::table& table) const; + void fromTable(const sol::table& data); +}; +#pragma pack(pop) +} // namespace Exdata diff --git a/src/map/items/exdata/perpetual_hourglass.h b/src/map/items/exdata/perpetual_hourglass.h index 133b43c5737..4591c246133 100644 --- a/src/map/items/exdata/perpetual_hourglass.h +++ b/src/map/items/exdata/perpetual_hourglass.h @@ -32,8 +32,8 @@ struct PerpetualHourglass uint8_t Flags : 3; uint8_t padding01 : 5; uint8_t padding02[5]; - uint32_t StartTime; uint32_t EndTime; + uint32_t StartTime; uint16_t ZoneId; uint8_t padding03[6]; diff --git a/src/map/items/exdata/serialized.cpp b/src/map/items/exdata/serialized.cpp new file mode 100644 index 00000000000..78cf32a7e74 --- /dev/null +++ b/src/map/items/exdata/serialized.cpp @@ -0,0 +1,44 @@ +/* +=========================================================================== + + Copyright (c) 2026 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#include "serialized.h" + +void Exdata::Serialized::toTable(sol::table& table) const +{ + table["augmentKind"] = static_cast(this->AugmentKind); + table["augmentSubKind"] = static_cast(this->AugmentSubKind); + table["serverIndex"] = this->ServerIndex; + table["serialNumber"] = this->SerialNumber; + table["signature"] = Exdata::decodeSignature(this->Signature); +} + +void Exdata::Serialized::fromTable(const sol::table& data) +{ + this->AugmentKind = AugmentKindFlags::HasAugments; + this->AugmentSubKind = AugmentSubKindFlags::Serialized; + this->ServerIndex = Exdata::get_or(data, "serverIndex", this->ServerIndex); + this->SerialNumber = Exdata::get_or(data, "serialNumber", this->SerialNumber); + + if (sol::optional sig = data["signature"]) + { + Exdata::encodeSignature(*sig, this->Signature); + } +} diff --git a/src/map/items/exdata/serialized.h b/src/map/items/exdata/serialized.h new file mode 100644 index 00000000000..1a02f4a6247 --- /dev/null +++ b/src/map/items/exdata/serialized.h @@ -0,0 +1,47 @@ +/* +=========================================================================== + + Copyright (c) 2026 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#pragma once + +#include "base.h" + +#include "enums/exdata.h" + +namespace Exdata +{ +// Serializes any item without conflicting exdata but the only known retail usage is LuShang +1 and Ebisu +1 +#pragma pack(push, 1) +struct Serialized +{ + AugmentKindFlags AugmentKind; + AugmentSubKindFlags AugmentSubKind; + uint16_t padding00; + uint8_t ServerIndex; + uint8_t padding01; + uint16_t SerialNumber; + uint8_t padding02[4]; + uint8_t Signature[12]; + + void toTable(sol::table& table) const; + void fromTable(const sol::table& data); +}; +#pragma pack(pop) +} // namespace Exdata diff --git a/src/map/items/exdata/storage_slip.h b/src/map/items/exdata/storage_slip.h new file mode 100644 index 00000000000..9367d9e054b --- /dev/null +++ b/src/map/items/exdata/storage_slip.h @@ -0,0 +1,35 @@ +/* +=========================================================================== + + Copyright (c) 2026 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#pragma once + +#include "base.h" + +namespace Exdata +{ +// Moogle Storage Slips. 24-byte bitmask, bit N = item at index N in porter_slip_items.lua. +#pragma pack(push, 1) +struct StorageSlip +{ + uint8_t Items[24]; +}; +#pragma pack(pop) +} // namespace Exdata diff --git a/src/map/items/exdata/timer_info.cpp b/src/map/items/exdata/timer_info.cpp new file mode 100644 index 00000000000..0ae5653d73d --- /dev/null +++ b/src/map/items/exdata/timer_info.cpp @@ -0,0 +1,45 @@ +/* +=========================================================================== + + Copyright (c) 2026 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#include "timer_info.h" + +void Exdata::ItemTimerInfo::toTable(sol::table& table) const +{ + table["remainingCharges"] = this->RemainingCharges; + table["flags"] = this->Flags; + table["timeValue1"] = this->TimeValue1; + table["timeValue2"] = this->TimeValue2; + table["signature"] = Exdata::decodeSignature(this->Signature); +} + +void Exdata::ItemTimerInfo::fromTable(const sol::table& data) +{ + this->Header = 1; // Must be 1 for client to recognize timer data + this->RemainingCharges = Exdata::get_or(data, "remainingCharges", this->RemainingCharges); + this->Flags = Exdata::get_or(data, "flags", this->Flags); + this->TimeValue1 = Exdata::get_or(data, "timeValue1", this->TimeValue1); + this->TimeValue2 = Exdata::get_or(data, "timeValue2", this->TimeValue2); + + if (sol::optional sig = data["signature"]) + { + Exdata::encodeSignature(*sig, this->Signature); + } +} diff --git a/src/map/items/exdata/timer_info.h b/src/map/items/exdata/timer_info.h new file mode 100644 index 00000000000..b560fec0425 --- /dev/null +++ b/src/map/items/exdata/timer_info.h @@ -0,0 +1,43 @@ +/* +=========================================================================== + + Copyright (c) 2026 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#pragma once + +#include "base.h" + +namespace Exdata +{ +#pragma pack(push, 1) +struct ItemTimerInfo +{ + uint8_t Header; + uint8_t RemainingCharges; + uint16_t Flags; // TODO: decode flag bits. Retail observations (high byte only, low always 0x00): + // 0x80=idle, 0xC0=just used, 0x90=charge consumed/cooldown, 0xD0=cooldown expired + uint32_t TimeValue1; + uint32_t TimeValue2; + uint8_t Signature[12]; + + void toTable(sol::table& table) const; + void fromTable(const sol::table& data); +}; +#pragma pack(pop) +} // namespace Exdata diff --git a/src/map/items/exdata/worn_item.h b/src/map/items/exdata/worn_item.h new file mode 100644 index 00000000000..886a27afad9 --- /dev/null +++ b/src/map/items/exdata/worn_item.h @@ -0,0 +1,36 @@ +/* +=========================================================================== + + Copyright (c) 2026 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#pragma once + +#include "base.h" + +namespace Exdata +{ +// BCNM orbs, testimonies, and other items that track use count at byte 0. +#pragma pack(push, 1) +struct WornItem +{ + uint8_t UseCount; + uint8_t padding00[23]; +}; +#pragma pack(pop) +} // namespace Exdata diff --git a/src/map/items/item.cpp b/src/map/items/item.cpp index e10bf087522..585eb094fa3 100644 --- a/src/map/items/item.cpp +++ b/src/map/items/item.cpp @@ -22,6 +22,8 @@ #include #include "common/utils.h" +#include "exdata/appraisable.h" +#include "exdata/augment_standard.h" #include "item.h" /************************************************************************ @@ -107,12 +109,12 @@ uint16 CItem::getFlag() const uint8 CItem::getAppraisalID() const { - return m_extra[0x16]; + return this->exdata().AppraisalId; } -void CItem::setAppraisalID(uint8 appraisailID) +void CItem::setAppraisalID(uint8 appraisalID) { - m_extra[0x16] = appraisailID; + this->exdata().AppraisalId = appraisalID; } /************************************************************************ @@ -302,10 +304,7 @@ void CItem::setReceiver(const std::string& receiver) const std::string CItem::getSignature() { - char signature[SignatureStringLength] = {}; - std::memcpy(&signature, m_extra + 0x0C, sizeof(signature)); - - return signature; // return string copy + return Exdata::decodeSignature(this->exdata().Signature); } void CItem::setSignature(const std::string& signature) diff --git a/src/map/items/item_equipment.cpp b/src/map/items/item_equipment.cpp index 600abc7b1fe..e36ad8916e1 100644 --- a/src/map/items/item_equipment.cpp +++ b/src/map/items/item_equipment.cpp @@ -23,6 +23,9 @@ #include "common/database.h" #include "common/logging.h" +#include "common/utils.h" +#include "exdata/augment_standard.h" +#include "exdata/augment_trial.h" #include @@ -356,26 +359,26 @@ bool CItemEquipment::delPetModifier(Mod mod, PetModType petType, int16 modValue) void CItemEquipment::setTrialNumber(uint16 trial) { + auto& trialData = this->exdata(); + if (trial) { - ref(m_extra, 0x01) |= 0x40; + trialData.AugmentSubKind |= Exdata::AugmentSubKindFlags::Trial; setSubType(ITEM_AUGMENTED); - ref(m_extra, 0x00) |= 0x02; - ref(m_extra, 0x01) |= 0x03; + trialData.AugmentKind |= Exdata::AugmentKindFlags::HasAugments; + trialData.AugmentSubKind |= Exdata::AugmentSubKindFlags::Standard; } else { - ref(m_extra, 0x01) &= ~0x40; + trialData.AugmentSubKind &= ~Exdata::AugmentSubKindFlags::Trial; } - trial &= 0x7FFF; // Trial is only 15 bits long - ref(m_extra, 0x0A) &= ~0x7FFF; - ref(m_extra, 0x0A) |= trial; + trialData.TrialId = trial; } uint16 CItemEquipment::getTrialNumber() { - return ref(m_extra, 0x0A) & 0x7FFF; + return this->exdata().TrialId; } /************************************************************************ @@ -385,17 +388,20 @@ uint16 CItemEquipment::getTrialNumber() ************************************************************************/ void CItemEquipment::LoadAugment(uint8 slot, uint16 augment) { - ref(m_extra, 2 + (slot * 2)) = augment; + auto& augData = this->exdata(); + std::memcpy(&augData.Augments[slot], &augment, sizeof(uint16)); } bool CItemEquipment::PushAugment(uint16 type, uint8 value) { + auto& augData = this->exdata(); uint8 slot = 0; - uint16 augment = ref(m_extra, 2 + (slot * 2)); + uint16 augment = 0; + std::memcpy(&augment, &augData.Augments[slot], sizeof(uint16)); while (augment != 0 && slot < 4) { ++slot; - augment = ref(m_extra, 2 + (slot * 2)); + std::memcpy(&augment, &augData.Augments[slot], sizeof(uint16)); } if (augment == 0) { @@ -407,13 +413,15 @@ bool CItemEquipment::PushAugment(uint16 type, uint8 value) void CItemEquipment::ApplyAugment(uint8 slot) { - SetAugmentMod((uint16)unpackBitsBE(m_extra, 2 + (slot * 2), 0, 11), (uint8)unpackBitsBE(m_extra, 2 + (slot * 2), 11, 5)); + auto& augData = this->exdata(); + SetAugmentMod(augData.Augments[slot].Id, augData.Augments[slot].Value); } void CItemEquipment::setAugment(uint8 slot, uint16 type, uint8 value) { - packBitsBE(m_extra, type, 2 + (slot * 2), 0, 11); - packBitsBE(m_extra, value, 2 + (slot * 2), 11, 5); + auto& augData = this->exdata(); + augData.Augments[slot].Id = type; + augData.Augments[slot].Value = value; SetAugmentMod(type, value); } @@ -428,9 +436,10 @@ void CItemEquipment::SetAugmentMod(uint16 type, uint8 value) if (type != 0) { + auto& augData = this->exdata(); setSubType(ITEM_AUGMENTED); - ref(m_extra, 0x00) |= 0x02; - ref(m_extra, 0x01) |= 0x03; + augData.AugmentKind |= Exdata::AugmentKindFlags::HasAugments; + augData.AugmentSubKind |= Exdata::AugmentSubKindFlags::Standard; } const auto& augmentDataModifiers = sAugmentData[type]; @@ -469,5 +478,8 @@ void CItemEquipment::SetAugmentMod(uint16 type, uint8 value) uint16 CItemEquipment::getAugment(uint8 slot) { - return ref(m_extra, 2 + (slot * 2)); + auto& augData = this->exdata(); + uint16 result = 0; + std::memcpy(&result, &augData.Augments[slot], sizeof(uint16)); + return result; } diff --git a/src/map/items/item_equipment.h b/src/map/items/item_equipment.h index a3e667410ab..46b45a3f2c5 100644 --- a/src/map/items/item_equipment.h +++ b/src/map/items/item_equipment.h @@ -22,8 +22,6 @@ #ifndef _CITEMEQUIPMENT_H #define _CITEMEQUIPMENT_H -#include "common/utils.h" - #include #include "item_usable.h" diff --git a/src/map/items/item_furnishing.cpp b/src/map/items/item_furnishing.cpp index 93c7c5e418d..de5c264f212 100644 --- a/src/map/items/item_furnishing.cpp +++ b/src/map/items/item_furnishing.cpp @@ -169,8 +169,7 @@ bool CItemFurnishing::getOn2ndFloor() auto CItemFurnishing::getSignature() -> const std::string { - auto& sig = this->exdata().Signature; - return std::string(reinterpret_cast(sig), sizeof(sig)); + return Exdata::decodeSignature(this->exdata().Signature); } void CItemFurnishing::setSignature(const std::string& signature) diff --git a/src/map/items/item_linkshell.cpp b/src/map/items/item_linkshell.cpp index 81c78447b5f..8a4f0187cdb 100644 --- a/src/map/items/item_linkshell.cpp +++ b/src/map/items/item_linkshell.cpp @@ -22,10 +22,9 @@ #include "item_linkshell.h" #include "common/utils.h" +#include "exdata/linkshell.h" -#include - -CItemLinkshell::CItemLinkshell(uint16 id) +CItemLinkshell::CItemLinkshell(const uint16 id) : CItem(id) { setType(ITEM_LINKSHELL); @@ -35,49 +34,54 @@ CItemLinkshell::~CItemLinkshell() = default; uint32 CItemLinkshell::GetLSID() { - return ref(m_extra, 0x00); + return this->exdata().GroupId; } -void CItemLinkshell::SetLSID(uint32 lsid) +void CItemLinkshell::SetLSID(const uint32 lsid) { - ref(m_extra, 0x00) = lsid; + this->exdata().GroupId = lsid; } LSTYPE CItemLinkshell::GetLSType() { - return ref(m_extra, 0x08); + return static_cast(this->exdata().Flag); } -lscolor_t CItemLinkshell::GetLSColor() +auto CItemLinkshell::GetLSColor() -> Exdata::lscolor_t { - return *(lscolor_t*)(m_extra + 0x06); + return this->exdata().Color; } uint16 CItemLinkshell::GetLSRawColor() { - return ref(m_extra, 0x06); + uint16 raw = 0; + std::memcpy(&raw, &this->exdata().Color, sizeof(raw)); + return raw; } -void CItemLinkshell::SetLSColor(uint16 color) +void CItemLinkshell::SetLSColor(const uint16 color) { - ref(m_extra, 0x06) = color; + std::memcpy(&this->exdata().Color, &color, sizeof(color)); } const std::string CItemLinkshell::getSignature() { - char signature[LinkshellStringLength] = {}; - std::memcpy(&signature, m_extra + 0x09, sizeof(signature)); - - return signature; // return string copy + auto& name = this->exdata().Name; + char decoded[LinkshellStringLength] = {}; + DecodeStringLinkshell(std::string(reinterpret_cast(name), sizeof(name)), decoded); + return decoded; } void CItemLinkshell::setSignature(const std::string& signature) { - std::memset(m_extra + 0x09, 0, sizeof(m_extra) - 0x09); - std::memcpy(m_extra + 0x09, signature.c_str(), signature.size()); + auto& name = this->exdata().Name; + char encoded[LinkshellStringLength] = {}; + EncodeStringLinkshell(signature, encoded); + std::memset(name, 0, sizeof(name)); + std::memcpy(name, encoded, std::min(sizeof(encoded), sizeof(name))); } -void CItemLinkshell::SetLSType(LSTYPE value) +void CItemLinkshell::SetLSType(const LSTYPE value) { - ref(m_extra, 0x08) = value; + this->exdata().Flag = static_cast(value); } diff --git a/src/map/items/item_linkshell.h b/src/map/items/item_linkshell.h index c23fce12cdc..7abf6da41b6 100644 --- a/src/map/items/item_linkshell.h +++ b/src/map/items/item_linkshell.h @@ -24,16 +24,9 @@ #include "common/cbasetypes.h" +#include "exdata/linkshell.h" #include "item.h" -struct lscolor_t -{ - uint8 R : 4; - uint8 G : 4; - uint8 B : 4; - uint8 A : 4; -}; - // TODO: The LSTYPE definition is wrong here and values are off by 1 compared to what is actually passed // by the client. The correct values are listed in 0x0e2_set_lsmsg.h in GP_CLI_COMMAND_SET_LSMSG_WRITELEVEL enum LSTYPE : uint8 @@ -53,7 +46,7 @@ class CItemLinkshell : public CItem uint32 GetLSID(); LSTYPE GetLSType(); - lscolor_t GetLSColor(); + Exdata::lscolor_t GetLSColor(); uint16 GetLSRawColor(); void SetLSID(uint32 lsid); void SetLSColor(uint16 color); diff --git a/src/map/items/item_usable.cpp b/src/map/items/item_usable.cpp index 6bd41e92e41..576b53942ab 100644 --- a/src/map/items/item_usable.cpp +++ b/src/map/items/item_usable.cpp @@ -21,9 +21,9 @@ #include "item_usable.h" -#include "common/utils.h" #include "common/vana_time.h" #include "enums/action/animation.h" +#include "exdata/timer_info.h" CItemUsable::CItemUsable(uint16 id) : CItem(id) @@ -66,8 +66,8 @@ timer::duration CItemUsable::getReuseDelay() const void CItemUsable::setLastUseTime(timer::time_point LastUseTime) { - m_LastUseTime = LastUseTime; - ref(m_extra, 0x04) = earth_time::vanadiel_timestamp(timer::to_utc(LastUseTime)); + m_LastUseTime = LastUseTime; + this->exdata().TimeValue1 = earth_time::vanadiel_timestamp(timer::to_utc(LastUseTime)); } timer::time_point CItemUsable::getLastUseTime() @@ -82,12 +82,12 @@ timer::time_point CItemUsable::getNextUseTime() void CItemUsable::setCurrentCharges(uint8 CurrCharges) { - ref(m_extra, 0x01) = std::clamp(CurrCharges, 0, m_MaxCharges); + this->exdata().RemainingCharges = std::clamp(CurrCharges, 0, m_MaxCharges); } uint8 CItemUsable::getCurrentCharges() { - return ref(m_extra, 0x01); + return this->exdata().RemainingCharges; } void CItemUsable::setMaxCharges(uint8 MaxCharges) diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index 38316a1ec0d..27dda5946b7 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -24,6 +24,8 @@ #include "lua_battlefield.h" #include "lua_instance.h" #include "lua_item.h" + +#include "items/exdata/worn_item.h" #include "lua_spell.h" #include "lua_statuseffect.h" #include "lua_trade_container.h" @@ -4168,8 +4170,7 @@ auto CLuaBaseEntity::addItem(sol::variadic_args va) const -> CItem* player:addItem({ id = itemID, quantity = quantity }) -- add quantity of itemID player:addItem({ id = itemID, silent = true }) -- silently add 1 of itemID player:addItem({ id = itemID, signature = "Char" }) -- add 1 signed of itemID - player:addItem({ id = itemID, augments = { [4] = 5, [10] = 10 } }) -- add 1 of itemID with augment id 4 and 10, with values of 5 and 10, respectively - player:addItem({ id = itemID, exdata = { [10] = 10 } }) -- add 1 item of itemID, with the exdata at index 10 (0-indexed!) set to 10 + player:addItem({ id = itemID, exdata = { ... } }) -- add 1 of itemID with typed exdata (falls back to raw byte indices) */ if (va.get_type(0) == sol::type::table) @@ -4216,28 +4217,6 @@ auto CLuaBaseEntity::addItem(sol::variadic_args va) const -> CItem* PItem->setAppraisalID(appraisalObj.as()); } - if (PItem->isType(ITEM_EQUIPMENT)) - { - uint16 trial = table.get_or("trial", 0); - if (trial != 0) - { - ((CItemEquipment*)PItem)->setTrialNumber(trial); - } - - sol::object augmentsObj = table["augments"]; - if (augmentsObj.is()) - { - auto augmentsTable = augmentsObj.as(); - for (const auto& entryPair : augmentsTable) - { - auto pair = entryPair.second.as(); - uint16 augid = pair[0]; - uint8 augval = pair[1]; - ((CItemEquipment*)PItem)->PushAugment(augid, augval); - } - } - } - sol::object exdataObj = table["exdata"]; if (exdataObj.is()) { @@ -4302,16 +4281,6 @@ auto CLuaBaseEntity::addItem(sol::variadic_args va) const -> CItem* } } - uint16 augment0 = va.get_type(2) == sol::type::number ? va.get(2) : 0; - uint8 augment0val = va.get_type(3) == sol::type::number ? va.get(3) : 0; - uint16 augment1 = va.get_type(4) == sol::type::number ? va.get(4) : 0; - uint8 augment1val = va.get_type(5) == sol::type::number ? va.get(5) : 0; - uint16 augment2 = va.get_type(6) == sol::type::number ? va.get(6) : 0; - uint8 augment2val = va.get_type(7) == sol::type::number ? va.get(7) : 0; - uint16 augment3 = va.get_type(8) == sol::type::number ? va.get(8) : 0; - uint8 augment3val = va.get_type(9) == sol::type::number ? va.get(9) : 0; - uint16 trialNumber = va.get_type(10) == sol::type::number ? va.get(10) : 0; - while (PChar->getStorage(LOC_INVENTORY)->GetFreeSlotsCount() != 0 && quantity > 0) { if (CItem* PItem = itemutils::GetItem(itemID)) @@ -4319,29 +4288,6 @@ auto CLuaBaseEntity::addItem(sol::variadic_args va) const -> CItem* PItem->setQuantity(quantity); quantity -= PItem->getStackSize(); - if (PItem->isType(ITEM_EQUIPMENT)) - { - if (augment0 != 0) - { - ((CItemEquipment*)PItem)->setAugment(0, augment0, augment0val); - } - if (augment1 != 0) - { - ((CItemEquipment*)PItem)->setAugment(1, augment1, augment1val); - } - if (augment2 != 0) - { - ((CItemEquipment*)PItem)->setAugment(2, augment2, augment2val); - } - if (augment3 != 0) - { - ((CItemEquipment*)PItem)->setAugment(3, augment3, augment3val); - } - if (trialNumber != 0) - { - ((CItemEquipment*)PItem)->setTrialNumber(trialNumber); - } - } SlotID = charutils::AddItem(PChar, LOC_INVENTORY, PItem, silence); // Paranoid check @@ -4536,18 +4482,16 @@ bool CLuaBaseEntity::addUsedItem(uint16 itemID) * Notes : Used mainly for Testimonies and BCNM orbs ************************************************************************/ -uint8 CLuaBaseEntity::getWornUses(uint16 itemID) +auto CLuaBaseEntity::getWornUses(const uint16 itemID) const -> uint8 { - auto* PChar = static_cast(m_PBaseEntity); - uint8 slotID = PChar->getStorage(LOC_INVENTORY)->SearchItem(itemID); - + const auto* PChar = static_cast(m_PBaseEntity); + const uint8 slotID = PChar->getStorage(LOC_INVENTORY)->SearchItem(itemID); if (slotID != ERROR_SLOTID) { CItem* PItem = PChar->getStorage(LOC_INVENTORY)->GetItem(slotID); - if (PItem != nullptr) { - return PItem->m_extra[0]; + return PItem->exdata().UseCount; } } @@ -4561,35 +4505,23 @@ uint8 CLuaBaseEntity::getWornUses(uint16 itemID) * Notes : Prevent Orbs and Testimonies from being used again ************************************************************************/ -uint8 CLuaBaseEntity::incrementItemWear(uint16 itemID) +auto CLuaBaseEntity::incrementItemWear(const uint16 itemID) const -> uint8 { - auto* PChar = static_cast(m_PBaseEntity); - uint8 slotID = PChar->getStorage(LOC_INVENTORY)->SearchItem(itemID); - + const auto* PChar = static_cast(m_PBaseEntity); + const uint8 slotID = PChar->getStorage(LOC_INVENTORY)->SearchItem(itemID); if (slotID != ERROR_SLOTID) { CItem* PItem = PChar->getStorage(LOC_INVENTORY)->GetItem(slotID); - if (PItem == nullptr) { return 0; } - if (PItem->m_extra[0] == UINT8_MAX) - { - return PItem->m_extra[0]; - } + auto& useCount = PItem->exdata().UseCount; + useCount = std::min(useCount + 1, UINT8_MAX); + PItem->setDirty(true); - ++PItem->m_extra[0]; - - const char* Query = "UPDATE char_inventory " - "SET extra = ? " - "WHERE charid = ? AND location = ? AND slot = ? " - "LIMIT 1"; - - db::preparedStmt(Query, PItem->m_extra, PChar->id, PItem->getLocationID(), PItem->getSlotID()); - - return PItem->m_extra[0]; + return useCount; } return 0; @@ -4914,11 +4846,7 @@ bool CLuaBaseEntity::addLinkpearl(const std::string& lsname, bool equip) if (rset && rset->rowsCount() && rset->next()) { // build linkpearl - char EncodedString[LinkshellStringLength]; - - std::memset(&EncodedString, 0, sizeof(EncodedString)); - EncodeStringLinkshell(lsname, EncodedString); - ((CItem*)PItemLinkPearl)->setSignature(EncodedString); + PItemLinkPearl->setSignature(lsname); PItemLinkPearl->SetLSID(rset->get("linkshellid")); PItemLinkPearl->SetLSColor(rset->get("color")); PItemLinkPearl->SetLSType(lstype); diff --git a/src/map/lua/lua_baseentity.h b/src/map/lua/lua_baseentity.h index 829dd86a80b..a3913127159 100644 --- a/src/map/lua/lua_baseentity.h +++ b/src/map/lua/lua_baseentity.h @@ -251,8 +251,8 @@ class CLuaBaseEntity bool delContainerItems(const sol::object& containerID); bool addUsedItem(uint16 itemID); bool addTempItem(uint16 itemID, const sol::object& arg1); - uint8 getWornUses(uint16 itemID); // Check if the item is already worn - uint8 incrementItemWear(uint16 itemID); // Increment the item's worn value and returns it + auto getWornUses(uint16 itemID) const -> uint8; // Check if the item is already worn + auto incrementItemWear(uint16 itemID) const -> uint8; // Increment the item's worn value and returns it auto findItem(uint16 itemID, const sol::object& location) -> CItem*; // Like hasItem, but returns the item object (nil if not found) auto findItems(uint16 itemID, const sol::object& location) -> sol::table; auto getItems(const sol::object& location) -> sol::table; diff --git a/src/map/lua/lua_item.cpp b/src/map/lua/lua_item.cpp index 0d4c7715825..0dd0bd9ba57 100644 --- a/src/map/lua/lua_item.cpp +++ b/src/map/lua/lua_item.cpp @@ -90,7 +90,7 @@ uint16 CLuaItem::getTrialNumber() uint8 CLuaItem::getWornUses() { - return m_PLuaItem->m_extra[0]; + return m_PLuaItem->exdata().UseCount; } bool CLuaItem::isType(uint8 type) @@ -307,18 +307,7 @@ uint8 CLuaItem::getShieldAbsorptionRate() auto CLuaItem::getSignature() -> std::string { - char signature[DecodeStringLength] = {}; - - if (m_PLuaItem->isType(ITEM_LINKSHELL)) - { - DecodeStringLinkshell(m_PLuaItem->getSignature(), signature); - } - else - { - DecodeStringSignature(m_PLuaItem->getSignature(), signature); - } - - return signature; + return m_PLuaItem->getSignature(); } uint8 CLuaItem::getCurrentCharges() @@ -333,12 +322,12 @@ uint8 CLuaItem::getCurrentCharges() uint8 CLuaItem::getAppraisalID() { - return m_PLuaItem->m_extra[0x16]; + return m_PLuaItem->getAppraisalID(); } void CLuaItem::setAppraisalID(uint8 id) { - m_PLuaItem->m_extra[0x16] = id; + m_PLuaItem->setAppraisalID(id); } bool CLuaItem::isInstalled() diff --git a/src/map/packets/c2s/0x0c4_group_comlink_active.cpp b/src/map/packets/c2s/0x0c4_group_comlink_active.cpp index 1ea03c94371..6628475203b 100644 --- a/src/map/packets/c2s/0x0c4_group_comlink_active.cpp +++ b/src/map/packets/c2s/0x0c4_group_comlink_active.cpp @@ -44,13 +44,10 @@ const auto createLinkshell = [](CCharEntity* PChar, CItemLinkshell* PItemLinkshe uint32_t linkshellId = 0; const uint16_t linkshellColor = (data.a << 12) | (data.b << 8) | (data.g << 4) | data.r; - char DecodedName[DecodeStringLength] = {}; - char EncodedName[LinkshellStringLength] = {}; - - const auto encodedRawName = asStringFromUntrustedSource(data.sComLinkName, sizeof(data.sComLinkName)); + char DecodedName[DecodeStringLength] = {}; + const auto encodedRawName = asStringFromUntrustedSource(data.sComLinkName, sizeof(data.sComLinkName)); DecodeStringLinkshell(encodedRawName, DecodedName); - EncodeStringLinkshell(DecodedName, EncodedName); const auto safeName = db::escapeString(DecodedName); linkshellId = linkshell::RegisterNewLinkshell(safeName, linkshellColor); @@ -68,7 +65,7 @@ const auto createLinkshell = [](CCharEntity* PChar, CItemLinkshell* PItemLinkshe PChar->getStorage(data.Category)->InsertItem(PItemLinkshell, data.ItemIndex); PItemLinkshell->SetLSID(linkshellId); PItemLinkshell->SetLSType(LSTYPE_LINKSHELL); - PItemLinkshell->setSignature(EncodedName); // because apparently the format from the packet isn't right, and is missing terminators + PItemLinkshell->setSignature(DecodedName); PItemLinkshell->SetLSColor(linkshellColor); const auto rset = db::preparedStmt("UPDATE char_inventory SET signature = ?, extra = ?, itemId = 513 WHERE charid = ? AND location = ? AND slot = ? LIMIT 1", diff --git a/src/map/packets/c2s/0x0fb_myroom_bankin.cpp b/src/map/packets/c2s/0x0fb_myroom_bankin.cpp index 2b77a711548..3bc1e0131b1 100644 --- a/src/map/packets/c2s/0x0fb_myroom_bankin.cpp +++ b/src/map/packets/c2s/0x0fb_myroom_bankin.cpp @@ -23,6 +23,7 @@ #include "entities/charentity.h" #include "enums/item_lockflg.h" +#include "items/exdata/mannequin.h" #include "items/item_furnishing.h" #include "lua/luautils.h" #include "packets/char_status.h" @@ -71,17 +72,19 @@ void GP_CLI_COMMAND_MYROOM_BANKIN::process(MapSession* PSession, CCharEntity* PC if (PItem->isMannequin()) { PChar->pushPacket(static_cast(MyroomCategory), MyroomItemIndex, 0, 0, 0, 0, 0, 0, 0, 0); - for (uint8 i = 0; i < 8; ++i) + auto& mannequin = PItem->exdata(); + uint8_t* slots[] = { &mannequin.EquipMain, &mannequin.EquipSub, &mannequin.EquipRanged, &mannequin.EquipHead, &mannequin.EquipBody, &mannequin.EquipHands, &mannequin.EquipLegs, &mannequin.EquipFeet }; + for (auto* slot : slots) { - if (PItem->m_extra[10 + i] > 0) + if (*slot > 0) { - auto* PEquippedItem = PChar->getStorage(LOC_STORAGE)->GetItem(i); - if (PEquippedItem == nullptr) + auto* PEquippedItem = PChar->getStorage(LOC_STORAGE)->GetItem(*slot); + if (PEquippedItem != nullptr) { - continue; + PChar->pushPacket(PEquippedItem, ItemLockFlg::Normal); } - PChar->pushPacket(PEquippedItem, ItemLockFlg::Normal); - PItem->m_extra[10 + i] = 0; + + *slot = 0; } } } diff --git a/src/map/packets/char_status.cpp b/src/map/packets/char_status.cpp index 78d89a15e4f..72b408745f7 100644 --- a/src/map/packets/char_status.cpp +++ b/src/map/packets/char_status.cpp @@ -224,7 +224,7 @@ CCharStatusPacket::CCharStatusPacket(CCharEntity* PChar) if (linkshell && linkshell->isType(ITEM_LINKSHELL)) { - lscolor_t LSColor = linkshell->GetLSColor(); + Exdata::lscolor_t LSColor = linkshell->GetLSColor(); // This seems wrong, but displays correctly? packet->r = (LSColor.R << 4) + 15; diff --git a/src/map/packets/char_update.cpp b/src/map/packets/char_update.cpp index 369cb7aafb3..761ab5844b3 100644 --- a/src/map/packets/char_update.cpp +++ b/src/map/packets/char_update.cpp @@ -319,7 +319,7 @@ void CCharUpdatePacket::updateWith(CCharEntity* PChar, ENTITYUPDATE type, uint8 if (linkshell && linkshell->isType(ITEM_LINKSHELL)) { - const lscolor_t LSColor = linkshell->GetLSColor(); + const Exdata::lscolor_t LSColor = linkshell->GetLSColor(); // This seems wrong, but displays correctly? packet->Flags2.r = (LSColor.R << 4) + 15; diff --git a/src/map/packets/s2c/0x023_item_trade_list.cpp b/src/map/packets/s2c/0x023_item_trade_list.cpp index 5b4fbeb196c..75868d1ab97 100644 --- a/src/map/packets/s2c/0x023_item_trade_list.cpp +++ b/src/map/packets/s2c/0x023_item_trade_list.cpp @@ -53,7 +53,7 @@ GP_SERV_COMMAND_ITEM_TRADE_LIST::GP_SERV_COMMAND_ITEM_TRADE_LIST(CItem* PItem, c std::memcpy(&packet.Attr[6], &lscolor, 2); packet.Attr[8] = static_cast(PItem)->GetLSType(); - std::memcpy(&packet.Attr[9], PItem->getSignature().c_str(), std::min(PItem->getSignature().size(), 15)); + std::memcpy(&packet.Attr[9], static_cast(PItem)->exdata().Name, sizeof(Exdata::Linkshell::Name)); } else { diff --git a/src/map/packets/s2c/0x0c9_equip_inspect_equipment.cpp b/src/map/packets/s2c/0x0c9_equip_inspect_equipment.cpp index 618418787aa..bbcb8105a1d 100644 --- a/src/map/packets/s2c/0x0c9_equip_inspect_equipment.cpp +++ b/src/map/packets/s2c/0x0c9_equip_inspect_equipment.cpp @@ -24,6 +24,7 @@ #include "common/vana_time.h" #include "entities/charentity.h" #include "enums/packet_c2s.h" +#include "items/exdata/augment_standard.h" #include "items/item_equipment.h" #include "items/item_usable.h" @@ -77,7 +78,8 @@ GP_SERV_COMMAND_EQUIP_INSPECT::EQUIPMENT::EQUIPMENT(CCharEntity* PChar, CCharEnt std::memcpy(&item.Data[8], &aug3, sizeof(uint16)); } - std::memcpy(&item.Data[12], PItem->getSignature().c_str(), std::clamp(PItem->getSignature().size(), 0, 12)); + // All equipment exdata types have Signature at offset 0x0C; struct choice is irrelevant. + std::memcpy(&item.Data[12], PItem->exdata().Signature, sizeof(Exdata::AugmentStandard::Signature)); count++; diff --git a/src/map/packets/s2c/0x0c9_equip_inspect_general.cpp b/src/map/packets/s2c/0x0c9_equip_inspect_general.cpp index 409111392bc..490cce30b84 100644 --- a/src/map/packets/s2c/0x0c9_equip_inspect_general.cpp +++ b/src/map/packets/s2c/0x0c9_equip_inspect_general.cpp @@ -36,7 +36,7 @@ GP_SERV_COMMAND_EQUIP_INSPECT::GENERAL::GENERAL(const CCharEntity* PChar, const if (PLinkshell && PLinkshell->isType(ITEM_LINKSHELL)) { packet.ItemNo = PLinkshell->getID(); - std::memcpy(packet.sComLinkName, PLinkshell->getSignature().c_str(), std::clamp(PLinkshell->getSignature().size(), 0, 15)); + std::memcpy(packet.sComLinkName, PLinkshell->exdata().Name, sizeof(PLinkshell->exdata().Name)); packet.sComColor = PLinkshell->GetLSRawColor(); } diff --git a/src/map/utils/charutils.cpp b/src/map/utils/charutils.cpp index 522296fba31..84ea3e13c5f 100644 --- a/src/map/utils/charutils.cpp +++ b/src/map/utils/charutils.cpp @@ -1077,9 +1077,7 @@ void LoadInventory(CCharEntity* PChar) { static_cast(PItem)->SetLSType((LSTYPE)(PItem->getID() - 0x200)); } - char EncodedString[LinkshellStringLength] = {}; - EncodeStringLinkshell(rset->get("signature").c_str(), EncodedString); - PItem->setSignature(EncodedString); + PItem->setSignature(rset->get("signature")); } else if (PItem->getFlag() & (ITEM_FLAG_INSCRIBABLE)) { @@ -1734,17 +1732,7 @@ uint8 AddItem(CCharEntity* PChar, uint8 LocationID, CItem* PItem, bool silence) "VALUES(?, ?, ?, ?, ?, ?, ?) " "LIMIT 1"; - char signature[DecodeStringLength]; - if (PItem->isType(ITEM_LINKSHELL)) - { - DecodeStringLinkshell(PItem->getSignature().c_str(), signature); - } - else - { - DecodeStringSignature(PItem->getSignature().c_str(), signature); - } - - if (!db::preparedStmt(Query, PChar->id, LocationID, SlotID, PItem->getID(), PItem->getQuantity(), signature, PItem->m_extra)) + if (!db::preparedStmt(Query, PChar->id, LocationID, SlotID, PItem->getID(), PItem->getQuantity(), PItem->getSignature(), PItem->m_extra)) { ShowError("AddItem: Cannot insert item to database"); PChar->getStorage(LocationID)->InsertItem(nullptr, SlotID);