mirror of
https://github.com/SapphireServer/Sapphire.git
synced 2025-04-27 22:57:45 +00:00
add ref to Data::Action inside action obj, move can cast logic to action
This commit is contained in:
parent
2f9d86f962
commit
9d6622b522
15 changed files with 190 additions and 341 deletions
|
@ -537,19 +537,19 @@ namespace Sapphire::Common
|
|||
Unaspected = 7 // Doesn't imply magical unaspected damage - could be unaspected physical
|
||||
};
|
||||
|
||||
enum class ActionCostType : uint8_t
|
||||
enum class ActionPrimaryCostType : uint8_t
|
||||
{
|
||||
None = 0, // ?
|
||||
// None = 0, // ?
|
||||
MagicPoints = 3,
|
||||
TacticsPoints = 5,
|
||||
WARGauge = 22,
|
||||
DRKGauge = 25,
|
||||
AetherflowStack = 30,
|
||||
Status = 32,
|
||||
PLDGauge = 41,
|
||||
RDMGaugeBoth = 74,
|
||||
// RDMGaugeBlack = 75, // not right?
|
||||
DRGGauge3Eyes = 76,
|
||||
// WARGauge = 22,
|
||||
// DRKGauge = 25,
|
||||
// AetherflowStack = 30,
|
||||
// Status = 32,
|
||||
// PLDGauge = 41,
|
||||
// RDMGaugeBoth = 74,
|
||||
//// RDMGaugeBlack = 75, // not right?
|
||||
// DRGGauge3Eyes = 76,
|
||||
};
|
||||
|
||||
enum class ActionType : int8_t
|
||||
|
|
|
@ -12,7 +12,7 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
void onCastFinish( Sapphire::Action::Action& currentAction ) override
|
||||
void onFinish( Sapphire::Action::Action& currentAction ) override
|
||||
{
|
||||
if( !currentAction.getSourceChara()->isPlayer() )
|
||||
return;
|
||||
|
|
|
@ -11,7 +11,7 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
void onCastFinish( Sapphire::Action::Action& currentAction ) override
|
||||
void onFinish( Sapphire::Action::Action& currentAction ) override
|
||||
{
|
||||
auto sourceChara = currentAction.getSourceChara();
|
||||
|
||||
|
|
|
@ -12,9 +12,10 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
void onCharaHit( Sapphire::Action::Action& currentAction, Sapphire::Entity::Chara& hitActor ) override
|
||||
void onFinish( Sapphire::Action::Action& currentAction ) override
|
||||
{
|
||||
currentAction.damageTarget( 150, hitActor );
|
||||
if( auto player = currentAction.getSourceChara()->getAsPlayer() )
|
||||
player->sendDebug( "Imagine you just hit an enemy for 150 potency. Incredible, right?" );
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -27,7 +27,7 @@ Sapphire::Action::Action::Action() = default;
|
|||
Sapphire::Action::Action::~Action() = default;
|
||||
|
||||
Sapphire::Action::Action::Action( Entity::CharaPtr caster, uint32_t actionId,
|
||||
Data::ActionPtr action, FrameworkPtr fw ) :
|
||||
Data::ActionPtr actionData, FrameworkPtr fw ) :
|
||||
m_pSource( std::move( caster ) ),
|
||||
m_pFw( std::move( fw ) ),
|
||||
m_id( actionId ),
|
||||
|
@ -35,17 +35,19 @@ Sapphire::Action::Action::Action( Entity::CharaPtr caster, uint32_t actionId,
|
|||
m_interruptType( Common::ActionInterruptType::None ),
|
||||
m_hasResidentTarget( false )
|
||||
{
|
||||
m_castTime = static_cast< uint32_t >( action->cast100ms * 100 );
|
||||
m_recastTime = static_cast< uint16_t >( action->recast100ms * 100 );
|
||||
m_cooldownGroup = action->cooldownGroup;
|
||||
m_range = action->range;
|
||||
m_effectRange = action->effectRange;
|
||||
m_aspect = static_cast< Common::ActionAspect >( action->aspect );
|
||||
m_actionData = actionData;
|
||||
|
||||
m_castTime = static_cast< uint32_t >( actionData->cast100ms * 100 );
|
||||
m_recastTime = static_cast< uint16_t >( actionData->recast100ms * 100 );
|
||||
m_cooldownGroup = actionData->cooldownGroup;
|
||||
m_range = actionData->range;
|
||||
m_effectRange = actionData->effectRange;
|
||||
m_aspect = static_cast< Common::ActionAspect >( actionData->aspect );
|
||||
|
||||
// a default range is set by the game for the class/job
|
||||
if( m_range == -1 )
|
||||
{
|
||||
switch( static_cast< Common::ClassJob >( action->classJob ) )
|
||||
switch( static_cast< Common::ClassJob >( actionData->classJob ) )
|
||||
{
|
||||
case Common::ClassJob::Bard:
|
||||
case Common::ClassJob::Archer:
|
||||
|
@ -57,12 +59,10 @@ Sapphire::Action::Action::Action( Entity::CharaPtr caster, uint32_t actionId,
|
|||
}
|
||||
}
|
||||
|
||||
m_actionCost.fill( { Common::ActionCostType::None, 0 } );
|
||||
m_primaryCostType = static_cast< Common::ActionPrimaryCostType >( actionData->costType );
|
||||
m_primaryCost = actionData->cost;
|
||||
|
||||
m_actionCost[ 0 ] = {
|
||||
static_cast< Common::ActionCostType >( action->costType ),
|
||||
action->cost
|
||||
};
|
||||
// todo: add missing rows for secondaryCostType/secondaryCostType and rename the current rows to primaryCostX
|
||||
|
||||
calculateActionCost();
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ bool Sapphire::Action::Action::update()
|
|||
|
||||
if( isInterrupted() )
|
||||
{
|
||||
castInterrupt();
|
||||
onInterrupt();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -158,14 +158,14 @@ bool Sapphire::Action::Action::update()
|
|||
|
||||
if( !hasCastTime() || std::difftime( currTime, m_startTime ) > m_castTime )
|
||||
{
|
||||
castFinish();
|
||||
onExecute();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Sapphire::Action::Action::castStart()
|
||||
void Sapphire::Action::Action::onStart()
|
||||
{
|
||||
assert( m_pSource );
|
||||
|
||||
|
@ -193,14 +193,14 @@ void Sapphire::Action::Action::castStart()
|
|||
}
|
||||
|
||||
auto pScriptMgr = m_pFw->get< Scripting::ScriptMgr >();
|
||||
if( !pScriptMgr->onCastStart( *this ) )
|
||||
if( !pScriptMgr->onStart( *this ) )
|
||||
{
|
||||
// script not implemented
|
||||
castInterrupt();
|
||||
onInterrupt();
|
||||
|
||||
if( player )
|
||||
{
|
||||
player->sendUrgent( "Action not implemented, missing script for actionId#{0}", getId() );
|
||||
player->sendUrgent( "Action not implemented, missing script for action#{0}", getId() );
|
||||
player->setCurrentAction( nullptr );
|
||||
}
|
||||
|
||||
|
@ -209,10 +209,10 @@ void Sapphire::Action::Action::castStart()
|
|||
|
||||
// instantly finish cast if there's no cast time
|
||||
if( !hasCastTime() )
|
||||
castFinish();
|
||||
onExecute();
|
||||
}
|
||||
|
||||
void Sapphire::Action::Action::castInterrupt()
|
||||
void Sapphire::Action::Action::onInterrupt()
|
||||
{
|
||||
assert( m_pSource );
|
||||
|
||||
|
@ -246,10 +246,10 @@ void Sapphire::Action::Action::castInterrupt()
|
|||
}
|
||||
|
||||
auto pScriptMgr = m_pFw->get< Scripting::ScriptMgr >();
|
||||
pScriptMgr->onCastInterrupt( *this );
|
||||
pScriptMgr->onInterrupt( *this );
|
||||
}
|
||||
|
||||
void Sapphire::Action::Action::castFinish()
|
||||
void Sapphire::Action::Action::onExecute()
|
||||
{
|
||||
assert( m_pSource );
|
||||
|
||||
|
@ -257,170 +257,60 @@ void Sapphire::Action::Action::castFinish()
|
|||
|
||||
if( hasCastTime() )
|
||||
{
|
||||
// todo: what's this?
|
||||
/*auto control = ActorControlPacket143( m_pTarget->getId(), ActorControlType::Unk7,
|
||||
0x219, m_id, m_id, m_id, m_id );
|
||||
m_pSource->sendToInRangeSet( control, true );*/
|
||||
|
||||
}
|
||||
|
||||
pScriptMgr->onCastFinish( *this );
|
||||
|
||||
if( !hasResidentTarget() )
|
||||
{
|
||||
assert( m_pTarget );
|
||||
// todo: calculate final hit targets and call onCharaHit in action script
|
||||
pScriptMgr->onCharaHit( *this, *m_pTarget );
|
||||
pScriptMgr->onExecute( *this );
|
||||
}
|
||||
else if( auto player = m_pSource->getAsPlayer() )
|
||||
{
|
||||
pScriptMgr->onEObjHit( *player, m_targetId );
|
||||
return;
|
||||
}
|
||||
|
||||
buildEffectPackets();
|
||||
}
|
||||
|
||||
void Sapphire::Action::Action::buildEffectPackets()
|
||||
{
|
||||
for( int i = 0; i < EffectPacketIdentity::MAX_ACTION_EFFECT_PACKET_IDENT; ++i )
|
||||
{
|
||||
auto& packetData = m_effects[ static_cast< EffectPacketIdentity >( i ) ];
|
||||
|
||||
auto actorsHit = packetData.m_hitActors.size();
|
||||
if( actorsHit == 0 )
|
||||
continue;
|
||||
|
||||
// get effect sequence
|
||||
auto zone = m_pSource->getCurrentZone();
|
||||
assert( zone );
|
||||
|
||||
auto sequence = zone->getNextEffectSequence();
|
||||
|
||||
if( actorsHit == 1 )
|
||||
{
|
||||
// send normal effect
|
||||
auto effectPacket = std::make_shared< Network::Packets::Server::EffectPacket >( m_pSource->getId(), m_pTarget->getId(), getId() );
|
||||
effectPacket->setTargetActor( packetData.m_hitActors[ 0 ] );
|
||||
effectPacket->setSequence( sequence );
|
||||
effectPacket->setDisplayType( Common::ActionEffectDisplayType::ShowActionName );
|
||||
|
||||
for( auto& effect : packetData.m_entries )
|
||||
{
|
||||
effectPacket->addEffect( effect );
|
||||
}
|
||||
|
||||
m_pSource->sendToInRangeSet( effectPacket, true );
|
||||
}
|
||||
else
|
||||
{
|
||||
// todo: aoe effects
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sapphire::Action::Action::damageTarget( uint16_t potency, Entity::Chara& chara )
|
||||
{
|
||||
// todo: scale potency into damage from stats
|
||||
|
||||
Common::EffectEntry entry{};
|
||||
|
||||
// todo: handle cases where the action misses/is blocked?
|
||||
entry.effectType = Common::ActionEffectType::Damage;
|
||||
|
||||
// todo: handle crits
|
||||
entry.hitSeverity = Common::ActionHitSeverityType::NormalDamage;
|
||||
|
||||
// todo: handle > 65535 damage values, not sure if this is right?
|
||||
if( potency > 65535 )
|
||||
{
|
||||
entry.value = static_cast< int16_t >( potency / 10 );
|
||||
// todo: rename this? need to confirm how it works again
|
||||
entry.valueMultiplier = 1;
|
||||
}
|
||||
else
|
||||
entry.value = static_cast< int16_t >( potency );
|
||||
|
||||
// add to aggro table
|
||||
// todo: probably move this into takeDamage? this is pretty garbage
|
||||
if( chara.isBattleNpc() )
|
||||
{
|
||||
auto bNpc = chara.getAsBNpc();
|
||||
if( bNpc )
|
||||
{
|
||||
if( bNpc->getStance() != Common::Stance::Active )
|
||||
{
|
||||
bNpc->aggro( getSourceChara() );
|
||||
bNpc->hateListUpdate( getSourceChara(), potency );
|
||||
}
|
||||
else
|
||||
{
|
||||
bNpc->hateListUpdate( getSourceChara(), potency );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// todo: aspected damage?
|
||||
chara.takeDamage( potency );
|
||||
|
||||
if( auto player = m_pSource->getAsPlayer() )
|
||||
player->sendDebug( "hit actorId#{0} for potency: {1}", chara.getId(), potency );
|
||||
|
||||
m_effects[ EffectPacketIdentity::DamageEffect ].m_entries.emplace_back( entry );
|
||||
|
||||
// todo: make sure that we don't add the same actor more than once
|
||||
m_effects[ EffectPacketIdentity::DamageEffect ].m_hitActors.emplace_back( chara.getId() );
|
||||
}
|
||||
|
||||
void Sapphire::Action::Action::healTarget( uint16_t potency, Entity::Chara& chara )
|
||||
{
|
||||
// todo: scale potency into healing from stats
|
||||
|
||||
Common::EffectEntry entry{};
|
||||
|
||||
entry.effectType = Common::ActionEffectType::Heal;
|
||||
|
||||
// todo: handle crits
|
||||
entry.hitSeverity = Common::ActionHitSeverityType::NormalHeal;
|
||||
|
||||
// todo: handle > 65535 healing values, not sure if this is right?
|
||||
if( potency > 65535 )
|
||||
{
|
||||
entry.value = static_cast< int16_t >( potency / 10 );
|
||||
// todo: rename this? need to confirm how it works again
|
||||
entry.valueMultiplier = 1;
|
||||
}
|
||||
else
|
||||
entry.value = static_cast< int16_t >( potency );
|
||||
|
||||
chara.heal( potency );
|
||||
|
||||
if( auto player = m_pSource->getAsPlayer() )
|
||||
player->sendDebug( "hit actorId#{0} for heal: {1}", chara.getId(), potency );
|
||||
|
||||
m_effects[ EffectPacketIdentity::HealingEffect ].m_entries.emplace_back( entry );
|
||||
|
||||
// todo: make sure that we don't add the same actor more than once
|
||||
m_effects[ EffectPacketIdentity::HealingEffect ].m_hitActors.emplace_back( chara.getId() );
|
||||
}
|
||||
|
||||
const Sapphire::Action::Action::ActionCostArray& Sapphire::Action::Action::getCostArray() const
|
||||
{
|
||||
return m_actionCost;
|
||||
}
|
||||
|
||||
void Sapphire::Action::Action::calculateActionCost()
|
||||
{
|
||||
for( uint8_t i = 0; i < m_actionCost.size(); ++i )
|
||||
// todo: just a test handler for now to get MP output for each cast, not sure where we should put this
|
||||
// check primary cost
|
||||
switch( m_primaryCostType )
|
||||
{
|
||||
auto& entry = m_actionCost[ i ];
|
||||
case ActionPrimaryCostType::MagicPoints:
|
||||
{
|
||||
calculateMPCost( m_primaryCost );
|
||||
break;
|
||||
}
|
||||
|
||||
if( entry.m_costType == ActionCostType::MagicPoints )
|
||||
calculateMPCost( i );
|
||||
case ActionPrimaryCostType::TacticsPoints:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
default:
|
||||
{
|
||||
if( auto player = m_pSource->getAsPlayer() )
|
||||
{
|
||||
player->sendDebug( "action#{0} is missing a handler for cost type: {1}",
|
||||
m_id, static_cast< uint8_t >( m_primaryCostType ) );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// todo: secondary cost type needs to be handled
|
||||
}
|
||||
|
||||
// todo: this shouldn't be in action and instead be in some general stat calc util
|
||||
void Sapphire::Action::Action::calculateMPCost( uint8_t costArrayIndex )
|
||||
void Sapphire::Action::Action::calculateMPCost( uint16_t baseCost )
|
||||
{
|
||||
auto level = m_pSource->getLevel();
|
||||
|
||||
|
@ -429,9 +319,7 @@ void Sapphire::Action::Action::calculateMPCost( uint8_t costArrayIndex )
|
|||
// dividing by 10 on the border will break this unless we subtract 1
|
||||
auto levelGroup = std::max< uint8_t >( level - 1, 1 ) / 10;
|
||||
|
||||
auto& costEntry = m_actionCost[ costArrayIndex ];
|
||||
|
||||
float cost = costEntry.m_cost;
|
||||
float cost = baseCost;
|
||||
|
||||
// thanks to andrew for helping me figure this shit out, should be pretty accurate
|
||||
switch( levelGroup )
|
||||
|
@ -444,7 +332,7 @@ void Sapphire::Action::Action::calculateMPCost( uint8_t costArrayIndex )
|
|||
break;
|
||||
}
|
||||
|
||||
// level 11-20
|
||||
// level 11-20
|
||||
case 1:
|
||||
{
|
||||
// r^2 = 1
|
||||
|
@ -452,7 +340,7 @@ void Sapphire::Action::Action::calculateMPCost( uint8_t costArrayIndex )
|
|||
break;
|
||||
}
|
||||
|
||||
// level 21-30
|
||||
// level 21-30
|
||||
case 2:
|
||||
{
|
||||
// r^2 = 1
|
||||
|
@ -460,7 +348,7 @@ void Sapphire::Action::Action::calculateMPCost( uint8_t costArrayIndex )
|
|||
break;
|
||||
}
|
||||
|
||||
// level 31-40
|
||||
// level 31-40
|
||||
case 3:
|
||||
{
|
||||
// r^2 = 1
|
||||
|
@ -476,7 +364,7 @@ void Sapphire::Action::Action::calculateMPCost( uint8_t costArrayIndex )
|
|||
break;
|
||||
}
|
||||
|
||||
// level 51-60
|
||||
// level 51-60
|
||||
case 5:
|
||||
{
|
||||
// r^2 = 1
|
||||
|
@ -484,7 +372,7 @@ void Sapphire::Action::Action::calculateMPCost( uint8_t costArrayIndex )
|
|||
break;
|
||||
}
|
||||
|
||||
// level 61-70
|
||||
// level 61-70
|
||||
case 6:
|
||||
{
|
||||
// r^2 = 0.9998
|
||||
|
@ -496,9 +384,70 @@ void Sapphire::Action::Action::calculateMPCost( uint8_t costArrayIndex )
|
|||
return;
|
||||
}
|
||||
|
||||
// m_cost is the base cost, cost is the multiplier for the current player level
|
||||
costEntry.m_cost = static_cast< uint16_t >( std::round( cost * costEntry.m_cost ) );
|
||||
// m_primaryCost is the base cost, cost is the multiplier for the current player level
|
||||
m_primaryCost = static_cast< uint16_t >( std::round( cost * baseCost ) );
|
||||
|
||||
if( auto player = m_pSource->getAsPlayer() )
|
||||
player->sendDebug( "calculated mp cost: {0}", costEntry.m_cost );
|
||||
player->sendDebug( "calculated mp cost: {0}", m_primaryCost );
|
||||
}
|
||||
|
||||
bool Sapphire::Action::Action::precheck()
|
||||
{
|
||||
if( auto player = m_pSource->getAsPlayer() )
|
||||
{
|
||||
if( !playerPrecheck( *player ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Sapphire::Action::Action::playerPrecheck( Entity::Player& player )
|
||||
{
|
||||
// lol
|
||||
if( !player.isAlive() )
|
||||
return false;
|
||||
|
||||
// npc actions/non player actions
|
||||
if( m_actionData->classJob == -1 )
|
||||
return false;
|
||||
|
||||
if( player.getLevel() < m_actionData->classJobLevel )
|
||||
return false;
|
||||
|
||||
auto currentClass = player.getClass();
|
||||
auto actionClass = static_cast< Common::ClassJob >( m_actionData->classJob );
|
||||
|
||||
if( actionClass != Common::ClassJob::Adventurer && currentClass != actionClass )
|
||||
{
|
||||
// check if not a base class action
|
||||
auto exdData = m_pFw->get< Data::ExdDataGenerated >();
|
||||
assert( exdData );
|
||||
|
||||
auto classJob = exdData->get< Data::ClassJob >( static_cast< uint8_t >( currentClass ) );
|
||||
if( !classJob )
|
||||
return false;
|
||||
|
||||
if( classJob->classJobParent != m_actionData->classJob )
|
||||
return false;
|
||||
}
|
||||
|
||||
// reset target on actions that can only be casted on yourself while having a target set
|
||||
// todo: check what actions send when targeting an enemy
|
||||
if( m_actionData->canTargetSelf &&
|
||||
!m_actionData->canTargetFriendly &&
|
||||
!m_actionData->canTargetHostile &&
|
||||
!m_actionData->canTargetParty )
|
||||
{
|
||||
setTargetChara( getSourceChara() );
|
||||
}
|
||||
|
||||
// todo: party/enemy validation
|
||||
|
||||
// validate range
|
||||
|
||||
|
||||
// todo: validate costs/conditionals here
|
||||
|
||||
return true;
|
||||
}
|
|
@ -18,16 +18,9 @@ namespace Sapphire::Action
|
|||
{
|
||||
|
||||
public:
|
||||
struct ActionCostEntry
|
||||
{
|
||||
Common::ActionCostType m_costType;
|
||||
uint16_t m_cost;
|
||||
};
|
||||
|
||||
using ActionCostArray = std::array< ActionCostEntry, 2 >;
|
||||
|
||||
Action();
|
||||
Action( Entity::CharaPtr caster, uint32_t actionId, Data::ActionPtr action, FrameworkPtr fw );
|
||||
Action( Entity::CharaPtr caster, uint32_t actionId, Data::ActionPtr actionData, FrameworkPtr fw );
|
||||
|
||||
virtual ~Action();
|
||||
|
||||
|
@ -53,36 +46,34 @@ namespace Sapphire::Action
|
|||
*/
|
||||
bool hasResidentTarget() const;
|
||||
|
||||
const ActionCostArray& getCostArray() const;
|
||||
|
||||
/*!
|
||||
* @brief Tests whether the action is instantly usable or has a cast assoc'd with it
|
||||
* @return true if action has a cast time
|
||||
*/
|
||||
bool hasCastTime() const;
|
||||
|
||||
void buildEffectPackets();
|
||||
|
||||
/*!
|
||||
* @brief Damages a target and adds the effect entry
|
||||
* @param potency The amount of damage the target takes
|
||||
* @param chara The chara to inflict damage upon
|
||||
* @brief Tests if an action is castable by the current source chara
|
||||
* @return true if castable, false if the caster doesn't meet the requirements
|
||||
*/
|
||||
void damageTarget( uint16_t potency, Entity::Chara& chara );
|
||||
|
||||
/*!
|
||||
* @brief Heals a target and adds the effect entry
|
||||
* @param potency Amount of healing to apply
|
||||
* @param chara Chara to receive healing
|
||||
*/
|
||||
void healTarget( uint16_t potency, Entity::Chara& chara );
|
||||
bool precheck();
|
||||
|
||||
/*!
|
||||
* @brief Starts the cast. Finishes it immediately if there is no cast time (weaponskills).
|
||||
*/
|
||||
virtual void castStart();
|
||||
virtual void castFinish();
|
||||
virtual void castInterrupt();
|
||||
virtual void onStart();
|
||||
|
||||
/*!
|
||||
* @brief Finishes the cast, effected targets are calculated here.
|
||||
*/
|
||||
virtual void onExecute();
|
||||
|
||||
/*!
|
||||
* @brief Called when a cast is interrupted for any reason
|
||||
*
|
||||
* m_interruptType will have the reason why the action was interrupted (eg. damage, movement, ...)
|
||||
*/
|
||||
virtual void onInterrupt();
|
||||
|
||||
// update action, if returns true, action is done and has to be removed from the actor
|
||||
virtual bool update();
|
||||
|
@ -90,31 +81,14 @@ namespace Sapphire::Action
|
|||
protected:
|
||||
|
||||
void calculateActionCost();
|
||||
void calculateMPCost( uint8_t costArrayIndex );
|
||||
void calculateMPCost( uint16_t baseCost );
|
||||
|
||||
/*!
|
||||
* @brief Some actions are capable of both healing and dealing damage. This identifies them.
|
||||
*/
|
||||
enum EffectPacketIdentity : uint8_t
|
||||
{
|
||||
DamageEffect,
|
||||
HealingEffect,
|
||||
|
||||
MAX_ACTION_EFFECT_PACKET_IDENT
|
||||
};
|
||||
|
||||
struct EffectPacketData
|
||||
{
|
||||
std::vector< Common::EffectEntry > m_entries;
|
||||
std::vector< uint32_t > m_hitActors;
|
||||
};
|
||||
bool playerPrecheck( Entity::Player& player );
|
||||
|
||||
uint32_t m_id;
|
||||
|
||||
Common::ActionCostType m_costType;
|
||||
uint16_t m_cost;
|
||||
|
||||
ActionCostArray m_actionCost;
|
||||
Common::ActionPrimaryCostType m_primaryCostType;
|
||||
uint16_t m_primaryCost;
|
||||
|
||||
uint64_t m_startTime;
|
||||
uint32_t m_castTime;
|
||||
|
@ -124,6 +98,7 @@ namespace Sapphire::Action
|
|||
uint8_t m_effectRange;
|
||||
Common::ActionAspect m_aspect;
|
||||
|
||||
|
||||
Entity::CharaPtr m_pSource;
|
||||
Entity::CharaPtr m_pTarget;
|
||||
uint64_t m_targetId;
|
||||
|
@ -132,10 +107,9 @@ namespace Sapphire::Action
|
|||
Common::ActionInterruptType m_interruptType;
|
||||
|
||||
FrameworkPtr m_pFw;
|
||||
Data::ActionPtr m_actionData;
|
||||
|
||||
Common::FFXIVARR_POSITION3 m_pos;
|
||||
|
||||
std::array< EffectPacketData, MAX_ACTION_EFFECT_PACKET_IDENT > m_effects;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ Sapphire::Action::EventAction::EventAction( Entity::CharaPtr pActor, uint32_t ev
|
|||
|
||||
Sapphire::Action::EventAction::~EventAction() = default;
|
||||
|
||||
void Sapphire::Action::EventAction::castStart()
|
||||
void Sapphire::Action::EventAction::onStart()
|
||||
{
|
||||
if( !m_pSource )
|
||||
return;
|
||||
|
@ -54,7 +54,7 @@ void Sapphire::Action::EventAction::castStart()
|
|||
m_pSource->sendToInRangeSet( control );
|
||||
}
|
||||
|
||||
void Sapphire::Action::EventAction::castFinish()
|
||||
void Sapphire::Action::EventAction::onExecute()
|
||||
{
|
||||
if( !m_pSource )
|
||||
return;
|
||||
|
@ -90,7 +90,7 @@ void Sapphire::Action::EventAction::castFinish()
|
|||
|
||||
}
|
||||
|
||||
void Sapphire::Action::EventAction::castInterrupt()
|
||||
void Sapphire::Action::EventAction::onInterrupt()
|
||||
{
|
||||
if( !m_pSource )
|
||||
return;
|
||||
|
|
|
@ -18,11 +18,11 @@ public:
|
|||
EventAction( Entity::CharaPtr pActor, uint32_t eventId, uint16_t action,
|
||||
ActionCallback finishRef, ActionCallback interruptRef, uint64_t additional, FrameworkPtr pFw );
|
||||
|
||||
void castStart() override;
|
||||
void onStart() override;
|
||||
|
||||
void castFinish() override;
|
||||
void onExecute() override;
|
||||
|
||||
void castInterrupt() override;
|
||||
void onInterrupt() override;
|
||||
|
||||
private:
|
||||
uint32_t m_eventId;
|
||||
|
|
|
@ -303,7 +303,7 @@ void Sapphire::Entity::Player::eventActionStart( uint32_t eventId,
|
|||
pEvent->setPlayedScene( true );
|
||||
|
||||
setCurrentAction( pEventAction );
|
||||
pEventAction->castStart();
|
||||
pEventAction->onStart();
|
||||
}
|
||||
|
||||
|
||||
|
@ -318,7 +318,7 @@ void Sapphire::Entity::Player::eventItemActionStart( uint32_t eventId,
|
|||
//
|
||||
// setCurrentAction( pEventItemAction );
|
||||
//
|
||||
// pEventItemAction->onCastStart();
|
||||
// pEventItemAction->onStart();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -29,7 +29,7 @@ void World::Manager::ActionMgr::handleAoEPlayerAction( Entity::Player& player, u
|
|||
if( !actionData->targetArea )
|
||||
{
|
||||
// not an action that has an aoe, cancel it
|
||||
action->castInterrupt();
|
||||
action->onInterrupt();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ void World::Manager::ActionMgr::handleTargetedPlayerAction( Entity::Player& play
|
|||
// cancel any aoe actions casted with this packet
|
||||
if( actionData->targetArea )
|
||||
{
|
||||
action->castInterrupt();
|
||||
action->onInterrupt();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -99,10 +99,10 @@ void World::Manager::ActionMgr::bootstrapAction( Entity::Player& player,
|
|||
Action::ActionPtr currentAction,
|
||||
Data::Action& actionData )
|
||||
{
|
||||
if( !canPlayerUseAction( player, *currentAction, actionData ) )
|
||||
if( !currentAction->precheck() )
|
||||
{
|
||||
// forcefully interrupt the action and reset the cooldown
|
||||
currentAction->castInterrupt();
|
||||
currentAction->onInterrupt();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -113,63 +113,7 @@ void World::Manager::ActionMgr::bootstrapAction( Entity::Player& player,
|
|||
}
|
||||
|
||||
// todo: what do in cases of swiftcast/etc? script callback?
|
||||
currentAction->castStart();
|
||||
}
|
||||
|
||||
bool World::Manager::ActionMgr::canPlayerUseAction( Entity::Player& player,
|
||||
Action::Action& currentAction,
|
||||
Data::Action& actionData )
|
||||
{
|
||||
// lol
|
||||
if( !player.isAlive() )
|
||||
return false;
|
||||
|
||||
// npc actions/non player actions
|
||||
if( actionData.classJob == -1 )
|
||||
return false;
|
||||
|
||||
if( player.getLevel() < actionData.classJobLevel )
|
||||
return false;
|
||||
|
||||
auto currentClass = player.getClass();
|
||||
auto actionClass = static_cast< Common::ClassJob >( actionData.classJob );
|
||||
|
||||
if( actionClass != Common::ClassJob::Adventurer && currentClass != actionClass )
|
||||
{
|
||||
// check if not a base class action
|
||||
auto exdData = framework()->get< Data::ExdDataGenerated >();
|
||||
assert( exdData );
|
||||
|
||||
auto classJob = exdData->get< Data::ClassJob >( static_cast< uint8_t >( currentClass ) );
|
||||
if( !classJob )
|
||||
return false;
|
||||
|
||||
if( classJob->classJobParent != actionData.classJob )
|
||||
return false;
|
||||
}
|
||||
|
||||
// reset target on actions that can only be casted on yourself while having a target set
|
||||
// todo: check what actions send when targeting an enemy
|
||||
if( actionData.canTargetSelf &&
|
||||
!actionData.canTargetFriendly &&
|
||||
!actionData.canTargetHostile &&
|
||||
!actionData.canTargetParty )
|
||||
{
|
||||
currentAction.setTargetChara( currentAction.getSourceChara() );
|
||||
}
|
||||
|
||||
// todo: party/enemy validation
|
||||
|
||||
// validate range
|
||||
|
||||
|
||||
auto& actionCost = currentAction.getCostArray();
|
||||
for( uint8_t i = 0; i < actionCost.size(); ++i )
|
||||
{
|
||||
// todo: validate costs/conditionals here
|
||||
}
|
||||
|
||||
return true;
|
||||
currentAction->onStart();
|
||||
}
|
||||
|
||||
void World::Manager::ActionMgr::handleItemActionVFX( Sapphire::Entity::Player& player, uint32_t itemId, uint16_t vfxId )
|
||||
|
|
|
@ -31,7 +31,6 @@ namespace Sapphire::World::Manager
|
|||
|
||||
private:
|
||||
void bootstrapAction( Entity::Player& player, Action::ActionPtr currentAction, Data::Action& actionData );
|
||||
bool canPlayerUseAction( Entity::Player& player, Action::Action& currentAction, Data::Action& actionData );
|
||||
|
||||
// item action handlers
|
||||
void handleItemActionVFX( Entity::Player& player, uint32_t itemId, uint16_t vfxId );
|
||||
|
|
|
@ -88,19 +88,15 @@ namespace Sapphire::ScriptAPI
|
|||
{
|
||||
}
|
||||
|
||||
void ActionScript::onCastStart( Sapphire::Action::Action& currentAction )
|
||||
void ActionScript::onStart( Sapphire::Action::Action& currentAction )
|
||||
{
|
||||
}
|
||||
|
||||
void ActionScript::onCastFinish( Sapphire::Action::Action& currentAction )
|
||||
void ActionScript::onFinish( Sapphire::Action::Action& currentAction )
|
||||
{
|
||||
}
|
||||
|
||||
void ActionScript::onCastInterrupt( Sapphire::Action::Action& currentAction )
|
||||
{
|
||||
}
|
||||
|
||||
void ActionScript::onCharaHit( Sapphire::Action::Action& currentAction, Sapphire::Entity::Chara& hitActor )
|
||||
void ActionScript::onInterrupt( Sapphire::Action::Action& currentAction )
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -140,13 +140,11 @@ namespace Sapphire::ScriptAPI
|
|||
public:
|
||||
explicit ActionScript( uint32_t abilityId );
|
||||
|
||||
virtual void onCastStart( Sapphire::Action::Action& currentAction );
|
||||
virtual void onStart( Sapphire::Action::Action& currentAction );
|
||||
|
||||
virtual void onCastFinish( Sapphire::Action::Action& currentAction );
|
||||
virtual void onFinish( Sapphire::Action::Action& currentAction );
|
||||
|
||||
virtual void onCastInterrupt( Sapphire::Action::Action& currentAction );
|
||||
|
||||
virtual void onCharaHit( Sapphire::Action::Action& currentAction, Sapphire::Entity::Chara& hitActor );
|
||||
virtual void onInterrupt( Sapphire::Action::Action& currentAction );
|
||||
};
|
||||
|
||||
/*!
|
||||
|
|
|
@ -328,50 +328,37 @@ bool Sapphire::Scripting::ScriptMgr::onEObjHit( Sapphire::Entity::Player& player
|
|||
return didCallScript;
|
||||
}
|
||||
|
||||
bool Sapphire::Scripting::ScriptMgr::onCastFinish( Action::Action& currentAction )
|
||||
bool Sapphire::Scripting::ScriptMgr::onExecute( Action::Action& currentAction )
|
||||
{
|
||||
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::ActionScript >( currentAction.getId() );
|
||||
|
||||
if( script )
|
||||
{
|
||||
script->onCastFinish( currentAction );
|
||||
script->onFinish( currentAction );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Sapphire::Scripting::ScriptMgr::onCastInterrupt( Action::Action& currentAction )
|
||||
bool Sapphire::Scripting::ScriptMgr::onInterrupt( Action::Action& currentAction )
|
||||
{
|
||||
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::ActionScript >( currentAction.getId() );
|
||||
|
||||
if( script )
|
||||
{
|
||||
script->onCastInterrupt( currentAction );
|
||||
script->onInterrupt( currentAction );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Sapphire::Scripting::ScriptMgr::onCastStart( Action::Action& currentAction )
|
||||
bool Sapphire::Scripting::ScriptMgr::onStart( Action::Action& currentAction )
|
||||
{
|
||||
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::ActionScript >( currentAction.getId() );
|
||||
|
||||
if( script )
|
||||
{
|
||||
script->onCastStart( currentAction );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Sapphire::Scripting::ScriptMgr::onCharaHit( Action::Action& currentAction, Entity::Chara& hitActor )
|
||||
{
|
||||
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::ActionScript >( currentAction.getId() );
|
||||
|
||||
if( script )
|
||||
{
|
||||
script->onCharaHit( currentAction, hitActor );
|
||||
script->onStart( currentAction );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -72,10 +72,11 @@ namespace Sapphire::Scripting
|
|||
|
||||
bool onEObjHit( Entity::Player& player, uint64_t actorId );
|
||||
|
||||
bool onCastStart( Action::Action& currentAction );
|
||||
bool onCastInterrupt( Action::Action& currentAction );
|
||||
bool onCastFinish( Action::Action& currentAction );
|
||||
bool onCharaHit( Action::Action& currentAction, Entity::Chara& hitActor );
|
||||
bool onStart( Action::Action& currentAction );
|
||||
|
||||
bool onInterrupt( Action::Action& currentAction );
|
||||
|
||||
bool onExecute( Action::Action& currentAction );
|
||||
|
||||
bool onStatusReceive( Entity::CharaPtr pActor, uint32_t effectId );
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue