diff --git a/src/common/CommonGen.cpp b/src/common/CommonGen.cpp index 82f1e886..9a44f607 100644 --- a/src/common/CommonGen.cpp +++ b/src/common/CommonGen.cpp @@ -20,6 +20,11 @@ bool operator==( const uint8_t& g, const BaseParam& t ) return static_cast< uint8_t >( t ) == g; } +bool operator!=( const uint8_t& g, const BaseParam& t ) +{ + return static_cast< uint8_t >( t ) != g; +} + bool operator==( const BeastReputationRank& t, const uint8_t& g ) { return static_cast< uint8_t >( t ) == g; diff --git a/src/world/Actor/Chara.h b/src/world/Actor/Chara.h index 87d9146b..2b58c5f0 100644 --- a/src/world/Actor/Chara.h +++ b/src/world/Actor/Chara.h @@ -65,6 +65,9 @@ namespace Sapphire::Entity } m_baseStats; + // array for bonuses, 80 to have some spare room. + uint32_t m_bonusStats[80]; + protected: char m_name[34]; /*! Last tick time for the actor ( in ms ) */ diff --git a/src/world/Actor/Player.cpp b/src/world/Actor/Player.cpp index 729315d5..9555a74a 100644 --- a/src/world/Actor/Player.cpp +++ b/src/world/Actor/Player.cpp @@ -311,28 +311,28 @@ void Sapphire::Entity::Player::sendStats() { auto statPacket = makeZonePacket< FFXIVIpcPlayerStats >( getId() ); - statPacket->data().strength = m_baseStats.str; - statPacket->data().dexterity = m_baseStats.dex; - statPacket->data().vitality = m_baseStats.vit; - statPacket->data().intelligence = m_baseStats.inte; - statPacket->data().mind = m_baseStats.mnd; - statPacket->data().piety = m_baseStats.pie; - statPacket->data().determination = m_baseStats.determination; - statPacket->data().hp = m_baseStats.max_hp; - statPacket->data().mp = m_baseStats.max_mp; + statPacket->data().strength = m_baseStats.str + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Strength ) ]; + statPacket->data().dexterity = m_baseStats.dex + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Dexterity ) ]; + statPacket->data().vitality = m_baseStats.vit + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Vitality ) ]; + statPacket->data().intelligence = m_baseStats.inte + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Intelligence ) ]; + statPacket->data().mind = m_baseStats.mnd + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Mind ) ]; + statPacket->data().piety = m_baseStats.pie + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Piety ) ]; + statPacket->data().determination = m_baseStats.determination + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Determination ) ]; + statPacket->data().hp = m_baseStats.max_hp + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::HP ) ]; + statPacket->data().mp = m_baseStats.max_mp + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::MP ) ]; statPacket->data().accuracy = m_baseStats.accuracy; - statPacket->data().attack = m_baseStats.attack; - statPacket->data().attackMagicPotency = m_baseStats.attackPotMagic; - statPacket->data().healingMagicPotency = m_baseStats.healingPotMagic; - statPacket->data().skillSpeed = m_baseStats.skillSpeed; - statPacket->data().spellSpeed = m_baseStats.spellSpeed; - statPacket->data().spellSpeed1 = m_baseStats.spellSpeed; + statPacket->data().attack = m_baseStats.attack + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::AttackPower ) ]; + statPacket->data().attackMagicPotency = m_baseStats.attackPotMagic + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::AttackMagicPotency ) ]; + statPacket->data().healingMagicPotency = m_baseStats.healingPotMagic + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::HealingMagicPotency ) ]; + statPacket->data().skillSpeed = m_baseStats.skillSpeed + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::SkillSpeed ) ]; + statPacket->data().spellSpeed = m_baseStats.spellSpeed + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::SpellSpeed ) ]; + statPacket->data().spellSpeed1 = m_baseStats.spellSpeed + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::SpellSpeed ) ]; statPacket->data().spellSpeedMod = 100; - statPacket->data().criticalHitRate = m_baseStats.critHitRate; - statPacket->data().defense = m_baseStats.defense; - statPacket->data().magicDefense = m_baseStats.magicDefense; - statPacket->data().tenacity = m_baseStats.tenacity; + statPacket->data().criticalHitRate = m_baseStats.critHitRate + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::CriticalHit ) ]; + statPacket->data().defense = m_baseStats.defense + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Defense ) ]; + statPacket->data().magicDefense = m_baseStats.magicDefense + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::MagicDefense ) ]; + statPacket->data().tenacity = m_baseStats.tenacity + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Tenacity ) ]; queuePacket( statPacket ); } diff --git a/src/world/Actor/Player.h b/src/world/Actor/Player.h index cbc8e680..667cac1b 100644 --- a/src/world/Actor/Player.h +++ b/src/world/Actor/Player.h @@ -319,7 +319,7 @@ namespace Sapphire::Entity void equipItem( Common::GearSetSlot equipSlotId, ItemPtr pItem, bool sendModel ); /*! remove an item from an equipment slot */ - void unequipItem( Common::GearSetSlot equipSlotId, ItemPtr pItem ); + void unequipItem( Common::GearSetSlot equipSlotId, ItemPtr pItem, bool sendModel ); /*! equip a weapon, possibly forcing a job change */ void equipWeapon( ItemPtr pItem, bool updateClass ); diff --git a/src/world/Actor/PlayerInventory.cpp b/src/world/Actor/PlayerInventory.cpp index bba7b9cf..8ed41a04 100644 --- a/src/world/Actor/PlayerInventory.cpp +++ b/src/world/Actor/PlayerInventory.cpp @@ -228,21 +228,52 @@ void Sapphire::Entity::Player::equipItem( Common::GearSetSlot equipSlotId, ItemP } else updateModels( equipSlotId, pItem, false ); + + auto baseParams = pItem->getBaseParams(); + for( auto i = 0; i < 6; ++i ) + { + if( baseParams[ i ].baseParam != static_cast< uint8_t >( Common::BaseParam::None ) ) + m_bonusStats[ baseParams[ i ].baseParam ] += baseParams[ i ].value; + } + + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Defense ) ] += pItem->getDefense(); + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::MagicDefense ) ] += pItem->getDefenseMag(); + + sendStats(); } -void Sapphire::Entity::Player::unequipItem( Common::GearSetSlot equipSlotId, ItemPtr pItem ) +void Sapphire::Entity::Player::unequipItem( Common::GearSetSlot equipSlotId, ItemPtr pItem, bool sendUpdate ) { auto modelSlot = equipSlotToModelSlot( equipSlotId ); if( modelSlot != GearModelSlot::ModelInvalid ) m_modelEquip[ static_cast< uint8_t >( modelSlot ) ] = 0; - sendModel(); - m_itemLevel = calculateEquippedGearItemLevel(); - sendItemLevel(); + if( sendUpdate ) + { + sendModel(); + + m_itemLevel = calculateEquippedGearItemLevel(); + sendItemLevel(); + } if ( equipSlotId == SoulCrystal ) unequipSoulCrystal( pItem ); + + auto baseParams = pItem->getBaseParams(); + for( auto i = 0; i < 6; ++i ) + { + if( baseParams[ i ].baseParam != static_cast< uint8_t >( Common::BaseParam::None ) ) + m_bonusStats[ baseParams[ i ].baseParam ] -= baseParams[ i ].value; + } + + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Defense ) ] -= pItem->getDefense(); + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::MagicDefense ) ] -= pItem->getDefenseMag(); + + if( sendUpdate ) + { + sendStats(); + } } void Sapphire::Entity::Player::unequipSoulCrystal( ItemPtr pItem ) @@ -615,7 +646,7 @@ Sapphire::Entity::Player::moveItem( uint16_t fromInventoryId, uint8_t fromSlotId equipItem( static_cast< GearSetSlot >( toSlot ), tmpItem, true ); if( static_cast< InventoryType >( fromInventoryId ) == GearSet0 ) - unequipItem( static_cast< GearSetSlot >( fromSlotId ), tmpItem ); + unequipItem( static_cast< GearSetSlot >( fromSlotId ), tmpItem, true ); } @@ -624,6 +655,7 @@ bool Sapphire::Entity::Player::updateContainer( uint16_t storageId, uint8_t slot { auto containerType = World::Manager::ItemMgr::getContainerType( storageId ); + auto pOldItem = getItemAt( storageId, slotId ); m_storageMap[ storageId ]->setItem( slotId, pItem ); switch( containerType ) @@ -639,9 +671,13 @@ bool Sapphire::Entity::Player::updateContainer( uint16_t storageId, uint8_t slot case GearSet: { if( pItem ) + { + if( pOldItem ) + unequipItem( static_cast< GearSetSlot >( slotId ), pOldItem, false ); equipItem( static_cast< GearSetSlot >( slotId ), pItem, true ); + } else - unequipItem( static_cast< GearSetSlot >( slotId ), pItem ); + unequipItem( static_cast< GearSetSlot >( slotId ), pItem, true ); writeInventory( static_cast< InventoryType >( storageId ) ); break; @@ -654,7 +690,7 @@ bool Sapphire::Entity::Player::updateContainer( uint16_t storageId, uint8_t slot } void Sapphire::Entity::Player::splitItem( uint16_t fromInventoryId, uint8_t fromSlotId, - uint16_t toInventoryId, uint8_t toSlot, uint16_t itemCount ) + uint16_t toInventoryId, uint8_t toSlot, uint16_t itemCount ) { if( itemCount == 0 ) return; @@ -687,7 +723,7 @@ void Sapphire::Entity::Player::splitItem( uint16_t fromInventoryId, uint8_t from } void Sapphire::Entity::Player::mergeItem( uint16_t fromInventoryId, uint8_t fromSlotId, - uint16_t toInventoryId, uint8_t toSlot ) + uint16_t toInventoryId, uint8_t toSlot ) { auto fromItem = m_storageMap[ fromInventoryId ]->getItem( fromSlotId ); auto toItem = m_storageMap[ toInventoryId ]->getItem( toSlot ); @@ -722,7 +758,7 @@ void Sapphire::Entity::Player::mergeItem( uint16_t fromInventoryId, uint8_t from } void Sapphire::Entity::Player::swapItem( uint16_t fromInventoryId, uint8_t fromSlotId, - uint16_t toInventoryId, uint8_t toSlot ) + uint16_t toInventoryId, uint8_t toSlot ) { auto fromItem = m_storageMap[ fromInventoryId ]->getItem( fromSlotId ); auto toItem = m_storageMap[ toInventoryId ]->getItem( toSlot ); diff --git a/src/world/Inventory/Item.cpp b/src/world/Inventory/Item.cpp index 31682052..1b75fcaf 100644 --- a/src/world/Inventory/Item.cpp +++ b/src/world/Inventory/Item.cpp @@ -33,6 +33,22 @@ Sapphire::Item::Item( uint64_t uId, uint32_t catalogId, FrameworkPtr pFw, bool i m_block = itemInfo->block; m_defense = itemInfo->defensePhys; m_defenseMag = itemInfo->defenseMag; + + for( int i = 0; i < 6; ++i ) + { + m_baseParam[i].baseParam = itemInfo->param[i].baseparam; + m_baseParam[i].value = itemInfo->param[i].value; + } +} + +uint16_t Sapphire::Item::getDefense() const +{ + return m_defense; +} + +uint16_t Sapphire::Item::getDefenseMag() const +{ + return m_defenseMag; } float Sapphire::Item::getAutoAttackDmg() const @@ -184,4 +200,9 @@ uint32_t Sapphire::Item::getReservedFlag() const void Sapphire::Item::setReservedFlag( uint32_t flag ) { m_reservedFlag = flag; -} \ No newline at end of file +} + +Sapphire::Item::BaseParamStruct* Sapphire::Item::getBaseParams() +{ + return m_baseParam; +} diff --git a/src/world/Inventory/Item.h b/src/world/Inventory/Item.h index b0fe3c34..35c11286 100644 --- a/src/world/Inventory/Item.h +++ b/src/world/Inventory/Item.h @@ -11,6 +11,13 @@ namespace Sapphire { public: + + struct BaseParamStruct + { + uint8_t baseParam; + int16_t value; + }; + Item( uint64_t uId, uint32_t catalogId, FrameworkPtr pFw, bool isHq = false ); virtual ~Item() = default; @@ -49,6 +56,10 @@ namespace Sapphire uint16_t getWeaponDmg() const; + uint16_t getDefense() const; + + uint16_t getDefenseMag() const; + bool isWeapon() const; float getAutoAttackDmg() const; @@ -71,6 +82,7 @@ namespace Sapphire void setReservedFlag( uint32_t flag ); uint32_t getReservedFlag() const; + BaseParamStruct* getBaseParams(); protected: uint32_t m_id; @@ -106,6 +118,9 @@ namespace Sapphire FrameworkPtr m_pFw; uint32_t m_additionalData; + + BaseParamStruct m_baseParam[6]; + }; }