diff --git a/src/common/Common.h b/src/common/Common.h index 0149a389..57db40f4 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -587,10 +587,12 @@ namespace Sapphire::Common // AetherflowStack = 30, // Status = 32, SAMKenki = 39, + SAMSen = 40, PLDGauge = 41, GNBAmmo = 55, WHMBloodLily = 56, WHMLily = 57, + SAMMeditation = 63, // RDMGaugeBoth = 74, //// RDMGaugeBlack = 75, // not right? // DRGGauge3Eyes = 76, @@ -1258,7 +1260,7 @@ namespace Sapphire::Common uint8_t unused2; uint8_t kenki; uint8_t meditationStacks; - SamSen senFlag; + SamSen sen; } sam; struct { diff --git a/src/world/Action/Action.cpp b/src/world/Action/Action.cpp index c2e4288e..31e72d1d 100644 --- a/src/world/Action/Action.cpp +++ b/src/world/Action/Action.cpp @@ -85,6 +85,7 @@ bool Action::Action::init() if( actionCategory == Common::ActionCategory::Spell || actionCategory == Common::ActionCategory::Weaponskill ) { m_castTimeMs = static_cast< uint32_t >( m_castTimeMs * ( m_pSource->getStatValue( Common::BaseParam::Haste ) / 100.0f ) ); + m_recastTimeMs = static_cast< uint32_t >( m_recastTimeMs * ( m_pSource->getStatValue( Common::BaseParam::Haste ) / 100.0f ) ); } m_cooldownGroup = m_actionData->cooldownGroup; @@ -478,6 +479,11 @@ void Action::Action::buildEffects() auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref(); + for( const auto& statusIt : m_pSource->getStatusEffectMap() ) + { + statusIt.second->onActionExecute( this ); + } + scriptMgr.onExecute( *this ); if( isInterrupted() ) @@ -999,6 +1005,28 @@ bool Action::Action::primaryCostCheck( bool subtractCosts ) return false; } + case Common::ActionPrimaryCostType::SAMSen: + { + auto pPlayer = m_pSource->getAsPlayer(); + if( pPlayer ) + { + // trust the client and assume the action is correct, you can cheat this by performing one sen midare :) + if( pPlayer->gaugeSamHasAnySen() ) + { + if( subtractCosts ) + pPlayer->gaugeSamSetSen( Common::SamSen::None ); + + return true; + } + } + return false; + } + + case Common::ActionPrimaryCostType::SAMMeditation: + { + return true; + } + // free casts, likely just pure ogcds case Common::ActionPrimaryCostType::None: { diff --git a/src/world/Actor/Chara.cpp b/src/world/Actor/Chara.cpp index 7a7c2581..d4785135 100644 --- a/src/world/Actor/Chara.cpp +++ b/src/world/Actor/Chara.cpp @@ -666,7 +666,7 @@ void Sapphire::Entity::Chara::sendStatusEffectUpdate() sendToInRangeSet( statusEffectList, true ); } -void Sapphire::Entity::Chara::sendEffectResultToUpdateShieldValue() +void Sapphire::Entity::Chara::sendShieldUpdate() { auto pPacket = makeZonePacket< FFXIVIpcEffectResult >( getId() ); @@ -730,7 +730,7 @@ std::pair< uint8_t, Sapphire::StatusEffect::StatusEffectPtr > Sapphire::Entity:: { for( auto effectIt : m_statusEffectMap ) { - if( effectIt.second->getId() == id ) + if( effectIt.second->getId() == id && !effectIt.second->isMarkedToRemove() ) { return std::make_pair( effectIt.first, effectIt.second ); } @@ -974,7 +974,7 @@ float Sapphire::Entity::Chara::applyShieldProtection( float damage ) } if( shieldChanged ) - sendEffectResultToUpdateShieldValue(); + sendShieldUpdate(); return remainingDamage; } diff --git a/src/world/Actor/Chara.h b/src/world/Actor/Chara.h index dce638aa..698343e4 100644 --- a/src/world/Actor/Chara.h +++ b/src/world/Actor/Chara.h @@ -166,7 +166,7 @@ namespace Sapphire::Entity void sendStatusEffectUpdate(); - void sendEffectResultToUpdateShieldValue(); + void sendShieldUpdate(); /*! return a const pointer to the look array */ const uint8_t* getLookArray() const; diff --git a/src/world/Actor/Player.cpp b/src/world/Actor/Player.cpp index 848f748d..82579332 100644 --- a/src/world/Actor/Player.cpp +++ b/src/world/Actor/Player.cpp @@ -2462,3 +2462,38 @@ uint8_t Sapphire::Entity::Player::gaugeSamGetKenki() { return m_gauge.sam.kenki; } + +void Sapphire::Entity::Player::gaugeSamSetSen( Sapphire::Common::SamSen type, bool value ) +{ + auto sen = static_cast< uint8_t >( type ); + auto current = static_cast< uint8_t >( gaugeSamGetSenRaw() ); + if( value ) + current |= sen; + else + current &= ~sen; + gaugeSamSetSen( static_cast< Sapphire::Common::SamSen >( current ) ); +} + +void Sapphire::Entity::Player::gaugeSamSetSen( Sapphire::Common::SamSen value ) +{ + assert( value >= 0 && value <= 7 ); + auto oldValue = gaugeSamGetSenRaw(); + m_gauge.sam.sen = value; + if( oldValue != value ) + sendActorGauge(); +} + +bool Sapphire::Entity::Player::gaugeSamGetSen( Sapphire::Common::SamSen type ) +{ + return ( static_cast< uint8_t >( m_gauge.sam.sen ) & static_cast< uint8_t >( type ) ) > 0; +} + +Sapphire::Common::SamSen Sapphire::Entity::Player::gaugeSamGetSenRaw() +{ + return m_gauge.sam.sen; +} + +bool Sapphire::Entity::Player::gaugeSamHasAnySen() +{ + return static_cast< uint8_t >( m_gauge.sam.sen ) > 0; +} diff --git a/src/world/Actor/Player.h b/src/world/Actor/Player.h index 8afe1bc8..db19a201 100644 --- a/src/world/Actor/Player.h +++ b/src/world/Actor/Player.h @@ -1008,6 +1008,11 @@ namespace Sapphire::Entity void gaugeSamSetKenki( uint8_t value ); uint8_t gaugeSamGetKenki(); + void gaugeSamSetSen( Common::SamSen type, bool value ); + void gaugeSamSetSen( Common::SamSen value ); + bool gaugeSamGetSen( Common::SamSen type ); + Common::SamSen gaugeSamGetSenRaw(); + bool gaugeSamHasAnySen(); ////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/world/StatusEffect/StatusEffect.cpp b/src/world/StatusEffect/StatusEffect.cpp index 91bf3208..74a33cdc 100644 --- a/src/world/StatusEffect/StatusEffect.cpp +++ b/src/world/StatusEffect/StatusEffect.cpp @@ -294,116 +294,35 @@ void Sapphire::StatusEffect::StatusEffect::replaceEffectEntry( Sapphire::World:: void Sapphire::StatusEffect::StatusEffect::onBeforeActionStart( Sapphire::World::Action::Action* action ) { // todo: add script function for this if needed - //auto pScriptMgr = m_pFw->get< Scripting::ScriptMgr >(); - //pScriptMgr->onBeforeActionStart( m_targetActor, *this ); - - auto checkAction1 = []( Sapphire::World::Action::Action* action, Sapphire::World::Action::StatusEffectEntry& effectEntry ) - { - // value1: remaining uses if >= 0 - // value2-4: affected action ids if value2 > 0, otherwise value3 is actionCategory filter (4 bytes for 4 categories) - if( effectEntry.effectValue1 == 0 ) - return false; - - if( effectEntry.effectValue2 != 0 ) - { - if( action->getId() != effectEntry.effectValue2 && - action->getId() != effectEntry.effectValue3 && - action->getId() != effectEntry.effectValue4 ) - return false; - } - else - { - if( effectEntry.effectValue3 != 0 ) - { - auto ac = action->getActionData()->actionCategory; - uint8_t f1 = static_cast< uint8_t >( effectEntry.effectValue3 ); - uint8_t f2 = static_cast< uint8_t >( effectEntry.effectValue3 >> 8 ); - uint8_t f3 = static_cast< uint8_t >( effectEntry.effectValue3 >> 16 ); - uint8_t f4 = static_cast< uint8_t >( effectEntry.effectValue3 >> 24 ); - bool passed = false; - if( f1 != 0 && ac == f1 ) - passed = true; - else if ( f2 != 0 && ac == f2 ) - passed = true; - else if ( f3 != 0 && ac == f3 ) - passed = true; - else if ( f4 != 0 && ac == f4 ) - passed = true; - if( !passed ) - return false; - } - } - - return true; - }; - switch( static_cast< Common::StatusEffectType >( m_effectEntry.effectType ) ) { case Common::StatusEffectType::InstantCast: { - if( action->hasCastTime() && checkAction1( action, m_effectEntry ) ) - { - if( m_effectEntry.effectValue1 > 0 ) - { - if( m_effectEntry.effectValue1 == getStacks() ) - { - // if stacks equal to remaining uses, assume it is synced - setStacks( getStacks() - 1 ); - m_targetActor->sendStatusEffectUpdate(); - } - m_effectEntry.effectValue1--; - if( m_effectEntry.effectValue1 == 0 ) - { - markToRemove(); - } - action->setCastTime( 0 ); - } - } + if( action->hasCastTime() && checkActionBoostType1( action ) ) + action->setCastTime( 0 ); break; } case Common::StatusEffectType::AlwaysCombo: { - if( checkAction1( action, m_effectEntry ) ) - { - if( m_effectEntry.effectValue1 > 0 ) - { - if( m_effectEntry.effectValue1 == getStacks() ) - { - // if stacks equal to remaining uses, assume it is synced - setStacks( getStacks() - 1 ); - m_targetActor->sendStatusEffectUpdate(); - } - m_effectEntry.effectValue1--; - if( m_effectEntry.effectValue1 == 0 ) - { - markToRemove(); - } - action->setAlwaysCombo(); - } - } + if( checkActionBoostType1( action ) ) + action->setAlwaysCombo(); break; } + } +} + +void Sapphire::StatusEffect::StatusEffect::onActionExecute( Sapphire::World::Action::Action* action ) +{ + // todo: add script function for this if needed + switch( static_cast< Common::StatusEffectType >( m_effectEntry.effectType ) ) + { case Common::StatusEffectType::PotencyMultiplier: { - if( checkAction1( action, m_effectEntry ) ) + if( checkActionBoostType1( action ) ) { - if( m_effectEntry.effectValue1 > 0 ) - { - if( m_effectEntry.effectValue1 == getStacks() ) - { - // if stacks equal to remaining uses, assume it is synced - setStacks( getStacks() - 1 ); - m_targetActor->sendStatusEffectUpdate(); - } - m_effectEntry.effectValue1--; - if( m_effectEntry.effectValue1 == 0 ) - { - markToRemove(); - } - action->getActionEntry().damagePotency *= 1.0 + ( m_effectEntry.effectValue4 / 100.0 ); - action->getActionEntry().damageComboPotency *= 1.0 + ( m_effectEntry.effectValue4 / 100.0 ); - action->getActionEntry().damageDirectionalPotency *= 1.0 + ( m_effectEntry.effectValue4 / 100.0 ); - } + action->getActionEntry().damagePotency *= 1.0 + ( m_effectEntry.effectValue4 / 100.0 ); + action->getActionEntry().damageComboPotency *= 1.0 + ( m_effectEntry.effectValue4 / 100.0 ); + action->getActionEntry().damageDirectionalPotency *= 1.0 + ( m_effectEntry.effectValue4 / 100.0 ); } break; } @@ -442,18 +361,108 @@ bool Sapphire::StatusEffect::StatusEffect::onActionHitTarget( World::Action::Act { if( victimCounter == 1 && action->isGCD() ) { - if( m_effectEntry.effectValue2 != 0 ) + if( checkActionBoostType2( action ) ) { - if( action->getId() != m_effectEntry.effectValue2 && - action->getId() != m_effectEntry.effectValue3 && - action->getId() != m_effectEntry.effectValue4 ) - break; + float restored = 0.01f * m_targetActor->getMaxMp() * m_effectEntry.effectValue1; + action->getEffectbuilder()->restoreMP( victim, m_targetActor, static_cast< uint32_t >( restored ), Sapphire::Common::ActionEffectResultFlag::EffectOnSource ); } - float restored = 0.01f * m_targetActor->getMaxMp() * m_effectEntry.effectValue1; - action->getEffectbuilder()->restoreMP( victim, m_targetActor, static_cast< uint32_t >( restored ), Sapphire::Common::ActionEffectResultFlag::EffectOnSource ); } break; } } + return true; +} + +bool Sapphire::StatusEffect::StatusEffect::checkActionBoostType1( World::Action::Action* action ) +{ + // value1: remaining uses if >= 0, infinite uses if < 0 and can be custom data + // value2-4: affected action ids if value2 > 0, + // otherwise value3 is actionCategory filter (4 bytes for 4 categories) and value4 is custom data + if( m_effectEntry.effectValue1 == 0 ) + return false; + + if( m_effectEntry.effectValue2 != 0 ) + { + if( action->getId() != m_effectEntry.effectValue2 && + action->getId() != m_effectEntry.effectValue3 && + action->getId() != m_effectEntry.effectValue4 ) + return false; + } + else + { + if( m_effectEntry.effectValue3 != 0 ) + { + auto ac = action->getActionData()->actionCategory; + uint8_t f1 = static_cast< uint8_t >( m_effectEntry.effectValue3 ); + uint8_t f2 = static_cast< uint8_t >( m_effectEntry.effectValue3 >> 8 ); + uint8_t f3 = static_cast< uint8_t >( m_effectEntry.effectValue3 >> 16 ); + uint8_t f4 = static_cast< uint8_t >( m_effectEntry.effectValue3 >> 24 ); + bool passed = false; + if( f1 != 0 && ac == f1 ) + passed = true; + else if ( f2 != 0 && ac == f2 ) + passed = true; + else if ( f3 != 0 && ac == f3 ) + passed = true; + else if ( f4 != 0 && ac == f4 ) + passed = true; + if( !passed ) + return false; + } + } + + if( m_effectEntry.effectValue1 > 0 ) + { + if( m_effectEntry.effectValue1 == getStacks() ) + { + // if stacks equal to remaining uses, assume it is synced + setStacks( getStacks() - 1 ); + m_targetActor->sendStatusEffectUpdate(); + } + m_effectEntry.effectValue1--; + if( m_effectEntry.effectValue1 == 0 ) + { + markToRemove(); + } + } + + return true; +} + +bool Sapphire::StatusEffect::StatusEffect::checkActionBoostType2( World::Action::Action* action ) +{ + // value1: custom data + // value2-4: affected action ids if value2 > 0, + // otherwise value3 is actionCategory filter (4 bytes for 4 categories) and value4 is custom data + if( m_effectEntry.effectValue2 != 0 ) + { + if( action->getId() != m_effectEntry.effectValue2 && + action->getId() != m_effectEntry.effectValue3 && + action->getId() != m_effectEntry.effectValue4 ) + return false; + } + else + { + if( m_effectEntry.effectValue3 != 0 ) + { + auto ac = action->getActionData()->actionCategory; + uint8_t f1 = static_cast< uint8_t >( m_effectEntry.effectValue3 ); + uint8_t f2 = static_cast< uint8_t >( m_effectEntry.effectValue3 >> 8 ); + uint8_t f3 = static_cast< uint8_t >( m_effectEntry.effectValue3 >> 16 ); + uint8_t f4 = static_cast< uint8_t >( m_effectEntry.effectValue3 >> 24 ); + bool passed = false; + if( f1 != 0 && ac == f1 ) + passed = true; + else if ( f2 != 0 && ac == f2 ) + passed = true; + else if ( f3 != 0 && ac == f3 ) + passed = true; + else if ( f4 != 0 && ac == f4 ) + passed = true; + if( !passed ) + return false; + } + } + return true; } \ No newline at end of file diff --git a/src/world/StatusEffect/StatusEffect.h b/src/world/StatusEffect/StatusEffect.h index b12ed407..c93fc263 100644 --- a/src/world/StatusEffect/StatusEffect.h +++ b/src/world/StatusEffect/StatusEffect.h @@ -21,6 +21,8 @@ public: void onBeforeActionStart( World::Action::Action* action ); + void onActionExecute( World::Action::Action* action ); + bool onActionHitTarget( World::Action::Action* action, Entity::CharaPtr victim, int victimCounter ); void applyStatus(); @@ -69,6 +71,9 @@ public: void refresh( Sapphire::World::Action::StatusEffectEntry newEntry ); private: + bool checkActionBoostType1( Sapphire::World::Action::Action* action ); // see cpp for more info + bool checkActionBoostType2( Sapphire::World::Action::Action* action ); // see cpp for more info + uint32_t m_id; Entity::CharaPtr m_sourceActor; Entity::CharaPtr m_targetActor;