diff --git a/data/actions/player.json b/data/actions/player.json index 8771bae7..d8cc5521 100644 --- a/data/actions/player.json +++ b/data/actions/player.json @@ -629,7 +629,7 @@ }, "44": { "name": "Vengeance", - "potency": 50, + "potency": 0, "comboPotency": 0, "flankPotency": 0, "frontPotency": 0, @@ -638,7 +638,18 @@ "restorePercentage": 0, "nextCombo": [], "statuses": { - "caster": [], + "caster": [ + { + "id": 89, + "duration": 20000, + "modifiers": [ + { + "modifier": "ReflectPhysical", + "value": 50 + } + ] + } + ], "target": [] } }, @@ -1263,7 +1274,19 @@ "restorePercentage": 0, "nextCombo": [], "statuses": { - "caster": [], + "caster": [ + { + "id": 116, + "duration": 10000, + "flag": 4096, + "modifiers": [ + { + "modifier": "CriticalHitPercent", + "value": 100 + } + ] + } + ], "target": [] } }, diff --git a/src/common/Common.h b/src/common/Common.h index 2fbb197c..da94d9b0 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -922,6 +922,23 @@ namespace Sapphire::Common ParryPercent = 1031 }; + enum class StatusEffectFlag : uint32_t + { + BuffCategory = 1, + DebuffCategory = 2, + Permanent = 4, + IsGaze = 8, + Transfiguration = 16, + CanDispel = 32, + LockActions = 64, + LockControl = 128, + LockMovement = 256, + Invisibilty = 512, + CanStatusOff = 1024, + FcBuff = 2048, + RemoveOnSuccessfulHit = 4096 + }; + enum struct ActionAspect : uint8_t { None = 0, // Doesn't imply unaspected diff --git a/src/tools/action_parse/main.cpp b/src/tools/action_parse/main.cpp index dee585cc..a5af43f9 100644 --- a/src/tools/action_parse/main.cpp +++ b/src/tools/action_parse/main.cpp @@ -41,6 +41,7 @@ struct StatusEntry { uint16_t id; int32_t duration; + uint32_t flag; std::vector< StatusModifier > modifiers; }; @@ -78,6 +79,7 @@ void to_json( nlohmann::ordered_json& j, const StatusEntry& statusEntry ) j = nlohmann::ordered_json{ { "id", statusEntry.id }, { "duration", statusEntry.duration }, + { "flag", statusEntry.flag }, { "modifiers", statusEntry.modifiers } }; } diff --git a/src/world/Action/Action.cpp b/src/world/Action/Action.cpp index 190965eb..2f8b6bc5 100644 --- a/src/world/Action/Action.cpp +++ b/src/world/Action/Action.cpp @@ -88,6 +88,7 @@ bool Action::Action::init() m_cooldownGroup = m_actionData->data().RecastGroup; m_range = m_actionData->data().SelectRange; m_effectRange = m_actionData->data().EffectRange; + m_effectWidth = m_actionData->data().EffectWidth; m_category = static_cast< Common::ActionCategory >( m_actionData->data().Category ); m_castType = static_cast< Common::CastType >( m_actionData->data().EffectType ); m_aspect = static_cast< Common::ActionAspect >( m_actionData->data().AttackType ); @@ -119,7 +120,7 @@ bool Action::Action::init() m_primaryCostType = static_cast< Common::ActionPrimaryCostType >( m_actionData->data().CostType ); m_primaryCost = m_actionData->data().CostValue; - /*if( !m_actionData->targetArea ) + if( !m_actionData->data().SelectGround ) { // override pos to target position // todo: this is kinda dirty @@ -131,7 +132,7 @@ bool Action::Action::init() break; } } - }*/ + } // todo: add missing rows for secondaryCostType/secondaryCostType and rename the current rows to primaryCostX @@ -607,11 +608,17 @@ void Action::Action::handleAction() } } - if( m_lutEntry.statuses.caster.size() > 0 || m_lutEntry.statuses.target.size() > 0 ) - handleStatusEffects(); + // If we hit an enemy + if( m_hitActors.size() > 0 && getHitChara()->getObjKind() != m_pSource->getObjKind() ) + { + m_pSource->removeStatusEffectByFlag( Common::StatusEffectFlag::RemoveOnSuccessfulHit ); + } handleJobAction(); + if( m_lutEntry.statuses.caster.size() > 0 || m_lutEntry.statuses.target.size() > 0 ) + handleStatusEffects(); + m_effectBuilder->buildAndSendPackets( m_hitActors ); // TODO: disabled, reset kills our queued actions @@ -630,7 +637,7 @@ void Action::Action::handleStatusEffects() for( auto& status : m_lutEntry.statuses.caster ) { applyStatusEffectSelf( status.id ); - m_pSource->addStatusEffectByIdIfNotExist( status.id, status.duration, *m_pSource, status.modifiers ); + m_pSource->addStatusEffectByIdIfNotExist( status.id, status.duration, *m_pSource, status ); } } @@ -642,7 +649,7 @@ void Action::Action::handleStatusEffects() for( auto& status : m_lutEntry.statuses.target ) { getEffectbuilder()->applyStatusEffect( actor, status.id, 0 ); - actor->addStatusEffectByIdIfNotExist( status.id, status.duration, *m_pSource, status.modifiers ); + actor->addStatusEffectByIdIfNotExist( status.id, status.duration, *m_pSource, status ); } if( actor->getStatusEffectMap().size() > 0 ) @@ -866,7 +873,6 @@ void Action::Action::addDefaultActorFilters() switch( m_castType ) { case Common::CastType::SingleTarget: - case Common::CastType::Type3: { auto filter = std::make_shared< World::Util::ActorFilterSingleTarget >( static_cast< uint32_t >( m_targetId ) ); addActorFilter( filter ); diff --git a/src/world/Actor/Chara.cpp b/src/world/Actor/Chara.cpp index 49f83f3d..442e212f 100644 --- a/src/world/Actor/Chara.cpp +++ b/src/world/Actor/Chara.cpp @@ -572,6 +572,17 @@ void Sapphire::Entity::Chara::addStatusEffectByIdIfNotExist( uint32_t id, int32_ addStatusEffect( effect ); } +void Sapphire::Entity::Chara::addStatusEffectByIdIfNotExist( uint32_t id, int32_t duration, Entity::Chara& source, + World::Action::StatusEntry& statusEntry, uint16_t param ) +{ + if( hasStatusEffect( id ) ) + return; + + auto effect = StatusEffect::make_StatusEffect( id, source.getAsChara(), getAsChara(), duration, statusEntry, 3000 ); + effect->setParam( param ); + addStatusEffect( effect ); +} + int8_t Sapphire::Entity::Chara::getStatusEffectFreeSlot() { int8_t freeEffectSlot = -1; @@ -614,6 +625,17 @@ void Sapphire::Entity::Chara::removeSingleStatusEffectById( uint32_t id ) } } +void Sapphire::Entity::Chara::removeStatusEffectByFlag( Common::StatusEffectFlag flag ) +{ + for( auto effectIt = m_statusEffectMap.begin(); effectIt != m_statusEffectMap.end(); ) + { + if( effectIt->second->getFlag() & static_cast< uint32_t >( flag ) ) + effectIt = removeStatusEffect( effectIt->first ); + else + ++effectIt; + } +} + std::map< uint8_t, Sapphire::StatusEffect::StatusEffectPtr >::iterator Sapphire::Entity::Chara::removeStatusEffect( uint8_t effectSlotId, bool sendOrder ) { auto pEffectIt = m_statusEffectMap.find( effectSlotId ); diff --git a/src/world/Actor/Chara.h b/src/world/Actor/Chara.h index df81d626..77ccdc25 100644 --- a/src/world/Actor/Chara.h +++ b/src/world/Actor/Chara.h @@ -114,6 +114,8 @@ namespace Sapphire::Entity void removeSingleStatusEffectById( uint32_t id ); + void removeStatusEffectByFlag( Common::StatusEffectFlag flag ); + void updateStatusEffects(); bool hasStatusEffect( uint32_t id ); @@ -143,6 +145,8 @@ namespace Sapphire::Entity void addStatusEffectByIdIfNotExist( uint32_t id, int32_t duration, Entity::Chara& source, uint16_t param = 0 ); void addStatusEffectByIdIfNotExist( uint32_t id, int32_t duration, Entity::Chara& source, std::vector< World::Action::StatusModifier > modifiers, uint16_t param = 0 ); + void addStatusEffectByIdIfNotExist( uint32_t id, int32_t duration, Entity::Chara& source, World::Action::StatusEntry& statusEntry, + uint16_t param = 0 ); // remove a status effect by id void removeSingleStatusEffectFromId( uint32_t id ); diff --git a/src/world/StatusEffect/StatusEffect.cpp b/src/world/StatusEffect/StatusEffect.cpp index 56d47746..56c41a15 100644 --- a/src/world/StatusEffect/StatusEffect.cpp +++ b/src/world/StatusEffect/StatusEffect.cpp @@ -28,6 +28,14 @@ Sapphire::StatusEffect::StatusEffect::StatusEffect( uint32_t id, Entity::CharaPt m_statusModifiers = std::move( modifiers ); } +Sapphire::StatusEffect::StatusEffect::StatusEffect( uint32_t id, Entity::CharaPtr sourceActor, Entity::CharaPtr targetActor, + uint32_t duration, World::Action::StatusEntry& statusEntry, uint32_t tickRate ) : + StatusEffect( id, sourceActor, targetActor, duration, tickRate ) +{ + m_statusModifiers = statusEntry.modifiers; + m_flag |= statusEntry.flag; +} + Sapphire::StatusEffect::StatusEffect::StatusEffect( uint32_t id, Entity::CharaPtr sourceActor, Entity::CharaPtr targetActor, uint32_t duration, uint32_t tickRate ) : m_id( id ), @@ -37,7 +45,8 @@ Sapphire::StatusEffect::StatusEffect::StatusEffect( uint32_t id, Entity::CharaPt m_modifiers( 0 ), m_startTime( 0 ), m_tickRate( tickRate ), - m_lastTick( 0 ) + m_lastTick( 0 ), + m_flag( 0 ) { auto& exdData = Common::Service< Data::ExdData >::ref(); auto entry = exdData.getRow< Excel::Status >( id ); @@ -52,6 +61,15 @@ Sapphire::StatusEffect::StatusEffect::StatusEffect( uint32_t id, Entity::CharaPt Util::eraseAll( m_name, '-' ); Util::eraseAll( m_name, '(' ); Util::eraseAll( m_name, ')' ); + + m_flag |= entry->data().Category; + m_flag |= static_cast< uint32_t >( entry->data().Forever ) << static_cast< uint32_t >( Common::StatusEffectFlag::Permanent ); + m_flag |= static_cast< uint32_t >( entry->data().CanOff ) << static_cast< uint32_t >( Common::StatusEffectFlag::CanStatusOff ); + m_flag |= static_cast< uint32_t >( entry->data().NotAction ) << static_cast< uint32_t >( Common::StatusEffectFlag::LockActions ); + m_flag |= static_cast< uint32_t >( entry->data().NotControl ) << static_cast< uint32_t >( Common::StatusEffectFlag::LockControl ); + m_flag |= static_cast< uint32_t >( entry->data().NotMove ) << static_cast< uint32_t >( Common::StatusEffectFlag::LockMovement ); + m_flag |= static_cast< uint32_t >( entry->data().NotLookAt ) << static_cast< uint32_t >( Common::StatusEffectFlag::IsGaze ); + m_flag |= static_cast< uint32_t >( entry->data().FcAction ) << static_cast< uint32_t >( Common::StatusEffectFlag::FcBuff ); } @@ -192,6 +210,16 @@ uint64_t Sapphire::StatusEffect::StatusEffect::getStartTimeMs() const return m_startTime; } +uint32_t Sapphire::StatusEffect::StatusEffect::getFlag() const +{ + return m_flag; +} + +void Sapphire::StatusEffect::StatusEffect::setFlag( uint32_t flag ) +{ + m_flag = flag; +} + void Sapphire::StatusEffect::StatusEffect::setLastTick( uint64_t lastTick ) { m_lastTick = lastTick; diff --git a/src/world/StatusEffect/StatusEffect.h b/src/world/StatusEffect/StatusEffect.h index 9b750121..ef77a9c9 100644 --- a/src/world/StatusEffect/StatusEffect.h +++ b/src/world/StatusEffect/StatusEffect.h @@ -14,6 +14,9 @@ public: StatusEffect( uint32_t id, Entity::CharaPtr sourceActor, Entity::CharaPtr targetActor, uint32_t duration, std::vector< World::Action::StatusModifier >& modifiers, uint32_t tickRate ); + StatusEffect( uint32_t id, Entity::CharaPtr sourceActor, Entity::CharaPtr targetActor, + uint32_t duration, World::Action::StatusEntry& statusEntry, uint32_t tickRate ); + StatusEffect( uint32_t id, Entity::CharaPtr sourceActor, Entity::CharaPtr targetActor, uint32_t duration, uint32_t tickRate ); @@ -47,10 +50,14 @@ public: uint16_t getParam() const; + uint32_t getFlag() const; + void setLastTick( uint64_t lastTick ); void setParam( uint16_t param ); + void setFlag( uint32_t flag ); + void registerTickEffect( Common::ParamModifier type, uint32_t param ); std::pair< Common::ParamModifier, uint32_t > getTickEffect(); @@ -66,6 +73,7 @@ private: uint32_t m_tickRate; uint64_t m_lastTick; uint16_t m_param; + uint32_t m_flag; std::string m_name; std::pair< Common::ParamModifier, uint32_t > m_currTickEffect; std::vector< World::Action::StatusModifier > m_statusModifiers;