diff --git a/src/servers/Server_Common/Network/PacketDef/Zone/ServerZoneDef.h b/src/servers/Server_Common/Network/PacketDef/Zone/ServerZoneDef.h index 29373409..6df1e762 100644 --- a/src/servers/Server_Common/Network/PacketDef/Zone/ServerZoneDef.h +++ b/src/servers/Server_Common/Network/PacketDef/Zone/ServerZoneDef.h @@ -815,7 +815,7 @@ struct FFXIVIpcPlayerStats : FFXIVIpcBasePacket uint32_t unknown; uint32_t unknown_1; uint32_t unknown_2; - uint32_t parry; + uint32_t tenacity; uint32_t attack; uint32_t defense; uint32_t accuracy; @@ -837,7 +837,11 @@ struct FFXIVIpcPlayerStats : FFXIVIpcBasePacket uint32_t skillSpeed; uint32_t spellSpeed1; uint32_t spellSpeedMod; - uint32_t unknown_6[5]; + uint32_t unknown_6; + uint32_t craftsmanship; + uint32_t control; + uint32_t gathering; + uint32_t perception; uint32_t resistanceSlow; uint32_t resistanceSilence; uint32_t resistanceBlind; @@ -846,7 +850,7 @@ struct FFXIVIpcPlayerStats : FFXIVIpcBasePacket uint32_t resistanceSleep; uint32_t resistanceBind; uint32_t resistanceHeavy; - uint32_t unknown_7[9]; + uint32_t unknown_7[9]; // possibly level sync stats. }; /** @@ -881,10 +885,11 @@ struct FFXIVIpcPlayerStateFlags : FFXIVIpcBasePacket struct FFXIVIpcPlayerClassInfo : FFXIVIpcBasePacket { uint16_t classId; - uint16_t unknown; - uint16_t level; - uint16_t level1; - uint8_t unknownFields[48]; + uint8_t unknown; + uint8_t isSpecialist; + uint16_t level; // Locks actions, equipment, prob more + uint16_t level1; // Locks roles, prob more + uint32_t roleActions[10]; }; /** diff --git a/src/servers/Server_REST/SapphireAPI.cpp b/src/servers/Server_REST/SapphireAPI.cpp index d3181696..5fbc108e 100644 --- a/src/servers/Server_REST/SapphireAPI.cpp +++ b/src/servers/Server_REST/SapphireAPI.cpp @@ -200,7 +200,7 @@ void Core::Network::SapphireAPI::deleteCharacter( std::string name, uint32_t acc g_charaDb.execute( "DELETE FROM charaitemcrystal WHERE CharacterId LIKE '" + std::to_string( id ) + "';" ); g_charaDb.execute( "DELETE FROM charaiteminventory WHERE CharacterId LIKE '" + std::to_string( id ) + "';" ); g_charaDb.execute( "DELETE FROM charaitemgearset WHERE CharacterId LIKE '" + std::to_string( id ) + "';" ); - g_charaDb.execute( "DELETE FROM charaquest WHERE CharacterId LIKE '" + std::to_string( id ) + "';" ); + g_charaDb.execute( "DELETE FROM charaquestnew WHERE CharacterId LIKE '" + std::to_string( id ) + "';" ); } std::vector< Core::PlayerMinimal > Core::Network::SapphireAPI::getCharList( uint32_t accountId ) diff --git a/src/servers/Server_Zone/Actor/Actor.h b/src/servers/Server_Zone/Actor/Actor.h index 6759d486..2ba456e5 100644 --- a/src/servers/Server_Zone/Actor/Actor.h +++ b/src/servers/Server_Zone/Actor/Actor.h @@ -80,7 +80,7 @@ public: uint32_t mnd = 0; uint32_t pie = 0; - uint32_t parry = 0; + uint32_t tenacity = 0; uint32_t attack = 0; uint32_t defense = 0; uint32_t accuracy = 0; diff --git a/src/servers/Server_Zone/Actor/Player.cpp b/src/servers/Server_Zone/Actor/Player.cpp index 4260e251..8d982fbc 100644 --- a/src/servers/Server_Zone/Actor/Player.cpp +++ b/src/servers/Server_Zone/Actor/Player.cpp @@ -236,12 +236,13 @@ void Core::Entity::Player::calculateStats() m_baseStats.mnd = static_cast< uint32_t >( base * ( static_cast< float >( classInfo.mod_mnd ) / 100 ) + tribeInfo.mod_mnd ); m_baseStats.pie = static_cast< uint32_t >( base * ( static_cast< float >( classInfo.mod_pie ) / 100 ) + tribeInfo.mod_pie ); - m_baseStats.skillSpeed = paramGrowthInfo.base_secondary; - m_baseStats.spellSpeed = paramGrowthInfo.base_secondary; - m_baseStats.accuracy = paramGrowthInfo.base_secondary; - m_baseStats.critHitRate = paramGrowthInfo.base_secondary; - m_baseStats.attackPotMagic = paramGrowthInfo.base_secondary; + m_baseStats.skillSpeed = paramGrowthInfo.base_secondary; + m_baseStats.spellSpeed = paramGrowthInfo.base_secondary; + m_baseStats.accuracy = paramGrowthInfo.base_secondary; + m_baseStats.critHitRate = paramGrowthInfo.base_secondary; + m_baseStats.attackPotMagic = paramGrowthInfo.base_secondary; m_baseStats.healingPotMagic = paramGrowthInfo.base_secondary; + m_baseStats.tenacity = paramGrowthInfo.base_secondary; m_baseStats.max_mp = Math::CalcStats::calculateMaxMp( getAsPlayer() ); @@ -434,6 +435,9 @@ void Core::Entity::Player::setZone( uint32_t zoneId ) gcAffPacket.data().gcRank[1] = m_gcRank[1]; gcAffPacket.data().gcRank[2] = m_gcRank[2]; queuePacket( gcAffPacket ); + + m_itemLevel = getInventory()->calculateEquippedGearItemLevel(); + sendItemLevel(); } ZoneChannelPacket< FFXIVIpcInitZone > initZonePacket( getId() ); @@ -1577,6 +1581,11 @@ void Core::Entity::Player::setOpeningSequence( uint8_t seq ) m_openingSequence = seq; } +uint16_t Core::Entity::Player::getItemLevel() const +{ + return m_itemLevel; +} + /// Tells client to offset their eorzean time by given timestamp. void Core::Entity::Player::setEorzeaTimeOffset( uint64_t timestamp ) { diff --git a/src/servers/Server_Zone/Actor/Player.h b/src/servers/Server_Zone/Actor/Player.h index 4688e5a5..97d85638 100644 --- a/src/servers/Server_Zone/Actor/Player.h +++ b/src/servers/Server_Zone/Actor/Player.h @@ -206,6 +206,10 @@ public: void unequipItem( Inventory::EquipSlot equipSlotId, ItemPtr pItem ); /*! equip a weapon, possibly forcing a job change */ void equipWeapon( ItemPtr pItem ); + /*! get player ilvl */ + uint16_t getItemLevel() const; + /*! send player ilvl */ + void sendItemLevel(); /*! get a const pointer to the inventory object */ InventoryPtr getInventory() const; /*! get the current main hand model */ @@ -592,16 +596,20 @@ private: uint8_t m_openingSequence; + uint16_t m_itemLevel; InventoryPtr m_pInventory; + std::map< uint32_t, Event::EventPtr > m_eventMap; std::map< uint32_t, uint8_t > m_playerIdToSpawnIdMap; // maps player to spawn id std::queue< uint8_t > m_freeSpawnIdQueue; // queue with spawn ids free to be assigned std::queue< uint8_t > m_freeHateSlotQueue; // queue with "hate slots" free to be assigned std::map< uint32_t, uint8_t > m_actorIdTohateSlotMap; + std::map< uint32_t, uint8_t > m_questIdToQuestIdx; // quest mapping, quest id to quest container index std::map< uint8_t, uint32_t > m_questIdxToQuestId; // quest mapping, quest container index to questId boost::shared_ptr< Common::QuestActive > m_activeQuests[30]; int16_t m_questTracking[5]; + uint8_t m_stateFlags[7]; uint8_t m_gmRank; uint16_t zoneId; diff --git a/src/servers/Server_Zone/Actor/PlayerInventory.cpp b/src/servers/Server_Zone/Actor/PlayerInventory.cpp index 8eaf65c9..b1d03721 100644 --- a/src/servers/Server_Zone/Actor/PlayerInventory.cpp +++ b/src/servers/Server_Zone/Actor/PlayerInventory.cpp @@ -1,17 +1,21 @@ #include +#include +#include +#include #include "Player.h" #include "src/servers/Server_Zone/Zone/ZoneMgr.h" #include "src/servers/Server_Zone/Zone/Zone.h" -#include - +#include "src/servers/Server_Zone/Network/PacketWrappers/ActorControlPacket142.h" #include "src/servers/Server_Zone/Network/PacketWrappers/ActorControlPacket143.h" #include "src/servers/Server_Zone/Inventory/Inventory.h" #include "src/servers/Server_Zone/Inventory/Item.h" +extern Core::Logger g_log; + using namespace Core::Common; using namespace Core::Network::Packets; using namespace Core::Network::Packets::Server; @@ -21,6 +25,11 @@ Core::InventoryPtr Core::Entity::Player::getInventory() const return m_pInventory; } +void Core::Entity::Player::sendItemLevel() +{ + queuePacket( ActorControlPacket142( getId(), SetItemLevel, getItemLevel(), 0 ) ); +} + // TODO: This has to be redone and simplified void Core::Entity::Player::equipWeapon( Core::ItemPtr pItem ) { @@ -77,10 +86,10 @@ void Core::Entity::Player::equipWeapon( Core::ItemPtr pItem ) } // equip an item -void Core::Entity::Player::equipItem( Inventory::EquipSlot equipSlotId, Core::ItemPtr pItem, bool sendModel ) +void Core::Entity::Player::equipItem( Inventory::EquipSlot equipSlotId, Core::ItemPtr pItem, bool sendUpdate ) { - // Console->outDebOnly("Equipping into slot %i", equipSlotID); + //g_log.debug( "Equipping into slot " + std::to_string( equipSlotId ) ); uint64_t model = pItem->getModelId1(); uint64_t model2 = pItem->getModelId2(); @@ -109,14 +118,21 @@ void Core::Entity::Player::equipItem( Inventory::EquipSlot equipSlotId, Core::It } - if( sendModel ) + if( sendUpdate ) + { this->sendModel(); + m_itemLevel = getInventory()->calculateEquippedGearItemLevel(); + sendItemLevel(); + } } void Core::Entity::Player::unequipItem( Inventory::EquipSlot equipSlotId, ItemPtr pItem ) { m_modelEquip[static_cast< uint8_t >( equipSlotId )] = 0; sendModel(); + + m_itemLevel = getInventory()->calculateEquippedGearItemLevel(); + sendItemLevel(); } uint32_t Core::Entity::Player::getCurrency( uint8_t type ) const diff --git a/src/servers/Server_Zone/Inventory/Inventory.cpp b/src/servers/Server_Zone/Inventory/Inventory.cpp index 6ac37b82..c03c9c00 100644 --- a/src/servers/Server_Zone/Inventory/Inventory.cpp +++ b/src/servers/Server_Zone/Inventory/Inventory.cpp @@ -13,6 +13,7 @@ #include "src/servers/Server_Zone/Network/PacketWrappers/ServerNoticePacket.h" #include +#include #include "src/servers/Server_Zone/Forwards.h" #include "src/servers/Server_Zone/Network/PacketWrappers/ActorControlPacket143.h" @@ -438,6 +439,30 @@ bool Core::Inventory::removeCrystal( CrystalType type, uint32_t amount ) return true; } +bool Core::Inventory::isOneHandedWeapon( ItemUICategory weaponCategory ) +{ + switch ( weaponCategory ) + { + case ItemUICategory::AlchemistsPrimaryTool: + case ItemUICategory::ArmorersPrimaryTool: + case ItemUICategory::BotanistsPrimaryTool: + case ItemUICategory::CulinariansPrimaryTool: + case ItemUICategory::OnehandedConjurersArm: + case ItemUICategory::CarpentersPrimaryTool: + case ItemUICategory::FishersPrimaryTool: + case ItemUICategory::GladiatorsArm: + case ItemUICategory::GoldsmithsPrimaryTool: + case ItemUICategory::LeatherworkersPrimaryTool: + case ItemUICategory::MinersPrimaryTool: + case ItemUICategory::OnehandedThaumaturgesArm: + case ItemUICategory::WeaversPrimaryTool: + case ItemUICategory::BlacksmithsPrimaryTool: + return true; + default: + return false; + } +} + bool Core::Inventory::isObtainable( uint32_t catalogId, uint8_t quantity ) { @@ -450,7 +475,8 @@ int16_t Core::Inventory::addItem( uint16_t inventoryId, int8_t slotId, uint32_t auto itemInfo = g_exdData.getItemInfo( catalogId ); - if( !itemInfo ) + // if item data doesn't exist or it's a blank field + if( !itemInfo || itemInfo->item_level == 0 ) { return -1; } @@ -828,6 +854,40 @@ void Core::Inventory::send() } +uint16_t Core::Inventory::calculateEquippedGearItemLevel() +{ + uint32_t iLvlResult = 0; + + auto gearSetMap = m_inventoryMap[GearSet0]->getItemMap(); + + auto it = gearSetMap.begin(); + + while ( it != gearSetMap.end() ) + { + auto currItem = it->second; + + if ( currItem ) + { + iLvlResult += currItem->getItemLevel(); + + // If item is weapon and isn't one-handed + if ( currItem->isWeapon() && !isOneHandedWeapon( currItem->getCategory() ) ) + { + iLvlResult += currItem->getItemLevel(); + } + else + { + g_log.debug( "Is one handed" ); + } + } + + it++; + } + + return boost::algorithm::clamp( iLvlResult / 12, 0, 9999 ); +} + + uint8_t Core::Inventory::getFreeSlotsInBags() { uint8_t slots = 0; diff --git a/src/servers/Server_Zone/Inventory/Inventory.h b/src/servers/Server_Zone/Inventory/Inventory.h index 7a0c16ad..b4663aa7 100644 --- a/src/servers/Server_Zone/Inventory/Inventory.h +++ b/src/servers/Server_Zone/Inventory/Inventory.h @@ -153,6 +153,10 @@ public: bool updateContainer( uint16_t containerId, uint8_t slotId, ItemPtr pItem ); + /*! heck if weapon category qualifies the weapon as onehanded */ + bool isOneHandedWeapon( Common::ItemUICategory weaponCategory ); + /*! calculate and return player ilvl based off equipped gear */ + uint16_t calculateEquippedGearItemLevel(); /*! return the current amount of currency of type */ uint32_t getCurrency( CurrencyType type ); /*! add amount to the current of type */ diff --git a/src/servers/Server_Zone/Inventory/Item.cpp b/src/servers/Server_Zone/Inventory/Item.cpp index 59171a82..a5e8dd84 100644 --- a/src/servers/Server_Zone/Inventory/Item.cpp +++ b/src/servers/Server_Zone/Inventory/Item.cpp @@ -30,6 +30,7 @@ Core::Item::Item( uint64_t uId, uint32_t catalogId, uint64_t model1, uint64_t mo m_magicalDmg = itemInfo->magical_damage; m_weaponDmg = ( m_physicalDmg != 0 ) ? m_physicalDmg : m_magicalDmg; m_autoAttackDmg = static_cast< float >( m_weaponDmg * m_delayMs ) / 3000; + m_itemLevel = itemInfo->item_level; } Core::Item::~Item() @@ -57,11 +58,21 @@ uint16_t Core::Item::getMagicalDmg() const return m_magicalDmg; } +uint16_t Core::Item::getItemLevel() const +{ + return m_itemLevel; +} + uint16_t Core::Item::getWeaponDmg() const { return m_weaponDmg; } +bool Core::Item::isWeapon() const +{ + return (m_weaponDmg != 0); +} + uint32_t Core::Item::getId() const { return m_id; diff --git a/src/servers/Server_Zone/Inventory/Item.h b/src/servers/Server_Zone/Inventory/Item.h index 0daa4c9a..198607f5 100644 --- a/src/servers/Server_Zone/Inventory/Item.h +++ b/src/servers/Server_Zone/Inventory/Item.h @@ -48,8 +48,12 @@ public: uint16_t getWeaponDmg() const; + bool isWeapon() const; + float getAutoAttackDmg() const; + uint16_t getItemLevel() const; + protected: uint32_t m_id; @@ -71,6 +75,7 @@ protected: uint16_t m_magicalDmg; uint16_t m_weaponDmg; float m_autoAttackDmg; + uint16_t m_itemLevel; }; diff --git a/src/servers/Server_Zone/Network/Handlers/ActionHandler.cpp b/src/servers/Server_Zone/Network/Handlers/ActionHandler.cpp index 5d3eef1d..2acde44d 100644 --- a/src/servers/Server_Zone/Network/Handlers/ActionHandler.cpp +++ b/src/servers/Server_Zone/Network/Handlers/ActionHandler.cpp @@ -228,10 +228,15 @@ void Core::Network::GameConnection::actionHandler( const Packets::GamePacket& in } break; } + case 0x1B5: // Dye item + { + break; + } default: { g_log.debug( "[" + std::to_string( m_pSession->getId() ) + "] Unhandled action: " + boost::str( boost::format( "%|04X|" ) % (uint32_t) ( commandId & 0xFFFF ) ) ); + break; } } }