diff --git a/src/common/Database/ZoneDbConnection.cpp b/src/common/Database/ZoneDbConnection.cpp index 5cd83bc2..0adc2ff6 100644 --- a/src/common/Database/ZoneDbConnection.cpp +++ b/src/common/Database/ZoneDbConnection.cpp @@ -275,7 +275,7 @@ void Sapphire::Db::ZoneDbConnection::doPrepareStatements() CONNECTION_BOTH ); prepareStatement( CHARA_ITEMGLOBAL_UP, - "UPDATE charaglobalitem SET stack = ?, durability = ?, stain = ? WHERE ItemId = ?;", + "UPDATE charaglobalitem SET stack = ?, durability = ?, stain = ?, pattern = ? WHERE ItemId = ?;", CONNECTION_BOTH ); prepareStatement( CHARA_ITEMGLOBAL_DELETE, diff --git a/src/common/Network/CommonActorControl.h b/src/common/Network/CommonActorControl.h index e6e36a3f..911244e0 100644 --- a/src/common/Network/CommonActorControl.h +++ b/src/common/Network/CommonActorControl.h @@ -549,6 +549,8 @@ namespace Sapphire::Network::ActorControl MOBHUNT_RECEIPT_ORDER = 0x1B3, MOBHUNT_BREAK_ORDER = 0x1B4, DYE_ITEM = 0x1B5, + GLAMOUR_ITEM = 0x1BA, + GLAMOUR_DISPEL = 0x1BB, EMOTE = 0x1F4, EMOTE_WITH_WARP = 0x1F5, EMOTE_CANCEL = 0x1F6, diff --git a/src/scripts/action/common/ActionGlamour2471.cpp b/src/scripts/action/common/ActionGlamour2471.cpp new file mode 100644 index 00000000..6599c96d --- /dev/null +++ b/src/scripts/action/common/ActionGlamour2471.cpp @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +class ActionGlamour2471 : + public Sapphire::ScriptAPI::ActionScript +{ +public: + ActionGlamour2471() : + Sapphire::ScriptAPI::ActionScript( 2471 ) + { + } + + void onExecute( Sapphire::World::Action::Action& action ) override + { + auto sourceChara = action.getSourceChara(); + + if( !sourceChara->isPlayer() ) + return; + + //TODO: Effect + sourceChara->getAsPlayer()->glamourItemFromGlamouringInfo(); + } +}; + +EXPOSE_SCRIPT(ActionGlamour2471); \ No newline at end of file diff --git a/src/world/Actor/Player.cpp b/src/world/Actor/Player.cpp index cdbdd059..4c8ae6d7 100644 --- a/src/world/Actor/Player.cpp +++ b/src/world/Actor/Player.cpp @@ -1505,6 +1505,68 @@ void Player::dyeItemFromDyeingInfo() queuePacket( dyePkt ); } +void Player::setGlamouringInfo( uint32_t itemToGlamourContainer, uint32_t itemToGlamourSlot, uint32_t glamourBagContainer, uint32_t glamourBagSlot, bool shouldGlamour ) +{ + m_glamouringInfo.itemToGlamourContainer = itemToGlamourContainer; + m_glamouringInfo.itemToGlamourSlot = itemToGlamourSlot; + m_glamouringInfo.glamourBagContainer = glamourBagContainer; + m_glamouringInfo.glamourBagSlot = glamourBagSlot; + m_glamouringInfo.shouldGlamour = shouldGlamour; +} + +void Player::glamourItemFromGlamouringInfo() +{ + auto& playerMgr = Service< World::Manager::PlayerMgr >::ref(); + + uint32_t itemToGlamourContainer = m_glamouringInfo.itemToGlamourContainer; + uint32_t itemToGlamourSlot = m_glamouringInfo.itemToGlamourSlot; + uint32_t glamourBagContainer = m_glamouringInfo.glamourBagContainer; + uint32_t glamourBagSlot = m_glamouringInfo.glamourBagSlot; + bool shouldGlamour = m_glamouringInfo.shouldGlamour; + + playerMgr.onSendStateFlags( *this, true ); + + auto itemToGlamour = getItemAt( itemToGlamourContainer, itemToGlamourSlot ); + auto glamourToUse = getItemAt( glamourBagContainer, glamourBagSlot ); + //auto prismToUse = getItemAt( glamourBagContainer, glamourBagSlot ); + + if( !itemToGlamour ) + return; + + //if( !removeItem( prismToUse->getId() ) ) + // return; + + uint32_t patternID = itemToGlamour->getPattern(); + bool invalidateGearSet = shouldGlamour ? patternID != glamourToUse->getId() : true; + + if( shouldGlamour ) + { + itemToGlamour->setPattern( glamourToUse->getId() ); + itemToGlamour->setStain( glamourToUse->getStain() ); + } + else + { + itemToGlamour->setPattern( 0 ); + itemToGlamour->setStain( 0 ); + } + + itemToGlamour->setGlamModelIds(); + + insertInventoryItem( static_cast< Sapphire::Common::InventoryType >( itemToGlamourContainer ), static_cast< uint16_t >( itemToGlamourSlot ), itemToGlamour ); + writeItem( itemToGlamour ); + + if( shouldGlamour ) + { + auto castGlamPkt = makeActorControlSelf( getId(), GlamourCastMsg, itemToGlamour->getId(), glamourToUse->getId(), invalidateGearSet ); + queuePacket( castGlamPkt ); + } + else + { + auto dispelGlamPkt = makeActorControlSelf( getId(), GlamourRemoveMsg, itemToGlamour->getId(), invalidateGearSet ); + queuePacket( dispelGlamPkt ); + } +} + void Player::resetObjSpawnIndex() { m_objSpawnIndexAllocator.freeAllSpawnIndexes(); diff --git a/src/world/Actor/Player.h b/src/world/Actor/Player.h index 41f0a5d4..cf42c2d2 100644 --- a/src/world/Actor/Player.h +++ b/src/world/Actor/Player.h @@ -357,6 +357,9 @@ namespace Sapphire::Entity void setDyeingInfo( uint32_t itemToDyeContainer, uint32_t itemToDyeSlot, uint32_t dyeBagContainer, uint32_t dyeBagSlot ); void dyeItemFromDyeingInfo(); + void setGlamouringInfo( uint32_t itemToGlamourContainer, uint32_t itemToGlamourSlot, uint32_t glamourBagContainer, uint32_t glamourBagSlot, bool shouldGlamour ); + void glamourItemFromGlamouringInfo(); + /*! get player's title list (available titles) */ TitleList& getTitleList(); @@ -968,6 +971,15 @@ namespace Sapphire::Entity uint32_t dyeBagSlot; } m_dyeingInfo{}; + struct PlayerGlamouringInfo + { + uint32_t itemToGlamourContainer; + uint32_t itemToGlamourSlot; + uint32_t glamourBagContainer; + uint32_t glamourBagSlot; + bool shouldGlamour; + } m_glamouringInfo{}; + Common::Util::SpawnIndexAllocator< uint8_t > m_objSpawnIndexAllocator; Common::Util::SpawnIndexAllocator< uint8_t > m_actorSpawnIndexAllocator; diff --git a/src/world/Actor/PlayerInventory.cpp b/src/world/Actor/PlayerInventory.cpp index a48515d8..8c636bed 100644 --- a/src/world/Actor/PlayerInventory.cpp +++ b/src/world/Actor/PlayerInventory.cpp @@ -529,8 +529,9 @@ void Sapphire::Entity::Player::writeItem( Sapphire::ItemPtr pItem ) const stmt->setInt( 1, pItem->getStackSize() ); stmt->setInt( 2, pItem->getDurability() ); stmt->setInt( 3, pItem->getStain() ); + stmt->setInt( 4, pItem->getPattern() ); - stmt->setInt64( 4, pItem->getUId() ); + stmt->setInt64( 5, pItem->getUId() ); db.directExecute( stmt ); } diff --git a/src/world/Inventory/Item.cpp b/src/world/Inventory/Item.cpp index b68ce33f..38d5daf2 100644 --- a/src/world/Inventory/Item.cpp +++ b/src/world/Inventory/Item.cpp @@ -132,6 +132,23 @@ Sapphire::Common::ItemUICategory Sapphire::Item::getCategory() const return m_category; } +void Sapphire::Item::setGlamModelIds() +{ + auto& exdData = Common::Service< Data::ExdData >::ref(); + + if( m_pattern != 0 ) + { + auto itemInfo = exdData.getRow< Excel::Item >( m_pattern ); + m_glamModel1 = itemInfo->data().ModelId; + m_glamModel2 = itemInfo->data().SubModelId; + } + else + { + m_glamModel1 = 0; + m_glamModel2 = 0; + } +} + void Sapphire::Item::setModelIds( uint64_t model1, uint64_t model2 ) { m_model1 = model1; @@ -140,12 +157,12 @@ void Sapphire::Item::setModelIds( uint64_t model1, uint64_t model2 ) uint64_t Sapphire::Item::getModelId1() const { - return m_model1; + return m_pattern != 0 ? m_glamModel1 : m_model1; } uint64_t Sapphire::Item::getModelId2() const { - return m_model2; + return m_pattern != 0 ? m_glamModel2 : m_model2; } bool Sapphire::Item::isHq() const @@ -183,6 +200,16 @@ void Sapphire::Item::setStain( uint16_t stain ) m_stain = stain; } +uint32_t Sapphire::Item::getPattern() const +{ + return m_pattern; +} + +void Sapphire::Item::setPattern( uint32_t pattern ) +{ + m_pattern = pattern; +} + uint32_t Sapphire::Item::getAdditionalData() const { return m_additionalData; diff --git a/src/world/Inventory/Item.h b/src/world/Inventory/Item.h index 5f641ebd..3a9deaf4 100644 --- a/src/world/Inventory/Item.h +++ b/src/world/Inventory/Item.h @@ -37,6 +37,8 @@ namespace Sapphire Common::ItemUICategory getCategory() const; + void setGlamModelIds(); + void setModelIds( uint64_t model1, uint64_t model2 ); uint64_t getModelId1() const; @@ -75,6 +77,9 @@ namespace Sapphire uint16_t getStain() const; void setStain( uint16_t stain ); + uint32_t getPattern() const; + void setPattern( uint32_t pattern ); + uint32_t getAdditionalData() const; void setSpiritbond( uint16_t spiritbond ); @@ -99,6 +104,9 @@ namespace Sapphire uint64_t m_model1; uint64_t m_model2; + uint64_t m_glamModel1; + uint64_t m_glamModel2; + bool m_isHq; uint16_t m_delayMs; @@ -110,6 +118,7 @@ namespace Sapphire uint8_t m_slot; uint16_t m_durability; uint16_t m_stain; + uint32_t m_pattern; uint16_t m_spiritBond; uint32_t m_reservedFlag; diff --git a/src/world/Manager/InventoryMgr.cpp b/src/world/Manager/InventoryMgr.cpp index 22a9748c..88e8cb9d 100644 --- a/src/world/Manager/InventoryMgr.cpp +++ b/src/world/Manager/InventoryMgr.cpp @@ -61,6 +61,7 @@ void InventoryMgr::sendInventoryContainer( Entity::Player& player, ItemContainer // todo: not sure if correct flag? itemInfoPacket->data().item.flags = static_cast< uint8_t >( itM.second->isHq() ? 1 : 0 ); itemInfoPacket->data().item.stain = static_cast< uint8_t >( itM.second->getStain() ); + itemInfoPacket->data().item.pattern = itM.second->getPattern(); server.queueForPlayer( player.getCharacterId(), itemInfoPacket ); } diff --git a/src/world/Manager/ItemMgr.cpp b/src/world/Manager/ItemMgr.cpp index 32ba611d..bd233091 100644 --- a/src/world/Manager/ItemMgr.cpp +++ b/src/world/Manager/ItemMgr.cpp @@ -141,7 +141,9 @@ ItemPtr ItemMgr::loadItem( uint64_t uId ) pItem->setStackSize( itemRes->getUInt( 2 ) ); pItem->setStain( itemRes->getUInt16( 13 ) ); + pItem->setPattern( itemRes->getUInt( 14 ) ); pItem->setDurability( itemRes->getInt16( 6 ) ); + pItem->setGlamModelIds(); return pItem; } diff --git a/src/world/Network/Handlers/PacketCommandHandler.cpp b/src/world/Network/Handlers/PacketCommandHandler.cpp index 289bf668..8c1657b4 100644 --- a/src/world/Network/Handlers/PacketCommandHandler.cpp +++ b/src/world/Network/Handlers/PacketCommandHandler.cpp @@ -184,6 +184,10 @@ const char* packetCommandToString( uint16_t commandId ) return "MOBHUNT_BREAK_ORDER"; case DYE_ITEM: return "DYE_ITEM"; + case GLAMOUR_ITEM: + return "GLAMOUR_ITEM"; + case GLAMOUR_DISPEL: + return "GLAMOUR_DISPEL"; case EMOTE: return "EMOTE"; case EMOTE_WITH_WARP: @@ -635,6 +639,16 @@ void Sapphire::Network::GameConnection::commandHandler( const Packets::FFXIVARR_ player.setDyeingInfo( param11, param12, param2, param4 ); break; } + case PacketCommand::GLAMOUR_ITEM: + { + player.setGlamouringInfo( param11, param12, param2, param4, true ); + break; + } + case PacketCommand::GLAMOUR_DISPEL: + { + player.setGlamouringInfo( param11, param12, param2, param4, false ); + break; + } case PacketCommand::DIRECTOR_INIT_RETURN: // Director init finish { pZone->onInitDirector( player ); diff --git a/src/world/Network/PacketWrappers/UpdateInventorySlotPacket.h b/src/world/Network/PacketWrappers/UpdateInventorySlotPacket.h index 3b843113..48da48b9 100644 --- a/src/world/Network/PacketWrappers/UpdateInventorySlotPacket.h +++ b/src/world/Network/PacketWrappers/UpdateInventorySlotPacket.h @@ -37,6 +37,7 @@ namespace Sapphire::Network::Packets::WorldPackets::Server m_data.item.flags = static_cast< uint8_t >( item.isHq() ? 1 : 0 ); m_data.item.refine = item.getSpiritbond(); m_data.item.stain = static_cast< uint8_t >( item.getStain() ); + m_data.item.pattern = item.getPattern(); m_data.item.durability = item.getDurability(); m_data.item.signatureId = 0; };