From 9b0853e13b2b69e12a1930a655e1e6c8cd84f9e4 Mon Sep 17 00:00:00 2001 From: sruon Date: Thu, 2 Apr 2026 22:50:48 -0600 Subject: [PATCH] Exdata definitions Escutcheons Weapon unlock Soul plate Soul reflectors Fish Co-Authored-By: atom0s --- scripts/commands/addfish.lua | 27 +-- scripts/enum/item.lua | 3 + scripts/globals/fishing_contest.lua | 82 ++------- scripts/globals/znm.lua | 92 +++++----- .../missions/asa/05_Enemy_Of_The_Empire_I.lua | 6 +- scripts/specs/core/CBaseEntity.lua | 10 -- scripts/specs/core/CItem.lua | 14 -- scripts/specs/core/Exdata.lua | 167 +++++++++++------- scripts/tests/systems/exdata.lua | 120 +++++++++++++ scripts/tests/systems/soultrapper.lua | 27 +++ src/common/utils.cpp | 86 ++++----- src/common/utils.h | 4 +- src/map/items.h | 6 + src/map/items/CMakeLists.txt | 10 ++ src/map/items/exdata.cpp | 65 +++++++ src/map/items/exdata.h | 5 + src/map/items/exdata/escutcheon.cpp | 48 +++++ src/map/items/exdata/escutcheon.h | 48 +++++ src/map/items/exdata/fish.cpp | 37 ++++ src/map/items/exdata/fish.h | 41 +++++ src/map/items/exdata/soul_plate.cpp | 51 ++++++ src/map/items/exdata/soul_plate.h | 44 +++++ src/map/items/exdata/soul_reflector.cpp | 121 +++++++++++++ src/map/items/exdata/soul_reflector.h | 51 ++++++ src/map/items/exdata/weapon_unlock.cpp | 32 ++++ src/map/items/exdata/weapon_unlock.h | 38 ++++ src/map/items/item.cpp | 33 ---- src/map/items/item.h | 2 - src/map/items/item_fish.cpp | 47 ++--- src/map/items/item_fish.h | 43 +---- src/map/items/item_weapon.cpp | 9 +- src/map/items/item_weapon.h | 1 + src/map/lua/lua_baseentity.cpp | 39 ---- src/map/lua/lua_baseentity.h | 2 - src/map/lua/lua_item.cpp | 21 --- src/map/lua/lua_item.h | 3 - src/map/utils/charutils.cpp | 10 +- src/map/utils/fishingutils.cpp | 29 +-- src/map/utils/fishingutils.h | 4 +- 39 files changed, 998 insertions(+), 480 deletions(-) create mode 100644 src/map/items/exdata/escutcheon.cpp create mode 100644 src/map/items/exdata/escutcheon.h create mode 100644 src/map/items/exdata/fish.cpp create mode 100644 src/map/items/exdata/fish.h create mode 100644 src/map/items/exdata/soul_plate.cpp create mode 100644 src/map/items/exdata/soul_plate.h create mode 100644 src/map/items/exdata/soul_reflector.cpp create mode 100644 src/map/items/exdata/soul_reflector.h create mode 100644 src/map/items/exdata/weapon_unlock.cpp create mode 100644 src/map/items/exdata/weapon_unlock.h diff --git a/scripts/commands/addfish.lua b/scripts/commands/addfish.lua index 9bfefe73c66..e953fb18ac1 100644 --- a/scripts/commands/addfish.lua +++ b/scripts/commands/addfish.lua @@ -17,32 +17,32 @@ local function error(player, msg) player:printToPlayer('!addfish ') end -commandObj.onTrigger = function(player, item, length, weight, ranked) +commandObj.onTrigger = function(player, itemId, length, weight, ranked) -- Load needed text ids for players current zone.. local ID = zones[player:getZoneID()] local itemToGet = 0 ranked = ranked and 1 or 0 -- validate item - if item == nil then + if itemId == nil then -- No Item Provided error(player, 'No Item ID given.') return - elseif item ~= nil and tonumber(item) == nil then + elseif itemId ~= nil and tonumber(itemId) == nil then -- Item was provided, but was not a number. Try text lookup. local retItems = utils.filterArray(xi.fishingContest.fish, function(_, fish) - return fish.name == item + return fish.name == itemId end) if #retItems ~= 1 then - player:printToPlayer(string.format('Item %s not found in fish table.', item)) + player:printToPlayer(string.format('Item %s not found in fish table.', itemId)) return end itemToGet = retItems[1].id - elseif tonumber(item) ~= nil then + elseif tonumber(itemId) ~= nil then -- Number was provided, so just use it - itemToGet = item + itemToGet = itemId end -- At this point, if there's no item found, exit out of the function @@ -68,11 +68,14 @@ commandObj.onTrigger = function(player, item, length, weight, ranked) return end - -- Give the GM the item... - local obtained = player:addItem({ id = itemToGet, - quantity = 1, - exdata = xi.fishingContest.createExData(length, weight, ranked) }) - if obtained then + -- Give the GM the item and set exdata... + local item = player:addItem({ id = itemToGet, quantity = 1 }) + if item then + item:setExData({ + size = length, + weight = weight, + isRanked = ranked == 1, + }) player:messageSpecial(ID.text.ITEM_OBTAINED, itemToGet) end end diff --git a/scripts/enum/item.lua b/scripts/enum/item.lua index 0888f3c0fee..b54806cac78 100644 --- a/scripts/enum/item.lua +++ b/scripts/enum/item.lua @@ -1669,6 +1669,7 @@ xi.item = CHUNK_OF_KAOLIN = 2475, SPOOL_OF_PLATINUM_SILK_THREAD = 2476, SOUL_PLATE = 2477, + SOUL_REFLECTOR = 2478, CHOCOBET_TICKET = 2479, RACE_COMPLETION_CERTIFICATE = 2481, MERCENARY_CAMP_ENTRY_SLIP = 2487, @@ -1676,6 +1677,7 @@ xi.item = FORBIDDEN_KEY = 2490, LEUJAOAM_OBSERVATION_LOG = 2491, ILRUSI_TRAVEL_LEDGER = 2495, + OFFICIAL_SOUL_REFLECTOR = 2500, BLACK_PUPPET_TURBAN = 2501, WHITE_PUPPET_TURBAN = 2502, HANDFUL_OF_ALMONDS = 2503, @@ -10565,6 +10567,7 @@ xi.item = TWINNED_SHIELD = 26414, ADAPA_SHIELD = 26420, NUSKU_SHIELD = 26421, + JOINERS_ASPIS = 26423, DIAMOND_ASPIS = 26488, TROTH = 26489, POROGGO_FLEECE = 26514, diff --git a/scripts/globals/fishing_contest.lua b/scripts/globals/fishing_contest.lua index c3ee4604e31..2c032fbc47f 100644 --- a/scripts/globals/fishing_contest.lua +++ b/scripts/globals/fishing_contest.lua @@ -4,13 +4,6 @@ xi = xi or {} xi.fishingContest = xi.fishingContest or {} -local exDataIndex = -{ - LENGTH = 0x00, - WEIGHT = 0x02, - RANKED = 0x04, -} - xi.fishingContest.fish = { -- NOTE: If you don't want certain fish included in the contest (era restrictions, or whatever), @@ -119,8 +112,6 @@ local function findFishSlot(trade, fish) return 0 end --- The npcUtil.giveItem function currently does not support custom exdata --- Since we need to include the measurements of the fish, we need a modified function local function giveFish(player, params) params = params or {} local ID = zones[player:getZoneID()] @@ -134,7 +125,12 @@ local function giveFish(player, params) end -- give items to player - if player:addItem(params) then + local fishItem = player:addItem({ id = fishid, quantity = params.quantity or 1 }) + if fishItem then + if params.exdata then + fishItem:setExData(params.exdata) + end + player:messageSpecial(ID.text.ITEM_OBTAINED, fishid) else player:messageSpecial(ID.text.ITEM_CANNOT_BE_OBTAINED, fishid) @@ -230,56 +226,6 @@ end -- GLOBAL FUNCTIONS ----------------------------------- --- Create the table of exdata -xi.fishingContest.createExData = function(length, weight, ranked) - -- If the provided data table has a nil value, the key will not be passed to the setExData function - -- setExData only accepts one-byte keys and vals so we need to break down the 16-bit vars - local exData = {} - if length ~= nil then - exData[exDataIndex.LENGTH ] = bit.band(length, 0x00FF) - exData[exDataIndex.LENGTH + 1] = bit.rshift(bit.band(length, 0xFF00), 8) - end - - if weight ~= nil then - exData[exDataIndex.WEIGHT ] = bit.band(weight, 0x00FF) - exData[exDataIndex.WEIGHT + 1] = bit.rshift(bit.band(weight, 0xFF00), 8) - end - - if ranked ~= nil then - exData[exDataIndex.RANKED ] = ranked - end - - return exData -end - --- Read the necessary data from the exdata -xi.fishingContest.getFishData = function(fishItem) - local fishData = fishItem:getExData() - local fishTable = {} - - fishTable['length'] = (bit.lshift(fishData[exDataIndex.LENGTH + 1], 8) + fishData[exDataIndex.LENGTH]) or 0 - fishTable['weight'] = (bit.lshift(fishData[exDataIndex.WEIGHT + 1], 8) + fishData[exDataIndex.WEIGHT]) or 0 - fishTable['ranked'] = fishData[exDataIndex.RANKED] - - return fishTable -end - --- Update the fish exdata -xi.fishingContest.setFishData = function(fishItem, length, weight, ranked) - -- Data Table should have only three possible options: 'length', 'width', and 'ranked' - if fishItem == nil then - return - end - - -- If the provided data table has a nil value, the key will not be passed to the setExData function - -- setExData only accepts one-byte keys and vals so we need to break down the 16-bit vars - local newExData = xi.fishingContest.createExData(length, weight, ranked) - - if newExData ~= nil then - fishItem:setExData(newExData) - end -end - xi.fishingContest.selectContestFish = function() return utils.randomEntry(xi.fishingContest.fish)['id'] end @@ -327,25 +273,25 @@ xi.fishingContest.onTrade = function(player, npc, trade) npcUtil.tradeHasExactly(trade, contest['fishid']) then local fishItem = trade:getItem(findFishSlot(trade, contest['fishid'])) - local fishData = xi.fishingContest.getFishData(fishItem) + local fishData = fishItem:getExData() if fishData == nil then return elseif - fishData['ranked'] == 1 or - fishData['length'] == 0 or - fishData['weight'] == 0 + fishData.isRanked or + fishData.size == 0 or + fishData.weight == 0 then -- Fish has already been ranked previously player:startEvent(10007, { [4] = 1 }) else -- Fish is a valid entry, not previously ranked -- Player local vars used to hold submission data until end of event - player:setLocalVar('[FishContest]Length', fishData['length']) - player:setLocalVar('[FishContest]Weight', fishData['weight']) + player:setLocalVar('[FishContest]Length', fishData.size) + player:setLocalVar('[FishContest]Weight', fishData.weight) player:startEvent(10007, { - [5] = scoreFish(fishData['length'], fishData['weight'], contest['criteria']), + [5] = scoreFish(fishData.size, fishData.weight, contest['criteria']), [6] = player:getContestScore(), }) end @@ -452,7 +398,7 @@ xi.fishingContest.onEventFinish = function(player, csid, option, npc) local weight = player:getLocalVar('[FishContest]Weight') local obtained = giveFish(player, { id = contest['fishid'], quantity = 1, - exdata = xi.fishingContest.createExData(length, weight, 1) }) + exdata = { size = length, weight = weight, isRanked = true } }) if obtained then player:confirmTrade() player:delGil(500) -- Pay the registration fee of 500 gil. diff --git a/scripts/globals/znm.lua b/scripts/globals/znm.lua index d3b12160b69..7eee7fb1590 100644 --- a/scripts/globals/znm.lua +++ b/scripts/globals/znm.lua @@ -13,22 +13,6 @@ local ID = zones[xi.zone.AHT_URHGAN_WHITEGATE] xi = xi or {} xi.znm = xi.znm or {} -local getHighBits16 = function(data) - return bit.rshift(data, 16) -end - -local getLowBits16 = function(data) - return bit.band(data, 0xFFFF) -end - -local setHighBits16 = function(data, diff) - return bit.bor(data, bit.lshift(getLowBits16(diff), 16)) -end - -local setLowBits16 = function(data, diff) - return bit.bor(data, getLowBits16(diff)) -end - ----------------------------------- -- Sanraku's Interest and Recommended Fauna -- Applies bonuses to soul plate zeni-value @@ -80,11 +64,8 @@ end --- Is this mob Sanraku's current 'Recommended Fauna'? xi.znm.isCurrentFauna = function(plateData) - -- local zeni = plateData.zeni - - local data = plateData.interestData - local zoneID = getHighBits16(data) - local mobName = plateData.name + local zoneID = plateData.zoneId + local mobName = plateData.signature local faunaRow = xi.znm.SANRAKUS_FAUNA[xi.znm.getSanrakusFauna()] if faunaRow.zone ~= zoneID then @@ -109,15 +90,14 @@ end -- Main interest objective xi.znm.isCurrentSuperFamily = function(plateData) - local data = plateData.interestData - local superFamily = getLowBits16(data) + local superFamily = plateData.superFamilyId local currectInterest = xi.znm.getSanrakusInterest() local interestRow = xi.znm.SANRAKUS_INTEREST[currectInterest] if superFamily == interestRow.superFamily then -- Handle elementals as all have same superFamily if currectInterest >= 45 and currectInterest <= 51 then - if plateData.name ~= interestRow.name then + if plateData.signature ~= interestRow.name then return false end end @@ -130,8 +110,7 @@ end -- Secondary interest objective xi.znm.isCurrentEcosystem = function(plateData) - local data = plateData.interestData - local superFamily = getLowBits16(data) + local superFamily = plateData.superFamilyId local currectInterest = xi.znm.getSanrakusInterest() local interestRow = xi.znm.SANRAKUS_INTEREST[currectInterest] @@ -144,7 +123,7 @@ end xi.znm.calculatePlateZeni = function(player, plateData) -- Cache the soulplate value on the player - local zeni = plateData.zeni + local zeni = plateData.quality local bonus = 'none' if xi.znm.isCurrentFauna(plateData) then @@ -169,7 +148,7 @@ xi.znm.calculatePlateZeni = function(player, plateData) zeni = utils.clamp(zeni, xi.znm.SOULPLATE_MIN_VALUE, xi.znm.SOULPLATE_MAX_VALUE) -- if player:getDebugMode() then - -- player:printToPlayer(string.format('name: %s zeni %i, bonus: %s', plateData.name, zeni, bonus)) + -- player:printToPlayer(string.format('name: %s zeni %i, bonus: %s', plateData.signature, zeni, bonus)) -- end return zeni @@ -234,23 +213,29 @@ xi.znm.soultrapper.onItemUse = function(target, player, item) player:removeAmmo(1) else - -- Determine Zeni starting value - local zeni = xi.znm.soultrapper.getZeniValue(target, player) + -- Deduct blank soul plate + player:removeAmmo(1) + + -- Determine quality starting value + local quality = xi.znm.soultrapper.getZeniValue(target, player) -- Pick a skill totally at random... local skillIndex, skillEntry = xi.pankration.getRandomFeralSkill(target) - local interestData = xi.znm.soultrapper.packInterestData(target) -- Add plate - local plate = player:addSoulPlate(target:getName(), interestData, zeni, skillIndex, skillEntry.fp) - local data = plate:getSoulPlateData() - - -- if player:getDebugMode() then - -- player:printToPlayer(string.format('mobName: %s zone: %i superID: %i SystemID: %i base zeni: %i', data.name, target:getZoneID(), target:getSuperFamily(), target:getSystem(), zeni)) - -- end - - utils.unused(plate) - utils.unused(data) + local plate = player:addItem({ id = xi.item.SOUL_PLATE, silent = true }) + if plate then + plate:setExData({ + signature = target:getName(), + zoneId = target:getZoneID(), + superFamilyId = target:getSuperFamily(), + poolId = target:getPool(), + level = target:getMainLvl(), + quality = quality, + feralSkill = skillIndex, + feralPoints = skillEntry.fp, + }) + end -- todo, message should show to all in area player:timer(4000, function(playerArg) @@ -335,15 +320,6 @@ xi.znm.soultrapper.getZeniValue = function(target, player) return zeni end -xi.znm.soultrapper.packInterestData = function(target) - local data = 0 - - data = setHighBits16(data, target:getZoneID()) - data = setLowBits16(data, target:getSuperFamily()) - - return data -end - ----------------------------------- -- Ryo ----------------------------------- @@ -358,7 +334,7 @@ xi.znm.ryo.onTrade = function(player, npc, trade) if npcUtil.tradeHasExactly(trade, xi.item.SOUL_PLATE) then -- Cache the soulplate value on the player local item = trade:getItem(0) - local zeni = xi.znm.calculatePlateZeni(player, item:getSoulPlateData()) + local zeni = xi.znm.calculatePlateZeni(player, item:getExData()) xi.znm.ryo.setTradedPlateValue(player, zeni) player:startEvent(914) @@ -424,6 +400,14 @@ xi.znm.ryo.onEventUpdate = function(player, csid, option, npc) end xi.znm.ryo.onEventFinish = function(player, csid, option, npc) + if csid == 914 then + local item = player:getTrade():getItem() + if item then + item:setReservedValue(0) + end + + player:getTrade():clean() + end end ----------------------------------- @@ -470,6 +454,12 @@ xi.znm.sanraku.handleTradeWithPlate = function(player, npc, item) if xi.znm.sanraku.platesTradedToday(player) >= tradeLimit then player:showText(npc, ID.text.APPRECIATE_MORE, 1, xi.item.SOUL_PLATE, tradeLimit) + local tradeItem = player:getTrade():getItem() + if tradeItem then + tradeItem:setReservedValue(0) + end + + player:getTrade():clean() return end else -- If you have the KI, clear out the tracking vars! @@ -477,7 +467,7 @@ xi.znm.sanraku.handleTradeWithPlate = function(player, npc, item) end -- Cache the soulplate value on the player - local zeni = xi.znm.calculatePlateZeni(player, item:getSoulPlateData()) + local zeni = xi.znm.calculatePlateZeni(player, item:getExData()) xi.znm.sanraku.setTradedPlateValue(player, zeni) xi.znm.serverPlateTrades() diff --git a/scripts/missions/asa/05_Enemy_Of_The_Empire_I.lua b/scripts/missions/asa/05_Enemy_Of_The_Empire_I.lua index 8e1a93bba81..070c559038d 100644 --- a/scripts/missions/asa/05_Enemy_Of_The_Empire_I.lua +++ b/scripts/missions/asa/05_Enemy_Of_The_Empire_I.lua @@ -31,7 +31,7 @@ local mobList = { 'Marsh Sahagin', 'MarshSahagin' }, { 'Rock Crab', 'RockCrab' }, { 'Razorjaw Pugil', 'RazorjawPugil' }, - { 'Sahagin Parasite', 'SahaginParasitd' }, + { 'Sahagin Parasite', 'SahaginParasite' }, { 'Swamp Sahagin', 'SwampSahagin' }, { 'Devil Manta', 'DevilManta' }, { 'Dire Bat', 'DireBat' }, @@ -52,11 +52,11 @@ local function handleTradeEvent(player, trade) local mobThree = mission:getVar(player, 'MobThree') local platesTraded = mission:getVar(player, 'Plates') local item = trade:getItem(0) - local plateData = item:getSoulPlateData() + local plateData = item:getExData() -- Check if traded Soulplate is one of the requested mobs for value, data in pairs(mobList) do - if data[2] == plateData.name then + if data[2] == plateData.signature then mission:setVar(player, 'Found', value) mission:setVar(player, 'Plates', platesTraded + 1) diff --git a/scripts/specs/core/CBaseEntity.lua b/scripts/specs/core/CBaseEntity.lua index 2a7cb9ddb4e..2edbbeb9803 100644 --- a/scripts/specs/core/CBaseEntity.lua +++ b/scripts/specs/core/CBaseEntity.lua @@ -1055,16 +1055,6 @@ end function CBaseEntity:addLinkpearl(lsname, equip) end ----@nodiscard ----@param name string ----@param interestData integer ----@param zeni integer ----@param skillIndex integer ----@param fp integer ----@return CItem? -function CBaseEntity:addSoulPlate(name, interestData, zeni, skillIndex, fp) -end - ---@nodiscard ---@param locationID integer ---@return integer diff --git a/scripts/specs/core/CItem.lua b/scripts/specs/core/CItem.lua index 0f183a2caf3..e6b98ade204 100644 --- a/scripts/specs/core/CItem.lua +++ b/scripts/specs/core/CItem.lua @@ -180,20 +180,6 @@ end function CItem:isInstalled() end ----@param name string ----@param interestData integer ----@param zeni integer ----@param skillIndex integer ----@param fp integer ----@return nil -function CItem:setSoulPlateData(name, interestData, zeni, skillIndex, fp) -end - ----@nodiscard ----@return table -function CItem:getSoulPlateData() -end - ---@nodiscard ---@return Exdata function CItem:getExData() diff --git a/scripts/specs/core/Exdata.lua b/scripts/specs/core/Exdata.lua index c5181490dff..39936090f4f 100644 --- a/scripts/specs/core/Exdata.lua +++ b/scripts/specs/core/Exdata.lua @@ -1,100 +1,141 @@ ---@meta ---@class ExdataLegionPass ----@field timestamp integer # How long until the pass expires. Usually 5 minutes from creation. ----@field title xi.legion.title # Legion chamber ----@field signature string # 12 characters pass owner name +---@field timestamp? integer # How long until the pass expires. Usually 5 minutes from creation. +---@field title? xi.legion.title # Legion chamber +---@field signature? string # 12 characters pass owner name ---@class ExdataPerpetualHourglass ----@field flags integer # Undocumented flags. Changes Hourglass text color to denote status. ----@field startTime integer # Reservation start time ----@field endTime integer # Reservation end time ----@field zoneId xi.zone # Zone reserved by Hourglass +---@field flags? integer # Undocumented flags. Changes Hourglass text color to denote status. +---@field startTime? integer # Reservation start time +---@field endTime? integer # Reservation end time +---@field zoneId? xi.zone # Zone reserved by Hourglass ---@class ExdataBettingSlip ----@field raceId integer # Chocobo race ID [0-262143] ----@field raceGrade xi.chocoboRacing.raceGrade ----@field racePairingL integer # [0-7], 0-indexed chocobo entry ----@field racePairingR integer # [0-7], 0-indexed chocobo entry ----@field quills integer # [0-999] +---@field raceId? integer # Chocobo race ID [0-262143] +---@field raceGrade? xi.chocoboRacing.raceGrade +---@field racePairingL? integer # [0-7], 0-indexed chocobo entry +---@field racePairingR? integer # [0-7], 0-indexed chocobo entry +---@field quills? integer # [0-999] ---@class ExdataAssaultLog ----@field flags boolean[] # 10 flags indexed [1-10] representing completed assaults. +---@field flags? boolean[] # 10 flags indexed [1-10] representing completed assaults. ---@class ExdataBrennerBook ----@field timeValue integer # Seconds since epoch 1009929600 (Jan 2, 2002 00:00 UTC) ----@field level xi.brenner.levelCap +---@field timeValue? integer # Seconds since epoch 1009929600 (Jan 2, 2002 00:00 UTC) +---@field level? xi.brenner.levelCap ---@class ExdataMeebleGrimoire ----@field clears table # clears[expeditionType][level] = count (0-7) ----@field count integer # Distinct expedition count ----@field zone xi.meeble.zone +---@field clears? table # clears[expeditionType][level] = count (0-7) +---@field count? integer # Distinct expedition count +---@field zone? xi.meeble.zone ---@class ExdataHoneymoonTicket ----@field plan xi.chocoboRaising.honeymoonPlan +---@field plan? xi.chocoboRaising.honeymoonPlan ---@class ExdataRaceCertificate ----@field raceId integer # [0-262143] ----@field raceGrade xi.chocoboRacing.raceGrade +---@field raceId? integer # [0-262143] +---@field raceGrade? xi.chocoboRacing.raceGrade ---@class ExdataLotteryTicket ----@field number integer # [0-16777215] ----@field title xi.bonanza.eventId +---@field number? integer # [0-16777215] +---@field title? xi.bonanza.eventId ---@class ExdataTabulaRune ----@field id xi.maze.rune ----@field rotation integer # Rune rotation [0-3] ----@field position integer # Grid index [0-24] on the 5x5 board +---@field id? xi.maze.rune +---@field rotation? integer # Rune rotation [0-3] +---@field position? integer # Grid index [0-24] on the 5x5 board ---@class ExdataTabula ----@field voucher xi.maze.voucher ----@field runes ExdataTabulaRune[] # Up to 12 runes ----@field uses integer # Uses count [0-127] (Tabula R only) +---@field voucher? xi.maze.voucher +---@field runes? ExdataTabulaRune[] # Up to 12 runes +---@field uses? integer # Uses count [0-127] (Tabula R only) ---@class ExdataEvolith ----@field augment integer # Augment ID [0-1023] ----@field shape xi.evolith.shape ----@field element xi.evolith.element ----@field bonus integer # Bonus [0-15] ----@field signature string # 12 characters +---@field augment? integer # Augment ID [0-1023] +---@field shape? xi.evolith.shape +---@field element? xi.evolith.element +---@field bonus? integer # Bonus [0-15] +---@field signature? string # 12 characters ---@class ExdataCraftingSet ----@field quality integer ----@field signature string # 12 characters +---@field quality? integer +---@field signature? string # 12 characters ---@class ExdataGlowingLamp ----@field chamberId xi.einherjar.chamber ----@field flags integer # Undocumented flags ----@field startTime integer # Reservation start time ----@field endTime integer # Reservation end time +---@field chamberId? xi.einherjar.chamber +---@field flags? integer # Undocumented flags +---@field startTime? integer # Reservation start time +---@field endTime? integer # Reservation end time ---@class ExdataChocoboEgg ----@field dna integer[] # 3 color genes indexed [1-3], each [0-7] ----@field ability xi.chocoboRaising.ability ----@field plan xi.chocoboRaising.honeymoonPlan ----@field isBred boolean # Whether the egg is from breeding +---@field dna? integer[] # 3 color genes indexed [1-3], each [0-7] +---@field ability? xi.chocoboRaising.ability +---@field plan? xi.chocoboRaising.honeymoonPlan +---@field isBred? boolean # Whether the egg is from breeding ---@class ExdataChocoboStatByte ----@field trait boolean # Physical trait flag (legs/tail/head) ----@field rp integer ----@field rank xi.chocoboRaising.statRank +---@field trait? boolean # Physical trait flag (legs/tail/head) +---@field rp? integer +---@field rank? xi.chocoboRaising.statRank ---@class ExdataChocoboStatByteRCP ----@field rp integer ----@field rank xi.chocoboRaising.statRank +---@field rp? integer +---@field rank? xi.chocoboRaising.statRank ---@class ExdataChocoboCard ----@field strength ExdataChocoboStatByte # Strength (legs trait) ----@field endurance ExdataChocoboStatByte # Endurance (tail trait) ----@field discernment ExdataChocoboStatByte # Discernment (head trait) ----@field receptivity ExdataChocoboStatByteRCP ----@field dna integer[] # 3 color genes indexed [1-3], each [0-7] ----@field abilities xi.chocoboRaising.ability[] ----@field temperament xi.chocoboRaising.temperament ----@field weather xi.chocoboRaising.weather ----@field gender xi.chocoboRaising.gender ----@field color xi.chocoboRaising.color ----@field size xi.chocoboRacing.jockeySize ----@field name string # 12 characters chocobo name - ----@alias Exdata ExdataLegionPass|ExdataPerpetualHourglass|ExdataBettingSlip|ExdataAssaultLog|ExdataBrennerBook|ExdataMeebleGrimoire|ExdataHoneymoonTicket|ExdataRaceCertificate|ExdataLotteryTicket|ExdataTabula|ExdataEvolith|ExdataCraftingSet|ExdataGlowingLamp|ExdataChocoboEgg|ExdataChocoboCard +---@field strength? ExdataChocoboStatByte # Strength (legs trait) +---@field endurance? ExdataChocoboStatByte # Endurance (tail trait) +---@field discernment? ExdataChocoboStatByte # Discernment (head trait) +---@field receptivity? ExdataChocoboStatByteRCP +---@field dna? integer[] # 3 color genes indexed [1-3], each [0-7] +---@field abilities? xi.chocoboRaising.ability[] +---@field temperament? xi.chocoboRaising.temperament +---@field weather? xi.chocoboRaising.weather +---@field gender? xi.chocoboRaising.gender +---@field color? xi.chocoboRaising.color +---@field size? xi.chocoboRacing.jockeySize +---@field name? string # 12 characters chocobo name + +---@class ExdataFish +---@field size? integer # Im, [0-65535] +---@field weight? integer # Pz, [0-65535] +---@field isRanked? boolean + +---@class ExdataEscutcheon +---@field status? integer # 0 = no craftsmanship data +---@field bonusObjective? xi.escutcheon.bonusObjective +---@field craftsmanship? integer # Raw craftsmanship accumulator +---@field stage? xi.escutcheon.stage +---@field successDownPenalty? integer # Scutum only. Accumulated penalty [0-65535]. Client display = (value-128)/3, capped at -30 +---@field signature? string # 12 characters + +---@class ExdataSoulPlate +---@field signature? string # 14 characters mob name +---@field zoneId? xi.zone +---@field superFamilyId? xi.mobSuperFamily +---@field poolId? xi.mobPool +---@field level? integer +---@field feralSkill? xi.pankration.feralSkill +---@field feralPoints? integer +---@field quality? integer # Zeni score based on distance, facing, HP%, claim [0-63] + +---@class ExdataFeralSkillSlot +---@field skillId? xi.pankration.feralSkill|integer +---@field level? integer + +---@class ExdataSoulReflector +---@field nameFirst? xi.pankration.firstName +---@field nameLast? xi.pankration.secondName +---@field poolId? xi.mobPool|integer +---@field exp? integer # Experience points [0-200] +---@field discipline? integer # Discipline accumulator [0-255]. /16 = xi.pankration.discipline +---@field temperament? integer # Tame/wild axis [0-15]. /2 = xi.pankration.temperament +---@field aggressiveness? integer # Aggressive/defensive axis [0-15]. /2 = xi.pankration.aggressiveness +---@field level? integer # Monster level [1-50] +---@field feralSkills? ExdataFeralSkillSlot[] # 7 slots: 1-5 equipped, 6-7 innate + +---@class ExdataWeaponUnlock +---@field unlockPoints? integer + +---@alias Exdata ExdataLegionPass|ExdataPerpetualHourglass|ExdataBettingSlip|ExdataAssaultLog|ExdataBrennerBook|ExdataMeebleGrimoire|ExdataHoneymoonTicket|ExdataRaceCertificate|ExdataLotteryTicket|ExdataTabula|ExdataEvolith|ExdataCraftingSet|ExdataGlowingLamp|ExdataChocoboEgg|ExdataChocoboCard|ExdataFish|ExdataEscutcheon|ExdataSoulPlate|ExdataSoulReflector|ExdataWeaponUnlock diff --git a/scripts/tests/systems/exdata.lua b/scripts/tests/systems/exdata.lua index edf49ee1ef0..399049a33a2 100644 --- a/scripts/tests/systems/exdata.lua +++ b/scripts/tests/systems/exdata.lua @@ -398,6 +398,126 @@ describe('Exdata', function() assert(ex.name == 'ChocoTest') end) + it('can get and set Fish exdata', function() + local item = player:addItem({ id = xi.item.LIK, quantity = 1 }) + assert(item) + + item:setExData( + { + size = 300, + weight = 1500, + isRanked = true, + }) + + local ex = item:getExData() + assert(ex.size == 300) + assert(ex.weight == 1500) + assert(ex.isRanked == true) + end) + + it('can get and set Escutcheon exdata', function() + local item = player:addItem({ id = xi.item.JOINERS_ASPIS, quantity = 1 }) + assert(item) + + item:setExData( + { + status = 1, + bonusObjective = xi.escutcheon.bonusObjective.CRAFT_DAYTIME, + craftsmanship = 2500, + stage = xi.escutcheon.stage.ASPIS, + successDownPenalty = 0, + signature = 'TestCrafter', + }) + + local ex = item:getExData() + assert(ex.status == 1) + assert(ex.bonusObjective == xi.escutcheon.bonusObjective.CRAFT_DAYTIME) + assert(ex.craftsmanship == 2500) + assert(ex.stage == xi.escutcheon.stage.ASPIS) + assert(ex.successDownPenalty == 0) + assert(ex.signature == 'TestCrafter') + end) + + it('can get and set Soul Plate exdata', function() + local item = player:addItem({ id = xi.item.SOUL_PLATE, quantity = 1 }) + assert(item) + + item:setExData( + { + signature = 'Goblin_Bounty_Hunter', + zoneId = xi.zone.QUFIM_ISLAND, + superFamilyId = xi.mobSuperFamily.GOBLIN, + poolId = xi.mobPool.BUGBEAR_MATMAN, + level = 12, + feralSkill = xi.pankration.feralSkill.MAIN_JOB_WARRIOR, + feralPoints = 50, + quality = 42, + }) + + local ex = item:getExData() + assert(ex.signature == 'GoblinBountyH') + assert(ex.zoneId == xi.zone.QUFIM_ISLAND) + assert(ex.superFamilyId == xi.mobSuperFamily.GOBLIN) + assert(ex.poolId == xi.mobPool.BUGBEAR_MATMAN) + assert(ex.level == 12) + assert(ex.feralSkill == xi.pankration.feralSkill.MAIN_JOB_WARRIOR) + assert(ex.feralPoints == 50) + assert(ex.quality == 42) + end) + + it('soul plate signature encodes and truncates correctly', function() + local item = player:addItem({ id = xi.item.SOUL_PLATE, quantity = 1 }) + assert(item) + + -- Goblin_Bounty_Hunter -> Goblin_Bounty_H -> GoblinBountyH + item:setExData({ signature = 'Goblin_Bounty_Hunter' }) + assert(item:getExData().signature == 'GoblinBountyH') + + -- Thunder_Elemental -> Thunder_Element -> ThunderElement + item:setExData({ signature = 'Thunder_Elemental' }) + assert(item:getExData().signature == 'ThunderElement') + + item:setExData({ signature = 'Crab' }) + assert(item:getExData().signature == 'Crab') + end) + + it('can get and set Soul Reflector exdata', function() + local item = player:addItem({ id = xi.item.SOUL_REFLECTOR, quantity = 1 }) + assert(item) + + item:setExData( + { + nameFirst = xi.pankration.firstName.BLOODY, + nameLast = xi.pankration.secondName.BEAST, + poolId = 200, + exp = 150, + discipline = 112, + temperament = 10, + aggressiveness = 8, + level = 60, + feralSkills = + { + { skillId = xi.pankration.feralSkill.MAIN_JOB_WARRIOR, level = 5 }, + { skillId = xi.pankration.feralSkill.SUPPORT_JOB_MONK, level = 3 }, + }, + }) + + local ex = item:getExData() + assert(ex.nameFirst == xi.pankration.firstName.BLOODY) + assert(ex.nameLast == xi.pankration.secondName.BEAST) + assert(ex.poolId == 200) + assert(ex.exp == 150) + assert(ex.discipline == 112) + assert(ex.temperament == 10) + assert(ex.aggressiveness == 8) + assert(ex.level == 60) + assert(#ex.feralSkills == 7) + assert(ex.feralSkills[1].skillId == xi.pankration.feralSkill.MAIN_JOB_WARRIOR) + assert(ex.feralSkills[1].level == 5) + assert(ex.feralSkills[2].skillId == xi.pankration.feralSkill.SUPPORT_JOB_MONK) + assert(ex.feralSkills[2].level == 3) + 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/scripts/tests/systems/soultrapper.lua b/scripts/tests/systems/soultrapper.lua index de9af8978e3..29bef62bc94 100644 --- a/scripts/tests/systems/soultrapper.lua +++ b/scripts/tests/systems/soultrapper.lua @@ -62,6 +62,33 @@ describe('Soultrapper', function() player.assert.no:hasItem(xi.item.SOUL_PLATE) end) + it('truncates long mob names to match retail encoding', function() + -- Thunder_Elemental -> Thunder_Element -> ThunderElement + local stormPlayer = xi.test.world:spawnPlayer( + { + job = xi.job.SMN, + level = 75, + zone = xi.zone.RUAUN_GARDENS, + }) + + stormPlayer:addItem(xi.item.SOULTRAPPER_2000) + stormPlayer:addItem(xi.item.BLANK_SOUL_PLATE, 1) + stormPlayer:equipItem(xi.item.SOULTRAPPER_2000) + stormPlayer:equipItem(xi.item.BLANK_SOUL_PLATE) + + local elemental = stormPlayer.entities:moveTo('Thunder_Elemental') + xi.test.world:skipTime(31) + stormPlayer.actions:useItem(elemental, stormPlayer:findItem(xi.item.SOULTRAPPER_2000):getSlotID()) + xi.test.world:skipTime(1) + xi.test.world:tickEntity(stormPlayer) + + local plate = stormPlayer:findItem(xi.item.SOUL_PLATE) + assert(plate) + + local ex = plate:getExData() + assert(ex.signature == 'ThunderElement') + end) + it('can be reused when cooldown reaches 0', function() -- Take first picture xi.test.world:skipTime(31) diff --git a/src/common/utils.cpp b/src/common/utils.cpp index 70519d7f3e6..881515e3de4 100644 --- a/src/common/utils.cpp +++ b/src/common/utils.cpp @@ -622,29 +622,24 @@ void DecodeStringSignature(const std::string& signature, char* target) // Take a regular string of 8-bit wide chars and packs it down into an // array of 7-bit wide chars. -void PackSoultrapperName(std::string name, uint8 output[]) +void PackSoultrapperName(std::string name, uint8* output) { - // Before anything else, sanitize the name string - // If contains underscore character - if (std::find(name.begin(), name.end(), '_') != name.end()) + // Truncate to entity name limit before removing underscores. + // e.g. Goblin_Bounty_Hunter -> Goblin_Bounty_H -> GoblinBountyH + // Thunder_Elemental -> Thunder_Element -> ThunderElement + if (name.length() > 15) { - // Remove underscores - name.erase(std::remove(name.begin(), name.end(), '_'), name.end()); + name.resize(15); } - // Add a space at the end to help with name truncation - // TODO: Remove the need for this - if (name.length() > 7) - { - name += ' '; - } + name.erase(std::ranges::remove(name, '_').begin(), name.end()); uint8 current = 0; uint8 next = 0; uint8 shift = 1; uint8 loops = 0; uint8 total = (uint8)name.length(); - uint8 maxSize = 13; // capped at 13 based on examples like GoblinBountyH + uint8 maxSize = 15; // Pack and shift 8-bit to 7-bit for (uint8 i = 0; i <= maxSize; ++i) @@ -669,7 +664,6 @@ void PackSoultrapperName(std::string name, uint8 output[]) shift = 1; loops++; i++; - total--; } else { @@ -678,52 +672,46 @@ void PackSoultrapperName(std::string name, uint8 output[]) } } -std::string UnpackSoultrapperName(uint8 input[]) +// Based on client logic for rendering plates names. +auto UnpackSoultrapperName(const uint8* input) -> std::string { - uint8 current = 0; - uint8 remainder = 0; - uint8 shift = 1; - uint8 maxSize = 13; // capped at 13 based on examples like GoblinBountyH - char indexChar = 0; - std::string output = ""; + constexpr uint8 bufSize = 14; // Retail is 18, but they never use the whole thing. Last 4 bytes repurposed as ZoneId/SuperFamilyId in LSB. + std::string output; + uint8 bitsLeft = 0; + uint8 byte = 0; + uint8 pos = 0; - // Unpack and shift 7-bit to 8-bit - for (uint8 i = 0; i <= maxSize; ++i) + for (;;) { - current = input[i]; - uint8 tempLeft = current; - uint8 tempRight = current; - - for (int j = 0; j < shift; ++j) + char c = 0; + for (int bit = 6; bit >= 0; --bit) { - tempLeft = tempLeft >> 1; - } - - indexChar = (char)(tempLeft | remainder); - if (indexChar >= '0' && indexChar <= 'z') - { - output += (char)(tempLeft | remainder); - } + if (bitsLeft == 0) + { + if (pos >= bufSize) + { + return output; + } - remainder = tempRight << (7 - shift); - if (remainder & 128) - { - remainder = remainder ^ 128; - } + byte = input[pos++]; + bitsLeft = 8; + } - if (shift == 7) - { - if (char(remainder) >= '0' && char(remainder) <= 'z') + if (byte & 0x80) { - output += char(remainder); + c |= (1 << bit); } - remainder = 0; - shift = 1; + + byte <<= 1; + --bitsLeft; } - else + + if (c == 0 || c < '0' || c > 'z') { - shift++; + break; } + + output += c; } return output; diff --git a/src/common/utils.h b/src/common/utils.h index 874ddcb07bc..72fc3b2e37f 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -149,8 +149,8 @@ void EncodeStringLinkshell(const std::string& signature, char* target); void DecodeStringLinkshell(const std::string& signature, char* target); std::string EncodeStringSignature(const std::string& signature, char* target); void DecodeStringSignature(const std::string& signature, char* target); -void PackSoultrapperName(std::string name, uint8 output[]); -std::string UnpackSoultrapperName(uint8 input[]); +void PackSoultrapperName(std::string name, uint8* output); +std::string UnpackSoultrapperName(const uint8* input); auto escape(const std::string& s) -> std::string; auto split(const std::string& s, const std::string& delimiter = " ") -> std::vector; diff --git a/src/map/items.h b/src/map/items.h index d5e6959b352..4f2f1598edc 100644 --- a/src/map/items.h +++ b/src/map/items.h @@ -57,13 +57,17 @@ enum ITEMID : uint16 VCS_HONEYMOON_TICKET = 2344, CRA_RACING_FORM = 2402, SOUL_PLATE = 2477, + SOUL_REFLECTOR = 2478, CHOCOBET_TICKET = 2479, + GAUGER_PLATE = 2480, RACE_COMPLETION_CERTIFICATE = 2481, LEUJAOAM_OBSERVATION_LOG = 2491, ILRUSI_TRAVEL_LEDGER = 2495, + OFFICIAL_SOUL_REFLECTOR = 2500, SANJAKU_TENUGUI = 2553, SOSHI = 2555, MOG_BONANZA_MARBLE = 2559, + FIEND_PLATE = 2560, KABENRO = 2642, JINKO = 2643, RYUNO = 2644, @@ -135,6 +139,8 @@ enum ITEMID : uint16 LADY_BELL = 18868, LADY_BELL_P1 = 18869, MARVELOUS_CHEER = 22283, + JOINERS_ASPIS = 26423, + CHEFS_SHIELD = 26461, MAZE_TABULA_M01 = 28672, MAZE_TABULA_M03 = 28674, MAZE_TABULA_R01 = 28704, diff --git a/src/map/items/CMakeLists.txt b/src/map/items/CMakeLists.txt index d5d776a0383..db4bdb177bb 100644 --- a/src/map/items/CMakeLists.txt +++ b/src/map/items/CMakeLists.txt @@ -14,8 +14,12 @@ set(ITEM_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/exdata/chocobo_egg.h ${CMAKE_CURRENT_SOURCE_DIR}/exdata/crafting_set.cpp ${CMAKE_CURRENT_SOURCE_DIR}/exdata/crafting_set.h + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/escutcheon.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/escutcheon.h ${CMAKE_CURRENT_SOURCE_DIR}/exdata/evolith.cpp ${CMAKE_CURRENT_SOURCE_DIR}/exdata/evolith.h + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/fish.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/fish.h ${CMAKE_CURRENT_SOURCE_DIR}/exdata/glowing_lamp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/exdata/glowing_lamp.h ${CMAKE_CURRENT_SOURCE_DIR}/exdata/honeymoon_ticket.cpp @@ -30,8 +34,14 @@ 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/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/weapon_unlock.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/weapon_unlock.h ${CMAKE_CURRENT_SOURCE_DIR}/item_currency.cpp ${CMAKE_CURRENT_SOURCE_DIR}/item_currency.h ${CMAKE_CURRENT_SOURCE_DIR}/item_equipment.cpp diff --git a/src/map/items/exdata.cpp b/src/map/items/exdata.cpp index 28d1a635eca..869d0968d69 100644 --- a/src/map/items/exdata.cpp +++ b/src/map/items/exdata.cpp @@ -22,8 +22,10 @@ #include "exdata.h" #include "item.h" +#include "item_weapon.h" #include "items.h" +#include "utils/fishingutils.h" #include namespace Exdata @@ -40,6 +42,11 @@ auto getType(const CItem* item) -> Type const auto itemId = item->getID(); + if (fishingutils::IsFish(item)) + { + return Type::Fish; + } + if (itemId == LEGION_PASS) { return Type::LegionPass; @@ -57,6 +64,16 @@ auto getType(const CItem* item) -> Type return Type::BrennerBook; } + if (itemId == SOUL_PLATE || itemId == GAUGER_PLATE || itemId == FIEND_PLATE) + { + return Type::SoulPlate; + } + + if (itemId == SOUL_REFLECTOR || itemId == OFFICIAL_SOUL_REFLECTOR) + { + return Type::SoulReflector; + } + if (itemId == CHOCOBET_TICKET) { return Type::BettingSlip; @@ -120,6 +137,24 @@ auto getType(const CItem* item) -> Type return Type::ChocoboCard; } + // Escutcheon (+4 is the finished item, no crafting exdata) + if (itemId >= JOINERS_ASPIS && itemId <= CHEFS_SHIELD && + (itemId - JOINERS_ASPIS) % 5 != 4) + { + return Type::Escutcheon; + } + + // Unlockable weapons use specific exdata + // Must be checked before augments + if (item->isType(ITEM_WEAPON)) + { + auto* PWeapon = static_cast(item); + if (PWeapon->isUnlockable()) + { + return Type::WeaponUnlock; + } + } + return Type::None; } @@ -173,6 +208,21 @@ auto toTable(const CItem* item, sol::table& table) -> bool case Type::ChocoboCard: item->exdata().toTable(table); return true; + case Type::Fish: + item->exdata().toTable(table); + return true; + case Type::Escutcheon: + item->exdata().toTable(table); + return true; + case Type::SoulPlate: + item->exdata().toTable(table); + return true; + case Type::SoulReflector: + item->exdata().toTable(table); + return true; + case Type::WeaponUnlock: + item->exdata().toTable(table); + return true; default: return false; } @@ -228,6 +278,21 @@ auto fromTable(CItem* item, const sol::table& data) -> bool case Type::ChocoboCard: item->exdata().fromTable(data); return true; + case Type::Fish: + item->exdata().fromTable(data); + return true; + case Type::Escutcheon: + item->exdata().fromTable(data); + return true; + case Type::SoulPlate: + item->exdata().fromTable(data); + return true; + case Type::SoulReflector: + item->exdata().fromTable(data); + return true; + case Type::WeaponUnlock: + item->exdata().fromTable(data); + return true; default: return false; } diff --git a/src/map/items/exdata.h b/src/map/items/exdata.h index 0a5d9254d73..46ed439f8cf 100644 --- a/src/map/items/exdata.h +++ b/src/map/items/exdata.h @@ -31,7 +31,9 @@ #include "exdata/chocobo_card.h" #include "exdata/chocobo_egg.h" #include "exdata/crafting_set.h" +#include "exdata/escutcheon.h" #include "exdata/evolith.h" +#include "exdata/fish.h" #include "exdata/glowing_lamp.h" #include "exdata/honeymoon_ticket.h" #include "exdata/legion_pass.h" @@ -39,7 +41,10 @@ #include "exdata/meeble_grimoire.h" #include "exdata/perpetual_hourglass.h" #include "exdata/race_certificate.h" +#include "exdata/soul_plate.h" +#include "exdata/soul_reflector.h" #include "exdata/tabula.h" +#include "exdata/weapon_unlock.h" class CItem; diff --git a/src/map/items/exdata/escutcheon.cpp b/src/map/items/exdata/escutcheon.cpp new file mode 100644 index 00000000000..dbdc2aa9d5d --- /dev/null +++ b/src/map/items/exdata/escutcheon.cpp @@ -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/ + +=========================================================================== +*/ + +#include "escutcheon.h" + +void Exdata::Escutcheon::toTable(sol::table& table) const +{ + table["status"] = this->Status; + table["bonusObjective"] = this->BonusObjective; + table["craftsmanship"] = this->Craftsmanship; + table["stage"] = static_cast(this->Stage); + table["successDownPenalty"] = static_cast(this->SuccessDownPenalty); + table["signature"] = Exdata::decodeSignature(this->Signature); +} + +void Exdata::Escutcheon::fromTable(const sol::table& data) +{ + this->AugmentKind = AugmentKindFlags::HasAugments; + this->AugmentSubKind = AugmentSubKindFlags::Escutcheon | AugmentSubKindFlags::Standard; + this->Status = Exdata::get_or(data, "status", this->Status); + this->BonusObjective = Exdata::get_or(data, "bonusObjective", this->BonusObjective); + this->Craftsmanship = Exdata::get_or(data, "craftsmanship", this->Craftsmanship); + this->Stage = Exdata::get_or(data, "stage", this->Stage); + this->SuccessDownPenalty = Exdata::get_or(data, "successDownPenalty", this->SuccessDownPenalty); + + if (sol::optional sig = data["signature"]) + { + Exdata::encodeSignature(*sig, this->Signature); + } +} diff --git a/src/map/items/exdata/escutcheon.h b/src/map/items/exdata/escutcheon.h new file mode 100644 index 00000000000..b00f60348b7 --- /dev/null +++ b/src/map/items/exdata/escutcheon.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 Escutcheon +{ + AugmentKindFlags AugmentKind; + AugmentSubKindFlags AugmentSubKind; + uint16_t padding00; + uint8_t Status; // 0 = no craftsmanship data. TODO: unreversed, values 3/4/8 observed + uint8_t BonusObjective; // Bonus objective granting extra Craftsmanship + uint16_t Craftsmanship; // Raw craftsmanship value, each tier has a different cap + uint32_t Stage : 4; // Upgrade stage (1=Aspis, 2=Ecu, 3=Scutum, 4=Shield) + uint32_t SuccessDownPenalty : 16; // Accumulated penalty. Scutum only. Client rate = (value-128)/3, capped -30. Unused for other tiers. + uint32_t padding01 : 12; + 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/fish.cpp b/src/map/items/exdata/fish.cpp new file mode 100644 index 00000000000..78457155ccb --- /dev/null +++ b/src/map/items/exdata/fish.cpp @@ -0,0 +1,37 @@ +/* +=========================================================================== + + 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 "fish.h" + +void Exdata::Fish::toTable(sol::table& table) const +{ + table["size"] = this->Size; + table["weight"] = this->Weight; + table["isRanked"] = static_cast(this->IsRanked); +} + +void Exdata::Fish::fromTable(const sol::table& data) +{ + this->Size = Exdata::get_or(data, "size", this->Size); + this->Weight = Exdata::get_or(data, "weight", this->Weight); + + this->IsRanked = Exdata::get_or(data, "isRanked", this->IsRanked) ? 1 : 0; +} diff --git a/src/map/items/exdata/fish.h b/src/map/items/exdata/fish.h new file mode 100644 index 00000000000..7d3267d09bd --- /dev/null +++ b/src/map/items/exdata/fish.h @@ -0,0 +1,41 @@ +/* +=========================================================================== + + 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 Fish +{ + uint16_t Size; + uint16_t Weight; + uint8_t IsRanked : 1; + uint8_t padding00 : 7; + uint8_t padding01[19]; + + void toTable(sol::table& table) const; + void fromTable(const sol::table& data); +}; +#pragma pack(pop) +} // namespace Exdata diff --git a/src/map/items/exdata/soul_plate.cpp b/src/map/items/exdata/soul_plate.cpp new file mode 100644 index 00000000000..3a97c6b972d --- /dev/null +++ b/src/map/items/exdata/soul_plate.cpp @@ -0,0 +1,51 @@ +/* +=========================================================================== + + 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 "soul_plate.h" + +void Exdata::SoulPlate::toTable(sol::table& table) const +{ + table["signature"] = UnpackSoultrapperName(this->Signature); + table["zoneId"] = this->ZoneId; + table["superFamilyId"] = this->SuperFamilyId; + table["poolId"] = this->PoolId; + table["level"] = this->Level; + table["feralSkill"] = this->FeralSkill; + table["feralPoints"] = this->FeralPoints; + table["quality"] = this->Quality; +} + +void Exdata::SoulPlate::fromTable(const sol::table& data) +{ + if (sol::optional sig = data["signature"]) + { + std::memset(this->Signature, 0, sizeof(this->Signature)); + PackSoultrapperName(*sig, this->Signature); + } + + this->ZoneId = Exdata::get_or(data, "zoneId", this->ZoneId); + this->SuperFamilyId = Exdata::get_or(data, "superFamilyId", this->SuperFamilyId); + this->PoolId = Exdata::get_or(data, "poolId", this->PoolId); + this->Level = Exdata::get_or(data, "level", this->Level); + this->FeralSkill = Exdata::get_or(data, "feralSkill", this->FeralSkill); + this->FeralPoints = Exdata::get_or(data, "feralPoints", this->FeralPoints); + this->Quality = Exdata::get_or(data, "quality", this->Quality); +} diff --git a/src/map/items/exdata/soul_plate.h b/src/map/items/exdata/soul_plate.h new file mode 100644 index 00000000000..4d433dc263d --- /dev/null +++ b/src/map/items/exdata/soul_plate.h @@ -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/ + +=========================================================================== +*/ + +#pragma once + +#include "base.h" + +namespace Exdata +{ +#pragma pack(push, 1) +struct SoulPlate +{ + uint8_t Signature[14]; // Retail: 18 bytes + uint16_t ZoneId; // Retail: does not exist + uint16_t SuperFamilyId; // Retail: does not exist + uint16_t PoolId; // Retail: Stores ID similar to pool IDs in LSB + uint32_t Level : 7; + uint32_t FeralSkill : 12; + uint32_t FeralPoints : 7; + uint32_t Quality : 6; // Zeni score + + void toTable(sol::table& table) const; + void fromTable(const sol::table& data); +}; +#pragma pack(pop) +} // namespace Exdata diff --git a/src/map/items/exdata/soul_reflector.cpp b/src/map/items/exdata/soul_reflector.cpp new file mode 100644 index 00000000000..aed723e70e2 --- /dev/null +++ b/src/map/items/exdata/soul_reflector.cpp @@ -0,0 +1,121 @@ +/* +=========================================================================== + + 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 "soul_reflector.h" + +#include "common/lua.h" + +namespace +{ +constexpr uint32_t SKILL_ID_BITS = 12; // 12 bits per skill ID +constexpr uint32_t SKILL_LEVEL_BITS = 7; // 7 bits per skill level +constexpr uint32_t BITS_PER_SLOT = SKILL_ID_BITS + SKILL_LEVEL_BITS; // 19 bits per feral skill slot +constexpr uint32_t FERAL_SLOTS = 7; // 5 equipped + 2 innate species abilities +constexpr int32_t FERAL_BIT_OFFSET = 59; // Feral Skills start + +// Bit-by-bit helpers for the feral skill bitstream that spans the uint64_t/uint8_t boundary. +// packBitsLE cannot be used here: it corrupts adjacent bits when fields straddle byte boundaries. +void setBitsLE(uint8_t* buf, uint64_t value, int32_t bitOffset, const uint8_t numBits) +{ + for (uint8_t i = 0; i < numBits; ++i, ++bitOffset, value >>= 1) + { + const auto mask = static_cast(1 << (bitOffset & 7)); + if (value & 1) + { + buf[bitOffset >> 3] |= mask; + } + else + { + buf[bitOffset >> 3] &= ~mask; + } + } +} + +auto getBitsLE(const uint8_t* buf, int32_t bitOffset, const uint8_t numBits) -> uint32_t +{ + uint32_t result = 0; + for (uint8_t i = 0; i < numBits; ++i, ++bitOffset) + { + if (buf[bitOffset >> 3] & 1 << (bitOffset & 7)) + { + result |= 1u << i; + } + } + + return result; +} +} // anonymous namespace + +void Exdata::SoulReflector::toTable(sol::table& table) const +{ + table["nameFirst"] = this->NameFirst; + table["nameLast"] = this->NameLast; + table["poolId"] = this->PoolId; + table["exp"] = this->Exp; + table["discipline"] = this->Discipline; + table["temperament"] = this->Temperament; + table["aggressiveness"] = this->Aggressiveness; + table["level"] = this->Level; + + const auto* raw = reinterpret_cast(this); + + sol::table skills = lua.create_table(); + for (uint32_t i = 0; i < FERAL_SLOTS; ++i) + { + const int32_t base = FERAL_BIT_OFFSET + i * BITS_PER_SLOT; + sol::table slot = lua.create_table(); + slot["skillId"] = getBitsLE(raw, base, SKILL_ID_BITS); + slot["level"] = getBitsLE(raw, base + SKILL_ID_BITS, SKILL_LEVEL_BITS); + skills[i + 1] = slot; + } + + table["feralSkills"] = skills; +} + +void Exdata::SoulReflector::fromTable(const sol::table& data) +{ + this->NameFirst = Exdata::get_or(data, "nameFirst", this->NameFirst); + this->NameLast = Exdata::get_or(data, "nameLast", this->NameLast); + this->PoolId = Exdata::get_or(data, "poolId", this->PoolId); + this->Exp = Exdata::get_or(data, "exp", this->Exp); + this->Discipline = Exdata::get_or(data, "discipline", this->Discipline); + this->Temperament = Exdata::get_or(data, "temperament", this->Temperament); + this->Aggressiveness = Exdata::get_or(data, "aggressiveness", this->Aggressiveness); + this->Level = Exdata::get_or(data, "level", this->Level); + + // Caution: Feral Skills bitstream straddles the uint64 used above. + auto* raw = reinterpret_cast(this); + if (sol::optional feralSkills = data["feralSkills"]) + { + for (uint32_t i = 0; i < FERAL_SLOTS; ++i) + { + if (sol::optional slot = (*feralSkills)[i + 1]) + { + const int32_t base = FERAL_BIT_OFFSET + i * BITS_PER_SLOT; + const uint32_t curSkillId = getBitsLE(raw, base, SKILL_ID_BITS); + const uint32_t curLevel = getBitsLE(raw, base + SKILL_ID_BITS, SKILL_LEVEL_BITS); + + setBitsLE(raw, Exdata::get_or(*slot, "skillId", curSkillId), base, SKILL_ID_BITS); + setBitsLE(raw, Exdata::get_or(*slot, "level", curLevel), base + SKILL_ID_BITS, SKILL_LEVEL_BITS); + } + } + } +} diff --git a/src/map/items/exdata/soul_reflector.h b/src/map/items/exdata/soul_reflector.h new file mode 100644 index 00000000000..c133ccacec1 --- /dev/null +++ b/src/map/items/exdata/soul_reflector.h @@ -0,0 +1,51 @@ +/* +=========================================================================== + + 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 SoulReflector +{ + uint64_t NameFirst : 6; + uint64_t NameLast : 6; + uint64_t PoolId : 16; + uint64_t Exp : 8; + uint64_t Discipline : 8; // Discipline accumulator. /16 = display index (0=extremely low .. 7=extremely high) + uint64_t Temperament : 4; // Tame/wild axis. /2 = display index (0=extremely tame .. 7=extremely wild) + uint64_t Aggressiveness : 4; // Aggressive/defensive axis. /2 = display index (0=extremely defensive .. 7=extremely aggressive) + uint64_t Level : 7; + + // Feral skill bitstream: 7 x 19-bit slots + // 1-5: equipped skills + // 6-7: innate species abilities + // Each slot: FeralSkillId:12 + SkillLevel:7 + uint64_t feralSkillStream0 : 5; + uint8_t feralSkillStream1[16]; + + void toTable(sol::table& table) const; + void fromTable(const sol::table& data); +}; +#pragma pack(pop) +} // namespace Exdata diff --git a/src/map/items/exdata/weapon_unlock.cpp b/src/map/items/exdata/weapon_unlock.cpp new file mode 100644 index 00000000000..9c5bf853a7d --- /dev/null +++ b/src/map/items/exdata/weapon_unlock.cpp @@ -0,0 +1,32 @@ +/* +=========================================================================== + + 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 "weapon_unlock.h" + +void Exdata::WeaponUnlock::toTable(sol::table& table) const +{ + table["unlockPoints"] = this->UnlockPoints; +} + +void Exdata::WeaponUnlock::fromTable(const sol::table& data) +{ + this->UnlockPoints = Exdata::get_or(data, "unlockPoints", this->UnlockPoints); +} diff --git a/src/map/items/exdata/weapon_unlock.h b/src/map/items/exdata/weapon_unlock.h new file mode 100644 index 00000000000..e94bcdf5dba --- /dev/null +++ b/src/map/items/exdata/weapon_unlock.h @@ -0,0 +1,38 @@ +/* +=========================================================================== + + 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 WeaponUnlock +{ + uint16_t UnlockPoints; + uint8_t padding00[22]; + + void toTable(sol::table& table) const; + void fromTable(const sol::table& data); +}; +#pragma pack(pop) +} // namespace Exdata diff --git a/src/map/items/item.cpp b/src/map/items/item.cpp index bae9e8befb3..bf985a6ecaa 100644 --- a/src/map/items/item.cpp +++ b/src/map/items/item.cpp @@ -388,39 +388,6 @@ bool CItem::isSoultrapper() const return m_id == 18721 || m_id == 18724; } -void CItem::setSoulPlateData(const std::string& name, uint32 interestData, uint8 zeni, uint16 skillIndex, uint8 fp) -{ - PackSoultrapperName(name, m_extra); - - // interestData is zone ID (high) + mob family ID (low) - m_extra[15] = (interestData >> 24) & 0xFF; - m_extra[16] = (interestData >> 16) & 0xFF; - m_extra[17] = (interestData >> 8) & 0xFF; - m_extra[18] = interestData & 0xFF; - - m_extra[19] = zeni; - - m_extra[20] = skillIndex << 7; - m_extra[21] = skillIndex >> 1; - m_extra[22] = skillIndex >> 9; - - m_extra[22] = fp << 3; - m_extra[23] = (0x03 << 4) & fp; -} - -auto CItem::getSoulPlateData() -> std::tuple -{ - auto name = UnpackSoultrapperName(m_extra); - uint32 interestData = (m_extra[15] << 24) | - (m_extra[16] << 16) | - (m_extra[17] << 8) | - m_extra[18]; - uint8 zeni = m_extra[19]; - uint16 skillIndex = (m_extra[20] >> 7) + (m_extra[21] << 1) + ((m_extra[22] & 0x03) << 9); - uint8 fp = (m_extra[22] >> 3) + ((m_extra[23] & 0x03) << 4); - return std::tuple(name, interestData, zeni, skillIndex, fp); -} - bool CItem::isMannequin() const { return m_id >= 256 && m_id <= 263; diff --git a/src/map/items/item.h b/src/map/items/item.h index 838568e8fba..7195d01529c 100644 --- a/src/map/items/item.h +++ b/src/map/items/item.h @@ -125,8 +125,6 @@ class CItem void setDirty(bool dirty); bool isSoultrapper() const; - void setSoulPlateData(const std::string& name, uint32 interestData, uint8 zeni, uint16 skillIndex, uint8 fp); - auto getSoulPlateData() -> std::tuple; bool isMannequin() const; diff --git a/src/map/items/item_fish.cpp b/src/map/items/item_fish.cpp index 21e27c7ca1a..a030f292295 100644 --- a/src/map/items/item_fish.cpp +++ b/src/map/items/item_fish.cpp @@ -1,4 +1,4 @@ -/* +/* =========================================================================== Copyright (c) 2010-2015 Darkstar Dev Teams @@ -21,60 +21,41 @@ #include "item_fish.h" -#include "common/utils.h" - -/************************************************************************ - * * - * * - * * - ************************************************************************/ +#include "exdata/fish.h" CItemFish::CItemFish(const CItem& PItem) : CItem(PItem) -, m_min(0) -, m_max(0) -, m_watertype(0) -, m_size(0) -, m_stamina(0) -, m_rodflag(0) { } CItemFish::~CItemFish() = default; -uint16 CItemFish::GetLength() +auto CItemFish::GetLength() -> uint16 { - return ref(m_extra, 0); + return this->exdata().Size; } -uint16 CItemFish::GetWeight() +auto CItemFish::GetWeight() -> uint16 { - return ref(m_extra, 2); + return this->exdata().Weight; } -bool CItemFish::IsRanked() +auto CItemFish::IsRanked() -> bool { - return (ref(m_extra, 4) & 0x01) == 0x01; + return this->exdata().IsRanked; } -void CItemFish::SetLength(uint16 length) +void CItemFish::SetLength(const uint16 length) { - ref(m_extra, 0) = length; + this->exdata().Size = length; } -void CItemFish::SetWeight(uint16 weight) +void CItemFish::SetWeight(const uint16 weight) { - ref(m_extra, 2) = weight; + this->exdata().Weight = weight; } -void CItemFish::SetRank(bool rank) +void CItemFish::SetRank(const bool rank) { - if (rank) - { - ref(m_extra, 4) |= 0x01; - } - else - { - ref(m_extra, 4) &= ~0x01; - } + this->exdata().IsRanked = rank ? 1 : 0; } diff --git a/src/map/items/item_fish.h b/src/map/items/item_fish.h index 8566a130c40..7e3dc6827cc 100644 --- a/src/map/items/item_fish.h +++ b/src/map/items/item_fish.h @@ -1,4 +1,4 @@ -/* +/* =========================================================================== Copyright (c) 2010-2015 Darkstar Dev Teams @@ -19,53 +19,22 @@ =========================================================================== */ -#ifndef _CITEMFISH_H -#define _CITEMFISH_H +#pragma once #include "common/cbasetypes.h" - #include "item.h" -/************************************************************************ - * * - * Fishing items. * - * * - ************************************************************************/ - class CItemFish : public CItem { public: CItemFish(const CItem& PItem); + virtual ~CItemFish(); - uint8 GetMin(); - uint8 GetMax(); - uint8 GetWatertype(); - uint8 GetSize(); - uint8 GetStamina(); - uint8 GetRodFlag(); - uint16 GetLength(); - uint16 GetWeight(); - bool IsRanked(); + auto GetLength() -> uint16; + auto GetWeight() -> uint16; + auto IsRanked() -> bool; - void SetMin(uint8); - void SetMax(uint8); - void SetWatertype(uint8); - void SetSize(uint8); - void SetStamina(uint8); - void SetRodFlag(uint8); void SetLength(uint16); void SetWeight(uint16); void SetRank(bool); - - virtual ~CItemFish(); - -private: - uint8 m_min; - uint8 m_max; - uint8 m_watertype; - uint8 m_size; - uint8 m_stamina; - uint8 m_rodflag; }; - -#endif diff --git a/src/map/items/item_weapon.cpp b/src/map/items/item_weapon.cpp index 9d2be2f1d48..b854ec8daca 100644 --- a/src/map/items/item_weapon.cpp +++ b/src/map/items/item_weapon.cpp @@ -364,9 +364,9 @@ uint16 CItemWeapon::getTotalUnlockPointsNeeded() const * * ************************************************************************/ -uint16 CItemWeapon::getCurrentUnlockPoints() +auto CItemWeapon::getCurrentUnlockPoints() -> uint16 { - return ref(m_extra, 0); + return this->exdata().UnlockPoints; } /************************************************************************ @@ -465,9 +465,10 @@ void CItemWeapon::setTotalUnlockPointsNeeded(uint16 points) * * ************************************************************************/ -void CItemWeapon::setCurrentUnlockPoints(uint16 points) +void CItemWeapon::setCurrentUnlockPoints(const uint16 points) { - ref(m_extra, 0) = points; + this->exdata().UnlockPoints = points; + setDirty(true); } /************************************************************************ diff --git a/src/map/items/item_weapon.h b/src/map/items/item_weapon.h index 2188ffe4ff5..2be93a92c75 100644 --- a/src/map/items/item_weapon.h +++ b/src/map/items/item_weapon.h @@ -26,6 +26,7 @@ #include "entities/battleentity.h" #include "item_equipment.h" +#include "items/exdata.h" class CItemWeapon : public CItemEquipment { diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index fd11cec7916..d5b564eada5 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -4954,43 +4954,6 @@ bool CLuaBaseEntity::addLinkpearl(const std::string& lsname, bool equip) return false; } -auto CLuaBaseEntity::addSoulPlate(const std::string& name, uint32 interestData, uint8 zeni, uint16 skillIndex, uint8 fp) -> CItem* -{ - if (m_PBaseEntity->objtype != TYPE_PC) - { - ShowWarning("Invalid entity type calling function (%s).", m_PBaseEntity->getName()); - return nullptr; - } - - if (auto* PChar = dynamic_cast(m_PBaseEntity)) - { - // Deduct Blank Plate - battleutils::RemoveAmmo(PChar); - - PChar->pushPacket(PChar); - - // Used Soul Plate - CItem* PItem = itemutils::GetItem(ITEMID::SOUL_PLATE); - - if (PItem == nullptr) - { - ShowError("PItem was null for soulplate"); - return nullptr; - } - - PItem->setQuantity(1); - PItem->setSoulPlateData(name, interestData, zeni, skillIndex, fp); - auto SlotID = charutils::AddItem(PChar, LOC_INVENTORY, PItem, true); - if (SlotID == ERROR_SLOTID) - { - return nullptr; - } - - return PItem; - } - return nullptr; -} - /************************************************************************ * Function: getContainerSize() * Purpose : Returns the size of an item container @@ -19883,8 +19846,6 @@ void CLuaBaseEntity::Register() SOL_REGISTER("breakLinkshell", CLuaBaseEntity::breakLinkshell); SOL_REGISTER("addLinkpearl", CLuaBaseEntity::addLinkpearl); - SOL_REGISTER("addSoulPlate", CLuaBaseEntity::addSoulPlate); - // Trading SOL_REGISTER("getContainerSize", CLuaBaseEntity::getContainerSize); SOL_REGISTER("changeContainerSize", CLuaBaseEntity::changeContainerSize); diff --git a/src/map/lua/lua_baseentity.h b/src/map/lua/lua_baseentity.h index b7b48533b76..829dd86a80b 100644 --- a/src/map/lua/lua_baseentity.h +++ b/src/map/lua/lua_baseentity.h @@ -263,8 +263,6 @@ class CLuaBaseEntity bool breakLinkshell(const std::string& lsname); bool addLinkpearl(const std::string& lsname, bool equip); - auto addSoulPlate(const std::string& name, uint32 interestData, uint8 zeni, uint16 skillIndex, uint8 fp) -> CItem*; - // Trading uint8 getContainerSize(uint8 locationID); void changeContainerSize(uint8 locationID, int8 newSize); // Increase/Decreases container size diff --git a/src/map/lua/lua_item.cpp b/src/map/lua/lua_item.cpp index e817eb2d384..0d4c7715825 100644 --- a/src/map/lua/lua_item.cpp +++ b/src/map/lua/lua_item.cpp @@ -351,25 +351,6 @@ bool CLuaItem::isInstalled() return PFurnishing->isInstalled(); } -void CLuaItem::setSoulPlateData(const std::string& name, uint32 interestData, uint8 zeni, uint16 skillIndex, uint8 fp) -{ - m_PLuaItem->setSoulPlateData(name, interestData, zeni, skillIndex, fp); -} - -auto CLuaItem::getSoulPlateData() -> sol::table -{ - auto data = m_PLuaItem->getSoulPlateData(); - sol::table table = lua.create_table(); - - table["name"] = std::get<0>(data); - table["interestData"] = std::get<1>(data); - table["zeni"] = std::get<2>(data); - table["skillIndex"] = std::get<3>(data); - table["fp"] = std::get<4>(data); - - return table; -} - /************************************************************************ * Function: getExData() * Purpose : Returns the item's extra data as a typed table. @@ -503,8 +484,6 @@ void CLuaItem::Register() SOL_REGISTER("setAppraisalID", CLuaItem::setAppraisalID); SOL_REGISTER("getCurrentCharges", CLuaItem::getCurrentCharges); SOL_REGISTER("isInstalled", CLuaItem::isInstalled); - SOL_REGISTER("setSoulPlateData", CLuaItem::setSoulPlateData); - SOL_REGISTER("getSoulPlateData", CLuaItem::getSoulPlateData); SOL_REGISTER("getExData", CLuaItem::getExData); SOL_REGISTER("setExData", CLuaItem::setExData); SOL_REGISTER("getExDataRaw", CLuaItem::getExDataRaw); diff --git a/src/map/lua/lua_item.h b/src/map/lua/lua_item.h index 655baf5723f..be6b19f4bed 100644 --- a/src/map/lua/lua_item.h +++ b/src/map/lua/lua_item.h @@ -92,9 +92,6 @@ class CLuaItem bool isInstalled(); - void setSoulPlateData(const std::string& name, uint32 interestData, uint8 zeni, uint16 skillIndex, uint8 fp); - auto getSoulPlateData() -> sol::table; - auto getExData() const -> sol::table; void setExData(const sol::table& data) const; auto getExDataRaw() const -> sol::table; // NOTE: 0-indexed, to be in line with the underlying C++ data diff --git a/src/map/utils/charutils.cpp b/src/map/utils/charutils.cpp index 19cac68f029..2725d30a1f1 100644 --- a/src/map/utils/charutils.cpp +++ b/src/map/utils/charutils.cpp @@ -4635,8 +4635,8 @@ EMobDifficulty CheckMob(uint8 charlvl, CBattleEntity* PMob) } } - auto IEPLevel = IncrediblyEasyPreyCheck.first; - auto IEPExp = IncrediblyEasyPreyCheck.second; + auto IEPExp = IncrediblyEasyPreyCheck.first; + auto IEPLevel = IncrediblyEasyPreyCheck.second; if (baseExp >= IEPExp && moblvl > IEPLevel) { @@ -7377,12 +7377,6 @@ bool AddWeaponSkillPoints(CCharEntity* PChar, SLOTTYPE slotid, int wspoints) PChar->pushPacket(PChar); } - db::preparedStmt("UPDATE char_inventory SET extra = ? WHERE charid = ? AND location = ? AND slot = ? LIMIT 1", - PWeapon->m_extra, - PChar->id, - PWeapon->getLocationID(), - PWeapon->getSlotID()); - return true; } return false; diff --git a/src/map/utils/fishingutils.cpp b/src/map/utils/fishingutils.cpp index b4c71dfe8bd..b58ef1fefdd 100644 --- a/src/map/utils/fishingutils.cpp +++ b/src/map/utils/fishingutils.cpp @@ -1145,34 +1145,15 @@ uint16 GetMessageOffset(uint16 ZoneID) return MessageOffset[ZoneID]; } -bool IsFish(CItem* fish) +auto IsFish(const CItem* fish) -> bool { - if (fish != nullptr && !FishList.empty()) - { - auto f = FishList.find(fish->getID()); - - if (f != FishList.end()) - { - return true; - } - } - - return false; + return fish && FishList.contains(fish->getID()); } -fish_t* GetFish(uint32 fishId) +auto GetFish(const uint32 fishId) -> fish_t* { - if (!FishList.empty()) - { - auto f = FishList.find(fishId); - - if (f != FishList.end()) - { - return f->second; - } - } - - return nullptr; + const auto f = FishList.find(fishId); + return f != FishList.end() ? f->second : nullptr; } /************************************************************************ diff --git a/src/map/utils/fishingutils.h b/src/map/utils/fishingutils.h index e005d40a953..191d4cbaa6f 100644 --- a/src/map/utils/fishingutils.h +++ b/src/map/utils/fishingutils.h @@ -953,8 +953,8 @@ std::vector GetItemPool(uint16 zoneID, uint8 areaID); std::vector GetMobPool(uint16 zoneId); std::vector GetChestPool(uint16 zoneId); uint16 GetMessageOffset(uint16 ZoneID); -bool IsFish(CItem* fish); -fish_t* GetFish(uint32 fishId); +auto IsFish(const CItem* fish) -> bool; +auto GetFish(uint32 fishId) -> fish_t*; // Fishing Areas bool onSegment(areavector_t p, areavector_t q, areavector_t r);