diff --git a/src/world/Action/Action.cpp b/src/world/Action/Action.cpp index a6478379..b350df8d 100644 --- a/src/world/Action/Action.cpp +++ b/src/world/Action/Action.cpp @@ -266,6 +266,13 @@ void Sapphire::Action::Action::execute() { assert( m_pSource ); + // subtract costs first, if somehow the caster stops meeting those requirements cancel the cast + if( !casterHasCostRequirements( true ) ) + { + interrupt(); + return; + } + auto pScriptMgr = m_pFw->get< Scripting::ScriptMgr >(); if( hasCastTime() ) @@ -398,9 +405,8 @@ bool Sapphire::Action::Action::playerPrecheck( Entity::Player& player ) // validate range - // todo: validate costs/conditionals here - - calculateActionCost(); + if( !casterHasCostRequirements() ) + return false; return true; } @@ -425,4 +431,58 @@ bool Sapphire::Action::Action::isComboAction() const } return m_actionData->actionCombo == lastActionId; +} + +bool Sapphire::Action::Action::casterHasCostRequirements( bool subtractCosts ) +{ + return primaryCostCheck( subtractCosts ) && secondaryCostCheck( subtractCosts ); +} + +bool Sapphire::Action::Action::primaryCostCheck( bool subtractCosts ) +{ + switch( m_primaryCostType ) + { + case Common::ActionPrimaryCostType::TacticsPoints: + { + auto curTp = m_pSource->getTp(); + + if( curTp < m_primaryCost ) + return false; + + if( subtractCosts ) + m_pSource->setTp( curTp - m_primaryCost ); + + return true; + } + + case Common::ActionPrimaryCostType::MagicPoints: + { + auto curMp = m_pSource->getMp(); + + auto cost = Math::CalcStats::calculateMpCost( *m_pSource, m_primaryCost ); + + if( curMp < cost ) + return false; + + if( subtractCosts ) + m_pSource->setMp( curMp - cost ); + + return true; + } + + // free casts, likely just pure ogcds + case Common::ActionPrimaryCostType::None: + { + return true; + } + + default: + return false; + } +} + +bool Sapphire::Action::Action::secondaryCostCheck( bool subtractCosts ) +{ + // todo: these need to be mapped + return true; } \ No newline at end of file diff --git a/src/world/Action/Action.h b/src/world/Action/Action.h index d1499c31..216e1b21 100644 --- a/src/world/Action/Action.h +++ b/src/world/Action/Action.h @@ -47,6 +47,13 @@ namespace Sapphire::Action bool isComboAction() const; + /*! + * @brief Checks whether the source chara has the required stats/debuffs/etc to cast an action. + * @param subtractCosts Whether we should subtract the costs (eg, mp/tp) from the player or to just check them + * @return whether the requirements are met by the caster + */ + bool casterHasCostRequirements( bool subtractCosts = false ); + /*! * @brief Checks if the action *may* target a resident instead of an actor * @return true if the target *may* be a resident and not an actor, otherwise false. @@ -91,7 +98,9 @@ namespace Sapphire::Action protected: void calculateActionCost(); - void calculateMPCost( uint16_t baseCost ); + + bool primaryCostCheck( bool subtractCosts ); + bool secondaryCostCheck( bool subtractCosts ); bool playerPrecheck( Entity::Player& player ); diff --git a/src/world/Actor/Chara.cpp b/src/world/Actor/Chara.cpp index 0250e005..fa9724bb 100644 --- a/src/world/Actor/Chara.cpp +++ b/src/world/Actor/Chara.cpp @@ -181,6 +181,13 @@ void Sapphire::Entity::Chara::setGp( uint32_t gp ) sendStatusUpdate(); } +/*! \param tp amount to set*/ +void Sapphire::Entity::Chara::setTp( uint32_t tp ) +{ + m_tp = tp; + sendStatusUpdate(); +} + /*! \param type invincibility type to set */ void Sapphire::Entity::Chara::setInvincibilityType( Common::InvincibilityType type ) { diff --git a/src/world/Actor/Chara.h b/src/world/Actor/Chara.h index 97acfa09..00b3148a 100644 --- a/src/world/Actor/Chara.h +++ b/src/world/Actor/Chara.h @@ -217,6 +217,8 @@ namespace Sapphire::Entity void setGp( uint32_t gp ); + void setTp( uint32_t tp ); + void setInvincibilityType( Common::InvincibilityType type ); void die();