1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-05-23 02:07:45 +00:00

Add status refresh policies (adapted from collett's work)

Co-authored-by: collett <collett0301@gmail.com>
This commit is contained in:
Rushi 2025-01-20 22:54:29 +01:00
parent bf3368224a
commit e442b735d3
8 changed files with 278 additions and 14 deletions

View file

@ -940,6 +940,16 @@ namespace Sapphire::Common
RemoveOnSuccessfulHit = 4096
};
enum class StatusRefreshPolicy : uint8_t
{
Stack = 0,
ReplaceOrApply = 1,
Extend = 2,
ExtendOrApply = 3,
Reject = 4,
Custom = 255
};
enum struct ActionAspect : uint8_t
{
None = 0, // Doesn't imply unaspected

View file

@ -398,7 +398,18 @@
"restorePercentage": 0,
"nextCombo": [],
"statuses": {
"caster": [],
"caster": [
{
"id": 83,
"duration": 20000,
"modifiers": [
{
"modifier": "DefensePercent",
"value": 20
}
]
}
],
"target": []
}
},
@ -414,7 +425,18 @@
"nextCombo": [],
"statuses": {
"caster": [],
"target": []
"target": [
{
"id": 244,
"duration": 30000,
"modifiers": [
{
"modifier": "TickDamage",
"value": 20
}
]
}
]
}
},
"34": {
@ -478,7 +500,18 @@
45
],
"statuses": {
"caster": [],
"caster": [
{
"id": 85,
"duration": 24000,
"modifiers": [
{
"modifier": "DamageDealtPercent",
"value": 20
}
]
}
],
"target": []
}
},
@ -493,7 +526,18 @@
"restorePercentage": 0,
"nextCombo": [],
"statuses": {
"caster": [],
"caster": [
{
"id": 86,
"duration": 20000,
"modifiers": [
{
"modifier": "AttackPowerPercent",
"value": 50
}
]
}
],
"target": []
}
},
@ -523,7 +567,18 @@
"restorePercentage": 0,
"nextCombo": [],
"statuses": {
"caster": [],
"caster": [
{
"id": 87,
"duration": 20000,
"modifiers": [
{
"modifier": "HPPercent",
"value": 20
}
]
}
],
"target": []
}
},
@ -583,7 +638,18 @@
"restorePercentage": 0,
"nextCombo": [],
"statuses": {
"caster": [],
"caster": [
{
"id": 89,
"duration": 20000,
"modifiers": [
{
"modifier": "ReflectPhysical",
"value": 50
}
]
}
],
"target": []
}
},
@ -643,7 +709,34 @@
"restorePercentage": 0,
"nextCombo": [],
"statuses": {
"caster": [],
"caster": [
{
"id": 91,
"duration": 0,
"modifiers": [
{
"modifier": "HPPercent",
"value": 25
},
{
"modifier": "DamageDealtPercent",
"value": -25
},
{
"modifier": "HealingMagicRecoveryPercent",
"value": 20
},
{
"modifier": "AccuracyPercent",
"value": 5
},
{
"modifier": "EnmityPercent",
"value": 20
}
]
}
],
"target": []
}
},
@ -1176,7 +1269,19 @@
"restorePercentage": 0,
"nextCombo": [],
"statuses": {
"caster": [],
"caster": [
{
"id": 116,
"duration": 10000,
"flag": 4096,
"modifiers": [
{
"modifier": "CriticalHitPercent",
"value": 100
}
]
}
],
"target": []
}
},
@ -2394,7 +2499,18 @@
"nextCombo": [],
"statuses": {
"caster": [],
"target": []
"target": [
{
"id": 179,
"duration": 18000,
"modifiers": [
{
"modifier": "TickDamage",
"value": 40
}
]
}
]
}
},
"165": {
@ -2454,7 +2570,38 @@
"nextCombo": [],
"statuses": {
"caster": [],
"target": []
"target": [
{
"id": 180,
"duration": 24000,
"modifiers": [
{
"modifier": "TickDamage",
"value": 35
}
]
},
{
"id": 191,
"duration": 24000,
"modifiers": [
{
"modifier": "HealingRecoveryPercent",
"value": -20
}
]
},
{
"id": 240,
"duration": 24000,
"modifiers": [
{
"modifier": "HeavyPercent",
"value": 40
}
]
}
]
}
},
"169": {

View file

@ -41,6 +41,8 @@ struct StatusEntry
{
uint16_t id;
uint32_t duration;
uint32_t maxDuration;
uint8_t statusRefreshPolicy;
uint32_t flag;
std::vector< StatusModifier > modifiers;
};
@ -79,6 +81,8 @@ void to_json( nlohmann::ordered_json& j, const StatusEntry& statusEntry )
j = nlohmann::ordered_json{
{ "id", statusEntry.id },
{ "duration", statusEntry.duration },
{ "maxDuration", statusEntry.maxDuration },
{ "statusRefreshPolicy", statusEntry.statusRefreshPolicy },
{ "flag", statusEntry.flag },
{ "modifiers", statusEntry.modifiers }
};
@ -98,7 +102,7 @@ void to_json( nlohmann::ordered_json& j, const ActionEntry& action )
{ "nextCombo", action.nextCombo },
{ "statuses", {
{ "caster", action.statuses.caster },
{ "target", action.statuses.target },
{ "target", action.statuses.target }
}
}
};

View file

@ -32,6 +32,8 @@
#include <Service.h>
#include "WorldServer.h"
#include "StatusEffect/StatusEffect.h"
#include "Job/Warrior.h"
using namespace Sapphire;
@ -607,6 +609,86 @@ void Action::Action::buildActionResults()
// m_effectBuilder.reset();
}
void Action::Action::applyStatusEffect( bool isSelf, Entity::CharaPtr& target, Entity::CharaPtr& source, World::Action::StatusEntry& status, bool statusToSource )
{
auto pActionBuilder = getActionResultBuilder();
if( !pActionBuilder )
return;
auto hasSameStatus = false;
auto hasSameStatusFromSameCaster = false;
Sapphire::StatusEffect::StatusEffectPtr referenceStatus = nullptr;
for( auto const& entry : statusToSource ? source->getStatusEffectMap() : target->getStatusEffectMap() )
{
auto statusEffect = entry.second;
if( statusEffect->getId() == status.id )
{
hasSameStatus = true;
if( !referenceStatus )
referenceStatus = statusEffect;
if( statusEffect->getSrcActorId() == source->getId() )
{
hasSameStatusFromSameCaster = true;
referenceStatus = statusEffect;
break;;
}
}
}
auto policy = getStatusRefreshPolicy( status.statusRefreshPolicy, isSelf );
switch( policy )
{
case Common::StatusRefreshPolicy::Stack:
{
pActionBuilder->applyStatusEffect( target, status.id, status.duration, 0, std::move( status.modifiers ), status.flag, statusToSource, false );
break;
}
case Common::StatusRefreshPolicy::ReplaceOrApply:
{
pActionBuilder->applyStatusEffect( target, status.id, status.duration, 0, std::move( status.modifiers ), status.flag, statusToSource, true );
break;
}
case Common::StatusRefreshPolicy::Extend:
case Common::StatusRefreshPolicy::ExtendOrApply:
{
int64_t remainingDuration = 0;
if( hasSameStatus )
{
remainingDuration = static_cast< int64_t >( referenceStatus->getDuration() ) - ( Common::Util::getTimeMs() - referenceStatus->getStartTimeMs() );
if( remainingDuration < 0 )
remainingDuration = 0;
}
if( hasSameStatus || policy == Common::StatusRefreshPolicy::ExtendOrApply )
{
pActionBuilder->applyStatusEffect( target, status.id, std::min( status.duration + remainingDuration, static_cast< int64_t >( status.maxDuration ) ), 0, std::move( status.modifiers ), status.flag, statusToSource, true );
}
break;
}
case Common::StatusRefreshPolicy::Reject:
{
if( !hasSameStatus )
{
pActionBuilder->applyStatusEffect( target, status.id, status.duration, 0, std::move( status.modifiers ), status.flag, statusToSource, true );
}
else
{
// add function to ActionBuilder for No Effect effect
}
break;
}
case Common::StatusRefreshPolicy::Custom:
{
// script should handle it
break;
}
}
}
void Action::Action::handleStatusEffects()
{
auto pActionBuilder = getActionResultBuilder();
@ -622,7 +704,12 @@ void Action::Action::handleStatusEffects()
{
for( auto& status : m_lutEntry.statuses.caster )
{
pActionBuilder->applyStatusEffectSelf( status.id, status.duration, 0, std::move( status.modifiers ), status.flag, true );
/*if( m_hitActors[ 0 ] ) // might need a firstValidVictim?
applyStatusEffect( true, m_hitActors[ 0 ], m_pSource, status, true );
// pActionBuilder->applyStatusEffectSelf( status.id, status.duration, 0, std::move( status.modifiers ), status.flag, true ); // statusToSource true
else if( m_lutEntry.potency == 0 )*/
applyStatusEffect( true, m_pSource, m_pSource, status, true );
// pActionBuilder->applyStatusEffectSelf( status.id, status.duration, 0, std::move( status.modifiers ), status.flag, true );
}
}
@ -633,7 +720,8 @@ void Action::Action::handleStatusEffects()
{
for( auto& status : m_lutEntry.statuses.target )
{
pActionBuilder->applyStatusEffect( actor, status.id, status.duration, 0, std::move( status.modifiers ), status.flag, true );
applyStatusEffect( false, actor, m_pSource, status );
// pActionBuilder->applyStatusEffect( actor, status.id, status.duration, 0, std::move( status.modifiers ), status.flag, true );
}
if( !actor->getStatusEffectMap().empty() )

View file

@ -113,6 +113,8 @@ namespace Sapphire::World::Action
void buildActionResults();
void applyStatusEffect( bool isSelf, Entity::CharaPtr& target, Entity::CharaPtr& source, World::Action::StatusEntry& status, bool statusToSource = false );
void handleStatusEffects();
void handleJobAction();

View file

@ -26,4 +26,10 @@ const ActionEntry& ActionLut::getEntry( uint16_t actionId )
assert( it != m_actionLut.end() );
return it->second;
}
Sapphire::Common::StatusRefreshPolicy Sapphire::World::Action::getStatusRefreshPolicy( uint8_t statusRefreshPolicy, bool sameSource )
{
uint8_t policy = sameSource ? statusRefreshPolicy >> 4 : statusRefreshPolicy & 0x0F;
return static_cast< Sapphire::Common::StatusRefreshPolicy >( policy );
}

View file

@ -2,7 +2,6 @@
#include <cstdint>
#include <unordered_map>
#include <string>
#include <vector>
#include "Common.h"
@ -18,6 +17,8 @@ namespace Sapphire::World::Action
{
uint16_t id;
uint32_t duration;
uint32_t maxDuration;
uint8_t statusRefreshPolicy;
uint32_t flag;
std::vector< StatusModifier > modifiers;
};
@ -51,4 +52,6 @@ namespace Sapphire::World::Action
static Lut m_actionLut;
};
Sapphire::Common::StatusRefreshPolicy getStatusRefreshPolicy( uint8_t statusRefreshPolicy, bool sameSource );
}

View file

@ -33,6 +33,10 @@ namespace Sapphire::World::Action
{
j.at( "id" ).get_to( statusEntry.id );
j.at( "duration" ).get_to( statusEntry.duration );
if( j.contains( "maxDuration" ) )
j.at( "maxDuration" ).get_to( statusEntry.maxDuration );
if( j.contains( "statusRefreshPolicy" ) )
j.at( "statusRefreshPolicy" ).get_to( statusEntry.statusRefreshPolicy );
if( j.contains( "flag" ) )
j.at( "flag" ).get_to( statusEntry.flag );
if( j.contains( "modifiers" ) )