2018-03-06 22:22:19 +01:00
|
|
|
#include <Util/Util.h>
|
|
|
|
#include <Util/UtilMath.h>
|
2021-11-27 00:53:57 +01:00
|
|
|
#include <Exd/ExdData.h>
|
2018-06-28 00:07:07 +02:00
|
|
|
#include <utility>
|
2018-06-23 21:38:04 +02:00
|
|
|
#include <Network/CommonActorControl.h>
|
2020-03-01 01:00:57 +11:00
|
|
|
#include <Service.h>
|
2018-09-20 23:31:38 +02:00
|
|
|
|
2018-02-20 22:46:44 +01:00
|
|
|
|
|
|
|
#include "Forwards.h"
|
|
|
|
|
2019-07-21 22:33:33 +10:00
|
|
|
#include "Territory/Territory.h"
|
2018-02-20 22:46:44 +01:00
|
|
|
|
|
|
|
#include "Network/GameConnection.h"
|
2019-10-09 18:14:53 +02:00
|
|
|
#include "Network/PacketWrappers/ActorControlPacket.h"
|
|
|
|
#include "Network/PacketWrappers/ActorControlTargetPacket.h"
|
2022-01-10 19:31:21 -03:00
|
|
|
#include "Network/PacketWrappers/EffectPacket1.h"
|
2023-02-20 23:39:05 +01:00
|
|
|
#include "Network/PacketWrappers/HudParamPacket.h"
|
2023-03-08 09:20:08 +01:00
|
|
|
#include "Network/Util/PacketUtil.h"
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2019-02-08 21:18:01 +11:00
|
|
|
#include "Action/Action.h"
|
2021-11-27 00:53:57 +01:00
|
|
|
#include "WorldServer.h"
|
2018-02-20 22:46:44 +01:00
|
|
|
#include "Session.h"
|
2019-04-25 21:57:41 +10:00
|
|
|
#include "Math/CalcStats.h"
|
2018-02-20 22:46:44 +01:00
|
|
|
#include "Chara.h"
|
|
|
|
#include "Player.h"
|
2018-12-01 00:27:16 +11:00
|
|
|
#include "Manager/TerritoryMgr.h"
|
2023-02-17 22:46:02 +01:00
|
|
|
#include "Manager/MgrUtil.h"
|
2018-08-28 19:05:52 +02:00
|
|
|
#include "Common.h"
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
using namespace Sapphire;
|
2018-11-29 16:55:48 +01:00
|
|
|
using namespace Sapphire::Common;
|
2023-03-08 09:20:08 +01:00
|
|
|
using namespace Sapphire::Entity;
|
|
|
|
using namespace Sapphire::World;
|
2023-02-17 22:46:02 +01:00
|
|
|
using namespace Sapphire::World::Manager;
|
2018-11-29 16:55:48 +01:00
|
|
|
using namespace Sapphire::Network::Packets;
|
2021-11-27 00:53:57 +01:00
|
|
|
using namespace Sapphire::Network::Packets::WorldPackets::Server;
|
2018-11-29 16:55:48 +01:00
|
|
|
using namespace Sapphire::Network::ActorControl;
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
Chara::Chara( ObjKind type ) :
|
2021-11-27 00:53:57 +01:00
|
|
|
GameObject( type ),
|
2018-09-13 22:14:31 +02:00
|
|
|
m_pose( 0 ),
|
2019-01-30 23:48:09 +01:00
|
|
|
m_targetId( INVALID_GAME_OBJECT_ID64 ),
|
2019-04-19 23:01:27 +10:00
|
|
|
m_directorId( 0 ),
|
2019-04-21 23:52:41 +10:00
|
|
|
m_radius( 1.f )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2019-01-31 23:44:53 +11:00
|
|
|
|
|
|
|
m_lastTickTime = 0;
|
|
|
|
m_lastUpdate = 0;
|
2023-03-08 09:20:08 +01:00
|
|
|
m_lastAttack = Common::Util::getTimeMs();
|
2019-01-31 23:44:53 +11:00
|
|
|
|
2019-03-25 21:46:58 +11:00
|
|
|
m_bonusStats.fill( 0 );
|
|
|
|
|
2018-08-29 21:40:59 +02:00
|
|
|
// initialize the free slot queue
|
|
|
|
for( uint8_t i = 0; i < MAX_STATUS_EFFECTS; i++ )
|
|
|
|
{
|
|
|
|
m_statusEffectFreeSlotQueue.push( i );
|
|
|
|
}
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
Chara::~Chara() = default;
|
2018-02-20 22:46:44 +01:00
|
|
|
|
|
|
|
/*! \return the actors name */
|
2023-03-08 09:20:08 +01:00
|
|
|
std::string Chara::getName() const
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2023-01-17 08:28:06 +01:00
|
|
|
return { m_name };
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*! \return current stance of the actors */
|
2023-03-08 09:20:08 +01:00
|
|
|
Stance Chara::getStance() const
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
return m_currentStance;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \return actor stats */
|
2023-03-08 09:20:08 +01:00
|
|
|
const Chara::ActorStatsArray& Chara::getStats() const
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
return m_baseStats;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \return current HP */
|
2023-03-08 09:20:08 +01:00
|
|
|
uint32_t Chara::getHp() const
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
return m_hp;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
uint32_t Chara::getHpPercent() const
|
2019-04-17 00:10:32 +02:00
|
|
|
{
|
|
|
|
return ( m_hp * 100 ) / m_maxHp;
|
|
|
|
}
|
|
|
|
|
2018-02-20 22:46:44 +01:00
|
|
|
/*! \return current MP */
|
2023-03-08 09:20:08 +01:00
|
|
|
uint32_t Chara::getMp() const
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
return m_mp;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \return current TP */
|
2023-03-08 09:20:08 +01:00
|
|
|
uint16_t Chara::getTp() const
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
return m_tp;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \return current GP */
|
2023-03-08 09:20:08 +01:00
|
|
|
uint16_t Chara::getGp() const
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
return m_gp;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \return current invincibility type */
|
2023-03-08 09:20:08 +01:00
|
|
|
InvincibilityType Chara::getInvincibilityType() const
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
return m_invincibilityType;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \return current class or job */
|
2023-03-08 09:20:08 +01:00
|
|
|
ClassJob Chara::getClass() const
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
return m_class;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \param ClassJob to set */
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::setClass( ClassJob classJob )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
m_class = classJob;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
Role Chara::getRole() const
|
2019-07-27 00:37:40 +10:00
|
|
|
{
|
|
|
|
switch( getClass() )
|
|
|
|
{
|
|
|
|
case ClassJob::Gladiator:
|
|
|
|
case ClassJob::Marauder:
|
|
|
|
case ClassJob::Paladin:
|
|
|
|
case ClassJob::Warrior:
|
|
|
|
case ClassJob::Darkknight:
|
|
|
|
return Role::Tank;
|
|
|
|
|
|
|
|
case ClassJob::Pugilist:
|
|
|
|
case ClassJob::Lancer:
|
|
|
|
case ClassJob::Monk:
|
|
|
|
case ClassJob::Dragoon:
|
|
|
|
case ClassJob::Rogue:
|
|
|
|
case ClassJob::Ninja:
|
|
|
|
return Role::Melee;
|
|
|
|
|
|
|
|
case ClassJob::Archer:
|
|
|
|
case ClassJob::Bard:
|
|
|
|
case ClassJob::Machinist:
|
|
|
|
return Role::RangedPhysical;
|
|
|
|
|
|
|
|
case ClassJob::Conjurer:
|
|
|
|
case ClassJob::Whitemage:
|
|
|
|
case ClassJob::Scholar:
|
|
|
|
case ClassJob::Astrologian:
|
|
|
|
return Role::Healer;
|
|
|
|
|
|
|
|
case ClassJob::Thaumaturge:
|
|
|
|
case ClassJob::Blackmage:
|
|
|
|
case ClassJob::Arcanist:
|
|
|
|
case ClassJob::Summoner:
|
|
|
|
return Role::RangedMagical;
|
|
|
|
|
|
|
|
case ClassJob::Carpenter:
|
|
|
|
case ClassJob::Blacksmith:
|
|
|
|
case ClassJob::Armorer:
|
|
|
|
case ClassJob::Goldsmith:
|
|
|
|
case ClassJob::Leatherworker:
|
|
|
|
case ClassJob::Weaver:
|
|
|
|
case ClassJob::Alchemist:
|
|
|
|
case ClassJob::Culinarian:
|
|
|
|
return Role::Crafter;
|
|
|
|
|
|
|
|
case ClassJob::Miner:
|
|
|
|
case ClassJob::Botanist:
|
|
|
|
case ClassJob::Fisher:
|
|
|
|
return Role::Gatherer;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return Role::None;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-20 22:46:44 +01:00
|
|
|
/*! \param Id of the target to set */
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::setTargetId( uint64_t targetId )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
m_targetId = targetId;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \return Id of the current target */
|
2023-03-08 09:20:08 +01:00
|
|
|
uint64_t Chara::getTargetId() const
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
return m_targetId;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \return True if the actor is alive */
|
2023-03-08 09:20:08 +01:00
|
|
|
bool Chara::isAlive() const
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
return ( m_hp > 0 );
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \return max hp for the actor */
|
2023-03-08 09:20:08 +01:00
|
|
|
uint32_t Chara::getMaxHp() const
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2023-02-21 07:58:53 +01:00
|
|
|
return m_maxHp;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \return max mp for the actor */
|
2023-03-08 09:20:08 +01:00
|
|
|
uint32_t Chara::getMaxMp() const
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2023-02-21 07:58:53 +01:00
|
|
|
return m_maxMp;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \return reset hp to current max hp */
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::resetHp()
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
m_hp = getMaxHp();
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \return reset mp to current max mp */
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::resetMp()
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
m_mp = getMaxMp();
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \param hp amount to set ( caps to maxHp ) */
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::setHp( uint32_t hp )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
m_hp = hp < getMaxHp() ? hp : getMaxHp();
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \param mp amount to set ( caps to maxMp ) */
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::setMp( uint32_t mp )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
m_mp = mp < getMaxMp() ? mp : getMaxMp();
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \param gp amount to set*/
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::setGp( uint32_t gp )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2019-12-26 11:09:15 +03:00
|
|
|
m_gp = static_cast< uint16_t >( gp );
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
2019-04-04 21:56:59 +11:00
|
|
|
/*! \param tp amount to set*/
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::setTp( uint32_t tp )
|
2019-04-04 21:56:59 +11:00
|
|
|
{
|
2019-12-26 11:09:15 +03:00
|
|
|
m_tp = static_cast< uint16_t >( tp );
|
2019-04-04 21:56:59 +11:00
|
|
|
}
|
|
|
|
|
2018-02-20 22:46:44 +01:00
|
|
|
/*! \param type invincibility type to set */
|
2018-11-29 16:55:48 +01:00
|
|
|
void Sapphire::Entity::Chara::setInvincibilityType( Common::InvincibilityType type )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
m_invincibilityType = type;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \return current status of the actor */
|
2023-03-08 09:20:08 +01:00
|
|
|
ActorStatus Chara::getStatus() const
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
return m_status;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \param status to set */
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::setStatus( ActorStatus status )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
m_status = status;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Performs necessary steps to mark an actor dead.
|
|
|
|
Sets hp/mp/tp, sets status, plays animation and fires onDeath event
|
|
|
|
*/
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::die()
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
m_status = ActorStatus::Dead;
|
|
|
|
m_hp = 0;
|
|
|
|
m_mp = 0;
|
|
|
|
m_tp = 0;
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2018-08-29 21:40:59 +02:00
|
|
|
// fire onDeath event
|
|
|
|
onDeath();
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2018-08-29 21:40:59 +02:00
|
|
|
// if the actor is a player, the update needs to be send to himself too
|
|
|
|
bool selfNeedsUpdate = isPlayer();
|
2023-03-08 09:20:08 +01:00
|
|
|
Network::Util::Packet::sendActorControl( getInRangePlayerIds( selfNeedsUpdate ), getId(), SetStatus, static_cast< uint8_t >( ActorStatus::Dead ) );
|
|
|
|
Network::Util::Packet::sendActorControl( getInRangePlayerIds( selfNeedsUpdate ), getId(), DeathAnimation );
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
2023-03-11 20:52:52 +01:00
|
|
|
uint64_t Chara::getLastAttack() const
|
|
|
|
{
|
|
|
|
return m_lastAttack;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Chara::setLastAttack( uint64_t tickCount )
|
|
|
|
{
|
|
|
|
m_lastAttack = tickCount;
|
|
|
|
}
|
|
|
|
|
2018-02-20 22:46:44 +01:00
|
|
|
/*!
|
|
|
|
Calculates and sets the rotation to look towards a specified
|
|
|
|
position
|
|
|
|
|
|
|
|
\param Position to look towards
|
|
|
|
*/
|
2023-03-08 09:20:08 +01:00
|
|
|
bool Chara::face( const FFXIVARR_POSITION3& p )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
float oldRot = getRot();
|
2023-03-08 09:20:08 +01:00
|
|
|
float rot = Common::Util::calcAngFrom( getPos().x, getPos().z, p.x, p.z );
|
2018-08-29 21:40:59 +02:00
|
|
|
float newRot = PI - rot + ( PI / 2 );
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2018-08-29 21:40:59 +02:00
|
|
|
setRot( newRot );
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2019-12-30 13:59:57 +03:00
|
|
|
return ( fabs( oldRot - newRot ) <= std::numeric_limits< float >::epsilon() * fmax( fabs( oldRot ), fabs( newRot ) ) );
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Set and propagate the actor stance to in range players
|
|
|
|
( not the actor himself )
|
|
|
|
|
|
|
|
\param stance to set
|
|
|
|
*/
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::setStance( Stance stance )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
m_currentStance = stance;
|
2023-03-08 09:20:08 +01:00
|
|
|
Network::Util::Packet::sendActorControl( getInRangePlayerIds(), getId(), ToggleWeapon, stance, 1 );
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Check if an action is queued for execution, if so update it
|
|
|
|
and if fully performed, clean up again.
|
|
|
|
|
|
|
|
\return true if a queued action has been updated
|
|
|
|
*/
|
2023-03-08 09:20:08 +01:00
|
|
|
bool Chara::checkAction()
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
|
|
|
|
2018-08-29 21:40:59 +02:00
|
|
|
if( m_pCurrentAction == nullptr )
|
|
|
|
return false;
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2018-08-29 21:40:59 +02:00
|
|
|
if( m_pCurrentAction->update() )
|
|
|
|
m_pCurrentAction.reset();
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2018-08-29 21:40:59 +02:00
|
|
|
return true;
|
2018-02-20 22:46:44 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::update( uint64_t tickCount )
|
2019-01-28 19:16:44 +11:00
|
|
|
{
|
2020-01-19 21:20:01 +09:00
|
|
|
updateStatusEffects();
|
|
|
|
|
2019-12-26 11:09:15 +03:00
|
|
|
if( std::difftime( static_cast< time_t >( tickCount ), m_lastTickTime ) > 3000 )
|
2019-01-28 19:16:44 +11:00
|
|
|
{
|
|
|
|
onTick();
|
|
|
|
|
2019-12-26 11:09:15 +03:00
|
|
|
m_lastTickTime = static_cast< time_t >( tickCount );
|
2019-01-28 19:16:44 +11:00
|
|
|
}
|
|
|
|
|
2019-12-26 11:09:15 +03:00
|
|
|
m_lastUpdate = static_cast< time_t >( tickCount );
|
2019-01-28 19:16:44 +11:00
|
|
|
}
|
|
|
|
|
2018-02-20 22:46:44 +01:00
|
|
|
/*!
|
|
|
|
Change the current target and propagate to in range players
|
|
|
|
|
|
|
|
\param target actor id
|
|
|
|
*/
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::changeTarget( uint64_t targetId )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
setTargetId( targetId );
|
2023-03-08 09:20:08 +01:00
|
|
|
Network::Util::Packet::sendActorControlTarget( getInRangePlayerIds(), getId(), ToggleWeapon, SetTarget, 0, 0, 0, 0, targetId );
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Dummy function \return 0
|
|
|
|
*/
|
2023-03-08 09:20:08 +01:00
|
|
|
uint8_t Chara::getLevel() const
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
return 0;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Let an actor take damage and perform necessary steps
|
|
|
|
according to resulting hp, propagates new hp value to players
|
|
|
|
in range
|
|
|
|
TODO: eventually this needs to distinguish between physical and
|
|
|
|
magical dmg and take status effects into account
|
|
|
|
|
|
|
|
\param amount of damage to be taken
|
|
|
|
*/
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::takeDamage( uint32_t damage )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
if( damage >= m_hp )
|
|
|
|
{
|
|
|
|
switch( m_invincibilityType )
|
|
|
|
{
|
|
|
|
case InvincibilityNone:
|
|
|
|
setHp( 0 );
|
|
|
|
die();
|
|
|
|
break;
|
|
|
|
case InvincibilityRefill:
|
|
|
|
resetHp();
|
|
|
|
break;
|
|
|
|
case InvincibilityStayAlive:
|
|
|
|
setHp( 0 );
|
|
|
|
break;
|
2019-01-26 13:40:02 +11:00
|
|
|
case InvincibilityIgnoreDamage:
|
|
|
|
break;
|
2018-08-29 21:40:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_hp -= damage;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Let an actor get healed and perform necessary steps
|
|
|
|
according to resulting hp, propagates new hp value to players
|
|
|
|
in range
|
|
|
|
|
|
|
|
\param amount of hp to be healed
|
|
|
|
*/
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::heal( uint32_t amount )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
if( ( m_hp + amount ) > getMaxHp() )
|
|
|
|
{
|
|
|
|
m_hp = getMaxHp();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_hp += amount;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::restoreMP( uint32_t amount )
|
2020-01-05 17:09:27 +09:00
|
|
|
{
|
|
|
|
if( ( m_mp + amount ) > getMaxMp() )
|
|
|
|
{
|
|
|
|
m_mp = getMaxMp();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_mp += amount;
|
|
|
|
}
|
|
|
|
|
2018-02-20 22:46:44 +01:00
|
|
|
/*! \return ActionPtr of the currently registered action, or nullptr */
|
2023-03-08 09:20:08 +01:00
|
|
|
Action::ActionPtr Chara::getCurrentAction() const
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
return m_pCurrentAction;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \param ActionPtr of the action to be registered */
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::setCurrentAction( Action::ActionPtr pAction )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2019-01-19 01:15:17 +01:00
|
|
|
m_pCurrentAction = std::move( pAction );
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Autoattack prototype implementation
|
|
|
|
TODO: move the check if the autoAttack can be performed to the callee
|
|
|
|
also rename autoAttack to autoAttack as that is more elaborate
|
|
|
|
On top of that, this only solves attacks from melee classes.
|
|
|
|
Will have to be extended for ranged attacks.
|
|
|
|
|
2021-11-27 00:53:57 +01:00
|
|
|
\param GameObjectPtr the autoAttack is performed on
|
2018-02-20 22:46:44 +01:00
|
|
|
*/
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::autoAttack( CharaPtr pTarget )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2023-03-08 09:20:08 +01:00
|
|
|
uint64_t tick = Common::Util::getTimeMs();
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2019-01-26 13:40:02 +11:00
|
|
|
// todo: this needs to use the auto attack delay for the equipped weapon
|
2018-08-29 21:40:59 +02:00
|
|
|
if( ( tick - m_lastAttack ) > 2500 )
|
|
|
|
{
|
2019-01-20 13:36:34 +01:00
|
|
|
pTarget->onActionHostile( getAsChara() );
|
2018-08-29 21:40:59 +02:00
|
|
|
m_lastAttack = tick;
|
|
|
|
srand( static_cast< uint32_t >( tick ) );
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2019-01-19 01:15:17 +01:00
|
|
|
auto damage = static_cast< uint16_t >( 10 + rand() % 12 );
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2022-01-10 19:31:21 -03:00
|
|
|
auto effectPacket = std::make_shared< EffectPacket1 >( getId(), pTarget->getId(), 7 );
|
2023-03-08 09:20:08 +01:00
|
|
|
effectPacket->setRotation( Common::Util::floatToUInt16Rot( getRot() ) );
|
2022-01-10 19:31:21 -03:00
|
|
|
|
2021-11-27 00:53:57 +01:00
|
|
|
Common::CalcResultParam effectEntry{};
|
|
|
|
effectEntry.Value = static_cast< int16_t >( damage );
|
|
|
|
effectEntry.Type = ActionEffectType::CALC_RESULT_TYPE_DAMAGE_HP;
|
|
|
|
effectEntry.Arg0 = static_cast< uint8_t >( ActionHitSeverityType::NormalDamage );
|
|
|
|
effectEntry.Arg2 = 0x71;
|
2022-01-10 19:31:21 -03:00
|
|
|
effectPacket->addTargetEffect( effectEntry );
|
2018-07-21 23:32:10 +10:00
|
|
|
|
2023-02-17 22:46:02 +01:00
|
|
|
server().queueForPlayers( getInRangePlayerIds(), effectPacket );
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2018-08-29 21:40:59 +02:00
|
|
|
pTarget->takeDamage( damage );
|
|
|
|
}
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \param StatusEffectPtr to be applied to the actor */
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::addStatusEffect( StatusEffect::StatusEffectPtr pEffect )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2023-03-08 09:20:08 +01:00
|
|
|
auto& teriMgr = Common::Service< Manager::TerritoryMgr >::ref();
|
2022-01-10 23:50:44 +01:00
|
|
|
auto pZone = teriMgr.getTerritoryByGuId( getTerritoryId() );
|
|
|
|
|
2018-08-29 21:40:59 +02:00
|
|
|
int8_t nextSlot = getStatusEffectFreeSlot();
|
|
|
|
// if there is no slot left, do not add the effect
|
|
|
|
if( nextSlot == -1 )
|
|
|
|
return;
|
|
|
|
|
|
|
|
pEffect->applyStatus();
|
2023-03-10 22:34:12 +01:00
|
|
|
pEffect->setSlot( nextSlot );
|
2018-08-29 21:40:59 +02:00
|
|
|
m_statusEffectMap[ nextSlot ] = pEffect;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \param StatusEffectPtr to be applied to the actor */
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::addStatusEffectById( uint32_t id, int32_t duration, Entity::Chara& source, uint16_t param )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2020-03-01 01:00:57 +11:00
|
|
|
auto effect = StatusEffect::make_StatusEffect( id, source.getAsChara(), getAsChara(), duration, 3000 );
|
2018-08-29 21:40:59 +02:00
|
|
|
effect->setParam( param );
|
|
|
|
addStatusEffect( effect );
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \param StatusEffectPtr to be applied to the actor */
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::addStatusEffectByIdIfNotExist( uint32_t id, int32_t duration, Entity::Chara& source, uint16_t param )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
if( hasStatusEffect( id ) )
|
|
|
|
return;
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2020-03-01 01:00:57 +11:00
|
|
|
auto effect = StatusEffect::make_StatusEffect( id, source.getAsChara(), getAsChara(), duration, 3000 );
|
2018-08-29 21:40:59 +02:00
|
|
|
effect->setParam( param );
|
|
|
|
addStatusEffect( effect );
|
2018-02-20 22:46:44 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
int8_t Chara::getStatusEffectFreeSlot()
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
int8_t freeEffectSlot = -1;
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2018-08-29 21:40:59 +02:00
|
|
|
if( m_statusEffectFreeSlotQueue.empty() )
|
|
|
|
return freeEffectSlot;
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2019-12-26 11:09:15 +03:00
|
|
|
freeEffectSlot = static_cast< int8_t >( m_statusEffectFreeSlotQueue.front() );
|
2018-08-29 21:40:59 +02:00
|
|
|
m_statusEffectFreeSlotQueue.pop();
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2018-08-29 21:40:59 +02:00
|
|
|
return freeEffectSlot;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::statusEffectFreeSlot( uint8_t slotId )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
m_statusEffectFreeSlotQueue.push( slotId );
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::removeSingleStatusEffectById( uint32_t id )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2023-01-17 08:28:06 +01:00
|
|
|
for( const auto& effectIt : m_statusEffectMap )
|
2018-08-29 21:40:59 +02:00
|
|
|
{
|
|
|
|
if( effectIt.second->getId() == id )
|
|
|
|
{
|
|
|
|
removeStatusEffect( effectIt.first );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
std::map< uint8_t, StatusEffect::StatusEffectPtr >::iterator Chara::removeStatusEffect( uint8_t effectSlotId )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
auto pEffectIt = m_statusEffectMap.find( effectSlotId );
|
|
|
|
if( pEffectIt == m_statusEffectMap.end() )
|
2023-02-21 06:10:46 +01:00
|
|
|
return pEffectIt;
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2018-08-29 21:40:59 +02:00
|
|
|
statusEffectFreeSlot( effectSlotId );
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2018-08-29 21:40:59 +02:00
|
|
|
auto pEffect = pEffectIt->second;
|
|
|
|
pEffect->removeStatus();
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
Network::Util::Packet::sendActorControl( getInRangePlayerIds( isPlayer() ), getId(), StatusEffectLose, pEffect->getId() );
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2023-02-21 06:10:46 +01:00
|
|
|
auto it = m_statusEffectMap.erase( pEffectIt );
|
2023-03-08 09:20:08 +01:00
|
|
|
Network::Util::Packet::sendHudParam( *this );
|
2023-02-21 06:10:46 +01:00
|
|
|
return it;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
std::map< uint8_t, StatusEffect::StatusEffectPtr > Chara::getStatusEffectMap() const
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
return m_statusEffectMap;
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
const uint8_t* Chara::getLookArray() const
|
2018-09-13 22:14:31 +02:00
|
|
|
{
|
|
|
|
return m_customize;
|
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
const uint32_t* Chara::getModelArray() const
|
2018-09-10 23:57:14 +02:00
|
|
|
{
|
|
|
|
return m_modelEquip;
|
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
uint8_t Chara::getPose() const
|
2018-09-13 22:14:31 +02:00
|
|
|
{
|
|
|
|
return m_pose;
|
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::setPose( uint8_t pose )
|
2018-09-13 22:14:31 +02:00
|
|
|
{
|
|
|
|
m_pose = pose;
|
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::sendStatusEffectUpdate()
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2023-03-08 09:20:08 +01:00
|
|
|
uint64_t currentTimeMs = Common::Util::getTimeMs();
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2018-07-03 00:01:26 +02:00
|
|
|
|
2021-11-27 00:53:57 +01:00
|
|
|
auto statusEffectList = makeZonePacket< FFXIVIpcStatus >( getId() );
|
2018-08-29 21:40:59 +02:00
|
|
|
uint8_t slot = 0;
|
2023-01-17 08:28:06 +01:00
|
|
|
for( const auto& effectIt : m_statusEffectMap )
|
2018-08-29 21:40:59 +02:00
|
|
|
{
|
|
|
|
float timeLeft = static_cast< float >( effectIt.second->getDuration() -
|
|
|
|
( currentTimeMs - effectIt.second->getStartTimeMs() ) ) / 1000;
|
2021-11-27 00:53:57 +01:00
|
|
|
statusEffectList->data().effect[ slot ].Time = timeLeft;
|
|
|
|
statusEffectList->data().effect[ slot ].Id = effectIt.second->getId();
|
|
|
|
statusEffectList->data().effect[ slot ].Source = effectIt.second->getSrcActorId();
|
2018-08-29 21:40:59 +02:00
|
|
|
slot++;
|
|
|
|
}
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2023-02-17 22:46:02 +01:00
|
|
|
server().queueForPlayers( getInRangePlayerIds( isPlayer() ), statusEffectList );
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::updateStatusEffects()
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2023-03-08 09:20:08 +01:00
|
|
|
uint64_t currentTimeMs = Common::Util::getTimeMs();
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2023-02-21 06:10:46 +01:00
|
|
|
for( auto effectIt = m_statusEffectMap.begin(); effectIt != m_statusEffectMap.end(); )
|
2018-08-29 21:40:59 +02:00
|
|
|
{
|
2023-02-21 06:10:46 +01:00
|
|
|
uint8_t effectIndex = effectIt->first;
|
|
|
|
auto effect = effectIt->second;
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2018-08-29 21:40:59 +02:00
|
|
|
uint64_t lastTick = effect->getLastTickMs();
|
|
|
|
uint64_t startTime = effect->getStartTimeMs();
|
|
|
|
uint32_t duration = effect->getDuration();
|
|
|
|
uint32_t tickRate = effect->getTickRate();
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2020-01-19 21:20:01 +09:00
|
|
|
if( duration > 0 && ( currentTimeMs - startTime ) > duration )
|
2023-02-21 06:10:46 +01:00
|
|
|
effectIt = removeStatusEffect( effectIndex );
|
|
|
|
else
|
|
|
|
++effectIt;
|
2018-02-20 22:46:44 +01:00
|
|
|
|
2018-08-29 21:40:59 +02:00
|
|
|
if( ( currentTimeMs - lastTick ) > tickRate )
|
|
|
|
{
|
|
|
|
effect->setLastTick( currentTimeMs );
|
|
|
|
effect->onTick();
|
|
|
|
}
|
|
|
|
}
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
bool Chara::hasStatusEffect( uint32_t id )
|
2018-02-20 22:46:44 +01:00
|
|
|
{
|
2019-01-19 01:15:17 +01:00
|
|
|
return m_statusEffectMap.find( id ) != m_statusEffectMap.end();
|
2018-02-20 22:46:44 +01:00
|
|
|
}
|
2018-02-22 15:31:10 +01:00
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
int64_t Chara::getLastUpdateTime() const
|
2019-01-31 23:44:53 +11:00
|
|
|
{
|
|
|
|
return m_lastUpdate;
|
|
|
|
}
|
2019-03-23 20:59:41 +11:00
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::setLastComboActionId( uint32_t actionId )
|
2019-03-23 20:59:41 +11:00
|
|
|
{
|
|
|
|
m_lastComboActionId = actionId;
|
2023-03-08 09:20:08 +01:00
|
|
|
m_lastComboActionTime = Common::Util::getTimeMs();
|
2019-03-23 20:59:41 +11:00
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
uint32_t Chara::getLastComboActionId() const
|
2019-03-23 20:59:41 +11:00
|
|
|
{
|
2019-03-24 16:32:20 +11:00
|
|
|
// initially check for the time passed first, if it's more than the threshold just return 0 for the combo
|
|
|
|
// we can hide the implementation detail this way and it just works:tm: for anything that uses it
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
if( std::difftime( static_cast< time_t >( Common::Util::getTimeMs() ),
|
2023-01-17 08:28:06 +01:00
|
|
|
static_cast< time_t >( m_lastComboActionTime ) ) > Common::MAX_COMBO_LENGTH )
|
2019-03-24 16:32:20 +11:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-03-23 20:59:41 +11:00
|
|
|
return m_lastComboActionId;
|
|
|
|
}
|
2019-04-15 23:41:43 +02:00
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
uint32_t Chara::getDirectorId() const
|
2019-04-15 23:41:43 +02:00
|
|
|
{
|
|
|
|
return m_directorId;
|
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::setDirectorId( uint32_t directorId )
|
2019-04-15 23:41:43 +02:00
|
|
|
{
|
|
|
|
m_directorId = directorId;
|
|
|
|
}
|
2019-04-19 00:39:42 +02:00
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
uint32_t Chara::getAgentId() const
|
2019-04-19 00:39:42 +02:00
|
|
|
{
|
|
|
|
return m_agentId;
|
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::setAgentId( uint32_t agentId )
|
2019-04-19 00:39:42 +02:00
|
|
|
{
|
|
|
|
m_agentId = agentId;
|
|
|
|
}
|
2019-04-19 23:01:27 +10:00
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
float Chara::getRadius() const
|
2019-04-19 23:01:27 +10:00
|
|
|
{
|
2019-04-21 23:52:41 +10:00
|
|
|
return m_radius;
|
2019-04-24 23:25:07 +10:00
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
Common::BaseParam Chara::getPrimaryStat() const
|
2019-04-24 23:25:07 +10:00
|
|
|
{
|
2021-11-27 00:53:57 +01:00
|
|
|
auto& exdData = Common::Service< Data::ExdData >::ref();
|
2019-04-24 23:25:07 +10:00
|
|
|
|
2022-01-27 21:24:54 +01:00
|
|
|
auto classJob = exdData.getRow< Excel::ClassJob >( static_cast< uint16_t >( getClass() ) );
|
2019-04-24 23:25:07 +10:00
|
|
|
assert( classJob );
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
return static_cast< Common::BaseParam >( classJob->data().Role );
|
2019-04-25 21:57:41 +10:00
|
|
|
}
|
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
uint32_t Chara::getStatValue( Common::BaseParam baseParam ) const
|
2019-04-25 21:57:41 +10:00
|
|
|
{
|
2021-11-27 00:53:57 +01:00
|
|
|
auto index = static_cast< uint32_t >( baseParam );
|
|
|
|
assert( index < m_baseStats.size() );
|
2019-04-25 21:57:41 +10:00
|
|
|
|
2021-11-27 00:53:57 +01:00
|
|
|
return m_baseStats[ index ] + m_bonusStats[ index ];
|
|
|
|
}
|
2019-04-25 21:57:41 +10:00
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
float Chara::getStatValueFloat( Common::BaseParam baseParam ) const
|
2021-11-27 00:53:57 +01:00
|
|
|
{
|
|
|
|
auto index = static_cast< uint32_t >( baseParam );
|
|
|
|
assert( index < m_baseStats.size() );
|
2019-04-25 21:57:41 +10:00
|
|
|
|
2021-11-27 00:53:57 +01:00
|
|
|
return static_cast< float >( m_baseStats[ index ] + m_bonusStats[ index ] );
|
|
|
|
}
|
2019-04-25 21:57:41 +10:00
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
uint32_t Chara::getBonusStat( Common::BaseParam baseParam ) const
|
2021-11-27 00:53:57 +01:00
|
|
|
{
|
|
|
|
auto index = static_cast< uint32_t >( baseParam );
|
|
|
|
assert( index < m_bonusStats.size() );
|
2019-04-25 21:57:41 +10:00
|
|
|
|
2021-11-27 00:53:57 +01:00
|
|
|
return m_bonusStats[ index ];
|
|
|
|
}
|
2019-04-25 21:57:41 +10:00
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::setStatValue( Common::BaseParam baseParam, uint32_t value )
|
2021-11-27 00:53:57 +01:00
|
|
|
{
|
|
|
|
auto index = static_cast< uint32_t >( baseParam );
|
|
|
|
assert( index < m_baseStats.size() );
|
2019-04-25 21:57:41 +10:00
|
|
|
|
2021-11-27 00:53:57 +01:00
|
|
|
m_baseStats[ index ] = value;
|
2019-12-26 11:09:15 +03:00
|
|
|
}
|
2020-01-19 21:20:01 +09:00
|
|
|
|
2023-03-08 09:20:08 +01:00
|
|
|
void Chara::onTick()
|
2020-01-19 21:20:01 +09:00
|
|
|
{
|
|
|
|
uint32_t thisTickDmg = 0;
|
|
|
|
uint32_t thisTickHeal = 0;
|
|
|
|
|
2023-01-17 08:28:06 +01:00
|
|
|
for( const auto& effectIt : m_statusEffectMap )
|
2020-01-19 21:20:01 +09:00
|
|
|
{
|
|
|
|
auto thisEffect = effectIt.second->getTickEffect();
|
|
|
|
switch( thisEffect.first )
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
thisTickDmg += thisEffect.second;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
thisTickHeal += thisEffect.second;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( thisTickDmg != 0 )
|
|
|
|
{
|
|
|
|
takeDamage( thisTickDmg );
|
2023-03-08 09:20:08 +01:00
|
|
|
Network::Util::Packet::sendActorControl( getInRangePlayerIds( isPlayer() ), getId(), HPFloatingText, 0,
|
|
|
|
ActionEffectType::CALC_RESULT_TYPE_DAMAGE_HP, thisTickDmg );
|
2020-01-19 21:20:01 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
if( thisTickHeal != 0 )
|
|
|
|
{
|
|
|
|
heal( thisTickHeal );
|
2023-03-08 09:20:08 +01:00
|
|
|
Network::Util::Packet::sendActorControl( getInRangePlayerIds( isPlayer() ), getId(), HPFloatingText, 0,
|
|
|
|
ActionEffectType::CALC_RESULT_TYPE_RECOVER_HP, thisTickHeal );
|
2020-01-19 21:20:01 +09:00
|
|
|
}
|
|
|
|
}
|