1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-05-08 19:57:46 +00:00

Merge pull request #919 from Skyliegirl33/actions-war

[3.x] More action work, begin implementing jobs
This commit is contained in:
Mordred 2023-03-08 21:49:57 +01:00 committed by GitHub
commit 4870b0b771
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 352 additions and 16 deletions

View file

@ -506,7 +506,7 @@
"duration": 24000, "duration": 24000,
"modifiers": [ "modifiers": [
{ {
"modifier": "AttackPowerPercent", "modifier": "DamageDealtPercent",
"value": 20 "value": 20
} }
] ]
@ -698,7 +698,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": []
} }
}, },
@ -758,7 +785,12 @@
"restorePercentage": 0, "restorePercentage": 0,
"nextCombo": [], "nextCombo": [],
"statuses": { "statuses": {
"caster": [], "caster": [
{
"id": 97,
"duration": 30000
}
],
"target": [] "target": []
} }
}, },

View file

@ -910,7 +910,14 @@ namespace Sapphire::Common
CriticalHitPowerPercent = 1020, CriticalHitPowerPercent = 1020,
CriticalHitResiliencePercent = 1021, CriticalHitResiliencePercent = 1021,
CriticalHitPercent = 1022, CriticalHitPercent = 1022,
EnmityPercent = 1023 EnmityPercent = 1023,
DamageDealtPercent = 1024,
DamageTakenPercent = 1025,
HealingMagicRecoveryPercent = 1026,
SlashingResistancePercent = 1027,
PiercingResistancePercent = 1028,
BluntResistancePercent = 1029,
ProjectileResistancePercent = 1030
}; };
enum struct ActionAspect : uint8_t enum struct ActionAspect : uint8_t
@ -931,6 +938,7 @@ namespace Sapphire::Common
MagicPoints = 3, MagicPoints = 3,
TacticsPoints = 5, TacticsPoints = 5,
TacticsPoints1 = 6, TacticsPoints1 = 6,
StatusEffect = 10,
Sprint = 18, Sprint = 18,
// WARGauge = 22, // WARGauge = 22,
// DRKGauge = 25, // DRKGauge = 25,

View file

@ -0,0 +1,45 @@
#include <Script/NativeScriptApi.h>
#include <ScriptObject.h>
#include <Actor/Player.h>
#include <Action/CommonAction.h>
#include <Action/Action.h>
using namespace Sapphire;
using namespace Sapphire::World::Action;
class ActionInnerBeast : public Sapphire::ScriptAPI::ActionScript
{
public:
ActionInnerBeast() : Sapphire::ScriptAPI::ActionScript( 49 )
{
}
static constexpr auto Potency = 300;
void onExecute( Sapphire::World::Action::Action& action ) override
{
auto pPlayer = action.getSourceChara()->getAsPlayer();
auto pSource = action.getSourceChara();
auto pTarget = action.getHitChara();
if( !pPlayer )
return;
if( !pPlayer->hasStatusEffect( Unchained ) )
pPlayer->delModifier( Common::ParamModifier::DamageDealtPercent, -25 );
auto dmg = action.calcDamage( Potency );
action.getEffectbuilder()->damage( pSource, pTarget, dmg.first, dmg.second );
action.getEffectbuilder()->heal( pTarget, pSource, dmg.first, Common::ActionHitSeverityType::NormalHeal,
Common::ActionEffectResultFlag::EffectOnSource );
action.applyStatusEffectSelf( InnerBeast );
pPlayer->addStatusEffectByIdIfNotExist( InnerBeast, 15000, *pSource,
{ StatusModifier{ Common::ParamModifier::DamageTakenPercent, -20 } } );
if( !pPlayer->hasStatusEffect( Unchained ) )
pPlayer->addModifier( Common::ParamModifier::DamageDealtPercent, -25 );
}
};
EXPOSE_SCRIPT( ActionInnerBeast );

View file

@ -0,0 +1,31 @@
#include <Script/NativeScriptApi.h>
#include <ScriptObject.h>
#include <Actor/Player.h>
#include <Action/CommonAction.h>
#include <Action/Action.h>
using namespace Sapphire;
using namespace Sapphire::World::Action;
class ActionUnchained : public Sapphire::ScriptAPI::ActionScript
{
public:
ActionUnchained() : Sapphire::ScriptAPI::ActionScript( 50 )
{
}
void onExecute( Sapphire::World::Action::Action& action ) override
{
auto pPlayer = action.getSourceChara()->getAsPlayer();
if( !pPlayer )
return;
pPlayer->delModifier( Common::ParamModifier::DamageDealtPercent, -25 );
action.applyStatusEffectSelf( Unchained );
pPlayer->addStatusEffectByIdIfNotExist( Unchained, 20000, *pPlayer->getAsChara() );
}
};
EXPOSE_SCRIPT( ActionUnchained );

View file

@ -0,0 +1,22 @@
#include <Script/NativeScriptApi.h>
#include <ScriptObject.h>
#include <Actor/Player.h>
#include <Action/CommonAction.h>
using namespace Sapphire;
using namespace Sapphire::World::Action;
class StatusEffectDefiance : public Sapphire::ScriptAPI::StatusEffectScript
{
public:
StatusEffectDefiance() : Sapphire::ScriptAPI::StatusEffectScript( 91 )
{
}
void onExpire( Entity::Chara& actor ) override
{
actor.removeSingleStatusEffectById( Unchained );
}
};
EXPOSE_SCRIPT( StatusEffectDefiance );

View file

@ -0,0 +1,23 @@
#include <Script/NativeScriptApi.h>
#include <ScriptObject.h>
#include <Actor/Player.h>
#include <Action/CommonAction.h>
using namespace Sapphire;
using namespace Sapphire::World::Action;
class StatusEffectUnchained : public Sapphire::ScriptAPI::StatusEffectScript
{
public:
StatusEffectUnchained() : Sapphire::ScriptAPI::StatusEffectScript( 92 )
{
}
void onExpire( Entity::Chara& actor ) override
{
if( actor.hasStatusEffect( Defiance ) )
actor.addModifier( Common::ParamModifier::DamageDealtPercent, -25 );
}
};
EXPOSE_SCRIPT( StatusEffectUnchained );

View file

@ -31,6 +31,8 @@
#include <Service.h> #include <Service.h>
#include "WorldServer.h" #include "WorldServer.h"
#include "Job/Warrior.h"
using namespace Sapphire; using namespace Sapphire;
using namespace Sapphire::Common; using namespace Sapphire::Common;
using namespace Sapphire::Network; using namespace Sapphire::Network;
@ -515,8 +517,9 @@ void Action::Action::handleAction()
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref(); auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
auto hasLutEntry = hasValidLutEntry(); auto hasLutEntry = hasValidLutEntry();
auto hasScript = scriptMgr.onExecute( *this );
if( !scriptMgr.onExecute( *this ) && !hasLutEntry ) if( !hasScript && !hasLutEntry )
{ {
if( auto player = m_pSource->getAsPlayer() ) if( auto player = m_pSource->getAsPlayer() )
{ {
@ -526,9 +529,12 @@ void Action::Action::handleAction()
return; return;
} }
if( !hasScript )
m_enableGenericHandler = true;
Network::Util::Player::sendHudParam( *m_pSource->getAsPlayer() ); Network::Util::Player::sendHudParam( *m_pSource->getAsPlayer() );
if( !hasLutEntry || m_hitActors.empty() ) if( !m_enableGenericHandler || !hasLutEntry || m_hitActors.empty() )
{ {
// send any effect packet added by script or an empty one just to play animation for other players // send any effect packet added by script or an empty one just to play animation for other players
m_effectBuilder->buildAndSendPackets( m_hitActors ); m_effectBuilder->buildAndSendPackets( m_hitActors );
@ -604,6 +610,8 @@ void Action::Action::handleAction()
if( m_lutEntry.statuses.caster.size() > 0 || m_lutEntry.statuses.target.size() > 0 ) if( m_lutEntry.statuses.caster.size() > 0 || m_lutEntry.statuses.target.size() > 0 )
handleStatusEffects(); handleStatusEffects();
handleJobAction();
m_effectBuilder->buildAndSendPackets( m_hitActors ); m_effectBuilder->buildAndSendPackets( m_hitActors );
// TODO: disabled, reset kills our queued actions // TODO: disabled, reset kills our queued actions
@ -643,6 +651,18 @@ void Action::Action::handleStatusEffects()
} }
} }
void Action::Action::handleJobAction()
{
switch( m_pSource->getClass() )
{
case ClassJob::Warrior:
{
Warrior::onAction( *m_pSource->getAsPlayer(), *this );
break;
}
}
}
bool Action::Action::preCheck() bool Action::Action::preCheck()
{ {
if( auto player = m_pSource->getAsPlayer() ) if( auto player = m_pSource->getAsPlayer() )
@ -767,6 +787,17 @@ bool Action::Action::primaryCostCheck( bool subtractCosts )
return true; return true;
} }
case Common::ActionPrimaryCostType::StatusEffect:
{
if( !m_pSource->hasStatusEffect( m_primaryCost ) )
return false;
if( subtractCosts )
m_pSource->removeSingleStatusEffectById( m_primaryCost );
return true;
}
// free casts, likely just pure ogcds // free casts, likely just pure ogcds
case Common::ActionPrimaryCostType::None: case Common::ActionPrimaryCostType::None:
{ {
@ -935,3 +966,8 @@ uint64_t Action::Action::getCastTimeRest() const
{ {
return m_castTimeRestMs; return m_castTimeRestMs;
} }
void Action::Action::enableGenericHandler()
{
m_enableGenericHandler = true;
}

View file

@ -53,6 +53,8 @@ namespace Sapphire::World::Action
uint64_t getCastTimeRest() const; uint64_t getCastTimeRest() const;
void enableGenericHandler();
/*! /*!
* @brief Checks if a chara has enough resources available to cast the action (tp/mp/etc) * @brief Checks if a chara has enough resources available to cast the action (tp/mp/etc)
* @return true if they have the required resources * @return true if they have the required resources
@ -111,6 +113,8 @@ namespace Sapphire::World::Action
void handleStatusEffects(); void handleStatusEffects();
void handleJobAction();
/*! /*!
* @brief Adds an actor filter to this action. * @brief Adds an actor filter to this action.
* @param filter The ptr to the ActorFilter to add * @param filter The ptr to the ActorFilter to add
@ -205,6 +209,7 @@ namespace Sapphire::World::Action
bool m_canTargetFriendly{}; bool m_canTargetFriendly{};
bool m_canTargetHostile{}; bool m_canTargetHostile{};
bool m_canTargetDead{}; bool m_canTargetDead{};
bool m_enableGenericHandler{};
Common::ActionInterruptType m_interruptType; Common::ActionInterruptType m_interruptType;

View file

@ -108,7 +108,14 @@ std::unordered_map< std::string, Common::ParamModifier > ActionLutData::m_modifi
{ "CriticalHitPowerPercent", Common::ParamModifier::CriticalHitPowerPercent }, { "CriticalHitPowerPercent", Common::ParamModifier::CriticalHitPowerPercent },
{ "CriticalHitResiliencePercent", Common::ParamModifier::CriticalHitResiliencePercent }, { "CriticalHitResiliencePercent", Common::ParamModifier::CriticalHitResiliencePercent },
{ "CriticalHitPercent", Common::ParamModifier::CriticalHitPercent }, { "CriticalHitPercent", Common::ParamModifier::CriticalHitPercent },
{ "EnmityPercent", Common::ParamModifier::EnmityPercent } { "EnmityPercent", Common::ParamModifier::EnmityPercent },
{ "DamageDealtPercent", Common::ParamModifier::DamageDealtPercent },
{ "DamageTakenPercent", Common::ParamModifier::DamageTakenPercent },
{ "HealingMagicRecoveryPercent", Common::ParamModifier::HealingMagicRecoveryPercent },
{ "SlashingResistancePercent", Common::ParamModifier::SlashingResistancePercent },
{ "PiercingResistancePercent", Common::ParamModifier::PiercingResistancePercent },
{ "BluntResistancePercent", Common::ParamModifier::BluntResistancePercent },
{ "ProjectileResistancePercent", Common::ParamModifier::ProjectileResistancePercent },
}; };
bool ActionLutData::cacheActions() bool ActionLutData::cacheActions()

View file

@ -0,0 +1,30 @@
#pragma once
#include <cstdint>
#include <vector>
#include <string>
namespace Sapphire::World::Action
{
enum ActionSkill
{
SkullSunder = 35,
Maim = 37,
StormsPath = 42,
StormsEye = 45,
ButchersBlock = 47
};
enum ActionStatus
{
Defiance = 91,
Unchained = 92,
Wrath = 93,
WrathII = 94,
WrathIII = 95,
WrathIV = 96,
Infuriated = 97,
InnerBeast = 411,
Deliverance = 729
};
}

View file

@ -0,0 +1,61 @@
#include "Warrior.h"
#include <Action/CommonAction.h>
#include <Action/Action.h>
#include <Actor/Player.h>
using namespace Sapphire;
using namespace Sapphire::World::Action;
void Warrior::onAction( Entity::Player& player, Action& action )
{
switch( action.getId() )
{
case Maim:
case StormsEye:
case StormsPath:
case SkullSunder:
case ButchersBlock:
{
if( action.isComboAction() && !action.isCorrectCombo() )
break;
if( player.hasStatusEffect( Defiance ) )
handleWrath( player, action );
break;
}
}
}
void Warrior::handleWrath( Entity::Player& player, Action& action )
{
auto effectToApply = Wrath;
auto asChara = player.getAsChara();
if( player.hasStatusEffect( Wrath ) )
{
player.replaceSingleStatusEffectById( Wrath );
effectToApply = WrathII;
}
else if( player.hasStatusEffect( WrathII ) )
{
player.replaceSingleStatusEffectById( WrathII );
effectToApply = WrathIII;
}
else if( player.hasStatusEffect( WrathIII ) )
{
player.replaceSingleStatusEffectById( WrathIII );
effectToApply = WrathIV;
}
else if( player.hasStatusEffect( WrathIV ) )
{
player.replaceSingleStatusEffectById( WrathIV );
effectToApply = Infuriated;
}
if( !player.hasStatusEffect( Infuriated ) )
{
action.applyStatusEffectSelf( effectToApply );
player.addStatusEffectByIdIfNotExist( effectToApply, 30000, *asChara );
}
}

View file

@ -0,0 +1,20 @@
#pragma once
#include <Common.h>
#include "ForwardsZone.h"
#include <cstdint>
#include <unordered_map>
#include <string>
#include <vector>
namespace Sapphire::World::Action
{
class Warrior
{
public:
static void onAction( Entity::Player& player, Action& action );
private:
static void handleWrath( Entity::Player& player, Action& action );
};
}

View file

@ -562,7 +562,7 @@ void Sapphire::Entity::Chara::addStatusEffectByIdIfNotExist( uint32_t id, int32_
} }
void Sapphire::Entity::Chara::addStatusEffectByIdIfNotExist( uint32_t id, int32_t duration, Entity::Chara& source, void Sapphire::Entity::Chara::addStatusEffectByIdIfNotExist( uint32_t id, int32_t duration, Entity::Chara& source,
std::vector< World::Action::StatusModifier >& modifiers, uint16_t param ) std::vector< World::Action::StatusModifier > modifiers, uint16_t param )
{ {
if( hasStatusEffect( id ) ) if( hasStatusEffect( id ) )
return; return;
@ -590,6 +590,18 @@ void Sapphire::Entity::Chara::statusEffectFreeSlot( uint8_t slotId )
m_statusEffectFreeSlotQueue.push( slotId ); m_statusEffectFreeSlotQueue.push( slotId );
} }
void Sapphire::Entity::Chara::replaceSingleStatusEffectById( uint32_t id )
{
for( const auto& effectIt : m_statusEffectMap )
{
if( effectIt.second->getId() == id )
{
removeStatusEffect( effectIt.first, false );
break;
}
}
}
void Sapphire::Entity::Chara::removeSingleStatusEffectById( uint32_t id ) void Sapphire::Entity::Chara::removeSingleStatusEffectById( uint32_t id )
{ {
for( const auto& effectIt : m_statusEffectMap ) for( const auto& effectIt : m_statusEffectMap )
@ -602,7 +614,7 @@ void Sapphire::Entity::Chara::removeSingleStatusEffectById( uint32_t id )
} }
} }
std::map< uint8_t, Sapphire::StatusEffect::StatusEffectPtr >::iterator Sapphire::Entity::Chara::removeStatusEffect( uint8_t effectSlotId ) std::map< uint8_t, Sapphire::StatusEffect::StatusEffectPtr >::iterator Sapphire::Entity::Chara::removeStatusEffect( uint8_t effectSlotId, bool sendOrder )
{ {
auto pEffectIt = m_statusEffectMap.find( effectSlotId ); auto pEffectIt = m_statusEffectMap.find( effectSlotId );
if( pEffectIt == m_statusEffectMap.end() ) if( pEffectIt == m_statusEffectMap.end() )
@ -613,7 +625,8 @@ std::map< uint8_t, Sapphire::StatusEffect::StatusEffectPtr >::iterator Sapphire:
auto pEffect = pEffectIt->second; auto pEffect = pEffectIt->second;
pEffect->removeStatus(); pEffect->removeStatus();
server().queueForPlayers( getInRangePlayerIds( isPlayer() ), makeActorControl( getId(), StatusEffectLose, pEffect->getId() ) ); if( sendOrder )
server().queueForPlayers( getInRangePlayerIds( isPlayer() ), makeActorControl( getId(), StatusEffectLose, pEffect->getId() ) );
auto it = m_statusEffectMap.erase( pEffectIt ); auto it = m_statusEffectMap.erase( pEffectIt );
@ -829,7 +842,9 @@ void Sapphire::Entity::Chara::addModifier( Common::ParamModifier paramModifier,
void Sapphire::Entity::Chara::delModifier( Common::ParamModifier paramModifier, int32_t value ) void Sapphire::Entity::Chara::delModifier( Common::ParamModifier paramModifier, int32_t value )
{ {
assert( m_modifiers.count( paramModifier ) != 0 ); if( m_modifiers.find( paramModifier ) == m_modifiers.end() )
return;
auto& mod = m_modifiers.at( paramModifier ); auto& mod = m_modifiers.at( paramModifier );
mod.erase( std::remove( mod.begin(), mod.end(), value ), mod.end() ); mod.erase( std::remove( mod.begin(), mod.end(), value ), mod.end() );

View file

@ -111,7 +111,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 ); std::map< uint8_t, StatusEffect::StatusEffectPtr >::iterator removeStatusEffect( uint8_t effectSlotId, bool sendOrder = true );
void replaceSingleStatusEffectById( uint32_t id );
void removeSingleStatusEffectById( uint32_t id ); void removeSingleStatusEffectById( uint32_t id );
@ -140,7 +142,8 @@ namespace Sapphire::Entity
void addStatusEffectById( uint32_t id, int32_t duration, Entity::Chara& source, uint16_t param = 0 ); void addStatusEffectById( uint32_t id, int32_t duration, Entity::Chara& source, uint16_t param = 0 );
// add a status effect by id if it doesn't exist // add a status effect by id if it doesn't exist
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, 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, std::vector< World::Action::StatusModifier > modifiers,
uint16_t param = 0 );
// remove a status effect by id // remove a status effect by id
void removeSingleStatusEffectFromId( uint32_t id ); void removeSingleStatusEffectFromId( uint32_t id );

View file

@ -7,6 +7,7 @@ file( GLOB SERVER_SOURCE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
*.cpp *.cpp
Actor/*.cpp Actor/*.cpp
Action/*.cpp Action/*.cpp
Action/Job/*.cpp
ContentFinder/*.cpp ContentFinder/*.cpp
DebugCommand/*.cpp DebugCommand/*.cpp
Event/*.cpp Event/*.cpp

View file

@ -644,9 +644,6 @@ bool Sapphire::Scripting::ScriptMgr::onStatusTick( Entity::CharaPtr pChara, Sapp
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::StatusEffectScript >( effect.getId() ); auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::StatusEffectScript >( effect.getId() );
if( script ) if( script )
{ {
if( pChara->isPlayer() )
PlayerMgr::sendDebug( *pChara->getAsPlayer(), "Calling status tick for statusid#{0}", effect.getId() );
script->onTick( *pChara ); script->onTick( *pChara );
return true; return true;
} }