mirror of
https://github.com/SapphireServer/Sapphire.git
synced 2025-05-25 19:17:45 +00:00
Merge branch 'action_data_update_pr' into develop_c
This commit is contained in:
commit
f5b6dc7282
16 changed files with 337 additions and 69 deletions
|
@ -624,6 +624,7 @@ namespace Sapphire::Common
|
|||
TpGain = 13,
|
||||
GpGain = 14,
|
||||
ApplyStatusEffect = 15,
|
||||
StatusNoEffect = 21,
|
||||
/*!
|
||||
* @brief Tells the client that it should show combo indicators on actions.
|
||||
*
|
||||
|
@ -650,6 +651,7 @@ namespace Sapphire::Common
|
|||
enum class ActionEffectResultFlag : uint8_t
|
||||
{
|
||||
None = 0,
|
||||
Absorbed = 4,
|
||||
EffectOnSource = 0x80,
|
||||
Reflected = 0xA0,
|
||||
};
|
||||
|
@ -1034,6 +1036,7 @@ namespace Sapphire::Common
|
|||
CritDHRateBonus = 7,
|
||||
DamageReceiveTrigger = 8,
|
||||
DamageDealtTrigger = 9,
|
||||
Shield = 10,
|
||||
};
|
||||
|
||||
enum class ActionTypeFilter : int32_t
|
||||
|
|
|
@ -422,7 +422,8 @@ namespace Sapphire::Network::Packets::Server
|
|||
uint16_t current_mp;
|
||||
uint16_t max_mp;
|
||||
uint16_t currentTp;
|
||||
uint16_t unknown1;
|
||||
uint8_t shieldPercentage;
|
||||
uint8_t unknown1;
|
||||
Common::StatusEffect effect[30];
|
||||
uint32_t padding;
|
||||
};
|
||||
|
@ -441,25 +442,16 @@ namespace Sapphire::Network::Packets::Server
|
|||
{
|
||||
uint32_t globalSequence;
|
||||
uint32_t actor_id;
|
||||
//uint8_t unknown1;
|
||||
//uint8_t unknown2;
|
||||
//uint16_t padding1;
|
||||
//uint32_t current_hp;
|
||||
//uint16_t current_mp;
|
||||
//uint16_t current_tp;
|
||||
//uint32_t max_hp;
|
||||
//uint16_t max_mp;
|
||||
//uint16_t max_something;
|
||||
uint32_t current_hp;
|
||||
uint32_t max_hp;
|
||||
uint16_t current_mp;
|
||||
uint16_t unknown1;
|
||||
uint16_t current_tp;
|
||||
uint16_t max_mp;
|
||||
uint8_t unknown2;
|
||||
uint8_t unknown1;
|
||||
uint8_t classId;
|
||||
uint8_t unknown4;
|
||||
uint8_t unkFlag;
|
||||
uint16_t unknown6;
|
||||
uint8_t shieldPercentage;
|
||||
uint8_t entryCount;
|
||||
uint16_t unknown2;
|
||||
|
||||
struct StatusEntry
|
||||
{
|
||||
|
@ -467,12 +459,12 @@ namespace Sapphire::Network::Packets::Server
|
|||
uint8_t unknown3;
|
||||
uint16_t id;
|
||||
uint16_t param;
|
||||
uint16_t unknown5; // Sort this out (old right half of power/param property)
|
||||
uint16_t unknown4; // Sort this out (old right half of power/param property)
|
||||
float duration;
|
||||
uint32_t sourceActorId;
|
||||
} statusEntries[4];
|
||||
|
||||
uint32_t unknown7;
|
||||
uint32_t unknown5;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
83
src/scripts/action/common/ActionAdloquium185.cpp
Normal file
83
src/scripts/action/common/ActionAdloquium185.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
const uint16_t STATUS_ID_GALVANIZE = 297;
|
||||
const uint16_t STATUS_ID_CATALYZE = 1918;
|
||||
|
||||
class ActionAdloquium185 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionAdloquium185() :
|
||||
ScriptAPI::ActionScript( 185 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto pTarget = action.getHitChara();
|
||||
if( pTarget )
|
||||
{
|
||||
// still pull out the lut entry to get potency values etc.
|
||||
auto lutEntry = action.getActionEntry();
|
||||
|
||||
// do healing part
|
||||
auto heal = action.calcHealing( lutEntry.healPotency );
|
||||
heal.first = Math::CalcStats::applyHealingReceiveMultiplier( *pTarget, heal.first );
|
||||
action.getEffectbuilder()->heal( pTarget, pTarget, heal.first, heal.second );
|
||||
|
||||
float shieldValue = heal.first * 1.25f;
|
||||
// apply new galvanize when not existing or larger than existing one
|
||||
auto oldEffect = pTarget->getStatusEffectById( STATUS_ID_GALVANIZE );
|
||||
if( !oldEffect.second || oldEffect.second->getEffectEntry().effectValue1 <= shieldValue )
|
||||
{
|
||||
World::Action::StatusEffectEntry effectEntry;
|
||||
effectEntry.effectType = static_cast< uint32_t >( Common::StatusEffectType::Shield );
|
||||
effectEntry.effectValue1 = static_cast< int32_t >( shieldValue );
|
||||
auto pNewEffect = action.createStatusEffect( STATUS_ID_GALVANIZE, action.getSourceChara(), pTarget, 30000, 3000 );
|
||||
pNewEffect->replaceEffectEntry( effectEntry );
|
||||
action.getEffectbuilder()->applyStatusEffect( pTarget, action.getSourceChara(), pNewEffect );
|
||||
}
|
||||
else
|
||||
action.getEffectbuilder()->statusNoEffect( pTarget, STATUS_ID_GALVANIZE );
|
||||
|
||||
if( heal.second == Common::ActionHitSeverityType::CritHeal )
|
||||
{
|
||||
// apply catalyze when crit, same rule as galvanize
|
||||
oldEffect = pTarget->getStatusEffectById( STATUS_ID_CATALYZE );
|
||||
if( !oldEffect.second || oldEffect.second->getEffectEntry().effectValue1 <= shieldValue )
|
||||
{
|
||||
World::Action::StatusEffectEntry effectEntry;
|
||||
effectEntry.effectType = static_cast< uint32_t >( Common::StatusEffectType::Shield );
|
||||
effectEntry.effectValue1 = static_cast< int32_t >( shieldValue ); // same shield value
|
||||
auto pNewEffect = action.createStatusEffect( STATUS_ID_CATALYZE, action.getSourceChara(), pTarget, 30000, 3000 );
|
||||
pNewEffect->replaceEffectEntry( effectEntry );
|
||||
action.getEffectbuilder()->applyStatusEffect( pTarget, action.getSourceChara(), pNewEffect );
|
||||
}
|
||||
else
|
||||
action.getEffectbuilder()->statusNoEffect( pTarget, STATUS_ID_CATALYZE );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onStart( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
/*
|
||||
don't run generic action handler for this action.
|
||||
for simpler actions like cure 1 we only want to apply the freecure proc in script,
|
||||
and let the generic handler do the heal so we don't have to copy heal code into scripts,
|
||||
unless in cases like adlo, the healing result matters so we have to.
|
||||
*/
|
||||
action.disableGenericHandler();
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionAdloquium185 );
|
|
@ -51,7 +51,8 @@ Action::Action::Action( Entity::CharaPtr caster, uint32_t actionId, uint16_t seq
|
|||
m_startTime( 0 ),
|
||||
m_interruptType( Common::ActionInterruptType::None ),
|
||||
m_sequence( sequence ),
|
||||
m_isAutoAttack( false )
|
||||
m_isAutoAttack( false ),
|
||||
m_disableGenericHandler( false )
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -295,7 +296,7 @@ void Action::Action::start()
|
|||
auto pScriptMgr = m_pFw->get< Scripting::ScriptMgr >();
|
||||
|
||||
// check the lut too and see if we have something usable, otherwise cancel the cast
|
||||
if( !pScriptMgr->onStart( *this ) && !ActionLut::validEntryExists( static_cast< uint16_t >( getId() ) ) )
|
||||
if( !pScriptMgr->onStart( *this ) && !hasValidLutEntry() )
|
||||
{
|
||||
// script not implemented and insufficient lut data (no potencies)
|
||||
interrupt();
|
||||
|
@ -424,9 +425,8 @@ void Action::Action::buildEffects()
|
|||
snapshotAffectedActors( m_hitActors );
|
||||
|
||||
auto pScriptMgr = m_pFw->get< Scripting::ScriptMgr >();
|
||||
auto hasLutEntry = hasValidLutEntry();
|
||||
|
||||
if( !pScriptMgr->onExecute( *this ) && !hasLutEntry )
|
||||
if( !pScriptMgr->onExecute( *this ) && !hasValidLutEntry() )
|
||||
{
|
||||
if( auto player = m_pSource->getAsPlayer() )
|
||||
{
|
||||
|
@ -436,7 +436,7 @@ void Action::Action::buildEffects()
|
|||
return;
|
||||
}
|
||||
|
||||
if( !hasLutEntry ) // this is just "if ( weCanNotUseGenericActionHandler )" in case we start to expand it.
|
||||
if( m_disableGenericHandler || !hasValidLutEntry() )
|
||||
{
|
||||
// send any effect packet added by script or an empty one just to play animation for other players
|
||||
m_effectBuilder->buildAndSendPackets();
|
||||
|
@ -469,7 +469,8 @@ void Action::Action::buildEffects()
|
|||
if( dmg.first > 0 )
|
||||
{
|
||||
actor->onActionHostile( m_pSource );
|
||||
m_effectBuilder->damage( actor, actor, dmg.first, dmg.second );
|
||||
dmg.first = Math::CalcStats::applyShieldProtection( actor, dmg.first );
|
||||
m_effectBuilder->damage( actor, actor, dmg.first, dmg.second, dmg.first == 0 ? Common::ActionEffectResultFlag::Absorbed : Common::ActionEffectResultFlag::None );
|
||||
}
|
||||
|
||||
auto reflectDmg = Math::CalcStats::calcDamageReflect( m_pSource, actor, dmg.first,
|
||||
|
@ -824,11 +825,21 @@ Data::ActionPtr Action::Action::getActionData() const
|
|||
return m_actionData;
|
||||
}
|
||||
|
||||
Action::ActionEntry Action::Action::getActionEntry() const
|
||||
{
|
||||
return m_lutEntry;
|
||||
}
|
||||
|
||||
void Action::Action::setAutoAttack()
|
||||
{
|
||||
m_isAutoAttack = true;
|
||||
}
|
||||
|
||||
void Action::Action::disableGenericHandler()
|
||||
{
|
||||
m_disableGenericHandler = true;
|
||||
}
|
||||
|
||||
bool Action::Action::isPhysical() const
|
||||
{
|
||||
return isAttackTypePhysical( static_cast< Common::AttackType >( m_actionData->attackType ) );
|
||||
|
@ -847,4 +858,10 @@ bool Action::Action::isAttackTypePhysical( Common::AttackType attackType )
|
|||
bool Action::Action::isAttackTypeMagical( Common::AttackType attackType )
|
||||
{
|
||||
return attackType == Common::AttackType::Magical;
|
||||
}
|
||||
|
||||
Sapphire::StatusEffect::StatusEffectPtr Action::Action::createStatusEffect( uint32_t id, Entity::CharaPtr sourceActor, Entity::CharaPtr targetActor, uint32_t duration, uint32_t tickRate )
|
||||
{
|
||||
// workaround to framework access issue in action script
|
||||
return StatusEffect::make_StatusEffect( id, sourceActor, targetActor, duration, tickRate, m_pFw );
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
#include "Util/ActorFilter.h"
|
||||
#include "ForwardsZone.h"
|
||||
#include "EffectBuilder.h"
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
namespace Sapphire::Data
|
||||
{
|
||||
|
@ -54,6 +55,10 @@ namespace Sapphire::World::Action
|
|||
|
||||
void setAutoAttack();
|
||||
|
||||
void disableGenericHandler();
|
||||
|
||||
StatusEffect::StatusEffectPtr createStatusEffect( uint32_t id, Entity::CharaPtr sourceActor, Entity::CharaPtr targetActor, uint32_t duration, uint32_t tickRate );
|
||||
|
||||
/*!
|
||||
* @brief Checks if a chara has enough resources available to cast the action (tp/mp/etc)
|
||||
* @return true if they have the required resources
|
||||
|
@ -122,6 +127,8 @@ namespace Sapphire::World::Action
|
|||
|
||||
Data::ActionPtr getActionData() const;
|
||||
|
||||
ActionEntry getActionEntry() const;
|
||||
|
||||
bool isPhysical() const;
|
||||
bool isMagical() const;
|
||||
|
||||
|
@ -191,6 +198,7 @@ namespace Sapphire::World::Action
|
|||
bool m_canTargetHostile;
|
||||
bool m_canTargetDead;
|
||||
bool m_isAutoAttack;
|
||||
bool m_disableGenericHandler;
|
||||
|
||||
Common::ActionInterruptType m_interruptType;
|
||||
|
||||
|
|
|
@ -91,6 +91,20 @@ void EffectBuilder::applyStatusEffect( Entity::CharaPtr& target, Entity::CharaPt
|
|||
moveToResultList( target, nextResult );
|
||||
}
|
||||
|
||||
void EffectBuilder::applyStatusEffect( Entity::CharaPtr& target, Entity::CharaPtr& source, StatusEffect::StatusEffectPtr pStatusEffect )
|
||||
{
|
||||
EffectResultPtr nextResult = make_EffectResult( target, source, getResultDelayMs() );
|
||||
nextResult->applyStatusEffect( pStatusEffect );
|
||||
moveToResultList( target, nextResult );
|
||||
}
|
||||
|
||||
void EffectBuilder::statusNoEffect( Entity::CharaPtr& target, uint16_t statusId )
|
||||
{
|
||||
EffectResultPtr nextResult = make_EffectResult( target, nullptr, 0 );
|
||||
nextResult->statusNoEffect( statusId );
|
||||
moveToResultList( target, nextResult );
|
||||
}
|
||||
|
||||
void EffectBuilder::buildAndSendPackets()
|
||||
{
|
||||
auto targetCount = m_resolvedEffects.size();
|
||||
|
|
|
@ -27,10 +27,12 @@ namespace Sapphire::World::Action
|
|||
void comboSucceed( Entity::CharaPtr& target );
|
||||
|
||||
void applyStatusEffect( Entity::CharaPtr& target, Entity::CharaPtr& source, uint16_t statusId, uint32_t duration, uint8_t param );
|
||||
void applyStatusEffect( Entity::CharaPtr& target, Entity::CharaPtr& source, StatusEffect::StatusEffectPtr pStatusEffect );
|
||||
|
||||
void statusNoEffect( Entity::CharaPtr& target, uint16_t statusId );
|
||||
|
||||
void buildAndSendPackets();
|
||||
|
||||
|
||||
private:
|
||||
void moveToResultList( Entity::CharaPtr& chara, EffectResultPtr result );
|
||||
|
||||
|
|
|
@ -18,7 +18,8 @@ EffectResult::EffectResult( Entity::CharaPtr target, Entity::CharaPtr source, ui
|
|||
m_param0( 0 ),
|
||||
m_param1( 0 ),
|
||||
m_param2( 0 ),
|
||||
m_flag( Common::ActionEffectResultFlag::None )
|
||||
m_flag( Common::ActionEffectResultFlag::None ),
|
||||
m_pPreBuiltStatusEffect( nullptr )
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -92,6 +93,22 @@ void EffectResult::applyStatusEffect( uint16_t statusId, uint32_t duration, uint
|
|||
m_type = Common::ActionEffectType::ApplyStatusEffect;
|
||||
}
|
||||
|
||||
void EffectResult::applyStatusEffect( StatusEffect::StatusEffectPtr pStatusEffect )
|
||||
{
|
||||
m_value = pStatusEffect->getId();
|
||||
m_param2 = pStatusEffect->getParam();
|
||||
m_pPreBuiltStatusEffect = std::move( pStatusEffect );
|
||||
|
||||
m_type = Common::ActionEffectType::ApplyStatusEffect;
|
||||
}
|
||||
|
||||
void EffectResult::statusNoEffect( uint16_t statusId )
|
||||
{
|
||||
m_value = statusId;
|
||||
|
||||
m_type = Common::ActionEffectType::StatusNoEffect;
|
||||
}
|
||||
|
||||
Common::EffectEntry EffectResult::buildEffectEntry() const
|
||||
{
|
||||
Common::EffectEntry entry{};
|
||||
|
@ -131,7 +148,22 @@ void EffectResult::execute()
|
|||
|
||||
case Common::ActionEffectType::ApplyStatusEffect:
|
||||
{
|
||||
m_target->addStatusEffectById( m_value, m_value2, *m_source, m_param2 );
|
||||
//remove same effect from the same source (refreshing old buff)
|
||||
for( auto const& entry : m_target->getStatusEffectMap() )
|
||||
{
|
||||
auto statusEffect = entry.second;
|
||||
if( statusEffect->getId() == m_value && statusEffect->getSrcActorId() == m_source->getId() )
|
||||
{
|
||||
// refreshing does not show "-status" flying text, and we don't send status list now because we are adding a new one
|
||||
m_target->removeStatusEffect( entry.first, false, false );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( m_pPreBuiltStatusEffect )
|
||||
m_target->addStatusEffect( m_pPreBuiltStatusEffect );
|
||||
else
|
||||
m_target->addStatusEffectById( m_value, m_value2, *m_source, m_param2 );
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include <ForwardsZone.h>
|
||||
#include <Common.h>
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
namespace Sapphire::World::Action
|
||||
{
|
||||
/*!
|
||||
|
@ -21,6 +23,8 @@ namespace Sapphire::World::Action
|
|||
void startCombo( uint16_t actionId );
|
||||
void comboSucceed();
|
||||
void applyStatusEffect( uint16_t statusId, uint32_t duration, uint8_t param );
|
||||
void applyStatusEffect( StatusEffect::StatusEffectPtr pStatusEffect );
|
||||
void statusNoEffect( uint16_t statusId );
|
||||
|
||||
Entity::CharaPtr getSource() const;
|
||||
Entity::CharaPtr getTarget() const;
|
||||
|
@ -48,6 +52,8 @@ namespace Sapphire::World::Action
|
|||
uint32_t m_value;
|
||||
uint32_t m_value2;
|
||||
Common::ActionEffectResultFlag m_flag;
|
||||
|
||||
StatusEffect::StatusEffectPtr m_pPreBuiltStatusEffect;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -502,13 +502,11 @@ void Sapphire::Entity::Chara::addStatusEffect( StatusEffect::StatusEffectPtr pEf
|
|||
statusEffectAdd->data().actor_id = pEffect->getTargetActorId();
|
||||
statusEffectAdd->data().current_hp = getHp();
|
||||
statusEffectAdd->data().current_mp = static_cast< uint16_t >( getMp() );
|
||||
//statusEffectAdd->data().current_tp = getTp();
|
||||
statusEffectAdd->data().current_tp = getTp();
|
||||
statusEffectAdd->data().max_hp = getMaxHp();
|
||||
statusEffectAdd->data().max_mp = static_cast< uint16_t >( getMaxMp() );
|
||||
//statusEffectAdd->data().max_something = 1;
|
||||
//statusEffectAdd->data().unknown2 = 28;
|
||||
statusEffectAdd->data().classId = static_cast< uint8_t >(getClass());
|
||||
statusEffectAdd->data().unkFlag = 1;
|
||||
statusEffectAdd->data().classId = static_cast< uint8_t >( getClass() );
|
||||
statusEffectAdd->data().entryCount = 1; // todo: add multiple status but send only one result
|
||||
|
||||
auto& status = statusEffectAdd->data().statusEntries[0];
|
||||
|
||||
|
@ -518,31 +516,47 @@ void Sapphire::Entity::Chara::addStatusEffect( StatusEffect::StatusEffectPtr pEf
|
|||
status.index = static_cast< uint8_t >( nextSlot );
|
||||
status.param = pEffect->getParam();
|
||||
|
||||
sendToInRangeSet( statusEffectAdd, isPlayer() );
|
||||
float totalShieldValue = 0;
|
||||
for( auto effectIt : m_statusEffectMap )
|
||||
{
|
||||
auto statusEffect = effectIt.second;
|
||||
if( static_cast< Common::StatusEffectType >( statusEffect->getEffectEntry().effectType ) == Common::StatusEffectType::Shield )
|
||||
{
|
||||
totalShieldValue += statusEffect->getEffectEntry().effectValue1;
|
||||
}
|
||||
}
|
||||
|
||||
if( totalShieldValue > 0 )
|
||||
{
|
||||
totalShieldValue /= getMaxHp();
|
||||
totalShieldValue *= 100;
|
||||
statusEffectAdd->data().shieldPercentage = totalShieldValue >= 255 ? 255 : static_cast< uint8_t >( totalShieldValue );
|
||||
}
|
||||
|
||||
sendToInRangeSet( statusEffectAdd, true );
|
||||
sendStatusEffectUpdate(); // although client buff displays correctly without this but retail sends it so we do it as well
|
||||
}
|
||||
|
||||
/*! \param StatusEffectPtr to be applied to the actor */
|
||||
void Sapphire::Entity::Chara::addStatusEffectById( uint32_t id, int32_t duration, Entity::Chara& source, uint16_t param )
|
||||
{
|
||||
if( hasStatusEffect( id ) ) // todo: check if we want to refresh it or discard and keep the old one
|
||||
removeSingleStatusEffectById( id, false );
|
||||
auto oldEffect = getStatusEffectById( id );
|
||||
if( oldEffect.second )
|
||||
removeStatusEffect( oldEffect.first, false, false );
|
||||
|
||||
auto effect = StatusEffect::make_StatusEffect( id, source.getAsChara(), getAsChara(), duration, 3000, m_pFw );
|
||||
effect->setParam( param );
|
||||
addStatusEffect( effect );
|
||||
}
|
||||
|
||||
/*! \param StatusEffectPtr to be applied to the actor */
|
||||
void Sapphire::Entity::Chara::addStatusEffectByIdIfNotExist( uint32_t id, int32_t duration, Entity::Chara& source,
|
||||
uint16_t param )
|
||||
{
|
||||
if( hasStatusEffect( id ) )
|
||||
if( getStatusEffectById( id ).second )
|
||||
return;
|
||||
|
||||
auto effect = StatusEffect::make_StatusEffect( id, source.getAsChara(), getAsChara(), duration, 3000, m_pFw );
|
||||
effect->setParam( param );
|
||||
addStatusEffect( effect );
|
||||
|
||||
}
|
||||
|
||||
int8_t Sapphire::Entity::Chara::getStatusEffectFreeSlot()
|
||||
|
@ -563,19 +577,19 @@ void Sapphire::Entity::Chara::statusEffectFreeSlot( uint8_t slotId )
|
|||
m_statusEffectFreeSlotQueue.push( slotId );
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Chara::removeSingleStatusEffectById( uint32_t id, bool sendPacket )
|
||||
void Sapphire::Entity::Chara::removeSingleStatusEffectById( uint32_t id, bool sendActorControl, bool sendStatusList )
|
||||
{
|
||||
for( auto effectIt : m_statusEffectMap )
|
||||
{
|
||||
if( effectIt.second->getId() == id )
|
||||
{
|
||||
removeStatusEffect( effectIt.first, sendPacket );
|
||||
removeStatusEffect( effectIt.first, sendActorControl, sendStatusList );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Chara::removeStatusEffect( uint8_t effectSlotId, bool sendPacket )
|
||||
void Sapphire::Entity::Chara::removeStatusEffect( uint8_t effectSlotId, bool sendActorControl, bool sendStatusList )
|
||||
{
|
||||
auto pEffectIt = m_statusEffectMap.find( effectSlotId );
|
||||
if( pEffectIt == m_statusEffectMap.end() )
|
||||
|
@ -586,12 +600,13 @@ void Sapphire::Entity::Chara::removeStatusEffect( uint8_t effectSlotId, bool sen
|
|||
auto pEffect = pEffectIt->second;
|
||||
pEffect->removeStatus();
|
||||
|
||||
if( sendPacket )
|
||||
if( sendActorControl )
|
||||
sendToInRangeSet( makeActorControl( getId(), StatusEffectLose, pEffect->getId() ), isPlayer() );
|
||||
|
||||
m_statusEffectMap.erase( effectSlotId );
|
||||
|
||||
sendStatusEffectUpdate();
|
||||
if( sendStatusList )
|
||||
sendStatusEffectUpdate();
|
||||
}
|
||||
|
||||
std::map< uint8_t, Sapphire::StatusEffect::StatusEffectPtr > Sapphire::Entity::Chara::getStatusEffectMap() const
|
||||
|
@ -623,7 +638,6 @@ void Sapphire::Entity::Chara::sendStatusEffectUpdate()
|
|||
{
|
||||
uint64_t currentTimeMs = Util::getTimeMs();
|
||||
|
||||
|
||||
auto statusEffectList = makeZonePacket< FFXIVIpcStatusEffectList >( getId() );
|
||||
statusEffectList->data().classId = static_cast< uint8_t >( getClass() );
|
||||
statusEffectList->data().level = getLevel();
|
||||
|
@ -634,18 +648,63 @@ void Sapphire::Entity::Chara::sendStatusEffectUpdate()
|
|||
statusEffectList->data().max_hp = getMaxHp();
|
||||
statusEffectList->data().max_mp = getMaxMp();
|
||||
uint8_t slot = 0;
|
||||
float totalShieldValue = 0;
|
||||
for( auto effectIt : m_statusEffectMap )
|
||||
{
|
||||
float timeLeft = static_cast< float >( effectIt.second->getDuration() -
|
||||
( currentTimeMs - effectIt.second->getStartTimeMs() ) ) / 1000;
|
||||
auto statusEffect = effectIt.second;
|
||||
if( static_cast< Common::StatusEffectType >( statusEffect->getEffectEntry().effectType ) == Common::StatusEffectType::Shield )
|
||||
{
|
||||
totalShieldValue += statusEffect->getEffectEntry().effectValue1;
|
||||
}
|
||||
|
||||
float timeLeft = static_cast< float >( statusEffect->getDuration() -
|
||||
( currentTimeMs - statusEffect->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();
|
||||
statusEffectList->data().effect[ slot ].effect_id = statusEffect->getId();
|
||||
statusEffectList->data().effect[ slot ].sourceActorId = statusEffect->getSrcActorId();
|
||||
slot++;
|
||||
}
|
||||
|
||||
sendToInRangeSet( statusEffectList, isPlayer() );
|
||||
if( totalShieldValue > 0 )
|
||||
{
|
||||
totalShieldValue /= getMaxHp();
|
||||
totalShieldValue *= 100;
|
||||
statusEffectList->data().shieldPercentage = totalShieldValue >= 255 ? 255 : static_cast< uint8_t >( totalShieldValue );
|
||||
}
|
||||
|
||||
sendToInRangeSet( statusEffectList, true );
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Chara::sendEffectResultToUpdateShieldValue()
|
||||
{
|
||||
auto pPacket = makeZonePacket< FFXIVIpcEffectResult >( getId() );
|
||||
|
||||
pPacket->data().actor_id = getId();
|
||||
pPacket->data().current_hp = getHp();
|
||||
pPacket->data().current_mp = static_cast< uint16_t >( getMp() );
|
||||
pPacket->data().current_tp = getTp();
|
||||
pPacket->data().max_hp = getMaxHp();
|
||||
pPacket->data().max_mp = static_cast< uint16_t >( getMaxMp() );
|
||||
pPacket->data().classId = static_cast< uint8_t >( getClass() );
|
||||
|
||||
float totalShieldValue = 0;
|
||||
for( auto effectIt : m_statusEffectMap )
|
||||
{
|
||||
auto statusEffect = effectIt.second;
|
||||
if( static_cast< Common::StatusEffectType >( statusEffect->getEffectEntry().effectType ) == Common::StatusEffectType::Shield )
|
||||
{
|
||||
totalShieldValue += statusEffect->getEffectEntry().effectValue1;
|
||||
}
|
||||
}
|
||||
|
||||
if( totalShieldValue > 0 )
|
||||
{
|
||||
totalShieldValue /= getMaxHp();
|
||||
totalShieldValue *= 100;
|
||||
pPacket->data().shieldPercentage = totalShieldValue >= 255 ? 255 : static_cast< uint8_t >( totalShieldValue );
|
||||
}
|
||||
|
||||
sendToInRangeSet( pPacket, true );
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Chara::updateStatusEffects()
|
||||
|
@ -668,7 +727,7 @@ void Sapphire::Entity::Chara::updateStatusEffects()
|
|||
if( duration > 0 && ( currentTimeMs - startTime ) > duration )
|
||||
{
|
||||
// remove status effect
|
||||
removeStatusEffect( effectIndex, true );
|
||||
removeStatusEffect( effectIndex, true, true );
|
||||
// break because removing invalidates iterators
|
||||
break;
|
||||
}
|
||||
|
@ -715,17 +774,16 @@ void Sapphire::Entity::Chara::updateStatusEffects()
|
|||
}
|
||||
}
|
||||
|
||||
bool Sapphire::Entity::Chara::hasStatusEffect( uint32_t id )
|
||||
std::pair< uint8_t, Sapphire::StatusEffect::StatusEffectPtr > Sapphire::Entity::Chara::getStatusEffectById( uint32_t id )
|
||||
{
|
||||
//return m_statusEffectMap.find( id ) != m_statusEffectMap.end();
|
||||
for( auto effectIt : m_statusEffectMap )
|
||||
{
|
||||
if( effectIt.second->getId() == id )
|
||||
{
|
||||
return true;
|
||||
return std::make_pair( effectIt.first, effectIt.second );
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return std::make_pair( 0, nullptr );
|
||||
}
|
||||
|
||||
int64_t Sapphire::Entity::Chara::getLastUpdateTime() const
|
||||
|
|
|
@ -146,13 +146,13 @@ namespace Sapphire::Entity
|
|||
/// Status effect functions
|
||||
void addStatusEffect( StatusEffect::StatusEffectPtr pEffect );
|
||||
|
||||
void removeStatusEffect( uint8_t effectSlotId, bool sendPacket );
|
||||
void removeStatusEffect( uint8_t effectSlotId, bool sendActorControl, bool sendStatusList );
|
||||
|
||||
void removeSingleStatusEffectById( uint32_t id, bool sendPacket );
|
||||
void removeSingleStatusEffectById( uint32_t id, bool sendActorControl, bool sendStatusList );
|
||||
|
||||
void updateStatusEffects();
|
||||
|
||||
bool hasStatusEffect( uint32_t id );
|
||||
std::pair< uint8_t, StatusEffect::StatusEffectPtr > getStatusEffectById( uint32_t id );
|
||||
|
||||
int8_t getStatusEffectFreeSlot();
|
||||
|
||||
|
@ -166,6 +166,8 @@ namespace Sapphire::Entity
|
|||
|
||||
void sendStatusEffectUpdate();
|
||||
|
||||
void sendEffectResultToUpdateShieldValue();
|
||||
|
||||
/*! return a const pointer to the look array */
|
||||
const uint8_t* getLookArray() const;
|
||||
|
||||
|
@ -177,8 +179,6 @@ namespace Sapphire::Entity
|
|||
// add a status effect by id if it doesn't exist
|
||||
void addStatusEffectByIdIfNotExist( uint32_t id, int32_t duration, Entity::Chara& source, uint16_t param = 0 );
|
||||
|
||||
// remove a status effect by id
|
||||
void removeSingleStatusEffectFromId( uint32_t id );
|
||||
/// End Status Effect Functions
|
||||
|
||||
std::string getName() const;
|
||||
|
|
|
@ -720,7 +720,7 @@ uint32_t CalcStats::primaryStatValue( const Sapphire::Entity::Chara& chara )
|
|||
|
||||
std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcDamageReflect( Sapphire::Entity::CharaPtr pCharaAttacker, Sapphire::Entity::CharaPtr pCharaVictim, float damage, Sapphire::Common::ActionTypeFilter filter )
|
||||
{
|
||||
for( auto entry : pCharaVictim->getStatusEffectMap() )
|
||||
for( auto const& entry : pCharaVictim->getStatusEffectMap() )
|
||||
{
|
||||
auto status = entry.second;
|
||||
auto effectEntry = status->getEffectEntry();
|
||||
|
@ -744,7 +744,7 @@ std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcDamag
|
|||
float CalcStats::calcAbsorbHP( Sapphire::Entity::CharaPtr pChara, float damage, Sapphire::Common::ActionTypeFilter filter )
|
||||
{
|
||||
float result = 0;
|
||||
for( auto entry : pChara->getStatusEffectMap() )
|
||||
for( auto const& entry : pChara->getStatusEffectMap() )
|
||||
{
|
||||
auto status = entry.second;
|
||||
auto effectEntry = status->getEffectEntry();
|
||||
|
@ -758,4 +758,49 @@ float CalcStats::calcAbsorbHP( Sapphire::Entity::CharaPtr pChara, float damage,
|
|||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
float CalcStats::applyShieldProtection( Sapphire::Entity::CharaPtr pChara, float damage )
|
||||
{
|
||||
float remainingDamage = damage;
|
||||
bool shieldChanged = false;
|
||||
std::vector< uint8_t > destroyedShieldSlotList;
|
||||
|
||||
for( auto const& entry : pChara->getStatusEffectMap() )
|
||||
{
|
||||
auto status = entry.second;
|
||||
auto effectEntry = status->getEffectEntry();
|
||||
|
||||
if( static_cast< Common::StatusEffectType >( effectEntry.effectType ) == Common::StatusEffectType::Shield )
|
||||
{
|
||||
shieldChanged = true;
|
||||
if( remainingDamage < effectEntry.effectValue1 )
|
||||
{
|
||||
effectEntry.effectValue1 -= static_cast< int32_t >( remainingDamage );
|
||||
status->replaceEffectEntry( effectEntry );
|
||||
remainingDamage = 0;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
remainingDamage -= effectEntry.effectValue1;
|
||||
destroyedShieldSlotList.push_back( entry.first );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( shieldChanged )
|
||||
{
|
||||
if( !destroyedShieldSlotList.empty() )
|
||||
{
|
||||
for( auto const& slotId : destroyedShieldSlotList )
|
||||
{
|
||||
pChara->removeStatusEffect( slotId, true, false );
|
||||
}
|
||||
pChara->sendStatusEffectUpdate();
|
||||
}
|
||||
else
|
||||
pChara->sendEffectResultToUpdateShieldValue(); // yes this is the packet to update shield value
|
||||
}
|
||||
return remainingDamage;
|
||||
}
|
|
@ -148,6 +148,8 @@ namespace Sapphire::Math
|
|||
|
||||
static float calcAbsorbHP( Sapphire::Entity::CharaPtr pChara, float damage, Sapphire::Common::ActionTypeFilter filter );
|
||||
|
||||
static float applyShieldProtection( Sapphire::Entity::CharaPtr pChara, float damage );
|
||||
|
||||
static std::random_device dev;
|
||||
static std::mt19937 rng;
|
||||
static std::uniform_int_distribution< std::mt19937::result_type > range100;
|
||||
|
|
|
@ -134,7 +134,7 @@ void Sapphire::Network::GameConnection::clientTriggerHandler( FrameworkPtr pFw,
|
|||
case ClientTriggerType::RemoveStatusEffect: // Remove status (clicking it off)
|
||||
{
|
||||
// todo: check if status can be removed by client from exd
|
||||
player.removeSingleStatusEffectById( static_cast< uint32_t >( param1 ), true );
|
||||
player.removeSingleStatusEffectById( static_cast< uint32_t >( param1 ), true, true );
|
||||
break;
|
||||
}
|
||||
case ClientTriggerType::CastCancel: // Cancel cast
|
||||
|
|
|
@ -29,7 +29,7 @@ Sapphire::StatusEffect::StatusEffect::StatusEffect( uint32_t id, Entity::CharaPt
|
|||
m_tickRate( tickRate ),
|
||||
m_lastTick( 0 ),
|
||||
m_pFw( pFw ),
|
||||
m_cachedHotOrDotValue( 0 ),
|
||||
m_value( 0 ),
|
||||
m_cachedSourceCrit( 0 ),
|
||||
m_cachedSourceCritBonus( 0 )
|
||||
{
|
||||
|
@ -53,7 +53,6 @@ Sapphire::StatusEffect::StatusEffect::StatusEffect( uint32_t id, Entity::CharaPt
|
|||
m_effectEntry.effectType = static_cast< uint32_t >( Common::StatusEffectType::Invalid );
|
||||
}
|
||||
|
||||
|
||||
Sapphire::StatusEffect::StatusEffect::~StatusEffect()
|
||||
{
|
||||
}
|
||||
|
@ -69,7 +68,7 @@ std::pair< uint8_t, uint32_t > Sapphire::StatusEffect::StatusEffect::getTickEffe
|
|||
auto statusEffectType = static_cast< Common::StatusEffectType >( m_effectEntry.effectType );
|
||||
if( statusEffectType == Common::StatusEffectType::Dot )
|
||||
{
|
||||
auto value = m_cachedHotOrDotValue;
|
||||
auto value = m_value;
|
||||
if( m_cachedSourceCrit > Sapphire::Math::CalcStats::range100( Sapphire::Math::CalcStats::rng ) )
|
||||
{
|
||||
value *= m_cachedSourceCritBonus;
|
||||
|
@ -79,7 +78,7 @@ std::pair< uint8_t, uint32_t > Sapphire::StatusEffect::StatusEffect::getTickEffe
|
|||
}
|
||||
else if( statusEffectType == Common::StatusEffectType::Hot )
|
||||
{
|
||||
auto value = m_cachedHotOrDotValue;
|
||||
auto value = m_value;
|
||||
if( m_cachedSourceCrit > Sapphire::Math::CalcStats::range100( Sapphire::Math::CalcStats::rng ) )
|
||||
{
|
||||
value *= m_cachedSourceCritBonus;
|
||||
|
@ -138,7 +137,7 @@ void Sapphire::StatusEffect::StatusEffect::applyStatus()
|
|||
}
|
||||
}
|
||||
|
||||
m_cachedHotOrDotValue = Sapphire::Math::CalcStats::applyDamageReceiveMultiplier( *m_targetActor, damage,
|
||||
m_value = Sapphire::Math::CalcStats::applyDamageReceiveMultiplier( *m_targetActor, damage,
|
||||
m_effectEntry.effectValue1 == static_cast< int32_t >( Common::ActionTypeFilter::Physical ) ? Common::AttackType::Physical :
|
||||
( m_effectEntry.effectValue1 == static_cast< int32_t >( Common::ActionTypeFilter::Magical ) ? Common::AttackType::Magical : Common::AttackType::Unknown_0 ) );
|
||||
m_cachedSourceCrit = Sapphire::Math::CalcStats::criticalHitProbability( *m_sourceActor, Common::CritDHBonusFilter::Damage );
|
||||
|
@ -160,7 +159,7 @@ void Sapphire::StatusEffect::StatusEffect::applyStatus()
|
|||
heal *= 1.0f + ( effectEntry.effectValue2 / 100.0f );
|
||||
}
|
||||
}
|
||||
m_cachedHotOrDotValue = Sapphire::Math::CalcStats::applyHealingReceiveMultiplier( *m_targetActor, heal );
|
||||
m_value = Sapphire::Math::CalcStats::applyHealingReceiveMultiplier( *m_targetActor, heal );
|
||||
m_cachedSourceCrit = Sapphire::Math::CalcStats::criticalHitProbability( *m_sourceActor, Common::CritDHBonusFilter::Heal );
|
||||
m_cachedSourceCritBonus = Sapphire::Math::CalcStats::criticalHitBonus( *m_sourceActor );
|
||||
}
|
||||
|
@ -218,3 +217,8 @@ const Sapphire::World::Action::StatusEffectEntry& Sapphire::StatusEffect::Status
|
|||
{
|
||||
return m_effectEntry;
|
||||
}
|
||||
|
||||
void Sapphire::StatusEffect::StatusEffect::replaceEffectEntry( Sapphire::World::Action::StatusEffectEntry entryOverride )
|
||||
{
|
||||
m_effectEntry = entryOverride;
|
||||
}
|
|
@ -51,6 +51,8 @@ public:
|
|||
|
||||
const Sapphire::World::Action::StatusEffectEntry& getEffectEntry() const;
|
||||
|
||||
void replaceEffectEntry( Sapphire::World::Action::StatusEffectEntry entryOverride );
|
||||
|
||||
private:
|
||||
uint32_t m_id;
|
||||
Entity::CharaPtr m_sourceActor;
|
||||
|
@ -64,7 +66,7 @@ private:
|
|||
std::pair< uint8_t, uint32_t > m_currTickEffect;
|
||||
FrameworkPtr m_pFw;
|
||||
Sapphire::World::Action::StatusEffectEntry m_effectEntry;
|
||||
uint32_t m_cachedHotOrDotValue;
|
||||
uint32_t m_value;
|
||||
float m_cachedSourceCrit;
|
||||
float m_cachedSourceCritBonus;
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue