1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-05-23 10:17:44 +00:00
This commit is contained in:
Mimi 2025-05-07 22:05:08 +02:00 committed by GitHub
commit 96e4afd200
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 498 additions and 79 deletions

View file

@ -504,6 +504,8 @@
{ {
"id": 85, "id": 85,
"duration": 24000, "duration": 24000,
"maxDuration": 60000,
"statusRefreshPolicy": 48,
"modifiers": [ "modifiers": [
{ {
"modifier": "DamageDealtPercent", "modifier": "DamageDealtPercent",
@ -2039,7 +2041,20 @@
"nextCombo": [], "nextCombo": [],
"statuses": { "statuses": {
"caster": [], "caster": [],
"target": [] "target": [
{
"id": 150,
"duration": 30000,
"statusRefreshPolicy": 17,
"flag": 8192,
"modifiers": [
{
"modifier": "TickHeal",
"value": 50
}
]
}
]
} }
}, },
"134": { "134": {
@ -2099,7 +2114,20 @@
"nextCombo": [], "nextCombo": [],
"statuses": { "statuses": {
"caster": [], "caster": [],
"target": [] "target": [
{
"id": 158,
"duration": 21000,
"statusRefreshPolicy": 17,
"flag": 8192,
"modifiers": [
{
"modifier": "TickHeal",
"value": 150
}
]
}
]
} }
}, },
"138": { "138": {

View file

@ -937,7 +937,18 @@ namespace Sapphire::Common
Invisibilty = 512, Invisibilty = 512,
CanStatusOff = 1024, CanStatusOff = 1024,
FcBuff = 2048, FcBuff = 2048,
RemoveOnSuccessfulHit = 4096 RemoveOnSuccessfulHit = 4096,
ReplaceSameCaster = 8192
};
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

View file

@ -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": {

View file

@ -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 }
} }
} }
}; };

View file

@ -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,89 @@ 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:
{
if( (status.flag & static_cast< uint32_t >( Common::StatusEffectFlag::ReplaceSameCaster ) && hasSameStatusFromSameCaster) || hasSameStatus )
pActionBuilder->replaceStatusEffect( referenceStatus, target, status.id, status.duration, 0, std::move( status.modifiers ), status.flag, statusToSource );
else
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 +707,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 +723,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() )

View file

@ -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();

View file

@ -27,3 +27,9 @@ const ActionEntry& ActionLut::getEntry( uint16_t actionId )
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 );
}

View file

@ -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 );
} }

View file

@ -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" ) )

View file

@ -9,6 +9,8 @@
#include "Actor/Player.h" #include "Actor/Player.h"
#include "StatusEffect/StatusEffect.h" #include "StatusEffect/StatusEffect.h"
#include "Network/Util/PacketUtil.h"
using namespace Sapphire; using namespace Sapphire;
using namespace Sapphire::Common; using namespace Sapphire::Common;
using namespace Sapphire::World::Action; using namespace Sapphire::World::Action;
@ -72,19 +74,21 @@ void ActionResult::applyStatusEffect( uint32_t id, int32_t duration, Entity::Cha
m_result.Arg2 = param; m_result.Arg2 = param;
m_result.Type = CalcResultType::TypeSetStatus; m_result.Type = CalcResultType::TypeSetStatus;
m_bOverrideStatus = shouldOverride; m_bShouldOverride = shouldOverride;
m_pStatus = Sapphire::StatusEffect::make_StatusEffect( id, source.getAsChara(), m_target, duration, 3000 ); m_pStatus = Sapphire::StatusEffect::make_StatusEffect( id, source.getAsChara(), m_target, duration, 3000 );
m_pStatus->setParam( param ); m_pStatus->setParam( param );
} }
void ActionResult::applyStatusEffect( uint32_t id, int32_t duration, Entity::Chara& source, uint8_t param, void ActionResult::applyStatusEffect( uint32_t id, int32_t duration, Entity::Chara& source, uint8_t param,
const std::vector< StatusModifier >& modifiers, uint32_t flag, bool shouldOverride ) const std::vector< StatusModifier >& modifiers, uint32_t flag, bool statusToSource, bool shouldOverride )
{ {
m_result.Value = static_cast< int16_t >( id ); m_result.Value = static_cast< int16_t >( id );
m_result.Arg2 = param; m_result.Arg2 = param;
m_result.Type = CalcResultType::TypeSetStatus; m_result.Type = statusToSource ? CalcResultType::TypeSetStatusMe : CalcResultType::TypeSetStatus;
if( statusToSource )
m_result.Flag = static_cast< uint8_t >( ActionResultFlag::EffectOnSource );
m_bOverrideStatus = shouldOverride; m_bShouldOverride = shouldOverride;
m_pStatus = Sapphire::StatusEffect::make_StatusEffect( id, source.getAsChara(), m_target, duration, modifiers, flag, 3000 ); m_pStatus = Sapphire::StatusEffect::make_StatusEffect( id, source.getAsChara(), m_target, duration, modifiers, flag, 3000 );
m_pStatus->setParam( param ); m_pStatus->setParam( param );
} }
@ -96,7 +100,7 @@ void ActionResult::applyStatusEffectSelf( uint32_t id, int32_t duration, uint8_t
m_result.Type = CalcResultType::TypeSetStatusMe; m_result.Type = CalcResultType::TypeSetStatusMe;
m_result.Flag = static_cast< uint8_t >( ActionResultFlag::EffectOnSource ); m_result.Flag = static_cast< uint8_t >( ActionResultFlag::EffectOnSource );
m_bOverrideStatus = shouldOverride; m_bShouldOverride = shouldOverride;
m_pStatus = Sapphire::StatusEffect::make_StatusEffect( id, m_target, m_target, duration, 3000 ); m_pStatus = Sapphire::StatusEffect::make_StatusEffect( id, m_target, m_target, duration, 3000 );
m_pStatus->setParam( param ); m_pStatus->setParam( param );
} }
@ -109,11 +113,27 @@ void ActionResult::applyStatusEffectSelf( uint32_t id, int32_t duration, uint8_t
m_result.Type = CalcResultType::TypeSetStatusMe; m_result.Type = CalcResultType::TypeSetStatusMe;
m_result.Flag = static_cast< uint8_t >( Common::ActionResultFlag::EffectOnSource ); m_result.Flag = static_cast< uint8_t >( Common::ActionResultFlag::EffectOnSource );
m_bOverrideStatus = shouldOverride; m_bShouldOverride = shouldOverride;
m_pStatus = Sapphire::StatusEffect::make_StatusEffect( id, m_target, m_target, duration, modifiers, flag, 3000 ); m_pStatus = Sapphire::StatusEffect::make_StatusEffect( id, m_target, m_target, duration, modifiers, flag, 3000 );
m_pStatus->setParam( param ); m_pStatus->setParam( param );
} }
void ActionResult::replaceStatusEffect( Sapphire::StatusEffect::StatusEffectPtr& pOldStatus, uint32_t id, int32_t duration, Entity::Chara& source, uint8_t param,
const std::vector< StatusModifier >& modifiers, uint32_t flag, bool statusToSource )
{
applyStatusEffect( id, duration, source, param, modifiers, flag, statusToSource, false );
m_pOldStatus = std::move( pOldStatus );
m_pStatus->setSlot( m_pOldStatus->getSlot() );
}
void ActionResult::replaceStatusEffectSelf( Sapphire::StatusEffect::StatusEffectPtr& pOldStatus, uint32_t id, int32_t duration, uint8_t param,
const std::vector< World::Action::StatusModifier >& modifiers, uint32_t flag )
{
applyStatusEffectSelf( id, duration, param, modifiers, flag, false );
m_pOldStatus = std::move( pOldStatus );
m_pStatus->setSlot( m_pOldStatus->getSlot() );
}
void ActionResult::mount( uint16_t mountId ) void ActionResult::mount( uint16_t mountId )
{ {
m_result.Value = static_cast< int16_t >( mountId ); m_result.Value = static_cast< int16_t >( mountId );
@ -161,7 +181,22 @@ void ActionResult::execute()
case CalcResultType::TypeSetStatus: case CalcResultType::TypeSetStatus:
case CalcResultType::TypeSetStatusMe: case CalcResultType::TypeSetStatusMe:
{ {
if( !m_bOverrideStatus ) for( auto const& entry : m_target->getStatusEffectMap() )
{
auto statusEffect = entry.second;
if( statusEffect->getId() == m_result.Value && m_bShouldOverride && statusEffect->getSrcActorId() == m_pStatus->getSrcActorId() )
{
statusEffect->refresh( m_pStatus->getDuration() );
m_pStatus->setSlot( statusEffect->getSlot() );
Network::Util::Packet::sendHudParam( *m_target );
return;
}
}
if( m_pOldStatus )
m_target->replaceSingleStatusEffect( m_pOldStatus->getSlot(), m_pStatus );
else if( !m_bShouldOverride )
m_target->addStatusEffectByIdIfNotExist( m_pStatus ); m_target->addStatusEffectByIdIfNotExist( m_pStatus );
else else
m_target->addStatusEffectById( m_pStatus ); m_target->addStatusEffectById( m_pStatus );

View file

@ -22,10 +22,14 @@ namespace Sapphire::World::Action
void comboSucceed(); void comboSucceed();
void applyStatusEffect( uint32_t id, int32_t duration, Entity::Chara& source, uint8_t param, bool shouldOverride ); void applyStatusEffect( uint32_t id, int32_t duration, Entity::Chara& source, uint8_t param, bool shouldOverride );
void applyStatusEffect( uint32_t id, int32_t duration, Entity::Chara& source, uint8_t param, void applyStatusEffect( uint32_t id, int32_t duration, Entity::Chara& source, uint8_t param,
const std::vector< World::Action::StatusModifier >& modifiers, uint32_t flag, bool shouldOverride ); const std::vector< World::Action::StatusModifier >& modifiers, uint32_t flag, bool statusToSource, bool shouldOverride );
void applyStatusEffectSelf( uint32_t id, int32_t duration, uint8_t param, bool shouldOverride ); void applyStatusEffectSelf( uint32_t id, int32_t duration, uint8_t param, bool shouldOverride );
void applyStatusEffectSelf( uint32_t id, int32_t duration, uint8_t param, const std::vector< World::Action::StatusModifier >& modifiers, void applyStatusEffectSelf( uint32_t id, int32_t duration, uint8_t param, const std::vector< World::Action::StatusModifier >& modifiers,
uint32_t flag, bool shouldOverride ); uint32_t flag, bool shouldOverride );
void replaceStatusEffect( Sapphire::StatusEffect::StatusEffectPtr& pOldStatus, uint32_t id, int32_t duration, Entity::Chara& source, uint8_t param,
const std::vector< StatusModifier >& modifiers, uint32_t flag, bool statusToSource );
void replaceStatusEffectSelf( Sapphire::StatusEffect::StatusEffectPtr& pOldStatus, uint32_t id, int32_t duration, uint8_t param,
const std::vector< World::Action::StatusModifier >& modifiers, uint32_t flag );
void mount( uint16_t mountId ); void mount( uint16_t mountId );
Entity::CharaPtr getTarget() const; Entity::CharaPtr getTarget() const;
@ -40,8 +44,9 @@ namespace Sapphire::World::Action
Common::CalcResultParam m_result; Common::CalcResultParam m_result;
bool m_bOverrideStatus { false }; bool m_bShouldOverride { false };
Sapphire::StatusEffect::StatusEffectPtr m_pStatus; Sapphire::StatusEffect::StatusEffectPtr m_pStatus;
Sapphire::StatusEffect::StatusEffectPtr m_pOldStatus;
}; };

View file

@ -1,4 +1,4 @@
#include "ActionResultBuilder.h" #include "ActionResultBuilder.h"
#include "ActionResult.h" #include "ActionResult.h"
#include <Actor/Player.h> #include <Actor/Player.h>
@ -94,10 +94,10 @@ void ActionResultBuilder::applyStatusEffect( Entity::CharaPtr& target, uint16_t
} }
void ActionResultBuilder::applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint32_t duration, uint8_t param, void ActionResultBuilder::applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint32_t duration, uint8_t param,
const std::vector< World::Action::StatusModifier >& modifiers, uint32_t flag, bool shouldOverride ) const std::vector< World::Action::StatusModifier >& modifiers, uint32_t flag, bool statusToSource, bool shouldOverride )
{ {
ActionResultPtr nextResult = make_ActionResult( target ); ActionResultPtr nextResult = make_ActionResult( target );
nextResult->applyStatusEffect( statusId, duration, *m_sourceChara, param, modifiers, flag, shouldOverride ); nextResult->applyStatusEffect( statusId, duration, *m_sourceChara, param, modifiers, flag, statusToSource, shouldOverride );
addResultToActor( target, nextResult ); addResultToActor( target, nextResult );
} }
@ -116,6 +116,22 @@ void ActionResultBuilder::applyStatusEffectSelf( uint16_t statusId, uint32_t dur
addResultToActor( m_sourceChara, nextResult ); addResultToActor( m_sourceChara, nextResult );
} }
void ActionResultBuilder::replaceStatusEffect( Sapphire::StatusEffect::StatusEffectPtr& pOldStatus, Entity::CharaPtr& target, uint16_t statusId, uint32_t duration, uint8_t param,
const std::vector< World::Action::StatusModifier >& modifiers, uint32_t flag, bool statusToSource )
{
ActionResultPtr nextResult = make_ActionResult( target );
nextResult->replaceStatusEffect( pOldStatus, statusId, duration, *m_sourceChara, param, modifiers, flag, statusToSource );
addResultToActor( target, nextResult );
}
void ActionResultBuilder::replaceStatusEffectSelf( Sapphire::StatusEffect::StatusEffectPtr& pOldStatus, uint16_t statusId, uint32_t duration, uint8_t param,
const std::vector< World::Action::StatusModifier >& modifiers, uint32_t flag )
{
ActionResultPtr nextResult = make_ActionResult( m_sourceChara );
nextResult->replaceStatusEffectSelf( pOldStatus, statusId, duration, param, modifiers, flag );
addResultToActor( m_sourceChara, nextResult );
}
void ActionResultBuilder::mount( Entity::CharaPtr& target, uint16_t mountId ) void ActionResultBuilder::mount( Entity::CharaPtr& target, uint16_t mountId )
{ {
ActionResultPtr nextResult = make_ActionResult( target ); ActionResultPtr nextResult = make_ActionResult( target );

View file

@ -28,11 +28,14 @@ namespace Sapphire::World::Action
void applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint32_t duration, uint8_t param, bool shouldOverride = false ); void applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint32_t duration, uint8_t param, bool shouldOverride = false );
void applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint32_t duration, uint8_t param, void applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint32_t duration, uint8_t param,
const std::vector< World::Action::StatusModifier >& modifiers, uint32_t flag = 0, bool shouldOverride = false ); const std::vector< World::Action::StatusModifier >& modifiers, uint32_t flag = 0, bool statusToSource = false, bool shouldOverride = false );
void applyStatusEffectSelf( uint16_t statusId, uint32_t duration, uint8_t param, bool shouldOverride = false ); void applyStatusEffectSelf( uint16_t statusId, uint32_t duration, uint8_t param, bool shouldOverride = false );
void applyStatusEffectSelf( uint16_t statusId, uint32_t duration, uint8_t param, const std::vector< World::Action::StatusModifier >& modifiers, void applyStatusEffectSelf( uint16_t statusId, uint32_t duration, uint8_t param, const std::vector< World::Action::StatusModifier >& modifiers,
uint32_t flag = 0, bool shouldOverride = false ); uint32_t flag = 0, bool shouldOverride = false );
void replaceStatusEffect( Sapphire::StatusEffect::StatusEffectPtr& pOldStatus, Entity::CharaPtr& target, uint16_t statusId, uint32_t duration, uint8_t param,
const std::vector< StatusModifier >& modifiers, uint32_t flag = 0, bool statusToSource = false );
void replaceStatusEffectSelf( Sapphire::StatusEffect::StatusEffectPtr& pOldStatus, uint16_t statusId, uint32_t duration, uint8_t param,
const std::vector< World::Action::StatusModifier >& modifiers, uint32_t flag = 0 );
void mount( Entity::CharaPtr& target, uint16_t mountId ); void mount( Entity::CharaPtr& target, uint16_t mountId );
void sendActionResults( const std::vector< Entity::CharaPtr >& targetList ); void sendActionResults( const std::vector< Entity::CharaPtr >& targetList );

View file

@ -3,6 +3,7 @@
#include <Action/CommonAction.h> #include <Action/CommonAction.h>
#include <Action/Action.h> #include <Action/Action.h>
#include <Actor/Player.h> #include <Actor/Player.h>
#include <StatusEffect/StatusEffect.h>
using namespace Sapphire; using namespace Sapphire;
using namespace Sapphire::World::Action; using namespace Sapphire::World::Action;
@ -29,6 +30,7 @@ void Warrior::onAction( Entity::Player& player, Action& action )
void Warrior::handleWrath( Entity::Player& player, Action& action ) void Warrior::handleWrath( Entity::Player& player, Action& action )
{ {
Sapphire::StatusEffect::StatusEffectPtr oldStatus = nullptr;
auto effectToApply = Wrath; auto effectToApply = Wrath;
auto parry = 2; auto parry = 2;
auto asChara = player.getAsChara(); auto asChara = player.getAsChara();
@ -38,33 +40,45 @@ void Warrior::handleWrath( Entity::Player& player, Action& action )
if( !pActionBuilder ) if( !pActionBuilder )
return; return;
if( player.hasStatusEffect( Wrath ) ) auto statusMap = player.getStatusEffectMap();
for( const auto& effectIt : statusMap )
{ {
player.replaceSingleStatusEffectById( Wrath ); if( effectIt.second->getId() == Wrath )
{
oldStatus = effectIt.second;
effectToApply = WrathII; effectToApply = WrathII;
parry += 2; parry += 2;
break;
} }
else if( player.hasStatusEffect( WrathII ) ) else if( effectIt.second->getId() == WrathII )
{ {
player.replaceSingleStatusEffectById( WrathII ); oldStatus = effectIt.second;
effectToApply = WrathIII; effectToApply = WrathIII;
parry += 2; parry += 2;
break;
} }
else if( player.hasStatusEffect( WrathIII ) ) else if( effectIt.second->getId() == WrathIII )
{ {
player.replaceSingleStatusEffectById( WrathIII ); oldStatus = effectIt.second;
effectToApply = WrathIV; effectToApply = WrathIV;
parry += 2; parry += 2;
break;
} }
else if( player.hasStatusEffect( WrathIV ) ) else if( effectIt.second->getId() == WrathIV )
{ {
player.replaceSingleStatusEffectById( WrathIV ); oldStatus = effectIt.second;
effectToApply = Infuriated; effectToApply = Infuriated;
parry += 2; parry += 2;
break;
}
} }
if( !player.hasStatusEffect( Infuriated ) ) if( !player.hasStatusEffect( Infuriated ) )
{ {
pActionBuilder->applyStatusEffectSelf( effectToApply, 30000, 0, { StatusModifier{ Common::ParamModifier::ParryPercent, parry } }, 0, false ); if( oldStatus )
pActionBuilder->replaceStatusEffectSelf( oldStatus, effectToApply, 30000, 0, { StatusModifier{ Common::ParamModifier::ParryPercent, parry } } );
else
pActionBuilder->applyStatusEffectSelf( effectToApply, 30000, 0, { StatusModifier{ Common::ParamModifier::ParryPercent, parry } } );
} }
} }

View file

@ -49,12 +49,6 @@ Chara::Chara( ObjKind type ) :
m_lastAttack = Common::Util::getTimeMs(); m_lastAttack = Common::Util::getTimeMs();
m_bonusStats.fill( 0 ); m_bonusStats.fill( 0 );
// initialize the free slot queue
for( uint8_t i = 0; i < MAX_STATUS_EFFECTS; i++ )
{
m_statusEffectFreeSlotQueue.push( i );
}
} }
Chara::~Chara() = default; Chara::~Chara() = default;
@ -521,18 +515,31 @@ int8_t Chara::getStatusEffectFreeSlot()
{ {
int8_t freeEffectSlot = -1; int8_t freeEffectSlot = -1;
if( m_statusEffectFreeSlotQueue.empty() ) if( m_statusEffectSlots.size() >= MAX_STATUS_EFFECTS )
return freeEffectSlot; return freeEffectSlot;
freeEffectSlot = static_cast< int8_t >( m_statusEffectFreeSlotQueue.front() ); if( m_statusEffectSlots.empty() )
m_statusEffectFreeSlotQueue.pop(); freeEffectSlot = 0;
else
freeEffectSlot = static_cast< int8_t >( *m_statusEffectSlots.rbegin() + 1 );
m_statusEffectSlots.insert( freeEffectSlot );
Logger::warn( "Slot id being added: {}", freeEffectSlot );
return freeEffectSlot; return freeEffectSlot;
} }
void Chara::statusEffectFreeSlot( uint8_t slotId ) void Chara::statusEffectFreeSlot( uint8_t slotId )
{ {
m_statusEffectFreeSlotQueue.push( slotId ); m_statusEffectSlots.erase( slotId );
}
void Chara::replaceSingleStatusEffect( uint32_t slotId, StatusEffect::StatusEffectPtr pStatus )
{
pStatus->setSlot( slotId );
m_statusEffectMap[ slotId ] = pStatus;
pStatus->applyStatus();
} }
void Chara::replaceSingleStatusEffectById( uint32_t id ) void Chara::replaceSingleStatusEffectById( uint32_t id )
@ -588,7 +595,7 @@ void Chara::removeStatusEffectByFlag( Common::StatusEffectFlag flag )
} }
} }
std::map< uint8_t, Sapphire::StatusEffect::StatusEffectPtr >::iterator Chara::removeStatusEffect( uint8_t effectSlotId, bool sendOrder ) std::map< uint8_t, Sapphire::StatusEffect::StatusEffectPtr >::iterator Chara::removeStatusEffect( uint8_t effectSlotId, bool updateStatus )
{ {
auto pEffectIt = m_statusEffectMap.find( effectSlotId ); auto pEffectIt = m_statusEffectMap.find( effectSlotId );
if( pEffectIt == m_statusEffectMap.end() ) if( pEffectIt == m_statusEffectMap.end() )
@ -599,11 +606,35 @@ std::map< uint8_t, Sapphire::StatusEffect::StatusEffectPtr >::iterator Chara::re
auto pEffect = pEffectIt->second; auto pEffect = pEffectIt->second;
pEffect->removeStatus(); pEffect->removeStatus();
if( sendOrder ) if( updateStatus )
{
Network::Util::Packet::sendActorControl( getInRangePlayerIds( isPlayer() ), getId(), StatusEffectLose, pEffect->getId() ); Network::Util::Packet::sendActorControl( getInRangePlayerIds( isPlayer() ), getId(), StatusEffectLose, pEffect->getId() );
Network::Util::Packet::sendHudParam( *this );
}
auto it = m_statusEffectMap.erase( pEffectIt ); auto it = m_statusEffectMap.erase( pEffectIt );
Network::Util::Packet::sendHudParam( *this );
for( auto effectIt = it; effectIt != m_statusEffectMap.end(); )
{
// if the status is *after* the one being removed, shift the slots down by one
auto shifted_slot = effectIt->first - 1;
auto node_slot = m_statusEffectSlots.extract( effectIt->first );
node_slot.value() = shifted_slot;
m_statusEffectSlots.insert( std::move( node_slot ) );
auto node_status = m_statusEffectMap.extract( effectIt->first );
node_status.key() = shifted_slot;
m_statusEffectMap.insert( std::move( node_status ) );
effectIt->second->setSlot( effectIt->second->getSlot() - 1 );
Logger::warn( "Shifted slot {} to slot: {}", effectSlotId, shifted_slot );
++effectIt;
}
Logger::warn( "Slot id being freed: {}", effectSlotId );
return it; return it;
} }

View file

@ -86,9 +86,9 @@ namespace Sapphire::Entity
/*! Status effects */ /*! Status effects */
const uint8_t MAX_STATUS_EFFECTS = 30; const uint8_t MAX_STATUS_EFFECTS = 30;
std::queue< uint8_t > m_statusEffectFreeSlotQueue;
std::vector< std::pair< uint8_t, uint32_t > > m_statusEffectList; std::vector< std::pair< uint8_t, uint32_t > > m_statusEffectList;
std::map< uint8_t, StatusEffect::StatusEffectPtr > m_statusEffectMap; std::map< uint8_t, StatusEffect::StatusEffectPtr > m_statusEffectMap;
std::set< uint8_t > m_statusEffectSlots;
/*! Detour Crowd AgentId */ /*! Detour Crowd AgentId */
uint32_t m_agentId; uint32_t m_agentId;
@ -108,7 +108,9 @@ namespace Sapphire::Entity
/// Status effect functions /// Status effect functions
void addStatusEffect( StatusEffect::StatusEffectPtr pEffect ); void addStatusEffect( StatusEffect::StatusEffectPtr pEffect );
std::map< uint8_t, StatusEffect::StatusEffectPtr >::iterator removeStatusEffect( uint8_t effectSlotId, bool sendOrder = true ); std::map< uint8_t, StatusEffect::StatusEffectPtr >::iterator removeStatusEffect( uint8_t effectSlotId, bool updateStatus = true );
void replaceSingleStatusEffect( uint32_t slotId, StatusEffect::StatusEffectPtr pStatus );
void replaceSingleStatusEffectById( uint32_t id ); void replaceSingleStatusEffectById( uint32_t id );

View file

@ -147,6 +147,8 @@ void Sapphire::StatusEffect::StatusEffect::applyStatus()
m_startTime = Util::getTimeMs(); m_startTime = Util::getTimeMs();
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref(); auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
if( m_modifiers.empty() )
{
for( const auto& mod : m_statusModifiers ) for( const auto& mod : m_statusModifiers )
{ {
if( mod.modifier != Common::ParamModifier::TickDamage && mod.modifier != Common::ParamModifier::TickHeal ) if( mod.modifier != Common::ParamModifier::TickDamage && mod.modifier != Common::ParamModifier::TickHeal )
@ -156,6 +158,7 @@ void Sapphire::StatusEffect::StatusEffect::applyStatus()
else if( mod.modifier == Common::ParamModifier::TickHeal ) else if( mod.modifier == Common::ParamModifier::TickHeal )
registerTickEffect( mod.modifier, mod.value ); registerTickEffect( mod.modifier, mod.value );
} }
}
m_targetActor->calculateStats(); m_targetActor->calculateStats();
@ -237,3 +240,14 @@ void Sapphire::StatusEffect::StatusEffect::setSlot( uint8_t slot )
{ {
m_slot = slot; m_slot = slot;
} }
void Sapphire::StatusEffect::StatusEffect::refresh()
{
applyStatus();
}
void Sapphire::StatusEffect::StatusEffect::refresh( uint32_t newDuration )
{
m_duration = newDuration;
refresh();
}

View file

@ -67,6 +67,8 @@ public:
uint8_t getSlot() const; uint8_t getSlot() const;
void setSlot( uint8_t slot ); void setSlot( uint8_t slot );
void refresh( uint32_t newDuration );
private: private:
uint32_t m_id; uint32_t m_id;
Entity::CharaPtr m_sourceActor; Entity::CharaPtr m_sourceActor;
@ -83,6 +85,7 @@ private:
std::unordered_map< Common::ParamModifier, int32_t > m_modifiers; std::unordered_map< Common::ParamModifier, int32_t > m_modifiers;
uint8_t m_slot; uint8_t m_slot;
void refresh();
}; };
} }