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..1271e9af 100644 --- a/src/world/Actor/Player.cpp +++ b/src/world/Actor/Player.cpp @@ -690,6 +690,11 @@ uint8_t Player::getLevelForClass( Common::ClassJob pClass ) const return static_cast< uint8_t >( m_classArray[ classJobIndex ] ); } +Common::ClassJob Player::getFirstClass() const +{ + return m_firstClass; +} + bool Player::isClassJobUnlocked( Common::ClassJob classJob ) const { // todo: need to properly check if a job is unlocked, at the moment we just check the class array which will return true for every job if the base class is unlocked @@ -1505,6 +1510,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..88820445 100644 --- a/src/world/Actor/Player.h +++ b/src/world/Actor/Player.h @@ -214,6 +214,9 @@ namespace Sapphire::Entity /*! returns the level of the provided class / job */ uint8_t getLevelForClass( Common::ClassJob pClass ) const; + /*! \return the first class of the player */ + Common::ClassJob getFirstClass() const; + /*! returns if the classjob is unlocked */ bool isClassJobUnlocked( Common::ClassJob classJob ) const; @@ -357,6 +360,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(); @@ -898,6 +904,8 @@ namespace Sapphire::Entity ExpList m_expArray{}; StateFlags m_stateFlags{}; + Common::ClassJob m_firstClass{}; + uint8_t m_homePoint; uint8_t m_startTown; uint16_t m_townWarpFstFlags; @@ -968,6 +976,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/Actor/PlayerSql.cpp b/src/world/Actor/PlayerSql.cpp index 4f06043c..ab60ba5a 100644 --- a/src/world/Actor/PlayerSql.cpp +++ b/src/world/Actor/PlayerSql.cpp @@ -78,6 +78,7 @@ bool Sapphire::Entity::Player::loadFromDb( uint64_t characterId ) m_class = static_cast< ClassJob >( res->getUInt( "Class" ) ); m_homePoint = res->getUInt8( "Homepoint" ); + m_firstClass = static_cast< ClassJob >( res->getUInt( "FirstClass" ) ); // Additional data m_voice = res->getUInt8( "Voice" ); 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..ff4a6461 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: @@ -413,8 +417,8 @@ void Sapphire::Network::GameConnection::commandHandler( const Packets::FFXIVARR_ const auto param3 = packet.data().Target; - Logger::debug( "\t\t {5} | {1:X} ( p1:{2:X} p2:{3:X} p3:{4:X} )", - m_pSession->getId(), commandId, param1, param2, param3, packetCommandToString( commandId ) ); + Logger::debug( "\t\t {8} | {1:X} ( p1:{2:X} p11:{3:X} p12:{4:X} p2:{5:X} p3:{6:X} p4:{7:X} )", + m_pSession->getId(), commandId, param1, param11, param12, param2, param3, param4, packetCommandToString( commandId ) ); //Logger::Log(LoggingSeverity::debug, "[" + std::to_string(m_pSession->getId()) + "] " + pInPacket->toString()); @@ -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/HudParamPacket.h b/src/world/Network/PacketWrappers/HudParamPacket.h index 9ca61bec..b808d575 100644 --- a/src/world/Network/PacketWrappers/HudParamPacket.h +++ b/src/world/Network/PacketWrappers/HudParamPacket.h @@ -26,6 +26,7 @@ namespace Sapphire::Network::Packets::WorldPackets::Server m_data.LvSync = 0; //player.getLevelSync(); m_data.Hp = player.getHp(); m_data.Mp = player.getMp(); + m_data.Tp = player.getTp(); m_data.HpMax = player.getMaxHp(); m_data.MpMax = player.getMaxMp();; }; diff --git a/src/world/Network/PacketWrappers/PlayerSetupPacket.h b/src/world/Network/PacketWrappers/PlayerSetupPacket.h index 59ce0e94..8e810042 100644 --- a/src/world/Network/PacketWrappers/PlayerSetupPacket.h +++ b/src/world/Network/PacketWrappers/PlayerSetupPacket.h @@ -34,7 +34,7 @@ namespace Sapphire::Network::Packets::WorldPackets::Server m_data.Tribe = player.getLookAt( Common::CharaLook::Tribe ); m_data.Sex = player.getLookAt( Common::CharaLook::Gender ); m_data.ClassJob = static_cast< uint8_t >( player.getClass() ); - m_data.FirstClass = static_cast< uint8_t >( player.getClass() ); + m_data.FirstClass = static_cast< uint8_t >( player.getFirstClass() ); //m_data.maxLevel = player.getLevel(); m_data.GuardianDeity = static_cast< uint8_t >( player.getGuardianDeity() ); m_data.BirthMonth = player.getBirthMonth(); 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; };