From 16c325eecb1534db6a5705df7b64ac1107348bac Mon Sep 17 00:00:00 2001 From: sruon Date: Sat, 4 Apr 2026 12:51:41 -0600 Subject: [PATCH] Exdata definitions Mannequin Furniture Flowerpots Co-Authored-By: atom0s --- .../otherAreas/Its_Raining_Mannequins.lua | 6 +- scripts/specs/core/Exdata.lua | 43 +++++++- scripts/tests/systems/exdata.lua | 67 +++++++++++++ src/map/items/CMakeLists.txt | 6 ++ src/map/items/exdata.cpp | 34 +++++++ src/map/items/exdata.h | 3 + src/map/items/exdata/flower_pot.cpp | 56 +++++++++++ src/map/items/exdata/flower_pot.h | 57 +++++++++++ src/map/items/exdata/furniture.cpp | 50 ++++++++++ src/map/items/exdata/furniture.h | 49 +++++++++ src/map/items/exdata/mannequin.cpp | 58 +++++++++++ src/map/items/exdata/mannequin.h | 54 ++++++++++ src/map/items/item.cpp | 4 +- src/map/items/item_flowerpot.cpp | 68 +++++-------- src/map/items/item_furnishing.cpp | 69 +++++++------ src/map/items/item_furnishing.h | 5 +- src/map/lua/lua_baseentity.cpp | 5 +- src/map/packets/c2s/0x03b_subcontainer.cpp | 99 +++++++++---------- .../packets/s2c/0x026_item_subcontainer.cpp | 44 +++++++++ src/map/packets/s2c/0x026_item_subcontainer.h | 7 ++ src/map/utils/charutils.cpp | 49 +-------- src/map/utils/synthutils.cpp | 5 +- 22 files changed, 650 insertions(+), 188 deletions(-) create mode 100644 src/map/items/exdata/flower_pot.cpp create mode 100644 src/map/items/exdata/flower_pot.h create mode 100644 src/map/items/exdata/furniture.cpp create mode 100644 src/map/items/exdata/furniture.h create mode 100644 src/map/items/exdata/mannequin.cpp create mode 100644 src/map/items/exdata/mannequin.h diff --git a/scripts/quests/otherAreas/Its_Raining_Mannequins.lua b/scripts/quests/otherAreas/Its_Raining_Mannequins.lua index d6a556ff980..e2a2870719c 100644 --- a/scripts/quests/otherAreas/Its_Raining_Mannequins.lua +++ b/scripts/quests/otherAreas/Its_Raining_Mannequins.lua @@ -159,7 +159,11 @@ quest.sections = if player:getFreeSlotsCount() > 0 and not player:hasItem(chosenMannequin) then if quest:complete(player) then player:tradeComplete() - player:addItem({ id = chosenMannequin, exdata = { [18] = race, [19] = 0 } }) + local item = player:addItem({ id = chosenMannequin }) + if item then + item:setExData({ race = race }) + end + player:messageSpecial(mhauraID.text.ITEM_OBTAINED, chosenMannequin) end else diff --git a/scripts/specs/core/Exdata.lua b/scripts/specs/core/Exdata.lua index 39936090f4f..d6c598d445a 100644 --- a/scripts/specs/core/Exdata.lua +++ b/scripts/specs/core/Exdata.lua @@ -138,4 +138,45 @@ ---@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 +---@class ExdataFurniture +---@field on2ndFloor? boolean +---@field installed? boolean +---@field x? integer +---@field z? integer +---@field y? integer +---@field rotation? integer +---@field order? integer # LSB-only: placement order for moghancement tiebreaking +---@field signature? string + +---@class ExdataFlowerPot +---@field step? integer # Flowerpot stage +---@field dried? boolean +---@field crystal1? integer # First crystal feed element - skipped for single crystal feed plants +---@field crystal2? integer # Second crystal feed element +---@field kind? integer # Seed/plant type +---@field examined? boolean # Examined since last wilt check +---@field strength? integer # RNG strength [0-127] +---@field x? integer +---@field z? integer +---@field y? integer +---@field rotation? integer +---@field timePlanted? integer # Vanatime when planted +---@field timeNextStep? integer # Vanatime of next stage + +---@class ExdataMannequin +---@field x? integer +---@field z? integer +---@field y? integer +---@field rotation? integer +---@field main? integer +---@field sub? integer +---@field ranged? integer +---@field head? integer +---@field body? integer +---@field hands? integer +---@field legs? integer +---@field feet? integer +---@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 diff --git a/scripts/tests/systems/exdata.lua b/scripts/tests/systems/exdata.lua index 399049a33a2..76395e24790 100644 --- a/scripts/tests/systems/exdata.lua +++ b/scripts/tests/systems/exdata.lua @@ -518,6 +518,73 @@ describe('Exdata', function() assert(ex.feralSkills[2].level == 3) end) + it('can get and set Furniture exdata', function() + local item = player:addItem({ id = xi.item.OAK_TABLE, quantity = 1 }) + assert(item) + + item:setExData( + { + installed = true, + on2ndFloor = false, + x = 5, + z = 0, + y = 10, + rotation = 3, + order = 1, + }) + + local ex = item:getExData() + assert(ex.installed == true) + assert(ex.on2ndFloor == false) + assert(ex.x == 5) + assert(ex.z == 0) + assert(ex.y == 10) + assert(ex.rotation == 3) + assert(ex.order == 1) + end) + + it('can get and set FlowerPot exdata', function() + local item = player:addItem({ id = xi.item.BRASS_FLOWERPOT, quantity = 1 }) + assert(item) + + item:setExData( + { + step = 3, + crystal1 = 1, + crystal2 = 4, + kind = 2, + examined = true, + strength = 50, + timePlanted = 100000, + timeNextStep = 200000, + }) + + local ex = item:getExData() + assert(ex.step == 3) + assert(ex.crystal1 == 1) + assert(ex.crystal2 == 4) + assert(ex.kind == 2) + assert(ex.examined == true) + assert(ex.strength == 50) + assert(ex.timePlanted == 100000) + assert(ex.timeNextStep == 200000) + end) + + it('can get and set Mannequin exdata', function() + local item = player:addItem({ id = xi.item.HUME_M_MANNEQUIN, quantity = 1 }) + assert(item) + + item:setExData( + { + race = xi.mannequin.type.HUME_M, + pose = xi.mannequin.pose.HURRAY, + }) + + local ex = item:getExData() + assert(ex.race == xi.mannequin.type.HUME_M) + assert(ex.pose == xi.mannequin.pose.HURRAY) + 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/CMakeLists.txt b/src/map/items/CMakeLists.txt index db4bdb177bb..107ba779c54 100644 --- a/src/map/items/CMakeLists.txt +++ b/src/map/items/CMakeLists.txt @@ -20,6 +20,10 @@ set(ITEM_SOURCES ${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/flower_pot.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/flower_pot.h + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/furniture.cpp + ${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/honeymoon_ticket.cpp @@ -28,6 +32,8 @@ set(ITEM_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/exdata/legion_pass.h ${CMAKE_CURRENT_SOURCE_DIR}/exdata/lottery_ticket.cpp ${CMAKE_CURRENT_SOURCE_DIR}/exdata/lottery_ticket.h + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/mannequin.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/exdata/mannequin.h ${CMAKE_CURRENT_SOURCE_DIR}/exdata/meeble_grimoire.cpp ${CMAKE_CURRENT_SOURCE_DIR}/exdata/meeble_grimoire.h ${CMAKE_CURRENT_SOURCE_DIR}/exdata/perpetual_hourglass.cpp diff --git a/src/map/items/exdata.cpp b/src/map/items/exdata.cpp index 869d0968d69..407570038e2 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_furnishing.h" #include "item_weapon.h" #include "items.h" @@ -144,6 +145,21 @@ auto getType(const CItem* item) -> Type return Type::Escutcheon; } + if (item->isType(ITEM_FURNISHING)) + { + if (item->isMannequin()) + { + return Type::Mannequin; + } + + if (static_cast(item)->isGardeningPot()) + { + return Type::FlowerPot; + } + + return Type::Furniture; + } + // Unlockable weapons use specific exdata // Must be checked before augments if (item->isType(ITEM_WEAPON)) @@ -223,6 +239,15 @@ auto toTable(const CItem* item, sol::table& table) -> bool case Type::WeaponUnlock: item->exdata().toTable(table); return true; + case Type::Furniture: + item->exdata().toTable(table); + return true; + case Type::FlowerPot: + item->exdata().toTable(table); + return true; + case Type::Mannequin: + item->exdata().toTable(table); + return true; default: return false; } @@ -293,6 +318,15 @@ auto fromTable(CItem* item, const sol::table& data) -> bool case Type::WeaponUnlock: item->exdata().fromTable(data); return true; + case Type::Furniture: + item->exdata().fromTable(data); + return true; + case Type::FlowerPot: + item->exdata().fromTable(data); + return true; + case Type::Mannequin: + item->exdata().fromTable(data); + return true; default: return false; } diff --git a/src/map/items/exdata.h b/src/map/items/exdata.h index 46ed439f8cf..a50cd41b348 100644 --- a/src/map/items/exdata.h +++ b/src/map/items/exdata.h @@ -34,10 +34,13 @@ #include "exdata/escutcheon.h" #include "exdata/evolith.h" #include "exdata/fish.h" +#include "exdata/flower_pot.h" +#include "exdata/furniture.h" #include "exdata/glowing_lamp.h" #include "exdata/honeymoon_ticket.h" #include "exdata/legion_pass.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" diff --git a/src/map/items/exdata/flower_pot.cpp b/src/map/items/exdata/flower_pot.cpp new file mode 100644 index 00000000000..7fc08b2ba5f --- /dev/null +++ b/src/map/items/exdata/flower_pot.cpp @@ -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/ + +=========================================================================== +*/ + +#include "flower_pot.h" + +void Exdata::FlowerPot::toTable(sol::table& table) const +{ + table["step"] = this->Step; + table["dried"] = static_cast(this->Dried); + table["crystal1"] = this->Crystal1; + table["crystal2"] = this->Crystal2; + table["kind"] = this->Kind; + table["examined"] = static_cast(this->Examined); + table["strength"] = this->Strength; + table["x"] = this->X; + table["z"] = this->Z; + table["y"] = this->Y; + table["rotation"] = this->Rotation; + table["timePlanted"] = this->TimePlanted; + table["timeNextStep"] = this->TimeNextStep; +} + +void Exdata::FlowerPot::fromTable(const sol::table& data) +{ + this->Step = Exdata::get_or(data, "step", this->Step); + this->Dried = Exdata::get_or(data, "dried", this->Dried) ? 1 : 0; + this->Crystal1 = Exdata::get_or(data, "crystal1", this->Crystal1); + this->Crystal2 = Exdata::get_or(data, "crystal2", this->Crystal2); + this->Kind = Exdata::get_or(data, "kind", this->Kind); + this->Examined = Exdata::get_or(data, "examined", this->Examined) ? 1 : 0; + this->Strength = Exdata::get_or(data, "strength", this->Strength); + this->X = Exdata::get_or(data, "x", this->X); + this->Z = Exdata::get_or(data, "z", this->Z); + this->Y = Exdata::get_or(data, "y", this->Y); + this->Rotation = Exdata::get_or(data, "rotation", this->Rotation); + this->TimePlanted = Exdata::get_or(data, "timePlanted", this->TimePlanted); + this->TimeNextStep = Exdata::get_or(data, "timeNextStep", this->TimeNextStep); +} diff --git a/src/map/items/exdata/flower_pot.h b/src/map/items/exdata/flower_pot.h new file mode 100644 index 00000000000..67d5ae2a290 --- /dev/null +++ b/src/map/items/exdata/flower_pot.h @@ -0,0 +1,57 @@ +/* +=========================================================================== + + 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 FlowerPot +{ + uint8_t Step; + uint8_t padding00 : 6; + uint8_t Installed : 1; + uint8_t Dried : 1; + uint8_t Crystal1; + uint8_t Crystal2; + uint8_t Kind; + uint8_t Examined : 1; + uint8_t Strength : 7; + uint8_t X; + uint8_t Z; + uint8_t Y; + uint8_t Rotation; + uint16_t padding01; + uint32_t TimePlanted; + uint32_t TimeNextStep; + // Observed as 00 00 00 00 on a freshly placed pot (never planted). + // Becomes 00 3C 04 08 after the first plant cycle and persists through + // harvest and subsequent replants. Seen on Ceramic Flowerpot with + // vegetable seeds (single-feed plant). Purpose unknown. + uint8_t unknown00[4]; + + void toTable(sol::table& table) const; + void fromTable(const sol::table& data); +}; +#pragma pack(pop) +} // namespace Exdata diff --git a/src/map/items/exdata/furniture.cpp b/src/map/items/exdata/furniture.cpp new file mode 100644 index 00000000000..34cbcd0132a --- /dev/null +++ b/src/map/items/exdata/furniture.cpp @@ -0,0 +1,50 @@ +/* +=========================================================================== + + 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 "furniture.h" + +void Exdata::Furniture::toTable(sol::table& table) const +{ + table["on2ndFloor"] = static_cast(this->On2ndFloor); + table["installed"] = static_cast(this->Installed); + table["x"] = this->X; + table["z"] = this->Z; + table["y"] = this->Y; + table["rotation"] = this->Rotation; + table["order"] = this->Order; + table["signature"] = Exdata::decodeSignature(this->Signature); +} + +void Exdata::Furniture::fromTable(const sol::table& data) +{ + this->On2ndFloor = Exdata::get_or(data, "on2ndFloor", this->On2ndFloor) ? 1 : 0; + this->Installed = Exdata::get_or(data, "installed", this->Installed) ? 1 : 0; + this->X = Exdata::get_or(data, "x", this->X); + this->Z = Exdata::get_or(data, "z", this->Z); + this->Y = Exdata::get_or(data, "y", this->Y); + this->Rotation = Exdata::get_or(data, "rotation", this->Rotation); + this->Order = Exdata::get_or(data, "order", this->Order); + + if (sol::optional sig = data["signature"]) + { + Exdata::encodeSignature(*sig, this->Signature); + } +} diff --git a/src/map/items/exdata/furniture.h b/src/map/items/exdata/furniture.h new file mode 100644 index 00000000000..4ab4ffc31b3 --- /dev/null +++ b/src/map/items/exdata/furniture.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 Furniture +{ + uint8_t Header; + uint8_t On2ndFloor : 1; + uint8_t padding00 : 5; + uint8_t Installed : 1; + uint8_t padding01 : 1; + uint8_t Order; // LSB-only: placement order for moghancement tiebreaking. Not present on retail. + uint8_t padding02[3]; + uint8_t X; + uint8_t Z; + uint8_t Y; + uint8_t Rotation; + uint8_t Signature[12]; + uint8_t padding03[2]; + + void toTable(sol::table& table) const; + void fromTable(const sol::table& data); +}; +#pragma pack(pop) +} // namespace Exdata diff --git a/src/map/items/exdata/mannequin.cpp b/src/map/items/exdata/mannequin.cpp new file mode 100644 index 00000000000..72b5298787b --- /dev/null +++ b/src/map/items/exdata/mannequin.cpp @@ -0,0 +1,58 @@ +/* +=========================================================================== + + 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 "mannequin.h" + +void Exdata::Mannequin::toTable(sol::table& table) const +{ + table["x"] = this->X; + table["z"] = this->Z; + table["y"] = this->Y; + table["rotation"] = this->Rotation; + table["main"] = this->EquipMain; + table["sub"] = this->EquipSub; + table["ranged"] = this->EquipRanged; + table["head"] = this->EquipHead; + table["body"] = this->EquipBody; + table["hands"] = this->EquipHands; + table["legs"] = this->EquipLegs; + table["feet"] = this->EquipFeet; + table["race"] = this->Race; + table["pose"] = this->Pose; +} + +void Exdata::Mannequin::fromTable(const sol::table& data) +{ + this->X = Exdata::get_or(data, "x", this->X); + this->Z = Exdata::get_or(data, "z", this->Z); + this->Y = Exdata::get_or(data, "y", this->Y); + this->Rotation = Exdata::get_or(data, "rotation", this->Rotation); + this->EquipMain = Exdata::get_or(data, "main", this->EquipMain); + this->EquipSub = Exdata::get_or(data, "sub", this->EquipSub); + this->EquipRanged = Exdata::get_or(data, "ranged", this->EquipRanged); + this->EquipHead = Exdata::get_or(data, "head", this->EquipHead); + this->EquipBody = Exdata::get_or(data, "body", this->EquipBody); + this->EquipHands = Exdata::get_or(data, "hands", this->EquipHands); + this->EquipLegs = Exdata::get_or(data, "legs", this->EquipLegs); + this->EquipFeet = Exdata::get_or(data, "feet", this->EquipFeet); + this->Race = Exdata::get_or(data, "race", this->Race); + this->Pose = Exdata::get_or(data, "pose", this->Pose); +} diff --git a/src/map/items/exdata/mannequin.h b/src/map/items/exdata/mannequin.h new file mode 100644 index 00000000000..fc79d5e7c9b --- /dev/null +++ b/src/map/items/exdata/mannequin.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" + +namespace Exdata +{ +#pragma pack(push, 1) +struct Mannequin +{ + uint8_t Header; + uint8_t Flags; + uint32_t padding00; + uint8_t X; + uint8_t Z; + uint8_t Y; + uint8_t Rotation; + uint8_t EquipMain; + uint8_t EquipSub; + uint8_t EquipRanged; + uint8_t EquipHead; + uint8_t EquipBody; + uint8_t EquipHands; + uint8_t EquipLegs; + uint8_t EquipFeet; + uint8_t Race; + uint8_t Pose; + uint8_t padding01[4]; + + 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 bf985a6ecaa..e10bf087522 100644 --- a/src/map/items/item.cpp +++ b/src/map/items/item.cpp @@ -310,8 +310,10 @@ const std::string CItem::getSignature() void CItem::setSignature(const std::string& signature) { + char encoded[SignatureStringLength] = {}; + EncodeStringSignature(signature, encoded); std::memset(m_extra + 0x0C, 0, sizeof(m_extra) - 0x0C); - std::memcpy(m_extra + 0x0C, signature.c_str(), signature.size()); + std::memcpy(m_extra + 0x0C, encoded, sizeof(m_extra) - 0x0C); } /************************************************************************ diff --git a/src/map/items/item_flowerpot.cpp b/src/map/items/item_flowerpot.cpp index b87a704bfcb..3ab7f153c1c 100644 --- a/src/map/items/item_flowerpot.cpp +++ b/src/map/items/item_flowerpot.cpp @@ -1,4 +1,4 @@ -/* +/* =========================================================================== Copyright (c) 2010-2015 Darkstar Dev Teams @@ -21,20 +21,7 @@ #include "item_flowerpot.h" -#include "common/utils.h" - -// Flowerpot Extra data explained: -// 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 -// Example: 08 40 03 07 01 37 0A 00 17 00 00 00 E5 C8 9A 1F 88 1A 9F 1F 08 3C 04 08 -// 00 - Flowerpot stage ID (FLOWERPOT_STAGE_TYPE). Plants do not necessarily follow this sequentially and will skip some stages based on seed type. -// 01 - Tracks if displayed as well as if the plant has been dried -// 02 - Crystal feed element ID (FLOWERPOT_ELEMENT_TYPE). 4095 + element ID is the crystal item ID. This one is only used by trees that take two feedings. -// 03 - Same as 0x02 but is the common one used by all plants. -// 04 - Seed type (FLOWERPOT_PLANT_TYPE) of the plant -// 05 - Bit 1 tracks if the plant was examined since the last wilting check. Bits 2-N store the RNG "strength" of the plant. -// 06-0B - MH Display info used in CItemFurnishing -// 0C-0F - Vanatime of when the seed was planted in the flowerpot -// 10-13 - Vanatime of when the next plant stage will occur +#include "exdata/flower_pot.h" CItemFlowerpot::CItemFlowerpot(uint16 id) : CItemFurnishing(id) @@ -56,7 +43,7 @@ void CItemFlowerpot::cleanPot() bool CItemFlowerpot::isPlanted() { - return ref(m_extra, 0x00) > 0; + return this->exdata().Step > 0; } bool CItemFlowerpot::isTree() @@ -79,35 +66,28 @@ bool CItemFlowerpot::isTree() void CItemFlowerpot::setDried(bool dried) { - if (dried) - { - ref(m_extra, 0x01) |= 0x80; - } - else - { - ref(m_extra, 0x01) &= ~0x80; - } + this->exdata().Dried = dried ? 1 : 0; } bool CItemFlowerpot::isDried() { - return ref(m_extra, 0x01) & 0x80; + return this->exdata().Dried; } bool CItemFlowerpot::canGrow() { - uint8 stage = ref(m_extra, 0x00); + uint8 stage = this->exdata().Step; return stage >= FLOWERPOT_STAGE_INITIAL && stage <= FLOWERPOT_STAGE_THIRD_SPROUTS && !isDried(); } void CItemFlowerpot::setPlant(FLOWERPOT_PLANT_TYPE plant) { - ref(m_extra, 0x04) = plant; + this->exdata().Kind = plant; } FLOWERPOT_PLANT_TYPE CItemFlowerpot::getPlant() { - return (FLOWERPOT_PLANT_TYPE)ref(m_extra, 0x04); + return static_cast(this->exdata().Kind); } uint16 CItemFlowerpot::getSeedID(FLOWERPOT_PLANT_TYPE plantType) @@ -163,32 +143,32 @@ FLOWERPOT_PLANT_TYPE CItemFlowerpot::getPlantFromSeed(uint16 seedID) void CItemFlowerpot::setStage(FLOWERPOT_STAGE_TYPE stage) { - ref(m_extra, 0x00) = stage; + this->exdata().Step = stage; } FLOWERPOT_STAGE_TYPE CItemFlowerpot::getStage() { - return (FLOWERPOT_STAGE_TYPE)ref(m_extra, 0x00); + return static_cast(this->exdata().Step); } void CItemFlowerpot::setFirstCrystalFeed(FLOWERPOT_ELEMENT_TYPE element) { - ref(m_extra, 0x02) = element; + this->exdata().Crystal1 = element; } void CItemFlowerpot::setSecondCrystalFeed(FLOWERPOT_ELEMENT_TYPE element) { - ref(m_extra, 0x03) = element; + this->exdata().Crystal2 = element; } FLOWERPOT_ELEMENT_TYPE CItemFlowerpot::getExtraCrystalFeed() { - return (FLOWERPOT_ELEMENT_TYPE)ref(m_extra, 0x02); + return static_cast(this->exdata().Crystal1); } FLOWERPOT_ELEMENT_TYPE CItemFlowerpot::getCommonCrystalFeed() { - return (FLOWERPOT_ELEMENT_TYPE)ref(m_extra, 0x03); + return static_cast(this->exdata().Crystal2); } int16 CItemFlowerpot::getItemFromElement(FLOWERPOT_ELEMENT_TYPE element) @@ -200,50 +180,50 @@ int16 CItemFlowerpot::getItemFromElement(FLOWERPOT_ELEMENT_TYPE element) FLOWERPOT_ELEMENT_TYPE CItemFlowerpot::getElementFromItem(int16 itemID) { // Element and crystal item id ordering is the same with an offset - return (FLOWERPOT_ELEMENT_TYPE)(itemID - 4095); + return static_cast(itemID - 4095); } void CItemFlowerpot::setPlantTimestamp(uint32 vanatime) { - ref(m_extra, 0x0C) = vanatime; + this->exdata().TimePlanted = vanatime; } uint32 CItemFlowerpot::getPlantTimestamp() { - return ref(m_extra, 0x0C); + return this->exdata().TimePlanted; } void CItemFlowerpot::setStageTimestamp(uint32 vanatime) { - ref(m_extra, 0x10) = vanatime; + this->exdata().TimeNextStep = vanatime; } uint32 CItemFlowerpot::getStageTimestamp() { - return ref(m_extra, 0x10); + return this->exdata().TimeNextStep; } void CItemFlowerpot::clearExamined() { - ref(m_extra, 0x05) &= ~1; + this->exdata().Examined = 0; } void CItemFlowerpot::markExamined() { - ref(m_extra, 0x05) |= 1; + this->exdata().Examined = 1; } bool CItemFlowerpot::wasExamined() { - return ref(m_extra, 0x05) & 1; + return this->exdata().Examined; } void CItemFlowerpot::setStrength(uint8 strength) { - ref(m_extra, 0x05) = strength << 1; + this->exdata().Strength = strength; } uint8 CItemFlowerpot::getStrength() { - return ref(m_extra, 0x05) >> 1; + return this->exdata().Strength; } diff --git a/src/map/items/item_furnishing.cpp b/src/map/items/item_furnishing.cpp index 19adaa015a9..93c7c5e418d 100644 --- a/src/map/items/item_furnishing.cpp +++ b/src/map/items/item_furnishing.cpp @@ -1,4 +1,4 @@ -/* +/* =========================================================================== Copyright (c) 2010-2015 Darkstar Dev Teams @@ -21,7 +21,8 @@ #include "item_furnishing.h" -#include "common/utils.h" +#include "exdata/furniture.h" +#include "exdata/mannequin.h" CItemFurnishing::CItemFurnishing(uint16 id) : CItem(id) @@ -38,19 +39,12 @@ CItemFurnishing::~CItemFurnishing() = default; void CItemFurnishing::setInstalled(bool installed) { - if (installed) - { - ref(m_extra, 0x01) |= 0x40; - } - else - { - ref(m_extra, 0x01) &= ~0x40; - } + this->exdata().Installed = installed ? 1 : 0; } bool CItemFurnishing::isInstalled() { - return ref(m_extra, 0x01) & 0x40; + return this->exdata().Installed; } void CItemFurnishing::setStorage(uint8 storage) @@ -95,91 +89,96 @@ uint8 CItemFurnishing::getAura() const void CItemFurnishing::setCol(uint8 col) { - ref(m_extra, 0x06) = col; + this->exdata().X = col; } uint8 CItemFurnishing::getCol() { - return ref(m_extra, 0x06); + return this->exdata().X; } void CItemFurnishing::setRow(uint8 row) { - ref(m_extra, 0x08) = row; + this->exdata().Y = row; } uint8 CItemFurnishing::getRow() { - return ref(m_extra, 0x08); + return this->exdata().Y; } void CItemFurnishing::setLevel(uint8 level) { - ref(m_extra, 0x07) = level; + this->exdata().Z = level; } uint8 CItemFurnishing::getLevel() { - return ref(m_extra, 0x07); + return this->exdata().Z; } void CItemFurnishing::setRotation(uint8 rotation) { - ref(m_extra, 0x09) = rotation; + this->exdata().Rotation = rotation; } uint8 CItemFurnishing::getRotation() { - return ref(m_extra, 0x09); + return this->exdata().Rotation; } void CItemFurnishing::setOrder(uint8 order) { - ref(m_extra, 0x0A) = order; + this->exdata().Order = order; } uint8 CItemFurnishing::getOrder() { - return ref(m_extra, 0x0A); + return this->exdata().Order; } void CItemFurnishing::setMannequinRace(uint8 race) { - ref(m_extra, 0x12) = race; + this->exdata().Race = race; } uint8 CItemFurnishing::getMannequinRace() { - return ref(m_extra, 0x12); + return this->exdata().Race; } + void CItemFurnishing::setMannequinPose(uint8 pose) { - ref(m_extra, 0x13) = pose; + this->exdata().Pose = pose; } uint8 CItemFurnishing::getMannequinPose() { - return ref(m_extra, 0x13); + return this->exdata().Pose; } void CItemFurnishing::setOn2ndFloor(bool on2ndFloor) { - if (on2ndFloor) - { - ref(m_extra, 0x01) |= 0x01; - } - else - { - ref(m_extra, 0x01) &= ~0x01; - } + this->exdata().On2ndFloor = on2ndFloor ? 1 : 0; } bool CItemFurnishing::getOn2ndFloor() { - return ref(m_extra, 0x01) & 01; + return this->exdata().On2ndFloor; +} + +auto CItemFurnishing::getSignature() -> const std::string +{ + auto& sig = this->exdata().Signature; + return std::string(reinterpret_cast(sig), sizeof(sig)); +} + +void CItemFurnishing::setSignature(const std::string& signature) +{ + Exdata::encodeSignature(signature, this->exdata().Signature); } -bool CItemFurnishing::isGardeningPot() +bool CItemFurnishing::isGardeningPot() const { const auto id = CItem::getID(); return id == 216 || // porcelain_flowerpot diff --git a/src/map/items/item_furnishing.h b/src/map/items/item_furnishing.h index 03df8b34802..03dc8fe8793 100644 --- a/src/map/items/item_furnishing.h +++ b/src/map/items/item_furnishing.h @@ -138,7 +138,10 @@ class CItemFurnishing : public CItem void setOn2ndFloor(bool on2ndFloor); bool getOn2ndFloor(); - bool isGardeningPot(); + auto getSignature() -> const std::string override; + void setSignature(const std::string& signature) override; + + bool isGardeningPot() const; private: uint8 m_storage; diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index d5b564eada5..38316a1ec0d 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -4207,10 +4207,7 @@ auto CLuaBaseEntity::addItem(sol::variadic_args va) const -> CItem* if (!signature.empty()) { - char encoded[SignatureStringLength]; - - std::memset(&encoded, 0, sizeof(encoded)); - PItem->setSignature(EncodeStringSignature(signature, encoded)); + PItem->setSignature(signature); } sol::object appraisalObj = table["appraisal"]; diff --git a/src/map/packets/c2s/0x03b_subcontainer.cpp b/src/map/packets/c2s/0x03b_subcontainer.cpp index 36b2d3adfa7..7db75a84654 100644 --- a/src/map/packets/c2s/0x03b_subcontainer.cpp +++ b/src/map/packets/c2s/0x03b_subcontainer.cpp @@ -23,7 +23,7 @@ #include "entities/charentity.h" #include "enums/item_lockflg.h" -#include "items/item_equipment.h" +#include "items/item_furnishing.h" #include "packets/s2c/0x01d_item_same.h" #include "packets/s2c/0x01f_item_list.h" #include "packets/s2c/0x020_item_attr.h" @@ -48,30 +48,6 @@ const auto setStatusOfStorageItemAtSlot = [](CCharEntity* PChar, const uint8 slo PChar->pushPacket(PItem, status); }; -// Build Mannequin model id list -const auto getModelIdFromStorageSlot = [](const CCharEntity* PChar, const uint8 slot) -> uint16 -{ - uint16 modelId = 0x0000; - - if (slot == 0) - { - return modelId; - } - - auto* PItem = PChar->getStorage(LOC_STORAGE)->GetItem(slot); - if (PItem == nullptr) - { - return modelId; - } - - if (const auto* PItemEquipment = dynamic_cast(PItem)) - { - modelId = PItemEquipment->getModelId(); - } - - return modelId; -}; - const auto validContainers = [](const CCharEntity* PChar) -> std::set { std::set allowedContainers = { @@ -100,13 +76,41 @@ auto GP_CLI_COMMAND_SUBCONTAINER::validate(MapSession* PSession, const CCharEnti void GP_CLI_COMMAND_SUBCONTAINER::process(MapSession* PSession, CCharEntity* PChar) const { - auto* PMannequin = PChar->getStorage(Category1)->GetItem(ItemIndex1); - if (PMannequin == nullptr) + auto* PItem = PChar->getStorage(Category1)->GetItem(ItemIndex1); + if (PItem == nullptr) { ShowWarning("GP_CLI_COMMAND_SUBCONTAINER: Unable to load mannequin from slot %u in location %u by %s", ItemIndex1, Category1, PChar->getName()); return; } + auto* PFurnishing = static_cast(PItem); + auto& mannequin = PFurnishing->exdata(); + + const auto getEquipSlot = [&mannequin](uint8 index) -> uint8& + { + switch (index) + { + case 0: + return mannequin.EquipMain; + case 1: + return mannequin.EquipSub; + case 2: + return mannequin.EquipRanged; + case 3: + return mannequin.EquipHead; + case 4: + return mannequin.EquipBody; + case 5: + return mannequin.EquipHands; + case 6: + return mannequin.EquipLegs; + case 7: + return mannequin.EquipFeet; + default: + return mannequin.EquipMain; + } + }; + switch (static_cast(Kind)) { case GP_CLI_COMMAND_SUBCONTAINER_KIND::Equip: @@ -118,63 +122,52 @@ void GP_CLI_COMMAND_SUBCONTAINER::process(MapSession* PSession, CCharEntity* PCh } // Action 1 Unequip Hack: Does this need to exist? - if (PMannequin->m_extra[10 + ContainerIndex] == ItemIndex2) + if (getEquipSlot(this->ContainerIndex) == this->ItemIndex2) { - setStatusOfStorageItemAtSlot(PChar, ItemIndex2, ItemLockFlg::Normal); - PMannequin->m_extra[10 + ContainerIndex] = 0; + setStatusOfStorageItemAtSlot(PChar, this->ItemIndex2, ItemLockFlg::Normal); + getEquipSlot(this->ContainerIndex) = 0; } else // Regular Logic { - setStatusOfStorageItemAtSlot(PChar, ItemIndex2, ItemLockFlg::Mannequin); - PMannequin->m_extra[10 + ContainerIndex] = ItemIndex2; + setStatusOfStorageItemAtSlot(PChar, this->ItemIndex2, ItemLockFlg::Mannequin); + getEquipSlot(this->ContainerIndex) = this->ItemIndex2; } } break; case GP_CLI_COMMAND_SUBCONTAINER_KIND::Unequip: { - setStatusOfStorageItemAtSlot(PChar, ItemIndex2, ItemLockFlg::Normal); - PMannequin->m_extra[10 + ContainerIndex] = 0; + setStatusOfStorageItemAtSlot(PChar, this->ItemIndex2, ItemLockFlg::Normal); + getEquipSlot(this->ContainerIndex) = 0; } break; case GP_CLI_COMMAND_SUBCONTAINER_KIND::UnequipAll: { for (uint8 i = 0; i < 8; ++i) { - if (PMannequin->m_extra[10 + i] > 0) + auto& slot = getEquipSlot(i); + if (slot > 0) { - setStatusOfStorageItemAtSlot(PChar, PMannequin->m_extra[10 + i], ItemLockFlg::Normal); + setStatusOfStorageItemAtSlot(PChar, slot, ItemLockFlg::Normal); } - PMannequin->m_extra[10 + i] = 0; + + slot = 0; } } break; } - uint16 mainId = getModelIdFromStorageSlot(PChar, PMannequin->m_extra[10 + 0]); - uint16 subId = getModelIdFromStorageSlot(PChar, PMannequin->m_extra[10 + 1]); - uint16 rangeId = getModelIdFromStorageSlot(PChar, PMannequin->m_extra[10 + 2]); - uint16 headId = getModelIdFromStorageSlot(PChar, PMannequin->m_extra[10 + 3]); - uint16 bodyId = getModelIdFromStorageSlot(PChar, PMannequin->m_extra[10 + 4]); - uint16 handsId = getModelIdFromStorageSlot(PChar, PMannequin->m_extra[10 + 5]); - uint16 legId = getModelIdFromStorageSlot(PChar, PMannequin->m_extra[10 + 6]); - uint16 feetId = getModelIdFromStorageSlot(PChar, PMannequin->m_extra[10 + 7]); - - // TODO: (?) - // 10 + 8 = Race - // 10 + 9 = Pose - const auto rset = db::preparedStmt("UPDATE char_inventory " "SET " "extra = ? " "WHERE location = ? AND slot = ? AND charid = ?", - PMannequin->m_extra, + PItem->m_extra, Category1, ItemIndex1, PChar->id); if (rset) { - PChar->pushPacket(PMannequin, static_cast(Category1), ItemIndex1); - PChar->pushPacket(static_cast(Category1), ItemIndex1, headId, bodyId, handsId, legId, feetId, mainId, subId, rangeId); + PChar->pushPacket(PFurnishing, static_cast(Category1), ItemIndex1); + PChar->pushPacket(PChar, static_cast(Category1), ItemIndex1, mannequin); PChar->pushPacket(PChar); } else diff --git a/src/map/packets/s2c/0x026_item_subcontainer.cpp b/src/map/packets/s2c/0x026_item_subcontainer.cpp index 1549b303672..6d338efff67 100644 --- a/src/map/packets/s2c/0x026_item_subcontainer.cpp +++ b/src/map/packets/s2c/0x026_item_subcontainer.cpp @@ -21,7 +21,9 @@ #include "0x026_item_subcontainer.h" +#include "entities/charentity.h" #include "item_container.h" +#include "items/item_equipment.h" GP_SERV_COMMAND_ITEM_SUBCONTAINER::GP_SERV_COMMAND_ITEM_SUBCONTAINER(const CONTAINER_ID locationId, const uint8_t slotId) { @@ -49,3 +51,45 @@ GP_SERV_COMMAND_ITEM_SUBCONTAINER::GP_SERV_COMMAND_ITEM_SUBCONTAINER(const CONTA packet.model_id_sub = subId + 0x7000; packet.model_id_range = rangeId + 0x8000; } + +GP_SERV_COMMAND_ITEM_SUBCONTAINER::GP_SERV_COMMAND_ITEM_SUBCONTAINER(CCharEntity* PChar, const CONTAINER_ID locationId, const uint8_t slotId, const Exdata::Mannequin& mannequin) +{ + const auto getModelId = [PChar](uint8 slot) -> uint16 + { + if (slot == 0) + { + return 0; + } + + auto* PItem = PChar->getStorage(LOC_STORAGE)->GetItem(slot); + if (PItem == nullptr) + { + return 0; + } + + if (const auto* PEquip = dynamic_cast(PItem)) + { + return PEquip->getModelId(); + } + + return 0; + }; + + auto& packet = this->data(); + + packet.is_used = 0x01; + packet.container = locationId; + packet.index = slotId; + + packet.model_id_race_hair = 0x01; // TODO: Verify correct format + packet.model_id_head = getModelId(mannequin.EquipHead) + 0x1000; + packet.model_id_body = getModelId(mannequin.EquipBody) + 0x2000; + packet.model_id_hands = getModelId(mannequin.EquipHands) + 0x3000; + packet.model_id_legs = getModelId(mannequin.EquipLegs) + 0x4000; + packet.model_id_feet = getModelId(mannequin.EquipFeet) + 0x5000; + packet.model_id_main = getModelId(mannequin.EquipMain) + 0x6000; + packet.model_id_sub = getModelId(mannequin.EquipSub) + 0x7000; + packet.model_id_range = getModelId(mannequin.EquipRanged) + 0x8000; + packet.race = mannequin.Race; + packet.pose = mannequin.Pose; +} diff --git a/src/map/packets/s2c/0x026_item_subcontainer.h b/src/map/packets/s2c/0x026_item_subcontainer.h index b909d3f9bc7..3ceddc99123 100644 --- a/src/map/packets/s2c/0x026_item_subcontainer.h +++ b/src/map/packets/s2c/0x026_item_subcontainer.h @@ -23,7 +23,11 @@ #include "base.h" +#include "items/exdata.h" + +class CCharEntity; enum CONTAINER_ID : uint8; + // https://github.com/atom0s/XiPackets/tree/main/world/server/0x0026 // This packet is sent by the server to inform the player of an items sub-container information. class GP_SERV_COMMAND_ITEM_SUBCONTAINER final : public GP_SERV_PACKET @@ -46,8 +50,11 @@ class GP_SERV_COMMAND_ITEM_SUBCONTAINER final : public GP_SERV_PACKETgetFlag() & (ITEM_FLAG_INSCRIBABLE)) { - char EncodedString[SignatureStringLength] = {}; - EncodeStringSignature(rset->get("signature").c_str(), EncodedString); - PItem->setSignature(EncodedString); + PItem->setSignature(rset->get("signature")); } if (auto PItemUsable = dynamic_cast(PItem)) @@ -8055,30 +8053,6 @@ bool isOrchestrionPlaced(CCharEntity* PChar) void updateMannequins(CCharEntity* PChar) { - // Build Mannequin model id list - auto getModelIdFromStorageSlot = [](CCharEntity* PChar, uint8 slot) -> uint16 - { - uint16 modelId = 0x0000; - - if (slot == 0) - { - return modelId; - } - - auto* PItem = PChar->getStorage(LOC_STORAGE)->GetItem(slot); - if (PItem == nullptr) - { - return modelId; - } - - if (auto* PItemEquipment = dynamic_cast(PItem)) - { - modelId = PItemEquipment->getModelId(); - } - - return modelId; - }; - for (auto safeContainerId : { LOC_MOGSAFE, LOC_MOGSAFE2 }) { CItemContainer* PContainer = PChar->getStorage(safeContainerId); @@ -8090,27 +8064,14 @@ void updateMannequins(CCharEntity* PChar) auto* PFurnishing = static_cast(PContainerItem); if (PFurnishing->isInstalled() && PFurnishing->isMannequin()) { - auto* PMannequin = PFurnishing; - - uint16 mainId = getModelIdFromStorageSlot(PChar, PMannequin->m_extra[10 + 0]); - uint16 subId = getModelIdFromStorageSlot(PChar, PMannequin->m_extra[10 + 1]); - uint16 rangeId = getModelIdFromStorageSlot(PChar, PMannequin->m_extra[10 + 2]); - uint16 headId = getModelIdFromStorageSlot(PChar, PMannequin->m_extra[10 + 3]); - uint16 bodyId = getModelIdFromStorageSlot(PChar, PMannequin->m_extra[10 + 4]); - uint16 handsId = getModelIdFromStorageSlot(PChar, PMannequin->m_extra[10 + 5]); - uint16 legId = getModelIdFromStorageSlot(PChar, PMannequin->m_extra[10 + 6]); - uint16 feetId = getModelIdFromStorageSlot(PChar, PMannequin->m_extra[10 + 7]); - uint8 race = PMannequin->m_extra[10 + 8]; - uint8 pose = PMannequin->m_extra[10 + 9]; - - std::ignore = pose; - - if (race == 0) + auto& mannequin = PFurnishing->exdata(); + + if (mannequin.Race == 0) { ShowWarning("Invalid Mannequin placed (race of 0 in exdata, when races start at 1). It will be unusable."); } - PChar->pushPacket(safeContainerId, slotIndex, headId, bodyId, handsId, legId, feetId, mainId, subId, rangeId); + PChar->pushPacket(PChar, safeContainerId, slotIndex, mannequin); } } } diff --git a/src/map/utils/synthutils.cpp b/src/map/utils/synthutils.cpp index 709f6c1f6a7..5ec726cb548 100644 --- a/src/map/utils/synthutils.cpp +++ b/src/map/utils/synthutils.cpp @@ -860,10 +860,7 @@ void handleSynthSuccess(CCharEntity* PChar) { if ((PItem->getFlag() & ITEM_FLAG_INSCRIBABLE) && (PChar->CraftContainer->getItemID(0) > 0x1080)) { - char encodedSignature[SignatureStringLength]; - - std::memset(&encodedSignature, 0, sizeof(encodedSignature)); - PItem->setSignature(EncodeStringSignature(PChar->name.c_str(), encodedSignature)); + PItem->setSignature(PChar->name); db::preparedStmt("UPDATE char_inventory SET signature = ? WHERE charid = ? AND location = 0 AND slot = ? LIMIT 1", PChar->name,