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:
parent
bf3368224a
commit
e442b735d3
8 changed files with 278 additions and 14 deletions
|
@ -940,6 +940,16 @@ namespace Sapphire::Common
|
||||||
RemoveOnSuccessfulHit = 4096
|
RemoveOnSuccessfulHit = 4096
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class StatusRefreshPolicy : uint8_t
|
||||||
|
{
|
||||||
|
Stack = 0,
|
||||||
|
ReplaceOrApply = 1,
|
||||||
|
Extend = 2,
|
||||||
|
ExtendOrApply = 3,
|
||||||
|
Reject = 4,
|
||||||
|
Custom = 255
|
||||||
|
};
|
||||||
|
|
||||||
enum struct ActionAspect : uint8_t
|
enum struct ActionAspect : uint8_t
|
||||||
{
|
{
|
||||||
None = 0, // Doesn't imply unaspected
|
None = 0, // Doesn't imply unaspected
|
||||||
|
|
|
@ -398,7 +398,18 @@
|
||||||
"restorePercentage": 0,
|
"restorePercentage": 0,
|
||||||
"nextCombo": [],
|
"nextCombo": [],
|
||||||
"statuses": {
|
"statuses": {
|
||||||
"caster": [],
|
"caster": [
|
||||||
|
{
|
||||||
|
"id": 83,
|
||||||
|
"duration": 20000,
|
||||||
|
"modifiers": [
|
||||||
|
{
|
||||||
|
"modifier": "DefensePercent",
|
||||||
|
"value": 20
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"target": []
|
"target": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -414,7 +425,18 @@
|
||||||
"nextCombo": [],
|
"nextCombo": [],
|
||||||
"statuses": {
|
"statuses": {
|
||||||
"caster": [],
|
"caster": [],
|
||||||
"target": []
|
"target": [
|
||||||
|
{
|
||||||
|
"id": 244,
|
||||||
|
"duration": 30000,
|
||||||
|
"modifiers": [
|
||||||
|
{
|
||||||
|
"modifier": "TickDamage",
|
||||||
|
"value": 20
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"34": {
|
"34": {
|
||||||
|
@ -478,7 +500,18 @@
|
||||||
45
|
45
|
||||||
],
|
],
|
||||||
"statuses": {
|
"statuses": {
|
||||||
"caster": [],
|
"caster": [
|
||||||
|
{
|
||||||
|
"id": 85,
|
||||||
|
"duration": 24000,
|
||||||
|
"modifiers": [
|
||||||
|
{
|
||||||
|
"modifier": "DamageDealtPercent",
|
||||||
|
"value": 20
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"target": []
|
"target": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -493,7 +526,18 @@
|
||||||
"restorePercentage": 0,
|
"restorePercentage": 0,
|
||||||
"nextCombo": [],
|
"nextCombo": [],
|
||||||
"statuses": {
|
"statuses": {
|
||||||
"caster": [],
|
"caster": [
|
||||||
|
{
|
||||||
|
"id": 86,
|
||||||
|
"duration": 20000,
|
||||||
|
"modifiers": [
|
||||||
|
{
|
||||||
|
"modifier": "AttackPowerPercent",
|
||||||
|
"value": 50
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"target": []
|
"target": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -523,7 +567,18 @@
|
||||||
"restorePercentage": 0,
|
"restorePercentage": 0,
|
||||||
"nextCombo": [],
|
"nextCombo": [],
|
||||||
"statuses": {
|
"statuses": {
|
||||||
"caster": [],
|
"caster": [
|
||||||
|
{
|
||||||
|
"id": 87,
|
||||||
|
"duration": 20000,
|
||||||
|
"modifiers": [
|
||||||
|
{
|
||||||
|
"modifier": "HPPercent",
|
||||||
|
"value": 20
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"target": []
|
"target": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -583,7 +638,18 @@
|
||||||
"restorePercentage": 0,
|
"restorePercentage": 0,
|
||||||
"nextCombo": [],
|
"nextCombo": [],
|
||||||
"statuses": {
|
"statuses": {
|
||||||
"caster": [],
|
"caster": [
|
||||||
|
{
|
||||||
|
"id": 89,
|
||||||
|
"duration": 20000,
|
||||||
|
"modifiers": [
|
||||||
|
{
|
||||||
|
"modifier": "ReflectPhysical",
|
||||||
|
"value": 50
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"target": []
|
"target": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -643,7 +709,34 @@
|
||||||
"restorePercentage": 0,
|
"restorePercentage": 0,
|
||||||
"nextCombo": [],
|
"nextCombo": [],
|
||||||
"statuses": {
|
"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": []
|
"target": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1176,7 +1269,19 @@
|
||||||
"restorePercentage": 0,
|
"restorePercentage": 0,
|
||||||
"nextCombo": [],
|
"nextCombo": [],
|
||||||
"statuses": {
|
"statuses": {
|
||||||
"caster": [],
|
"caster": [
|
||||||
|
{
|
||||||
|
"id": 116,
|
||||||
|
"duration": 10000,
|
||||||
|
"flag": 4096,
|
||||||
|
"modifiers": [
|
||||||
|
{
|
||||||
|
"modifier": "CriticalHitPercent",
|
||||||
|
"value": 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"target": []
|
"target": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -2394,7 +2499,18 @@
|
||||||
"nextCombo": [],
|
"nextCombo": [],
|
||||||
"statuses": {
|
"statuses": {
|
||||||
"caster": [],
|
"caster": [],
|
||||||
"target": []
|
"target": [
|
||||||
|
{
|
||||||
|
"id": 179,
|
||||||
|
"duration": 18000,
|
||||||
|
"modifiers": [
|
||||||
|
{
|
||||||
|
"modifier": "TickDamage",
|
||||||
|
"value": 40
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"165": {
|
"165": {
|
||||||
|
@ -2454,7 +2570,38 @@
|
||||||
"nextCombo": [],
|
"nextCombo": [],
|
||||||
"statuses": {
|
"statuses": {
|
||||||
"caster": [],
|
"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": {
|
"169": {
|
||||||
|
|
|
@ -41,6 +41,8 @@ struct StatusEntry
|
||||||
{
|
{
|
||||||
uint16_t id;
|
uint16_t id;
|
||||||
uint32_t duration;
|
uint32_t duration;
|
||||||
|
uint32_t maxDuration;
|
||||||
|
uint8_t statusRefreshPolicy;
|
||||||
uint32_t flag;
|
uint32_t flag;
|
||||||
std::vector< StatusModifier > modifiers;
|
std::vector< StatusModifier > modifiers;
|
||||||
};
|
};
|
||||||
|
@ -79,6 +81,8 @@ void to_json( nlohmann::ordered_json& j, const StatusEntry& statusEntry )
|
||||||
j = nlohmann::ordered_json{
|
j = nlohmann::ordered_json{
|
||||||
{ "id", statusEntry.id },
|
{ "id", statusEntry.id },
|
||||||
{ "duration", statusEntry.duration },
|
{ "duration", statusEntry.duration },
|
||||||
|
{ "maxDuration", statusEntry.maxDuration },
|
||||||
|
{ "statusRefreshPolicy", statusEntry.statusRefreshPolicy },
|
||||||
{ "flag", statusEntry.flag },
|
{ "flag", statusEntry.flag },
|
||||||
{ "modifiers", statusEntry.modifiers }
|
{ "modifiers", statusEntry.modifiers }
|
||||||
};
|
};
|
||||||
|
@ -98,7 +102,7 @@ void to_json( nlohmann::ordered_json& j, const ActionEntry& action )
|
||||||
{ "nextCombo", action.nextCombo },
|
{ "nextCombo", action.nextCombo },
|
||||||
{ "statuses", {
|
{ "statuses", {
|
||||||
{ "caster", action.statuses.caster },
|
{ "caster", action.statuses.caster },
|
||||||
{ "target", action.statuses.target },
|
{ "target", action.statuses.target }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -32,6 +32,8 @@
|
||||||
#include <Service.h>
|
#include <Service.h>
|
||||||
#include "WorldServer.h"
|
#include "WorldServer.h"
|
||||||
|
|
||||||
|
#include "StatusEffect/StatusEffect.h"
|
||||||
|
|
||||||
#include "Job/Warrior.h"
|
#include "Job/Warrior.h"
|
||||||
|
|
||||||
using namespace Sapphire;
|
using namespace Sapphire;
|
||||||
|
@ -607,6 +609,86 @@ void Action::Action::buildActionResults()
|
||||||
// m_effectBuilder.reset();
|
// 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()
|
void Action::Action::handleStatusEffects()
|
||||||
{
|
{
|
||||||
auto pActionBuilder = getActionResultBuilder();
|
auto pActionBuilder = getActionResultBuilder();
|
||||||
|
@ -622,7 +704,12 @@ void Action::Action::handleStatusEffects()
|
||||||
{
|
{
|
||||||
for( auto& status : m_lutEntry.statuses.caster )
|
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 )
|
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() )
|
if( !actor->getStatusEffectMap().empty() )
|
||||||
|
|
|
@ -113,6 +113,8 @@ namespace Sapphire::World::Action
|
||||||
|
|
||||||
void buildActionResults();
|
void buildActionResults();
|
||||||
|
|
||||||
|
void applyStatusEffect( bool isSelf, Entity::CharaPtr& target, Entity::CharaPtr& source, World::Action::StatusEntry& status, bool statusToSource = false );
|
||||||
|
|
||||||
void handleStatusEffects();
|
void handleStatusEffects();
|
||||||
|
|
||||||
void handleJobAction();
|
void handleJobAction();
|
||||||
|
|
|
@ -26,4 +26,10 @@ const ActionEntry& ActionLut::getEntry( uint16_t actionId )
|
||||||
assert( it != m_actionLut.end() );
|
assert( it != m_actionLut.end() );
|
||||||
|
|
||||||
return it->second;
|
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 );
|
||||||
}
|
}
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
|
||||||
|
@ -18,6 +17,8 @@ namespace Sapphire::World::Action
|
||||||
{
|
{
|
||||||
uint16_t id;
|
uint16_t id;
|
||||||
uint32_t duration;
|
uint32_t duration;
|
||||||
|
uint32_t maxDuration;
|
||||||
|
uint8_t statusRefreshPolicy;
|
||||||
uint32_t flag;
|
uint32_t flag;
|
||||||
std::vector< StatusModifier > modifiers;
|
std::vector< StatusModifier > modifiers;
|
||||||
};
|
};
|
||||||
|
@ -51,4 +52,6 @@ namespace Sapphire::World::Action
|
||||||
|
|
||||||
static Lut m_actionLut;
|
static Lut m_actionLut;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Sapphire::Common::StatusRefreshPolicy getStatusRefreshPolicy( uint8_t statusRefreshPolicy, bool sameSource );
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,10 @@ namespace Sapphire::World::Action
|
||||||
{
|
{
|
||||||
j.at( "id" ).get_to( statusEntry.id );
|
j.at( "id" ).get_to( statusEntry.id );
|
||||||
j.at( "duration" ).get_to( statusEntry.duration );
|
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" ) )
|
if( j.contains( "flag" ) )
|
||||||
j.at( "flag" ).get_to( statusEntry.flag );
|
j.at( "flag" ).get_to( statusEntry.flag );
|
||||||
if( j.contains( "modifiers" ) )
|
if( j.contains( "modifiers" ) )
|
||||||
|
|
Loading…
Add table
Reference in a new issue