1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-05-24 18:47:45 +00:00

Merge branch 'master' of https://github.com/SapphireMordred/Sapphire into party_crap

This commit is contained in:
Maru 2017-12-07 11:22:09 -02:00
commit 899f0fa272
22 changed files with 424 additions and 395 deletions

View file

@ -22,7 +22,7 @@ When making a PR, please make sure that it follows our style guidelines and good
### Coding style
Indentations are Allman-style based, 4-space, no tabs.
Indentations are Allman-style based, 3-space, no tabs.
Space between arguments in function calls, as well as for types.
Example (shortened from ActionHandler.cpp):
@ -30,34 +30,34 @@ Example (shortened from ActionHandler.cpp):
```cpp
switch( commandId )
{
case 0x01: // Toggle sheathe
{
if ( param11 == 1 )
pPlayer->setStance( Entity::Actor::Stance::Active );
else
{
pPlayer->setStance( Entity::Actor::Stance::Passive );
pPlayer->setAutoattack( false );
}
case 0x01: // Toggle sheathe
{
if ( param11 == 1 )
pPlayer->setStance( Entity::Actor::Stance::Active );
else
{
pPlayer->setStance( Entity::Actor::Stance::Passive );
pPlayer->setAutoattack( false );
}
pPlayer->sendToInRangeSet( ActorControlPacket142( pPlayer->getId(), 0, param11, 1 ) );
pPlayer->sendToInRangeSet( ActorControlPacket142( pPlayer->getId(), 0, param11, 1 ) );
break;
}
case 0x03: // Change target
{
uint64_t targetId = inPacket.getValAt< uint64_t >( 0x24 );
pPlayer->changeTarget( targetId );
break;
}
default:
{
break;
}
break;
}
case 0x03: // Change target
{
uint64_t targetId = inPacket.getValAt< uint64_t >( 0x24 );
pPlayer->changeTarget( targetId );
break;
}
default:
{
break;
}
}
```
### Feature implementation
Please make sure edge cases have been tested, behavior is aligned with retail and (if applicable) your queries make sense.
Any changes to the SQL base should be noted (and reflected in the update.sql file in rootDirectory/sql).
Any changes to the SQL base should be noted (and reflected in the update.sql file in rootDirectory/sql).

View file

@ -3,15 +3,16 @@
<!-- Port the lobby server accepts client connections on -->
<ListenPort>54994</ListenPort>
<AuthPort>54998</AuthPort>
<!-- Ip the lobby server listens on -->
<!-- Ip the lobby server listens on -->
<ListenIp>127.0.0.1</ListenIp>
<!-- Path of FFXIV dat files -->
<DataPath>C:\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\sqpack\\ffxiv</DataPath>
<!-- <DataPath>/opt/sapphire_3_15_0/bin/sqpack</DataPath> -->
<!-- IP of the lobby server -->
<LobbyHost>127.0.0.1</LobbyHost>
<!-- IP of the frontier server -->
<!-- IP of the frontier server -->
<FrontierHost>127.0.0.1</FrontierHost>
<!-- Secret used for server auth - you *must* change this for public servers -->
<!-- Secret used for server auth - you *must* change this for public servers -->
<ServerSecret>default</ServerSecret>
<!-- Web server port -->
<HttpPort>80</HttpPort>
@ -28,4 +29,4 @@
<!-- GM Rank for newly created characters - should be changed to 0 for public servers -->
<DefaultGMRank>255</DefaultGMRank>
</Parameters>
</Settings>
</Settings>

View file

@ -855,7 +855,7 @@ struct FFXIVIpcPlayerStats : FFXIVIpcBasePacket<PlayerStats>
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;
@ -877,7 +877,11 @@ struct FFXIVIpcPlayerStats : FFXIVIpcBasePacket<PlayerStats>
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;
@ -886,7 +890,7 @@ struct FFXIVIpcPlayerStats : FFXIVIpcBasePacket<PlayerStats>
uint32_t resistanceSleep;
uint32_t resistanceBind;
uint32_t resistanceHeavy;
uint32_t unknown_7[9];
uint32_t unknown_7[9]; // possibly level sync stats.
};
/**
@ -921,10 +925,11 @@ struct FFXIVIpcPlayerStateFlags : FFXIVIpcBasePacket<PlayerStateFlags>
struct FFXIVIpcPlayerClassInfo : FFXIVIpcBasePacket<PlayerClassInfo>
{
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];
};
/**

View file

@ -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 )

View file

@ -15,7 +15,6 @@
#include "src/servers/Server_Zone/Network/PacketWrappers/ActorControlPacket144.h"
#include "src/servers/Server_Zone/Network/PacketWrappers/UpdateHpMpTpPacket.h"
#include "src/servers/Server_Zone/StatusEffect/StatusEffectContainer.h"
#include "src/servers/Server_Zone/StatusEffect/StatusEffect.h"
#include "src/servers/Server_Zone/Action/ActionCollision.h"
#include "src/servers/Server_Zone/ServerZone.h"
@ -33,6 +32,11 @@ using namespace Core::Network::Packets::Server;
Core::Entity::Actor::Actor()
{
// initialize the free slot queue
for( uint8_t i = 0; i < MAX_STATUS_EFFECTS; i++ )
{
m_statusEffectFreeSlotQueue.push( i );
}
}
Core::Entity::Actor::~Actor()
@ -794,7 +798,30 @@ void Core::Entity::Actor::handleScriptSkill( uint32_t type, uint16_t actionId, u
/*! \param StatusEffectPtr to be applied to the actor */
void Core::Entity::Actor::addStatusEffect( StatusEffect::StatusEffectPtr pEffect )
{
m_pStatusEffectContainer->addStatusEffect( pEffect );
int8_t nextSlot = getStatusEffectFreeSlot();
// if there is no slot left, do not add the effect
if( nextSlot == -1 )
return;
pEffect->applyStatus();
m_statusEffectMap[nextSlot] = pEffect;
ZoneChannelPacket< Server::FFXIVIpcAddStatusEffect > statusEffectAdd( getId() );
statusEffectAdd.data().actor_id = pEffect->getTargetActorId();
statusEffectAdd.data().actor_id1 = pEffect->getSrcActorId();
statusEffectAdd.data().current_hp = getHp();
statusEffectAdd.data().current_mp = getMp();
statusEffectAdd.data().current_tp = getTp();
statusEffectAdd.data().duration = static_cast< float >( pEffect->getDuration() ) / 1000;
statusEffectAdd.data().effect_id = pEffect->getId();
statusEffectAdd.data().effect_index = nextSlot;
statusEffectAdd.data().max_hp = getMaxHp();
statusEffectAdd.data().max_mp = getMaxMp();
statusEffectAdd.data().max_something = 1;
//statusEffectAdd.data().unknown2 = 28;
statusEffectAdd.data().param = pEffect->getParam();
sendToInRangeSet( statusEffectAdd, isPlayer() );
}
/*! \param StatusEffectPtr to be applied to the actor */
@ -809,7 +836,7 @@ void Core::Entity::Actor::addStatusEffectById( uint32_t id, int32_t duration, En
/*! \param StatusEffectPtr to be applied to the actor */
void Core::Entity::Actor::addStatusEffectByIdIfNotExist( uint32_t id, int32_t duration, Entity::Actor& pSource, uint16_t param )
{
if( !m_pStatusEffectContainer->hasStatusEffect( id ) )
if( !hasStatusEffect( id ) )
{
StatusEffect::StatusEffectPtr effect( new StatusEffect::StatusEffect( id, pSource.shared_from_this(),
shared_from_this(), duration, 3000 ) );
@ -818,17 +845,6 @@ void Core::Entity::Actor::addStatusEffectByIdIfNotExist( uint32_t id, int32_t du
}
}
/*! \param Status that should be removed, based on its ID. */
void Core::Entity::Actor::removeSingleStatusEffectFromId( uint32_t id )
{
m_pStatusEffectContainer->removeSingleStatusEffectFromId( id );
}
Core::StatusEffect::StatusEffectContainerPtr Core::Entity::Actor::getStatusEffectContainer() const
{
return m_pStatusEffectContainer;
}
float Core::Entity::Actor::getRotation() const
{
return m_rot;
@ -838,3 +854,154 @@ void Core::Entity::Actor::setRotation( float rot )
{
m_rot = rot;
}
int8_t Core::Entity::Actor::getStatusEffectFreeSlot()
{
int8_t freeEffectSlot = -1;
if( m_statusEffectFreeSlotQueue.empty() )
return freeEffectSlot;
freeEffectSlot = m_statusEffectFreeSlotQueue.front();
m_statusEffectFreeSlotQueue.pop();
return freeEffectSlot;
}
void Core::Entity::Actor::statusEffectFreeSlot( uint8_t slotId )
{
m_statusEffectFreeSlotQueue.push( slotId );
}
void Core::Entity::Actor::removeSingleStatusEffectById( uint32_t id )
{
for( auto effectIt : m_statusEffectMap )
{
if( effectIt.second->getId() == id )
{
removeStatusEffect( effectIt.first );
break;
}
}
}
void Core::Entity::Actor::removeStatusEffect( uint8_t effectSlotId )
{
auto pEffectIt = m_statusEffectMap.find( effectSlotId );
if( pEffectIt == m_statusEffectMap.end() )
return;
statusEffectFreeSlot( effectSlotId );
auto pEffect = pEffectIt->second;
pEffect->removeStatus();
sendToInRangeSet( ActorControlPacket142( getId(), StatusEffectLose, pEffect->getId() ), isPlayer() );
m_statusEffectMap.erase( effectSlotId );
sendStatusEffectUpdate();
}
std::map< uint8_t, Core::StatusEffect::StatusEffectPtr > Core::Entity::Actor::getStatusEffectMap() const
{
return m_statusEffectMap;
}
void Core::Entity::Actor::sendStatusEffectUpdate()
{
uint64_t currentTimeMs = Util::getTimeMs();
ZoneChannelPacket< Server::FFXIVIpcStatusEffectList > statusEffectList( getId() );
statusEffectList.data().current_hp = getHp();
statusEffectList.data().current_mp = getMp();
statusEffectList.data().currentTp = getTp();
statusEffectList.data().max_hp = getMaxHp();
statusEffectList.data().max_mp = getMaxMp();
uint8_t slot = 0;
for( auto effectIt : m_statusEffectMap )
{
float timeLeft = static_cast< float >( effectIt.second->getDuration() -
( currentTimeMs - effectIt.second->getStartTimeMs() ) ) / 1000;
statusEffectList.data().effect[slot].duration = timeLeft;
statusEffectList.data().effect[slot].effect_id = effectIt.second->getId();
statusEffectList.data().effect[slot].sourceActorId = effectIt.second->getSrcActorId();
slot++;
}
sendToInRangeSet( statusEffectList, isPlayer() );
}
void Core::Entity::Actor::updateStatusEffects()
{
uint64_t currentTimeMs = Util::getTimeMs();
uint32_t thisTickDmg = 0;
uint32_t thisTickHeal = 0;
for( auto effectIt : m_statusEffectMap )
{
uint8_t effectIndex = effectIt.first;
auto effect = effectIt.second;
uint64_t lastTick = effect->getLastTickMs();
uint64_t startTime = effect->getStartTimeMs();
uint32_t duration = effect->getDuration();
uint32_t tickRate = effect->getTickRate();
if( ( currentTimeMs - startTime ) > duration )
{
// remove status effect
removeStatusEffect( effectIndex );
// break because removing invalidates iterators
break;
}
if( ( currentTimeMs - lastTick ) > tickRate )
{
effect->setLastTick( currentTimeMs );
effect->onTick();
auto thisEffect = effect->getTickEffect();
switch( thisEffect.first )
{
case 1:
{
thisTickDmg += thisEffect.second;
break;
}
case 2:
{
thisTickHeal += thisEffect.second;
break;
}
}
}
}
if( thisTickDmg != 0 )
{
takeDamage( thisTickDmg );
sendToInRangeSet( ActorControlPacket142( getId(), HPFloatingText, 0, static_cast< uint8_t >( ActionEffectType::Damage ), thisTickDmg ) );
}
if( thisTickHeal != 0 )
{
heal( thisTickDmg );
sendToInRangeSet( ActorControlPacket142( getId(), HPFloatingText, 0, static_cast< uint8_t >( ActionEffectType::Heal ), thisTickHeal ) );
}
}
bool Core::Entity::Actor::hasStatusEffect( uint32_t id )
{
if( m_statusEffectMap.find( id ) != m_statusEffectMap.end() )
return true;
return false;
}

View file

@ -7,6 +7,7 @@
#include "src/servers/Server_Zone/Forwards.h"
#include <set>
#include <map>
#include <queue>
namespace Core {
namespace Entity {
@ -79,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;
@ -160,11 +161,15 @@ protected:
uint64_t m_targetId;
/*! Ptr to a queued action */
Action::ActionPtr m_pCurrentAction;
/*! Container for status effects */
StatusEffect::StatusEffectContainerPtr m_pStatusEffectContainer;
/*! Invincibility type */
Common::InvincibilityType m_invincibilityType;
/*! Status effects */
const uint8_t MAX_STATUS_EFFECTS = 30;
std::queue< uint8_t > m_statusEffectFreeSlotQueue;
std::vector< std::pair< uint8_t, uint32_t> > m_statusEffectList;
std::map< uint8_t, StatusEffect::StatusEffectPtr > m_statusEffectMap;
public:
Actor();
@ -174,6 +179,30 @@ public:
uint32_t getId() const;
/// Status effect functions
void addStatusEffect( StatusEffect::StatusEffectPtr pEffect );
void removeStatusEffect( uint8_t effectSlotId );
void removeSingleStatusEffectById( uint32_t id );
void updateStatusEffects();
bool hasStatusEffect( uint32_t id );
int8_t getStatusEffectFreeSlot();
void statusEffectFreeSlot( uint8_t slotId );
std::map< uint8_t, Core::StatusEffect::StatusEffectPtr > getStatusEffectMap() const;
void sendStatusEffectUpdate();
// add a status effect by id
void addStatusEffectById( uint32_t id, int32_t duration, Entity::Actor& pSource, uint16_t param = 0 );
// add a status effect by id if it doesn't exist
void addStatusEffectByIdIfNotExist( uint32_t id, int32_t duration, Entity::Actor& pSource, uint16_t param = 0 );
// remove a status effect by id
void removeSingleStatusEffectFromId( uint32_t id );
/// End Status Effect Functions
void setPosition( const Common::FFXIVARR_POSITION3& pos );
void setPosition( float x, float y, float z );
@ -303,20 +332,6 @@ public:
// set the current cell
void setCell( Cell* pCell );
// add a status effect
void addStatusEffect( StatusEffect::StatusEffectPtr pEffect );
// add a status effect by id
void addStatusEffectById( uint32_t id, int32_t duration, Entity::Actor& pSource, uint16_t param = 0 );
// add a status effect by id if it doesn't exist
void addStatusEffectByIdIfNotExist( uint32_t id, int32_t duration, Entity::Actor& pSource, uint16_t param = 0 );
// remove a status effect by id
void removeSingleStatusEffectFromId( uint32_t id );
//get the status effect container
StatusEffect::StatusEffectContainerPtr getStatusEffectContainer() const;
// TODO: Why did i even declare them publicly here?!
std::set< ActorPtr > m_inRangeActors;

View file

@ -15,7 +15,6 @@
#include "src/servers/Server_Zone/Network/PacketWrappers/MoveActorPacket.h"
#include "src/servers/Server_Zone/Network/PacketWrappers/ActorControlPacket142.h"
#include "src/servers/Server_Zone/Network/PacketWrappers/ActorControlPacket143.h"
#include "src/servers/Server_Zone/StatusEffect/StatusEffectContainer.h"
using namespace Core::Common;
using namespace Core::Network::Packets;
@ -40,7 +39,7 @@ Core::Entity::BattleNpc::~BattleNpc()
Core::Entity::BattleNpc::BattleNpc( uint16_t modelId, uint16_t nameid, const Common::FFXIVARR_POSITION3& spawnPos,
uint16_t bnpcBaseId, uint32_t type, uint8_t level, uint8_t behaviour,
uint32_t mobType )
uint32_t mobType ) : Actor()
{
BattleNpc::m_nextID++;
m_id = BattleNpc::m_nextID;
@ -87,11 +86,6 @@ Core::Entity::BattleNpc::BattleNpc( uint16_t modelId, uint16_t nameid, const Com
}
void Core::Entity::BattleNpc::initStatusEffectContainer()
{
m_pStatusEffectContainer = StatusEffect::StatusEffectContainerPtr( new StatusEffect::StatusEffectContainer( shared_from_this() ) );
}
// spawn this player for pTarget
void Core::Entity::BattleNpc::spawn( Core::Entity::PlayerPtr pTarget )
{
@ -487,9 +481,7 @@ void Core::Entity::BattleNpc::update( int64_t currTime )
return;
}
if ( !m_pStatusEffectContainer )
initStatusEffectContainer();
m_pStatusEffectContainer->update();
updateStatusEffects();
float distance = Math::Util::distance( m_pos.x, m_pos.y, m_pos.z,
m_posOrigin.x, m_posOrigin.y, m_posOrigin.z );

View file

@ -31,8 +31,6 @@
#include "src/servers/Server_Zone/Script/ScriptManager.h"
#include "src/servers/Server_Zone/StatusEffect/StatusEffectContainer.h"
#include "src/servers/Server_Zone/Inventory/Item.h"
#include "src/servers/Server_Zone/Inventory/Inventory.h"
@ -57,6 +55,7 @@ using namespace Core::Network::Packets::Server;
// player constructor
Core::Entity::Player::Player() :
Actor(),
m_lastWrite( 0 ),
m_lastPing( 0 ),
m_bIsLogin( false ),
@ -237,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() );
@ -440,6 +440,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() );
@ -1050,7 +1053,7 @@ void Core::Entity::Player::update( int64_t currTime )
if( !isAlive() )
return;
m_pStatusEffectContainer->update();
updateStatusEffects();
m_lastUpdate = currTime;
@ -1583,6 +1586,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 )
{

View file

@ -208,6 +208,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 */
@ -599,16 +603,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;

View file

@ -1,17 +1,21 @@
#include <src/servers/Server_Common/Common.h>
#include <src/servers/Server_Common/Exd/ExdData.h>
#include <src/servers/Server_Common/Network/GamePacket.h>
#include <src/servers/Server_Common/Logging/Logger.h>
#include "Player.h"
#include "src/servers/Server_Zone/Zone/ZoneMgr.h"
#include "src/servers/Server_Zone/Zone/Zone.h"
#include <src/servers/Server_Common/Network/GamePacket.h>
#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

View file

@ -26,7 +26,6 @@
#include "src/servers/Server_Zone/Network/GameConnection.h"
#include "src/servers/Server_Zone/Network/PacketWrappers/InitUIPacket.h"
#include "src/servers/Server_Zone/StatusEffect/StatusEffectContainer.h"
#include "src/servers/Server_Zone/Inventory/Inventory.h"
#include <Server_Common/Database/DatabaseDef.h>
@ -178,7 +177,8 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession )
m_lastTickTime = 0;
auto pPlayer = getAsPlayer();
m_pInventory = InventoryPtr( new Inventory( pPlayer ) );
// TODO: remove Inventory and actually inline it in Player class
m_pInventory = InventoryPtr( new Inventory( pPlayer.get() ) );
pPlayer->calculateStats();
@ -211,8 +211,6 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession )
initSpawnIdQueue();
m_pStatusEffectContainer = StatusEffect::StatusEffectContainerPtr( new StatusEffect::StatusEffectContainer( shared_from_this() ) );
if( !m_playerIdToSpawnIdMap.empty() )
m_playerIdToSpawnIdMap.clear();
@ -508,4 +506,4 @@ void Core::Entity::Player::insertQuest( uint16_t questId, uint8_t index, uint8_t
stmt->setInt( 11, 0 );
stmt->setInt( 12, 0 );
g_charaDb.execute( stmt );
}
}

View file

@ -13,6 +13,7 @@
#include "src/servers/Server_Zone/Network/PacketWrappers/ServerNoticePacket.h"
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/clamp.hpp>
#include "src/servers/Server_Zone/Forwards.h"
#include "src/servers/Server_Zone/Network/PacketWrappers/ActorControlPacket143.h"
@ -27,7 +28,7 @@ using namespace Core::Network;
using namespace Core::Network::Packets;
using namespace Core::Network::Packets::Server;
Core::Inventory::Inventory( Core::Entity::PlayerPtr pOwner )
Core::Inventory::Inventory( Core::Entity::Player* pOwner )
{
m_pOwner = pOwner;
@ -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;

View file

@ -13,7 +13,7 @@ using InventoryMap = std::map< uint16_t, ItemContainerPtr >;
class Inventory
{
public:
Inventory( Entity::PlayerPtr pOwner );
Inventory( Entity::Player* pOwner );
~Inventory();
enum ContainerType : uint16_t
@ -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 */
@ -191,7 +195,7 @@ public:
private:
Entity::PlayerPtr m_pOwner;
Entity::Player* m_pOwner;
InventoryMap m_inventoryMap;
};

View file

@ -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;

View file

@ -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;
};

View file

@ -110,7 +110,7 @@ void Core::Network::GameConnection::actionHandler( const Packets::GamePacket& in
case 0x68: // Remove status (clicking it off)
{
// todo: check if status can be removed by client from exd
pPlayer->removeSingleStatusEffectFromId( static_cast< uint32_t >( param1 ) );
pPlayer->removeSingleStatusEffectById( static_cast< uint32_t >( param1 ) );
break;
}
case 0x69: // Cancel cast
@ -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;
}
}
}

View file

@ -8,7 +8,6 @@
#include "src/servers/Server_Zone/Forwards.h"
#include "src/servers/Server_Zone/Inventory/Inventory.h"
#include "src/servers/Server_Zone/Inventory/Item.h"
#include "src/servers/Server_Zone/StatusEffect/StatusEffectContainer.h"
#include "src/servers/Server_Zone/StatusEffect/StatusEffect.h"
namespace Core {
@ -122,10 +121,11 @@ namespace Server {
uint64_t currentTimeMs = Util::getTimeMs();
for( auto const& effect : pPlayer->getStatusEffectContainer()->getEffectMap() )
for( auto const& effect : pPlayer->getStatusEffectMap() )
{
m_data.effect[effect.first].effect_id = effect.second->getId();
m_data.effect[effect.first].duration = static_cast< float >( effect.second->getDuration() - ( currentTimeMs - effect.second->getStartTimeMs() ) ) / 1000;
m_data.effect[effect.first].duration = static_cast< float >( effect.second->getDuration() -
( currentTimeMs - effect.second->getStartTimeMs() ) ) / 1000;
m_data.effect[effect.first].sourceActorId = effect.second->getSrcActorId();
m_data.effect[effect.first].unknown1 = effect.second->getParam();
}
@ -138,4 +138,4 @@ namespace Server {
}
}
#endif /*_PlayerSpawn_H*/
#endif /*_PlayerSpawn_H*/

View file

@ -61,7 +61,7 @@ Core::XMLConfigPtr Core::ServerZone::getConfig() const
size_t Core::ServerZone::getSessionCount() const
{
return m_sessionMap.size();
return m_sessionMapById.size();
}
bool Core::ServerZone::registerBnpcTemplate( std::string templateName, uint32_t bnpcBaseId,
@ -263,7 +263,7 @@ void Core::ServerZone::mainLoop()
auto currTime = static_cast< uint32_t >( time( nullptr ) );
lock_guard< std::mutex > lock( this->m_sessionMutex );
for( auto sessionIt : this->m_sessionMap )
for( auto sessionIt : this->m_sessionMapById )
{
auto session = sessionIt.second;
if( session && session->getPlayer() )
@ -284,8 +284,8 @@ void Core::ServerZone::mainLoop()
}
auto it = this->m_sessionMap.begin();
for( ; it != this->m_sessionMap.end(); )
auto it = this->m_sessionMapById.begin();
for( ; it != this->m_sessionMapById.end(); )
{
uint32_t diff = currTime - it->second->getLastDataTime();
@ -298,7 +298,8 @@ void Core::ServerZone::mainLoop()
// if( it->second.unique() )
{
g_log.info("[" + std::to_string(it->second->getId() ) + "] Session removal" );
it = this->m_sessionMap.erase( it );
it = this->m_sessionMapById.erase( it );
removeSession( pPlayer->getName() );
continue;
}
}
@ -310,7 +311,8 @@ void Core::ServerZone::mainLoop()
it->second->close();
// if( it->second.unique() )
{
it = this->m_sessionMap.erase(it );
it = this->m_sessionMapById.erase( it );
removeSession( pPlayer->getName() );
}
}
else
@ -329,9 +331,9 @@ bool Core::ServerZone::createSession( uint32_t sessionId )
const std::string session_id_str = std::to_string( sessionId );
auto it = m_sessionMap.find( sessionId );
auto it = m_sessionMapById.find( sessionId );
if( it != m_sessionMap.end() )
if( it != m_sessionMapById.end() )
{
g_log.error( "[" + session_id_str + "] Error creating session" );
return false;
@ -340,7 +342,7 @@ bool Core::ServerZone::createSession( uint32_t sessionId )
g_log.info( "[" + session_id_str + "] Creating new session" );
boost::shared_ptr<Session> newSession( new Session( sessionId ) );
m_sessionMap[sessionId] = newSession;
m_sessionMapById[sessionId] = newSession;
if( !newSession->loadPlayer() )
{
@ -348,7 +350,7 @@ bool Core::ServerZone::createSession( uint32_t sessionId )
return false;
}
m_playerSessionMap[newSession->getPlayer()->getName()] = newSession;
m_sessionMapByName[newSession->getPlayer()->getName()] = newSession;
return true;
@ -356,15 +358,15 @@ bool Core::ServerZone::createSession( uint32_t sessionId )
void Core::ServerZone::removeSession( uint32_t sessionId )
{
m_sessionMap.erase( sessionId );
m_sessionMapById.erase( sessionId );
}
void Core::ServerZone::updateSession( uint32_t id )
{
std::lock_guard< std::mutex > lock( m_sessionMutex );
auto it = m_sessionMap.find( id );
auto it = m_sessionMapById.find( id );
if( it != m_sessionMap.end() )
if( it != m_sessionMapById.end() )
it->second->loadPlayer();
}
@ -372,9 +374,9 @@ Core::SessionPtr Core::ServerZone::getSession( uint32_t id )
{
//std::lock_guard<std::mutex> lock( m_sessionMutex );
auto it = m_sessionMap.find( id );
auto it = m_sessionMapById.find( id );
if( it != m_sessionMap.end() )
if( it != m_sessionMapById.end() )
return ( it->second );
return nullptr;
@ -384,9 +386,9 @@ Core::SessionPtr Core::ServerZone::getSession( std::string playerName )
{
//std::lock_guard<std::mutex> lock( m_sessionMutex );
auto it = m_playerSessionMap.find( playerName );
auto it = m_sessionMapByName.find( playerName );
if (it != m_playerSessionMap.end())
if (it != m_sessionMapByName.end())
return (it->second);
return nullptr;
@ -394,15 +396,15 @@ Core::SessionPtr Core::ServerZone::getSession( std::string playerName )
void Core::ServerZone::removeSession( std::string playerName )
{
m_playerSessionMap.erase( playerName );
m_sessionMapByName.erase( playerName );
}
void Core::ServerZone::updateSession( std::string playerName )
{
std::lock_guard< std::mutex > lock( m_sessionMutex );
auto it = m_playerSessionMap.find( playerName );
auto it = m_sessionMapByName.find( playerName );
if( it != m_playerSessionMap.end() )
if( it != m_sessionMapByName.end() )
it->second->loadPlayer();
}

View file

@ -61,8 +61,8 @@ namespace Core {
std::mutex m_sessionMutex;
std::map< uint32_t, SessionPtr > m_sessionMap;
std::map< std::string, SessionPtr > m_playerSessionMap;
std::map< uint32_t, SessionPtr > m_sessionMapById;
std::map< std::string, SessionPtr > m_sessionMapByName;
std::map< uint32_t, uint32_t > m_zones;

View file

@ -1,218 +0,0 @@
#include <src/servers/Server_Common/Util/Util.h>
#include <src/servers/Server_Common/Network/PacketDef/Zone/ServerZoneDef.h>
#include "src/servers/Server_Zone/Actor/Actor.h"
#include "StatusEffect.h"
#include "StatusEffectContainer.h"
#include "src/servers/Server_Zone/Network/PacketWrappers/ActorControlPacket142.h"
#include "src/servers/Server_Zone/Network/PacketWrappers/ActorControlPacket143.h"
using namespace Core::Common;
using namespace Core::Network::Packets;
using namespace Core::Network::Packets::Server;
Core::StatusEffect::StatusEffectContainer::StatusEffectContainer( Entity::ActorPtr pOwner )
: m_pOwner( pOwner )
{
// initialize the free slot queue
for( uint8_t i = 0; i < MAX_EFFECTS; i++ )
{
m_freeEffectSlotQueue.push( i );
}
}
int8_t Core::StatusEffect::StatusEffectContainer::getFreeSlot()
{
int8_t freeEffectSlot = -1;
if( m_freeEffectSlotQueue.empty() )
return freeEffectSlot;
freeEffectSlot = m_freeEffectSlotQueue.front();
m_freeEffectSlotQueue.pop();
return freeEffectSlot;
}
void Core::StatusEffect::StatusEffectContainer::freeSlot( uint8_t slotId )
{
m_freeEffectSlotQueue.push( slotId );
}
Core::StatusEffect::StatusEffectContainer::~StatusEffectContainer()
{
}
void Core::StatusEffect::StatusEffectContainer::addStatusEffect( StatusEffectPtr pEffect )
{
int8_t nextSlot = getFreeSlot();
// if there is no slot left, do not add the effect
if( nextSlot == -1 )
return;
pEffect->applyStatus();
m_effectMap[nextSlot] = pEffect;
ZoneChannelPacket< Server::FFXIVIpcAddStatusEffect > statusEffectAdd( m_pOwner->getId() );
statusEffectAdd.data().actor_id = pEffect->getTargetActorId();
statusEffectAdd.data().actor_id1 = pEffect->getSrcActorId();
statusEffectAdd.data().current_hp = m_pOwner->getHp();
statusEffectAdd.data().current_mp = m_pOwner->getMp();
statusEffectAdd.data().current_tp = m_pOwner->getTp();
statusEffectAdd.data().duration = static_cast< float >( pEffect->getDuration() ) / 1000;
statusEffectAdd.data().effect_id = pEffect->getId();
statusEffectAdd.data().effect_index = nextSlot;
statusEffectAdd.data().max_hp = m_pOwner->getMaxHp();
statusEffectAdd.data().max_mp = m_pOwner->getMaxMp();
statusEffectAdd.data().max_something = 1;
//statusEffectAdd.data().unknown2 = 28;
statusEffectAdd.data().param = pEffect->getParam();
bool sendToSelf = m_pOwner->isPlayer() ? true : false;
m_pOwner->sendToInRangeSet( statusEffectAdd, sendToSelf );
}
void Core::StatusEffect::StatusEffectContainer::removeSingleStatusEffectFromId( uint32_t id )
{
for (auto effectIt : m_effectMap)
{
if (effectIt.second->getId() == id)
{
removeStatusEffect( effectIt.first );
break;
}
}
}
void Core::StatusEffect::StatusEffectContainer::removeStatusEffect( uint8_t effectSlotId )
{
auto pEffectIt = m_effectMap.find( effectSlotId );
if( pEffectIt == m_effectMap.end() )
return;
freeSlot( effectSlotId );
auto pEffect = pEffectIt->second;
pEffect->removeStatus();
bool sendToSelf = m_pOwner->isPlayer() ? true : false;
m_pOwner->sendToInRangeSet( ActorControlPacket142( m_pOwner->getId(), StatusEffectLose, pEffect->getId() ), sendToSelf );
m_effectMap.erase( effectSlotId );
sendUpdate();
}
std::map< uint8_t, Core::StatusEffect::StatusEffectPtr > Core::StatusEffect::StatusEffectContainer::getEffectMap() const
{
return m_effectMap;
}
void Core::StatusEffect::StatusEffectContainer::sendUpdate()
{
uint64_t currentTimeMs = Util::getTimeMs();
ZoneChannelPacket< Server::FFXIVIpcStatusEffectList > statusEffectList( m_pOwner->getId() );
statusEffectList.data().current_hp = m_pOwner->getHp();
statusEffectList.data().current_mp = m_pOwner->getMp();
statusEffectList.data().currentTp = m_pOwner->getTp();
statusEffectList.data().max_hp = m_pOwner->getMaxHp();
statusEffectList.data().max_mp = m_pOwner->getMaxMp();
uint8_t slot = 0;
for( auto effectIt : m_effectMap )
{
float timeLeft = static_cast< float >( effectIt.second->getDuration() - ( currentTimeMs - effectIt.second->getStartTimeMs() ) ) / 1000;
statusEffectList.data().effect[slot].duration = timeLeft;
statusEffectList.data().effect[slot].effect_id = effectIt.second->getId();
statusEffectList.data().effect[slot].sourceActorId = effectIt.second->getSrcActorId();
slot++;
}
bool sendToSelf = m_pOwner->isPlayer();
m_pOwner->sendToInRangeSet( statusEffectList, sendToSelf );
}
void Core::StatusEffect::StatusEffectContainer::update()
{
uint64_t currentTimeMs = Util::getTimeMs();
uint32_t thisTickDmg = 0;
uint32_t thisTickHeal = 0;
for( auto effectIt : m_effectMap )
{
uint8_t effectIndex = effectIt.first;
auto effect = effectIt.second;
uint64_t lastTick = effect->getLastTickMs();
uint64_t startTime = effect->getStartTimeMs();
uint32_t duration = effect->getDuration();
uint32_t tickRate = effect->getTickRate();
if( ( currentTimeMs - startTime ) > duration )
{
// remove status effect
removeStatusEffect( effectIndex );
// break because removing invalidates iterators
break;
}
if( ( currentTimeMs - lastTick ) > tickRate )
{
effect->setLastTick( currentTimeMs );
effect->onTick();
auto thisEffect = effect->getTickEffect();
switch( thisEffect.first )
{
case 1:
{
thisTickDmg += thisEffect.second;
break;
}
case 2:
{
thisTickHeal += thisEffect.second;
break;
}
}
}
}
if( thisTickDmg != 0 )
{
m_pOwner->takeDamage( thisTickDmg );
m_pOwner->sendToInRangeSet( ActorControlPacket142( m_pOwner->getId(), HPFloatingText, 0, static_cast< uint8_t >( ActionEffectType::Damage ), thisTickDmg ) );
}
if( thisTickHeal != 0 )
{
m_pOwner->heal( thisTickDmg );
m_pOwner->sendToInRangeSet( ActorControlPacket142( m_pOwner->getId(), HPFloatingText, 0, static_cast< uint8_t >( ActionEffectType::Heal ), thisTickHeal ) );
}
}
bool Core::StatusEffect::StatusEffectContainer::hasStatusEffect( uint32_t id )
{
for( auto effectIt : m_effectMap )
{
if( effectIt.second->getId() == id )
{
return true;
}
}
return false;
}

View file

@ -1,49 +0,0 @@
#ifndef _STATUSEFFECTCONTAINER_H_
#define _STATUSEFFECTCONTAINER_H_
#include <boost/shared_ptr.hpp>
#include <queue>
#include <map>
#include "src/servers/Server_Zone/Forwards.h"
namespace Core
{
namespace StatusEffect
{
class StatusEffectContainer
{
public:
StatusEffectContainer( Entity::ActorPtr pOwner );
~StatusEffectContainer();
void addStatusEffect( StatusEffectPtr pEffect );
void removeStatusEffect( uint8_t effectSlotId );
void removeSingleStatusEffectFromId( uint32_t id );
void update();
bool hasStatusEffect( uint32_t id );
int8_t getFreeSlot();
void freeSlot( uint8_t slotId );
std::map< uint8_t, Core::StatusEffect::StatusEffectPtr > getEffectMap() const;
void sendUpdate();
private:
const uint8_t MAX_EFFECTS = 30;
Entity::ActorPtr m_pOwner;
std::queue< uint8_t > m_freeEffectSlotQueue;
std::vector< std::pair< uint8_t, uint32_t> > m_tickEffectList;
std::map< uint8_t, StatusEffectPtr > m_effectMap;
};
}
}
#endif

View file

@ -47,7 +47,6 @@ namespace Core
{
entry->setCurrentZone( m_pZone );
entry->getAsBattleNpc()->initStatusEffectContainer();
m_pZone->pushActor( entry );
}
@ -170,4 +169,4 @@ namespace Core
m_bUnloadPending = false;
}
}
}