mirror of
https://github.com/SapphireServer/Sapphire.git
synced 2025-05-03 17:27:47 +00:00
Sapphire 5.58 now with actions.
This commit is contained in:
parent
f608e1e8cf
commit
f00f930366
72 changed files with 7853 additions and 2017 deletions
|
@ -1103,6 +1103,73 @@ namespace Sapphire::Common
|
|||
Gatherer
|
||||
};
|
||||
|
||||
enum class StatusEffectType : uint32_t
|
||||
{
|
||||
Invalid = 0,
|
||||
DamageMultiplier = 1,
|
||||
DamageReceiveMultiplier = 2,
|
||||
Hot = 3,
|
||||
Dot = 4,
|
||||
HealReceiveMultiplier = 5,
|
||||
HealCastMultiplier = 6,
|
||||
CritDHRateBonus = 7,
|
||||
DamageReceiveTrigger = 8,
|
||||
DamageDealtTrigger = 9,
|
||||
Shield = 10,
|
||||
MPRestore = 11,
|
||||
Haste = 12,
|
||||
InstantCast = 13,
|
||||
BlockParryRateBonus = 14,
|
||||
MPRestorePerGCD = 15,
|
||||
AlwaysCombo = 16,
|
||||
PotencyMultiplier = 17,
|
||||
};
|
||||
|
||||
enum class ActionTypeFilter : int32_t
|
||||
{
|
||||
Unknown = 0,
|
||||
Physical = 1,
|
||||
Magical = 2,
|
||||
Slashing = 4,
|
||||
Piercing = 8,
|
||||
Blunt = 16,
|
||||
All = 255
|
||||
};
|
||||
|
||||
enum class CritDHBonusFilter : int32_t
|
||||
{
|
||||
None = 0,
|
||||
Damage = 1,
|
||||
Heal = 2,
|
||||
All = 255,
|
||||
};
|
||||
|
||||
enum class StatusEffectTriggerResult : int32_t
|
||||
{
|
||||
None = 0,
|
||||
ReflectDamage = 1,
|
||||
AbsorbHP = 2,
|
||||
};
|
||||
|
||||
enum ActionBonusEffect : uint8_t
|
||||
{
|
||||
NoBonus = 0,
|
||||
CritBonus = 1,
|
||||
DHBonus = 2,
|
||||
GainMPPercentage = 4,
|
||||
GainJobResource = 8,
|
||||
SelfHeal = 16,
|
||||
DamageFallOff = 32,
|
||||
GainJobTimer = 64,
|
||||
};
|
||||
|
||||
enum ActionBonusEffectRequirement : uint8_t
|
||||
{
|
||||
NoRequirement = 0,
|
||||
RequireCorrectCombo = 1,
|
||||
RequireCorrectPositional = 2,
|
||||
};
|
||||
|
||||
enum class AstCardType : uint8_t
|
||||
{
|
||||
None = 0,
|
||||
|
@ -1224,7 +1291,7 @@ namespace Sapphire::Common
|
|||
{
|
||||
uint8_t ammo;
|
||||
uint8_t unused;
|
||||
uint16_t maxTimerDuration;
|
||||
uint16_t maxTimerDuration; // what is this?
|
||||
uint8_t ammoComboStep;
|
||||
} gnb;
|
||||
struct
|
||||
|
|
|
@ -271,7 +271,7 @@ namespace Sapphire::Network::Packets
|
|||
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
DuelChallenge = 0x0277, // 4.2; this is responsible for opening the ui
|
||||
DuelChallenge = 0xF277, // 4.2; this is responsible for opening the ui
|
||||
PerformNote = 0x0127, // updated 5.58 hotfix
|
||||
|
||||
PrepareZoning = 0x02AB, // updated 5.58 hotfix
|
||||
|
|
33
src/scripts/action/brd/ActionHeavyShot97.cpp
Normal file
33
src/scripts/action/brd/ActionHeavyShot97.cpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
const uint16_t STATUS_ID_STRAIGHT_SHOT_READY = 122;
|
||||
|
||||
class ActionHeavyShot97 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionHeavyShot97() :
|
||||
ScriptAPI::ActionScript( 97 )
|
||||
{
|
||||
}
|
||||
|
||||
void onBeforeBuildEffect( Sapphire::World::Action::Action& action, uint8_t victimCounter, uint8_t validVictimCounter ) override
|
||||
{
|
||||
if( validVictimCounter > 0 && action.getSourceChara()->getLevel() >= 2 && Math::CalcStats::getRandomNumber0To100() < 20 )
|
||||
{
|
||||
auto pEffect = Sapphire::StatusEffect::make_StatusEffect( STATUS_ID_STRAIGHT_SHOT_READY, action.getSourceChara(), action.getSourceChara(), 10000, 3000 );
|
||||
action.getEffectbuilder()->applyStatusEffect( action.getSourceChara(), action.getSourceChara(), pEffect );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionHeavyShot97 );
|
|
@ -1,27 +0,0 @@
|
|||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
|
||||
class ActionSprint3 :
|
||||
public Sapphire::ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionSprint3() :
|
||||
Sapphire::ScriptAPI::ActionScript( 3 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto sourceChara = action.getSourceChara();
|
||||
|
||||
if( !sourceChara->isPlayer() )
|
||||
return;
|
||||
|
||||
action.getEffectbuilder()->applyStatusEffect( sourceChara, 50, 30 );
|
||||
|
||||
sourceChara->getAsPlayer()->addStatusEffectByIdIfNotExist( 50, 20000, *sourceChara, 30 );
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionSprint3 );
|
27
src/scripts/action/drg/ActionBloodOfTheDragon3553.cpp
Normal file
27
src/scripts/action/drg/ActionBloodOfTheDragon3553.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
|
||||
using namespace Sapphire;
|
||||
|
||||
class ActionBloodOfTheDragon3553 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionBloodOfTheDragon3553() :
|
||||
ScriptAPI::ActionScript( 3553 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
pPlayer->gaugeDrgSetDragonTimer( 30000, true );
|
||||
pPlayer->gaugeDrgSetDragonState( Common::DrgState::BloodOfTheDragon );
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionBloodOfTheDragon3553 );
|
36
src/scripts/action/drg/ActionChaosThrust88.cpp
Normal file
36
src/scripts/action/drg/ActionChaosThrust88.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
const uint16_t STATUS_ID_SHARPER_FANG_AND_CLAW = 802;
|
||||
const uint16_t STATUS_ID_ENHANCED_WHEELING_THRUST = 803;
|
||||
|
||||
class ActionChaosThrust88 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionChaosThrust88() :
|
||||
ScriptAPI::ActionScript( 88 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
if( pPlayer->gaugeDrgGetDragonState( Common::DrgState::BloodOfTheDragon ) || pPlayer->gaugeDrgGetDragonState( Common::DrgState::LifeOfTheDragon ) )
|
||||
{
|
||||
auto pEffect = Sapphire::StatusEffect::make_StatusEffect( STATUS_ID_ENHANCED_WHEELING_THRUST, action.getSourceChara(), action.getSourceChara(), 10000, 3000 );
|
||||
action.getEffectbuilder()->applyStatusEffect( action.getSourceChara(), action.getSourceChara(), pEffect );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionChaosThrust88 );
|
34
src/scripts/action/drg/ActionCoerthanTorment16477.cpp
Normal file
34
src/scripts/action/drg/ActionCoerthanTorment16477.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
using namespace Sapphire;
|
||||
|
||||
class ActionCoerthanTorment16477 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionCoerthanTorment16477() :
|
||||
ScriptAPI::ActionScript( 16477 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
|
||||
if( pPlayer->getLastComboActionId() == 7397 )
|
||||
{
|
||||
if( pPlayer->gaugeDrgGetDragonState( Common::DrgState::BloodOfTheDragon ) )
|
||||
{
|
||||
uint16_t dragonTimer = pPlayer->gaugeDrgGetDragonTimer();
|
||||
dragonTimer = std::min( 30000, dragonTimer + 10000 );
|
||||
pPlayer->gaugeDrgSetDragonTimer( dragonTimer, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionCoerthanTorment16477 );
|
57
src/scripts/action/drg/ActionFangAndClaw3554.cpp
Normal file
57
src/scripts/action/drg/ActionFangAndClaw3554.cpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
const uint16_t STATUS_ID_SHARPER_FANG_AND_CLAW = 802;
|
||||
const uint16_t STATUS_ID_ENHANCED_WHEELING_THRUST = 803;
|
||||
const uint16_t STATUS_ID_RAIDEN_THRUST_READY = 1863;
|
||||
|
||||
class ActionFangAndClaw3554 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionFangAndClaw3554() :
|
||||
ScriptAPI::ActionScript( 3554 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto effectEntry = action.getSourceChara()->getStatusEffectById( STATUS_ID_SHARPER_FANG_AND_CLAW );
|
||||
if( effectEntry.second )
|
||||
{
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
action.getSourceChara()->removeStatusEffect( effectEntry.first );
|
||||
if( pPlayer->gaugeDrgGetDragonState( Common::DrgState::BloodOfTheDragon ) )
|
||||
{
|
||||
uint16_t dragonTimer = pPlayer->gaugeDrgGetDragonTimer();
|
||||
dragonTimer = std::min( 30000, dragonTimer + 10000 );
|
||||
pPlayer->gaugeDrgSetDragonTimer( dragonTimer, true );
|
||||
}
|
||||
if( pPlayer->getLastComboActionId() == 84 )
|
||||
{
|
||||
auto pEffect = Sapphire::StatusEffect::make_StatusEffect( STATUS_ID_ENHANCED_WHEELING_THRUST, action.getSourceChara(), action.getSourceChara(), 10000, 3000 );
|
||||
action.getEffectbuilder()->applyStatusEffect( action.getSourceChara(), action.getSourceChara(), pEffect );
|
||||
}
|
||||
if( pPlayer->getLastComboActionId() == 3556 )
|
||||
{
|
||||
auto pEffect = Sapphire::StatusEffect::make_StatusEffect( STATUS_ID_RAIDEN_THRUST_READY, action.getSourceChara(), action.getSourceChara(), 10000, 3000 );
|
||||
action.getEffectbuilder()->applyStatusEffect( action.getSourceChara(), action.getSourceChara(), pEffect );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
action.disableGenericHandler();
|
||||
action.interrupt();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionFangAndClaw3554 );
|
35
src/scripts/action/drg/ActionFullThrust84.cpp
Normal file
35
src/scripts/action/drg/ActionFullThrust84.cpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
const uint16_t STATUS_ID_SHARPER_FANG_AND_CLAW = 802;
|
||||
|
||||
class ActionFullThrust84 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionFullThrust84() :
|
||||
ScriptAPI::ActionScript( 84 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
if( pPlayer->gaugeDrgGetDragonState( Common::DrgState::BloodOfTheDragon ) || pPlayer->gaugeDrgGetDragonState( Common::DrgState::LifeOfTheDragon ) )
|
||||
{
|
||||
auto pEffect = Sapphire::StatusEffect::make_StatusEffect( STATUS_ID_SHARPER_FANG_AND_CLAW, action.getSourceChara(), action.getSourceChara(), 10000, 3000 );
|
||||
action.getEffectbuilder()->applyStatusEffect( action.getSourceChara(), action.getSourceChara(), pEffect );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionFullThrust84 );
|
42
src/scripts/action/drg/ActionGeirskogul3555.cpp
Normal file
42
src/scripts/action/drg/ActionGeirskogul3555.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
class ActionGeirskogul3555 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionGeirskogul3555() :
|
||||
ScriptAPI::ActionScript( 3555 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
if( pPlayer->gaugeDrgGetDragonState( Common::DrgState::BloodOfTheDragon ) )
|
||||
{
|
||||
if( pPlayer->gaugeDrgGetEyes() == 2 )
|
||||
{
|
||||
pPlayer->gaugeDrgSetDragonState( Common::DrgState::LifeOfTheDragon );
|
||||
pPlayer->gaugeDrgSetDragonTimer( 30000, true );
|
||||
pPlayer->setVisualEffect( 34, true );
|
||||
pPlayer->gaugeDrgSetEyes( 0 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
action.disableGenericHandler();
|
||||
action.interrupt();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionGeirskogul3555 );
|
35
src/scripts/action/drg/ActionHighJump16478.cpp
Normal file
35
src/scripts/action/drg/ActionHighJump16478.cpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
const uint16_t STATUS_ID_DIVE_READY = 1243;
|
||||
|
||||
class ActionHighJump16478 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionHighJump16478() :
|
||||
ScriptAPI::ActionScript( 16478 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
if( pPlayer->gaugeDrgGetDragonState( Common::DrgState::BloodOfTheDragon ) || pPlayer->gaugeDrgGetDragonState( Common::DrgState::LifeOfTheDragon ) )
|
||||
{
|
||||
auto pEffect = Sapphire::StatusEffect::make_StatusEffect( STATUS_ID_DIVE_READY, action.getSourceChara(), action.getSourceChara(), 15000, 3000 );
|
||||
action.getEffectbuilder()->applyStatusEffect( action.getSourceChara(), action.getSourceChara(), pEffect );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionHighJump16478 );
|
45
src/scripts/action/drg/ActionMirageDive7399.cpp
Normal file
45
src/scripts/action/drg/ActionMirageDive7399.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
const uint16_t STATUS_ID_DIVE_READY = 1243;
|
||||
|
||||
class ActionMirageDive7399 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionMirageDive7399() :
|
||||
ScriptAPI::ActionScript( 7399 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto effectEntry = action.getSourceChara()->getStatusEffectById( STATUS_ID_DIVE_READY );
|
||||
if( effectEntry.second )
|
||||
{
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
action.getSourceChara()->removeStatusEffect( effectEntry.first );
|
||||
if( pPlayer->gaugeDrgGetDragonState( Common::DrgState::BloodOfTheDragon ) || pPlayer->gaugeDrgGetDragonState( Common::DrgState::LifeOfTheDragon ) )
|
||||
{
|
||||
uint8_t eyes = pPlayer->gaugeDrgGetEyes();
|
||||
eyes = std::min( 2, eyes + 1 );
|
||||
pPlayer->gaugeDrgSetEyes( eyes );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
action.disableGenericHandler();
|
||||
action.interrupt();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionMirageDive7399 );
|
40
src/scripts/action/drg/ActionRaidenThrust16479.cpp
Normal file
40
src/scripts/action/drg/ActionRaidenThrust16479.cpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
const uint16_t STATUS_ID_RAIDEN_THRUST_READY = 1863;
|
||||
|
||||
class ActionRaidenThrust16479 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionRaidenThrust16479() :
|
||||
ScriptAPI::ActionScript( 16479 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto effectEntry = action.getSourceChara()->getStatusEffectById( STATUS_ID_RAIDEN_THRUST_READY );
|
||||
if( effectEntry.second )
|
||||
{
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
action.getSourceChara()->removeStatusEffect( effectEntry.first );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
action.disableGenericHandler();
|
||||
action.interrupt();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionRaidenThrust16479 );
|
34
src/scripts/action/drg/ActionSonicThrust7397.cpp
Normal file
34
src/scripts/action/drg/ActionSonicThrust7397.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
using namespace Sapphire;
|
||||
|
||||
class ActionSonicThrust7397 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionSonicThrust7397() :
|
||||
ScriptAPI::ActionScript( 7397 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
|
||||
if( pPlayer->getLastComboActionId() == 86 )
|
||||
{
|
||||
if( pPlayer->gaugeDrgGetDragonState( Common::DrgState::BloodOfTheDragon ) )
|
||||
{
|
||||
uint16_t dragonTimer = pPlayer->gaugeDrgGetDragonTimer();
|
||||
dragonTimer = std::min( 30000, dragonTimer + 10000 );
|
||||
pPlayer->gaugeDrgSetDragonTimer( dragonTimer, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionSonicThrust7397 );
|
57
src/scripts/action/drg/ActionWheelingThrust3556.cpp
Normal file
57
src/scripts/action/drg/ActionWheelingThrust3556.cpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
const uint16_t STATUS_ID_SHARPER_FANG_AND_CLAW = 802;
|
||||
const uint16_t STATUS_ID_ENHANCED_WHEELING_THRUST = 803;
|
||||
const uint16_t STATUS_ID_RAIDEN_THRUST_READY = 1863;
|
||||
|
||||
class ActionWheelingThrust3556 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionWheelingThrust3556() :
|
||||
ScriptAPI::ActionScript( 3556 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto effectEntry = action.getSourceChara()->getStatusEffectById( STATUS_ID_ENHANCED_WHEELING_THRUST );
|
||||
if( effectEntry.second )
|
||||
{
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
action.getSourceChara()->removeStatusEffect( effectEntry.first );
|
||||
if( pPlayer->gaugeDrgGetDragonState( Common::DrgState::BloodOfTheDragon ) )
|
||||
{
|
||||
uint16_t dragonTimer = pPlayer->gaugeDrgGetDragonTimer();
|
||||
dragonTimer = std::min( 30000, dragonTimer + 10000 );
|
||||
pPlayer->gaugeDrgSetDragonTimer( dragonTimer, true );
|
||||
}
|
||||
if( pPlayer->getLastComboActionId() == 88 )
|
||||
{
|
||||
auto pEffect = Sapphire::StatusEffect::make_StatusEffect( STATUS_ID_SHARPER_FANG_AND_CLAW, action.getSourceChara(), action.getSourceChara(), 10000, 3000 );
|
||||
action.getEffectbuilder()->applyStatusEffect( action.getSourceChara(), action.getSourceChara(), pEffect );
|
||||
}
|
||||
if( pPlayer->getLastComboActionId() == 3554 )
|
||||
{
|
||||
auto pEffect = Sapphire::StatusEffect::make_StatusEffect( STATUS_ID_RAIDEN_THRUST_READY, action.getSourceChara(), action.getSourceChara(), 10000, 3000 );
|
||||
action.getEffectbuilder()->applyStatusEffect( action.getSourceChara(), action.getSourceChara(), pEffect );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
action.disableGenericHandler();
|
||||
action.interrupt();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionWheelingThrust3556 );
|
39
src/scripts/action/drk/ActionTheBlackestNight7393.cpp
Normal file
39
src/scripts/action/drk/ActionTheBlackestNight7393.cpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
const uint16_t STATUS_ID_THE_BLACKEST_NIGHT = 1178;
|
||||
|
||||
class ActionTheBlackestNight7393 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionTheBlackestNight7393() :
|
||||
ScriptAPI::ActionScript( 7393 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto pTarget = action.getHitChara();
|
||||
World::Action::StatusEffectEntry effectEntry;
|
||||
effectEntry.init( Common::StatusEffectType::Shield, static_cast< int32_t >( ( 1.0 * pTarget->getMaxHp() ) * 0.25 ), 0, 0, 0 );
|
||||
auto pNewEffect = Sapphire::StatusEffect::make_StatusEffect( STATUS_ID_THE_BLACKEST_NIGHT, action.getSourceChara(), pTarget, 7000, 3000 );
|
||||
pNewEffect->replaceEffectEntry( effectEntry );
|
||||
action.getEffectbuilder()->applyStatusEffect( pTarget, action.getSourceChara(), pNewEffect );
|
||||
}
|
||||
|
||||
void onStart( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
action.disableGenericHandler();
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionTheBlackestNight7393 );
|
32
src/scripts/action/gnb/ActionBloodfest16164.cpp
Normal file
32
src/scripts/action/gnb/ActionBloodfest16164.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
using namespace Sapphire;
|
||||
|
||||
class ActionBloodfest16164 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionBloodfest16164() :
|
||||
ScriptAPI::ActionScript( 16164 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto chara = action.getHitChara();
|
||||
if( chara )
|
||||
{
|
||||
auto source = action.getSourceChara();
|
||||
auto player = source->getAsPlayer();
|
||||
assert( player );
|
||||
chara->onActionHostile( source );
|
||||
player->gaugeGnbSetAmmo( 2 );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionBloodfest16164 );
|
44
src/scripts/action/gnb/ActionBrutalShell16139.cpp
Normal file
44
src/scripts/action/gnb/ActionBrutalShell16139.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
const uint16_t STATUS_ID_BRUTAL_SHELL = 1898;
|
||||
|
||||
class ActionBrutalShell16139 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionBrutalShell16139() :
|
||||
ScriptAPI::ActionScript( 16139 )
|
||||
{
|
||||
}
|
||||
|
||||
void onBeforeBuildEffect( Sapphire::World::Action::Action& action, uint8_t victimCounter, uint8_t validVictimCounter ) override
|
||||
{
|
||||
if( validVictimCounter > 0 && action.isCorrectCombo() )
|
||||
{
|
||||
auto chara = action.getSourceChara();
|
||||
auto heal = action.calcHealing( 150 );
|
||||
heal.first = Math::CalcStats::applyHealingReceiveMultiplier( *chara, heal.first );
|
||||
action.getEffectbuilder()->heal( chara, chara, heal.first, heal.second );
|
||||
|
||||
if( chara->getLevel() >= 52 && heal.first > 0 )
|
||||
{
|
||||
World::Action::StatusEffectEntry effectEntry;
|
||||
effectEntry.init( Common::StatusEffectType::Shield, heal.first, 0, 0, 0 );
|
||||
auto pNewEffect = Sapphire::StatusEffect::make_StatusEffect( STATUS_ID_BRUTAL_SHELL, action.getSourceChara(), chara, 10000, 3000 );
|
||||
pNewEffect->replaceEffectEntry( effectEntry );
|
||||
action.getEffectbuilder()->applyStatusEffect( chara, chara, pNewEffect );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionBrutalShell16139 );
|
41
src/scripts/action/gnb/ActionGnashingFang16146.cpp
Normal file
41
src/scripts/action/gnb/ActionGnashingFang16146.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
const uint16_t STATUS_ID_READY_TO_RIP = 1842;
|
||||
const uint16_t STATUS_ID_READY_TO_TEAR = 1843;
|
||||
const uint16_t STATUS_ID_READY_TO_GOUGE = 1844;
|
||||
|
||||
class ActionGnashingFang16146 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionGnashingFang16146() :
|
||||
ScriptAPI::ActionScript( 16146 )
|
||||
{
|
||||
}
|
||||
|
||||
void onBeforeBuildEffect( Sapphire::World::Action::Action& action, uint8_t victimCounter, uint8_t validVictimCounter ) override
|
||||
{
|
||||
if( validVictimCounter > 0 )
|
||||
{
|
||||
auto chara = action.getSourceChara();
|
||||
auto player = chara->getAsPlayer();
|
||||
assert( player );
|
||||
player->removeSingleStatusEffectById( STATUS_ID_READY_TO_TEAR, false );
|
||||
player->removeSingleStatusEffectById( STATUS_ID_READY_TO_GOUGE, false );
|
||||
auto pNewEffect = Sapphire::StatusEffect::make_StatusEffect( STATUS_ID_READY_TO_RIP, action.getSourceChara(), player, 10000, 3000 );
|
||||
action.getEffectbuilder()->applyStatusEffect( chara, chara, pNewEffect, 0 );
|
||||
player->gaugeGnbSetComboStep( 1 );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionGnashingFang16146 );
|
41
src/scripts/action/gnb/ActionSavageClaw16147.cpp
Normal file
41
src/scripts/action/gnb/ActionSavageClaw16147.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
const uint16_t STATUS_ID_READY_TO_RIP = 1842;
|
||||
const uint16_t STATUS_ID_READY_TO_TEAR = 1843;
|
||||
const uint16_t STATUS_ID_READY_TO_GOUGE = 1844;
|
||||
|
||||
class ActionSavageClaw16147 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionSavageClaw16147() :
|
||||
ScriptAPI::ActionScript( 16147 )
|
||||
{
|
||||
}
|
||||
|
||||
void onBeforeBuildEffect( Sapphire::World::Action::Action& action, uint8_t victimCounter, uint8_t validVictimCounter ) override
|
||||
{
|
||||
if( validVictimCounter > 0 )
|
||||
{
|
||||
auto chara = action.getSourceChara();
|
||||
auto player = chara->getAsPlayer();
|
||||
assert( player );
|
||||
player->removeSingleStatusEffectById( STATUS_ID_READY_TO_RIP, false );
|
||||
player->removeSingleStatusEffectById( STATUS_ID_READY_TO_GOUGE, false );
|
||||
auto pNewEffect = Sapphire::StatusEffect::make_StatusEffect( STATUS_ID_READY_TO_TEAR, action.getSourceChara(), player, 10000, 3000 );
|
||||
action.getEffectbuilder()->applyStatusEffect( chara, chara, pNewEffect, 0 );
|
||||
player->gaugeGnbSetComboStep( 2 );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionSavageClaw16147 );
|
41
src/scripts/action/gnb/ActionWickedTalon16150.cpp
Normal file
41
src/scripts/action/gnb/ActionWickedTalon16150.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
const uint16_t STATUS_ID_READY_TO_RIP = 1842;
|
||||
const uint16_t STATUS_ID_READY_TO_TEAR = 1843;
|
||||
const uint16_t STATUS_ID_READY_TO_GOUGE = 1844;
|
||||
|
||||
class ActionWickedTalon16150 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionWickedTalon16150() :
|
||||
ScriptAPI::ActionScript( 16150 )
|
||||
{
|
||||
}
|
||||
|
||||
void onBeforeBuildEffect( Sapphire::World::Action::Action& action, uint8_t victimCounter, uint8_t validVictimCounter ) override
|
||||
{
|
||||
if( validVictimCounter > 0 )
|
||||
{
|
||||
auto chara = action.getSourceChara();
|
||||
auto player = chara->getAsPlayer();
|
||||
assert( player );
|
||||
player->removeSingleStatusEffectById( STATUS_ID_READY_TO_RIP, false );
|
||||
player->removeSingleStatusEffectById( STATUS_ID_READY_TO_TEAR, false );
|
||||
auto pNewEffect = Sapphire::StatusEffect::make_StatusEffect( STATUS_ID_READY_TO_GOUGE, action.getSourceChara(), player, 10000, 3000 );
|
||||
action.getEffectbuilder()->applyStatusEffect( chara, chara, pNewEffect, 0 );
|
||||
player->gaugeGnbSetComboStep( 0 );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionWickedTalon16150 );
|
29
src/scripts/action/role/ActionProvoke7533.cpp
Normal file
29
src/scripts/action/role/ActionProvoke7533.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
using namespace Sapphire;
|
||||
|
||||
class ActionProvoke7533 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionProvoke7533() :
|
||||
ScriptAPI::ActionScript( 7533 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto chara = action.getHitChara();
|
||||
if( chara )
|
||||
{
|
||||
chara->onActionHostile( action.getSourceChara() );
|
||||
action.getEffectbuilder()->provoke( chara );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionProvoke7533 );
|
59
src/scripts/action/sam/ActionEnpi7486.cpp
Normal file
59
src/scripts/action/sam/ActionEnpi7486.cpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
using namespace Sapphire;
|
||||
|
||||
const uint16_t STATUS_ID_ENHANCED_ENPI = 1236;
|
||||
|
||||
class ActionEnpi7486 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionEnpi7486() :
|
||||
ScriptAPI::ActionScript( 7486 )
|
||||
{
|
||||
}
|
||||
|
||||
void onBeforeBuildEffect( Sapphire::World::Action::Action& action, uint8_t victimCounter, uint8_t validVictimCounter ) override
|
||||
{
|
||||
if( validVictimCounter > 0 )
|
||||
{
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
assert( pPlayer );
|
||||
int kenki = pPlayer->gaugeSamGetKenki();
|
||||
auto level = pPlayer->getLevel();
|
||||
if( level >= 52 )
|
||||
{
|
||||
kenki += 5;
|
||||
}
|
||||
if( level >= 62 )
|
||||
{
|
||||
kenki += 5;
|
||||
}
|
||||
pPlayer->gaugeSamSetKenki( std::min( 100, kenki ) );
|
||||
}
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto effectEntry = action.getSourceChara()->getStatusEffectById( STATUS_ID_ENHANCED_ENPI );
|
||||
if( effectEntry.second )
|
||||
{
|
||||
action.getSourceChara()->removeStatusEffect( effectEntry.first );
|
||||
}
|
||||
}
|
||||
|
||||
void onBeforePreCheck( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto effectEntry = action.getSourceChara()->getStatusEffectById( STATUS_ID_ENHANCED_ENPI );
|
||||
if( effectEntry.second )
|
||||
{
|
||||
action.getActionEntry().damagePotency = 320;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionEnpi7486 );
|
32
src/scripts/action/sam/ActionFuga7483.cpp
Normal file
32
src/scripts/action/sam/ActionFuga7483.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
using namespace Sapphire;
|
||||
|
||||
class ActionFuga7483 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionFuga7483() :
|
||||
ScriptAPI::ActionScript( 7483 )
|
||||
{
|
||||
}
|
||||
|
||||
void onBeforeBuildEffect( Sapphire::World::Action::Action& action, uint8_t victimCounter, uint8_t validVictimCounter ) override
|
||||
{
|
||||
if( validVictimCounter > 0 )
|
||||
{
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
assert( pPlayer );
|
||||
if( pPlayer->getLevel() >= 62 )
|
||||
{
|
||||
pPlayer->gaugeSamSetKenki( std::min( 100, pPlayer->gaugeSamGetKenki() + 5 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionFuga7483 );
|
43
src/scripts/action/sam/ActionGekko7481.cpp
Normal file
43
src/scripts/action/sam/ActionGekko7481.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
using namespace Sapphire;
|
||||
|
||||
class ActionGekko7481 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionGekko7481() :
|
||||
ScriptAPI::ActionScript( 7481 )
|
||||
{
|
||||
}
|
||||
|
||||
void onBeforeBuildEffect( Sapphire::World::Action::Action& action, uint8_t victimCounter, uint8_t validVictimCounter ) override
|
||||
{
|
||||
if( validVictimCounter > 0 && action.isCorrectCombo() )
|
||||
{
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
assert( pPlayer );
|
||||
int kenki = pPlayer->gaugeSamGetKenki();
|
||||
auto level = pPlayer->getLevel();
|
||||
if( /*positional &&*/ level >= 52 )
|
||||
{
|
||||
kenki += 5;
|
||||
}
|
||||
if( level >= 62 )
|
||||
{
|
||||
kenki += 5;
|
||||
}
|
||||
pPlayer->gaugeSamSetKenki( std::min( 100, kenki ) );
|
||||
if( level >= 30 )
|
||||
{
|
||||
pPlayer->gaugeSamSetSen( Common::SamSen::Getsu, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionGekko7481 );
|
33
src/scripts/action/sam/ActionHagakure7495.cpp
Normal file
33
src/scripts/action/sam/ActionHagakure7495.cpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
using namespace Sapphire;
|
||||
|
||||
class ActionHagakure7495 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionHagakure7495() :
|
||||
ScriptAPI::ActionScript( 7495 )
|
||||
{
|
||||
}
|
||||
|
||||
void onStart( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
assert( pPlayer );
|
||||
int kenki = pPlayer->gaugeSamGetKenki();
|
||||
if( pPlayer->gaugeSamGetSen( Common::SamSen::Getsu ) )
|
||||
kenki += 10;
|
||||
if( pPlayer->gaugeSamGetSen( Common::SamSen::Setsu ) )
|
||||
kenki += 10;
|
||||
if( pPlayer->gaugeSamGetSen( Common::SamSen::Ka ) )
|
||||
kenki += 10;
|
||||
pPlayer->gaugeSamSetKenki( std::min( 100, kenki ) );
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionHagakure7495 );
|
32
src/scripts/action/sam/ActionHakaze7477.cpp
Normal file
32
src/scripts/action/sam/ActionHakaze7477.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
using namespace Sapphire;
|
||||
|
||||
class ActionHakaze7477 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionHakaze7477() :
|
||||
ScriptAPI::ActionScript( 7477 )
|
||||
{
|
||||
}
|
||||
|
||||
void onBeforeBuildEffect( Sapphire::World::Action::Action& action, uint8_t victimCounter, uint8_t validVictimCounter ) override
|
||||
{
|
||||
if( validVictimCounter > 0 )
|
||||
{
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
assert( pPlayer );
|
||||
if( pPlayer->getLevel() >= 62 )
|
||||
{
|
||||
pPlayer->gaugeSamSetKenki( std::min( 100, pPlayer->gaugeSamGetKenki() + 5 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionHakaze7477 );
|
26
src/scripts/action/sam/ActionIkishoten16482.cpp
Normal file
26
src/scripts/action/sam/ActionIkishoten16482.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
using namespace Sapphire;
|
||||
|
||||
class ActionIkishoten16482 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionIkishoten16482() :
|
||||
ScriptAPI::ActionScript( 16482 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
assert( pPlayer );
|
||||
pPlayer->gaugeSamSetKenki( std::min( 100, pPlayer->gaugeSamGetKenki() + 50 ) );
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionIkishoten16482 );
|
32
src/scripts/action/sam/ActionJinpu7478.cpp
Normal file
32
src/scripts/action/sam/ActionJinpu7478.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
using namespace Sapphire;
|
||||
|
||||
class ActionJinpu7478 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionJinpu7478() :
|
||||
ScriptAPI::ActionScript( 7478 )
|
||||
{
|
||||
}
|
||||
|
||||
void onBeforeBuildEffect( Sapphire::World::Action::Action& action, uint8_t victimCounter, uint8_t validVictimCounter ) override
|
||||
{
|
||||
if( validVictimCounter > 0 && action.isCorrectCombo() )
|
||||
{
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
assert( pPlayer );
|
||||
if( pPlayer->getLevel() >= 62 )
|
||||
{
|
||||
pPlayer->gaugeSamSetKenki( std::min( 100, pPlayer->gaugeSamGetKenki() + 5 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionJinpu7478 );
|
43
src/scripts/action/sam/ActionKasha7482.cpp
Normal file
43
src/scripts/action/sam/ActionKasha7482.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
using namespace Sapphire;
|
||||
|
||||
class ActionKasha7482 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionKasha7482() :
|
||||
ScriptAPI::ActionScript( 7482 )
|
||||
{
|
||||
}
|
||||
|
||||
void onBeforeBuildEffect( Sapphire::World::Action::Action& action, uint8_t victimCounter, uint8_t validVictimCounter ) override
|
||||
{
|
||||
if( validVictimCounter > 0 && action.isCorrectCombo() )
|
||||
{
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
assert( pPlayer );
|
||||
int kenki = pPlayer->gaugeSamGetKenki();
|
||||
auto level = pPlayer->getLevel();
|
||||
if( /*positional &&*/ level >= 52 )
|
||||
{
|
||||
kenki += 5;
|
||||
}
|
||||
if( level >= 62 )
|
||||
{
|
||||
kenki += 5;
|
||||
}
|
||||
pPlayer->gaugeSamSetKenki( std::min( 100, kenki ) );
|
||||
if( level >= 40 )
|
||||
{
|
||||
pPlayer->gaugeSamSetSen( Common::SamSen::Ka, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionKasha7482 );
|
43
src/scripts/action/sam/ActionMangetsu7484.cpp
Normal file
43
src/scripts/action/sam/ActionMangetsu7484.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
using namespace Sapphire;
|
||||
|
||||
class ActionMangetsu7484 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionMangetsu7484() :
|
||||
ScriptAPI::ActionScript( 7484 )
|
||||
{
|
||||
}
|
||||
|
||||
void onBeforeBuildEffect( Sapphire::World::Action::Action& action, uint8_t victimCounter, uint8_t validVictimCounter ) override
|
||||
{
|
||||
if( validVictimCounter > 0 && action.isCorrectCombo() )
|
||||
{
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
assert( pPlayer );
|
||||
int kenki = pPlayer->gaugeSamGetKenki();
|
||||
auto level = pPlayer->getLevel();
|
||||
if( level >= 52 )
|
||||
{
|
||||
kenki += 5;
|
||||
}
|
||||
if( level >= 62 )
|
||||
{
|
||||
kenki += 5;
|
||||
}
|
||||
pPlayer->gaugeSamSetKenki( std::min( 100, kenki ) );
|
||||
if( level >= 35 )
|
||||
{
|
||||
pPlayer->gaugeSamSetSen( Common::SamSen::Getsu, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionMangetsu7484 );
|
43
src/scripts/action/sam/ActionOka7485.cpp
Normal file
43
src/scripts/action/sam/ActionOka7485.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
using namespace Sapphire;
|
||||
|
||||
class ActionOka7485 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionOka7485() :
|
||||
ScriptAPI::ActionScript( 7485 )
|
||||
{
|
||||
}
|
||||
|
||||
void onBeforeBuildEffect( Sapphire::World::Action::Action& action, uint8_t victimCounter, uint8_t validVictimCounter ) override
|
||||
{
|
||||
if( validVictimCounter > 0 && action.isCorrectCombo() )
|
||||
{
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
assert( pPlayer );
|
||||
int kenki = pPlayer->gaugeSamGetKenki();
|
||||
auto level = pPlayer->getLevel();
|
||||
if( level >= 52 )
|
||||
{
|
||||
kenki += 5;
|
||||
}
|
||||
if( level >= 62 )
|
||||
{
|
||||
kenki += 5;
|
||||
}
|
||||
pPlayer->gaugeSamSetKenki( std::min( 100, kenki ) );
|
||||
if( level >= 45 )
|
||||
{
|
||||
pPlayer->gaugeSamSetSen( Common::SamSen::Ka, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionOka7485 );
|
32
src/scripts/action/sam/ActionShifu7479.cpp
Normal file
32
src/scripts/action/sam/ActionShifu7479.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
using namespace Sapphire;
|
||||
|
||||
class ActionShifu7479 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionShifu7479() :
|
||||
ScriptAPI::ActionScript( 7479 )
|
||||
{
|
||||
}
|
||||
|
||||
void onBeforeBuildEffect( Sapphire::World::Action::Action& action, uint8_t victimCounter, uint8_t validVictimCounter ) override
|
||||
{
|
||||
if( validVictimCounter > 0 && action.isCorrectCombo() )
|
||||
{
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
assert( pPlayer );
|
||||
if( pPlayer->getLevel() >= 62 )
|
||||
{
|
||||
pPlayer->gaugeSamSetKenki( std::min( 100, pPlayer->gaugeSamGetKenki() + 5 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionShifu7479 );
|
43
src/scripts/action/sam/ActionYukikaze7480.cpp
Normal file
43
src/scripts/action/sam/ActionYukikaze7480.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
using namespace Sapphire;
|
||||
|
||||
class ActionYukikaze7480 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionYukikaze7480() :
|
||||
ScriptAPI::ActionScript( 7480 )
|
||||
{
|
||||
}
|
||||
|
||||
void onBeforeBuildEffect( Sapphire::World::Action::Action& action, uint8_t victimCounter, uint8_t validVictimCounter ) override
|
||||
{
|
||||
if( validVictimCounter > 0 && action.isCorrectCombo() )
|
||||
{
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
assert( pPlayer );
|
||||
int kenki = pPlayer->gaugeSamGetKenki();
|
||||
auto level = pPlayer->getLevel();
|
||||
if( level >= 52 )
|
||||
{
|
||||
kenki += 10;
|
||||
}
|
||||
if( level >= 62 )
|
||||
{
|
||||
kenki += 5;
|
||||
}
|
||||
pPlayer->gaugeSamSetKenki( std::min( 100, kenki ) );
|
||||
if( level >= 50 )
|
||||
{
|
||||
pPlayer->gaugeSamSetSen( Common::SamSen::Setsu, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionYukikaze7480 );
|
81
src/scripts/action/sch/ActionAdloquium185.cpp
Normal file
81
src/scripts/action/sch/ActionAdloquium185.cpp
Normal file
|
@ -0,0 +1,81 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
const uint16_t STATUS_ID_GALVANIZE = 297;
|
||||
const uint16_t STATUS_ID_CATALYZE = 1918;
|
||||
|
||||
class ActionAdloquium185 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionAdloquium185() :
|
||||
ScriptAPI::ActionScript( 185 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto pTarget = action.getHitChara();
|
||||
if( pTarget )
|
||||
{
|
||||
// still pull out the lut entry to get potency values etc.
|
||||
auto lutEntry = action.getActionEntry();
|
||||
|
||||
// do healing part
|
||||
auto heal = action.calcHealing( lutEntry.healPotency );
|
||||
heal.first = Math::CalcStats::applyHealingReceiveMultiplier( *pTarget, heal.first );
|
||||
action.getEffectbuilder()->heal( pTarget, pTarget, heal.first, heal.second );
|
||||
|
||||
float shieldValue = heal.first * 1.25f;
|
||||
// apply new galvanize when not existing or larger than existing one
|
||||
auto oldEffect = pTarget->getStatusEffectById( STATUS_ID_GALVANIZE );
|
||||
if( !oldEffect.second || oldEffect.second->getEffectEntry().getRemainingShield() <= shieldValue )
|
||||
{
|
||||
World::Action::StatusEffectEntry effectEntry;
|
||||
effectEntry.init( Common::StatusEffectType::Shield, shieldValue, 0, 0, 0 );
|
||||
auto pNewEffect = Sapphire::StatusEffect::make_StatusEffect( STATUS_ID_GALVANIZE, action.getSourceChara(), pTarget, 30000, 3000 );
|
||||
pNewEffect->replaceEffectEntry( effectEntry );
|
||||
action.getEffectbuilder()->applyStatusEffect( pTarget, action.getSourceChara(), pNewEffect );
|
||||
}
|
||||
else
|
||||
action.getEffectbuilder()->statusNoEffect( pTarget, STATUS_ID_GALVANIZE );
|
||||
|
||||
if( heal.second == Common::ActionHitSeverityType::CritHeal )
|
||||
{
|
||||
// apply catalyze when crit, same rule as galvanize
|
||||
oldEffect = pTarget->getStatusEffectById( STATUS_ID_CATALYZE );
|
||||
if( !oldEffect.second || oldEffect.second->getEffectEntry().getRemainingShield() <= shieldValue )
|
||||
{
|
||||
World::Action::StatusEffectEntry effectEntry;
|
||||
effectEntry.init( Common::StatusEffectType::Shield, shieldValue, 0, 0, 0 );
|
||||
auto pNewEffect = Sapphire::StatusEffect::make_StatusEffect( STATUS_ID_CATALYZE, action.getSourceChara(), pTarget, 30000, 3000 );
|
||||
pNewEffect->replaceEffectEntry( effectEntry );
|
||||
action.getEffectbuilder()->applyStatusEffect( pTarget, action.getSourceChara(), pNewEffect );
|
||||
}
|
||||
else
|
||||
action.getEffectbuilder()->statusNoEffect( pTarget, STATUS_ID_CATALYZE );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onStart( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
/*
|
||||
don't run generic action handler for this action.
|
||||
for simpler actions like cure 1 we only want to apply the freecure proc in script,
|
||||
and let the generic handler do the heal so we don't have to copy heal code into scripts,
|
||||
unless in cases like adlo, the healing result matters so we have to.
|
||||
*/
|
||||
action.disableGenericHandler();
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionAdloquium185 );
|
36
src/scripts/action/war/ActionChaoticCyclone16463.cpp
Normal file
36
src/scripts/action/war/ActionChaoticCyclone16463.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
const uint16_t STATUS_ID_NASCENT_CHAOS = 1897;
|
||||
|
||||
class ActionChaoticCyclone16463 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionChaoticCyclone16463() :
|
||||
ScriptAPI::ActionScript( 16463 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto effectEntry = action.getSourceChara()->getStatusEffectById( STATUS_ID_NASCENT_CHAOS );
|
||||
if( effectEntry.second )
|
||||
{
|
||||
action.getSourceChara()->removeStatusEffect( effectEntry.first );
|
||||
}
|
||||
else
|
||||
{
|
||||
action.disableGenericHandler();
|
||||
action.interrupt();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionChaoticCyclone16463 );
|
36
src/scripts/action/war/ActionInfuriate52.cpp
Normal file
36
src/scripts/action/war/ActionInfuriate52.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
const uint16_t STATUS_ID_NASCENT_CHAOS = 1897;
|
||||
|
||||
class ActionInfuriate52 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionInfuriate52() :
|
||||
ScriptAPI::ActionScript( 52 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto pPlayer = action.getSourceChara()->getAsPlayer();
|
||||
assert( pPlayer );
|
||||
uint8_t ib = pPlayer->gaugeWarGetIb();
|
||||
ib = std::min( 100, ib + 50 );
|
||||
pPlayer->gaugeWarSetIb( ib );
|
||||
if( pPlayer->getLevel() >= 72 )
|
||||
{
|
||||
auto pEffect = Sapphire::StatusEffect::make_StatusEffect( STATUS_ID_NASCENT_CHAOS, action.getSourceChara(), action.getSourceChara(), 30000, 3000 );
|
||||
action.getEffectbuilder()->applyStatusEffect( action.getSourceChara(), action.getSourceChara(), pEffect, 0 );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionInfuriate52 );
|
36
src/scripts/action/war/ActionInnerChaos16465.cpp
Normal file
36
src/scripts/action/war/ActionInnerChaos16465.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
const uint16_t STATUS_ID_NASCENT_CHAOS = 1897;
|
||||
|
||||
class ActionInnerChaos16465 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionInnerChaos16465() :
|
||||
ScriptAPI::ActionScript( 16465 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto effectEntry = action.getSourceChara()->getStatusEffectById( STATUS_ID_NASCENT_CHAOS );
|
||||
if( effectEntry.second )
|
||||
{
|
||||
action.getSourceChara()->removeStatusEffect( effectEntry.first );
|
||||
}
|
||||
else
|
||||
{
|
||||
action.disableGenericHandler();
|
||||
action.interrupt();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionInnerChaos16465 );
|
28
src/scripts/action/whm/ActionBenediction140.cpp
Normal file
28
src/scripts/action/whm/ActionBenediction140.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
using namespace Sapphire;
|
||||
|
||||
class ActionBenediction140 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionBenediction140() :
|
||||
ScriptAPI::ActionScript( 140 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto pTarget = action.getHitChara();
|
||||
if( pTarget )
|
||||
{
|
||||
action.getEffectbuilder()->heal( pTarget, pTarget, pTarget->getMaxHp() );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionBenediction140 );
|
33
src/scripts/action/whm/ActionCure120.cpp
Normal file
33
src/scripts/action/whm/ActionCure120.cpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
const uint16_t STATUS_ID_FREECURE = 155;
|
||||
|
||||
class ActionCure120 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionCure120() :
|
||||
ScriptAPI::ActionScript( 120 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
if( action.getSourceChara()->getLevel() >= 30 && Math::CalcStats::getRandomNumber0To100() < 15 )
|
||||
{
|
||||
auto pEffect = Sapphire::StatusEffect::make_StatusEffect( STATUS_ID_FREECURE, action.getSourceChara(), action.getSourceChara(), 15000, 3000 );
|
||||
action.getEffectbuilder()->applyStatusEffect( action.getSourceChara(), action.getSourceChara(), pEffect );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionCure120 );
|
42
src/scripts/action/whm/ActionCureII135.cpp
Normal file
42
src/scripts/action/whm/ActionCureII135.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
const uint16_t STATUS_ID_FREECURE = 155;
|
||||
|
||||
class ActionCureII135 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionCureII135() :
|
||||
ScriptAPI::ActionScript( 135 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto effectEntry = action.getSourceChara()->getStatusEffectById( STATUS_ID_FREECURE );
|
||||
if( effectEntry.second )
|
||||
{
|
||||
action.getSourceChara()->removeStatusEffect( effectEntry.first );
|
||||
}
|
||||
}
|
||||
|
||||
void onBeforePreCheck( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto effectEntry = action.getSourceChara()->getStatusEffectById( STATUS_ID_FREECURE );
|
||||
if( effectEntry.second )
|
||||
{
|
||||
action.setPrimaryCost( Common::ActionPrimaryCostType::None, 0 );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionCureII135 );
|
|
@ -11,6 +11,8 @@
|
|||
#include "Actor/Player.h"
|
||||
#include "Actor/BNpc.h"
|
||||
|
||||
#include "Action/ActionLut.h"
|
||||
|
||||
#include "Territory/Territory.h"
|
||||
|
||||
#include <Network/CommonActorControl.h>
|
||||
|
@ -49,7 +51,10 @@ Action::Action::Action( Entity::CharaPtr caster, uint32_t actionId, uint16_t seq
|
|||
m_targetId( 0 ),
|
||||
m_startTime( 0 ),
|
||||
m_interruptType( Common::ActionInterruptType::None ),
|
||||
m_sequence( sequence )
|
||||
m_sequence( sequence ),
|
||||
m_isAutoAttack( false ),
|
||||
m_disableGenericHandler( false ),
|
||||
m_shouldAlwaysCombo( false )
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -75,6 +80,13 @@ bool Action::Action::init()
|
|||
|
||||
m_castTimeMs = static_cast< uint32_t >( m_actionData->cast100ms * 100 );
|
||||
m_recastTimeMs = static_cast< uint32_t >( m_actionData->recast100ms * 100 );
|
||||
auto actionCategory = static_cast< Common::ActionCategory >( m_actionData->actionCategory );
|
||||
if( actionCategory == Common::ActionCategory::Spell || actionCategory == Common::ActionCategory::Weaponskill )
|
||||
{
|
||||
m_castTimeMs = static_cast< uint32_t >( m_castTimeMs * ( m_pSource->getStatValue( Common::BaseParam::Haste ) / 100.0f ) );
|
||||
m_recastTimeMs = static_cast< uint32_t >( m_recastTimeMs * ( m_pSource->getStatValue( Common::BaseParam::Haste ) / 100.0f ) );
|
||||
}
|
||||
|
||||
m_cooldownGroup = m_actionData->cooldownGroup;
|
||||
m_range = m_actionData->range;
|
||||
m_effectRange = m_actionData->effectRange;
|
||||
|
@ -107,6 +119,57 @@ bool Action::Action::init()
|
|||
m_primaryCostType = static_cast< Common::ActionPrimaryCostType >( m_actionData->primaryCostType );
|
||||
m_primaryCost = m_actionData->primaryCostValue;
|
||||
|
||||
if( m_primaryCostType != Common::ActionPrimaryCostType::None )
|
||||
{
|
||||
for( auto const& entry : m_pSource->getStatusEffectMap() )
|
||||
{
|
||||
if( entry.second->getParam() == 65436 ) // todo: decode this shit and figure out exact percentage to apply to primary cost, this magic number is 0%
|
||||
{
|
||||
/*
|
||||
Since the client is displaying correctly without additional data, there should be a "primary primary cost type" defined for each class.
|
||||
In the case of 65436, on whm, mp cost is removed, on drk, blood cost is removed but mp cost remains.
|
||||
*/
|
||||
auto affectedPrimaryCost = Common::ActionPrimaryCostType::MagicPoints;
|
||||
switch( m_pSource->getClass() )
|
||||
{
|
||||
case Common::ClassJob::Marauder:
|
||||
case Common::ClassJob::Warrior:
|
||||
{
|
||||
affectedPrimaryCost = Common::ActionPrimaryCostType::WARGauge;
|
||||
break;
|
||||
}
|
||||
case Common::ClassJob::Darkknight:
|
||||
{
|
||||
affectedPrimaryCost = Common::ActionPrimaryCostType::DRKGauge;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( m_primaryCostType == affectedPrimaryCost )
|
||||
{
|
||||
setPrimaryCost( Common::ActionPrimaryCostType::None, 0 );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( auto player = m_pSource->getAsPlayer() )
|
||||
{
|
||||
switch( player->getClass() )
|
||||
{
|
||||
case Common::ClassJob::Darkknight:
|
||||
{
|
||||
if( m_primaryCostType == Common::ActionPrimaryCostType::MagicPoints && player->gaugeDrkGetDarkArts() )
|
||||
{
|
||||
setPrimaryCost( Common::ActionPrimaryCostType::None, 0 );
|
||||
player->gaugeDrkSetDarkArts( false );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*if( !m_actionData->targetArea )
|
||||
{
|
||||
// override pos to target position
|
||||
|
@ -134,6 +197,8 @@ bool Action::Action::init()
|
|||
|
||||
addDefaultActorFilters();
|
||||
|
||||
m_effectBuilder->setAnimationLock( getAnimationLock() );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -172,11 +237,6 @@ Common::ActionInterruptType Action::Action::getInterruptType() const
|
|||
return m_interruptType;
|
||||
}
|
||||
|
||||
void Action::Action::setInterrupted( Common::ActionInterruptType type )
|
||||
{
|
||||
m_interruptType = type;
|
||||
}
|
||||
|
||||
uint32_t Action::Action::getCastTime() const
|
||||
{
|
||||
return m_castTimeMs;
|
||||
|
@ -205,7 +265,6 @@ bool Action::Action::update()
|
|||
|
||||
if( isInterrupted() )
|
||||
{
|
||||
interrupt();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -240,7 +299,6 @@ bool Action::Action::update()
|
|||
if( !m_pTarget->isAlive() )
|
||||
{
|
||||
// interrupt the cast if target died
|
||||
setInterrupted( Common::ActionInterruptType::RegularInterrupt );
|
||||
interrupt();
|
||||
return true;
|
||||
}
|
||||
|
@ -283,37 +341,31 @@ void Action::Action::start()
|
|||
}
|
||||
}
|
||||
|
||||
// todo: m_recastTimeMs needs to be adjusted for player sks/sps
|
||||
auto actionStartPkt = makeActorControlSelf( m_pSource->getId(), ActorControlType::ActionStart, 1, getId(),
|
||||
m_recastTimeMs / 10 );
|
||||
player->queuePacket( actionStartPkt );
|
||||
if( player )
|
||||
{
|
||||
// todo: m_recastTimeMs needs to be adjusted for player sks/sps
|
||||
auto actionStartPkt = makeActorControlSelf( m_pSource->getId(), ActorControlType::ActionStart, 1, getId(),
|
||||
m_recastTimeMs / 10 );
|
||||
player->queuePacket( actionStartPkt );
|
||||
}
|
||||
|
||||
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
|
||||
|
||||
// check the lut too and see if we have something usable, otherwise cancel the cast
|
||||
if( !scriptMgr.onStart( *this ) && !ActionLut::validEntryExists( static_cast< uint16_t >( getId() ) ) )
|
||||
{
|
||||
// script not implemented and insufficient lut data (no potencies)
|
||||
interrupt();
|
||||
|
||||
if( player )
|
||||
{
|
||||
player->sendUrgent( "Action not implemented, missing script/lut entry for action#{0}", getId() );
|
||||
player->setCurrentAction( nullptr );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
scriptMgr.onStart( *this );
|
||||
|
||||
// instantly finish cast if there's no cast time
|
||||
if( !hasCastTime() )
|
||||
execute();
|
||||
}
|
||||
|
||||
void Action::Action::interrupt()
|
||||
void Action::Action::interrupt( ActionInterruptType type )
|
||||
{
|
||||
if( isInterrupted() )
|
||||
return;
|
||||
|
||||
assert( m_pSource );
|
||||
|
||||
m_interruptType = type;
|
||||
|
||||
// things that aren't players don't care about cooldowns and state flags
|
||||
if( m_pSource->isPlayer() )
|
||||
{
|
||||
|
@ -326,7 +378,7 @@ void Action::Action::interrupt()
|
|||
player->unsetStateFlag( PlayerStateFlag::Casting );
|
||||
}
|
||||
|
||||
if( hasCastTime() )
|
||||
if( m_startTime > 0 && hasCastTime() )
|
||||
{
|
||||
uint8_t interruptEffect = 0;
|
||||
if( m_interruptType == ActionInterruptType::DamageInterrupt )
|
||||
|
@ -404,49 +456,15 @@ void Action::Action::execute()
|
|||
|
||||
std::pair< uint32_t, Common::ActionHitSeverityType > Action::Action::calcDamage( uint32_t potency )
|
||||
{
|
||||
// todo: what do for npcs?
|
||||
auto wepDmg = 1.f;
|
||||
|
||||
if( auto player = m_pSource->getAsPlayer() )
|
||||
{
|
||||
auto item = player->getEquippedWeapon();
|
||||
assert( item );
|
||||
|
||||
auto role = player->getRole();
|
||||
if( role == Common::Role::RangedMagical || role == Common::Role::Healer )
|
||||
{
|
||||
wepDmg = item->getMagicalDmg();
|
||||
}
|
||||
else
|
||||
{
|
||||
wepDmg = item->getPhysicalDmg();
|
||||
}
|
||||
}
|
||||
|
||||
return Math::CalcStats::calcActionDamage( *m_pSource, potency, wepDmg );
|
||||
if( m_isAutoAttack )
|
||||
return Math::CalcStats::calcAutoAttackDamage( *m_pSource, potency );
|
||||
else
|
||||
return Math::CalcStats::calcActionDamage( this, *m_pSource, potency, Math::CalcStats::getWeaponDamage( m_pSource ) );
|
||||
}
|
||||
|
||||
std::pair< uint32_t, Common::ActionHitSeverityType > Action::Action::calcHealing( uint32_t potency )
|
||||
{
|
||||
auto wepDmg = 1.f;
|
||||
|
||||
if( auto player = m_pSource->getAsPlayer() )
|
||||
{
|
||||
auto item = player->getEquippedWeapon();
|
||||
assert( item );
|
||||
|
||||
auto role = player->getRole();
|
||||
if( role == Common::Role::RangedMagical || role == Common::Role::Healer )
|
||||
{
|
||||
wepDmg = item->getMagicalDmg();
|
||||
}
|
||||
else
|
||||
{
|
||||
wepDmg = item->getPhysicalDmg();
|
||||
}
|
||||
}
|
||||
|
||||
return Math::CalcStats::calcActionHealing( *m_pSource, potency, wepDmg );
|
||||
return Math::CalcStats::calcActionHealing( this, *m_pSource, potency, Math::CalcStats::getWeaponDamage( m_pSource ) );
|
||||
}
|
||||
|
||||
void Action::Action::buildEffects()
|
||||
|
@ -454,92 +472,258 @@ void Action::Action::buildEffects()
|
|||
snapshotAffectedActors( m_hitActors );
|
||||
|
||||
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
|
||||
auto hasLutEntry = hasValidLutEntry();
|
||||
|
||||
if( !scriptMgr.onExecute( *this ) && !hasLutEntry )
|
||||
for( const auto& statusIt : m_pSource->getStatusEffectMap() )
|
||||
{
|
||||
if( auto player = m_pSource->getAsPlayer() )
|
||||
{
|
||||
player->sendUrgent( "missing lut entry for action#{}", getId() );
|
||||
}
|
||||
|
||||
return;
|
||||
statusIt.second->onActionExecute( this );
|
||||
}
|
||||
|
||||
if( !hasLutEntry || m_hitActors.empty() )
|
||||
scriptMgr.onExecute( *this );
|
||||
|
||||
if( isInterrupted() )
|
||||
return;
|
||||
|
||||
if( m_disableGenericHandler || !hasValidLutEntry() )
|
||||
{
|
||||
// send any effect packet added by script or an empty one just to play animation for other players
|
||||
m_effectBuilder->buildAndSendPackets();
|
||||
scriptMgr.onBeforeBuildEffect( *this, 0, 0 );
|
||||
m_effectBuilder->buildAndSendPackets();
|
||||
scriptMgr.onAfterBuildEffect( *this );
|
||||
return;
|
||||
}
|
||||
|
||||
// no script exists but we have a valid lut entry
|
||||
if( auto player = getSourceChara()->getAsPlayer() )
|
||||
|
||||
// we have a valid lut entry
|
||||
auto player = getSourceChara()->getAsPlayer();
|
||||
if( player )
|
||||
{
|
||||
player->sendDebug( "Hit target: pot: {} (c: {}, f: {}, r: {}), heal pot: {}, mpp: {}",
|
||||
m_lutEntry.potency, m_lutEntry.comboPotency, m_lutEntry.flankPotency, m_lutEntry.rearPotency,
|
||||
m_lutEntry.curePotency, m_lutEntry.restoreMPPercentage );
|
||||
player->sendDebug( "type: {}, dpot: {} (dcpot: {}, ddpot: {}), hpot: {}, ss: {}, ts: {}, bonus: {}, breq: {}, bdata: {}",
|
||||
m_actionData->attackType,
|
||||
m_lutEntry.damagePotency, m_lutEntry.damageComboPotency, m_lutEntry.damageDirectionalPotency,
|
||||
m_lutEntry.healPotency, m_lutEntry.selfStatus, m_lutEntry.targetStatus,
|
||||
m_lutEntry.bonusEffect, m_lutEntry.bonusRequirement, m_lutEntry.getRawBonusData() );
|
||||
}
|
||||
|
||||
// when aoe, these effects are in the target whatever is hit first
|
||||
bool shouldRestoreMP = true;
|
||||
bool shouldApplyComboSucceedEffect = true;
|
||||
uint8_t victimCounter = 0, validVictimCounter = 0;
|
||||
|
||||
for( auto& actor : m_hitActors )
|
||||
{
|
||||
if( m_lutEntry.potency > 0 )
|
||||
victimCounter++;
|
||||
bool shouldHitThisTarget = true;
|
||||
for( const auto& statusIt : getSourceChara()->getStatusEffectMap() )
|
||||
{
|
||||
auto dmg = calcDamage( isCorrectCombo() ? m_lutEntry.comboPotency : m_lutEntry.potency );
|
||||
m_effectBuilder->damage( actor, actor, dmg.first, dmg.second );
|
||||
bool result = statusIt.second->onActionHitTarget( this, actor, victimCounter );
|
||||
if( !result )
|
||||
shouldHitThisTarget = false;
|
||||
}
|
||||
if( !shouldHitThisTarget )
|
||||
continue;
|
||||
if( m_lutEntry.damagePotency > 0 )
|
||||
{
|
||||
Common::AttackType attackType = static_cast< Common::AttackType >( m_actionData->attackType );
|
||||
actor->onActionHostile( m_pSource );
|
||||
|
||||
auto dmg = calcDamage( isCorrectCombo() ? m_lutEntry.damageComboPotency : m_lutEntry.damagePotency );
|
||||
if( victimCounter > 1 )
|
||||
{
|
||||
if( m_lutEntry.bonusEffect & Common::ActionBonusEffect::DamageFallOff )
|
||||
{
|
||||
if( checkActionBonusRequirement() )
|
||||
{
|
||||
dmg.first = static_cast< uint32_t >( 1.0 * dmg.first * ( m_lutEntry.getDamageFallOffPercentage() / 100.0 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
dmg.first = Math::CalcStats::applyDamageReceiveMultiplier( *actor, dmg.first, getActionTypeFilterFromAttackType( attackType ) );
|
||||
|
||||
float originalDamage = dmg.first;
|
||||
bool dodged = false;
|
||||
float blocked = 0;
|
||||
float parried = 0;
|
||||
|
||||
if( dmg.first > 0 )
|
||||
actor->onActionHostile( m_pSource );
|
||||
|
||||
if( isCorrectCombo() && shouldApplyComboSucceedEffect )
|
||||
{
|
||||
m_effectBuilder->comboSucceed( actor );
|
||||
shouldApplyComboSucceedEffect = false;
|
||||
dodged = Math::CalcStats::calcDodge( *actor );
|
||||
|
||||
if( !dodged && dmg.second == Common::ActionHitSeverityType::NormalDamage && actor->isPlayer() )
|
||||
{
|
||||
blocked = Math::CalcStats::calcBlock( *actor, dmg.first );
|
||||
}
|
||||
|
||||
if( !dodged && blocked == 0 && dmg.second == Common::ActionHitSeverityType::NormalDamage && actor->isPlayer() )
|
||||
{
|
||||
if( isPhysical() )
|
||||
{
|
||||
parried = Math::CalcStats::calcParry( *actor, dmg.first );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( dodged )
|
||||
dmg.first = 0;
|
||||
else
|
||||
{
|
||||
dmg.first -= blocked;
|
||||
dmg.first -= parried;
|
||||
}
|
||||
|
||||
if( dmg.first > 0 )
|
||||
{
|
||||
dmg.first = actor->applyShieldProtection( dmg.first );
|
||||
if( blocked > 0 )
|
||||
m_effectBuilder->blockedDamage( actor, actor, dmg.first, static_cast< uint16_t >( blocked / originalDamage * 100 ) , dmg.first == 0 ? Common::ActionEffectResultFlag::Absorbed : Common::ActionEffectResultFlag::None, getExecutionDelay() + victimCounter * 100 );
|
||||
else if (parried > 0 )
|
||||
m_effectBuilder->parriedDamage( actor, actor, dmg.first, static_cast< uint16_t >( parried / originalDamage * 100 ), dmg.first == 0 ? Common::ActionEffectResultFlag::Absorbed : Common::ActionEffectResultFlag::None, getExecutionDelay() + victimCounter * 100 );
|
||||
else
|
||||
m_effectBuilder->damage( actor, actor, dmg.first, dmg.second, dmg.first == 0 ? Common::ActionEffectResultFlag::Absorbed : Common::ActionEffectResultFlag::None, getExecutionDelay() + victimCounter * 100 );
|
||||
|
||||
auto reflectDmg = Math::CalcStats::calcDamageReflect( m_pSource, actor, dmg.first,
|
||||
attackType == Common::AttackType::Physical ? Common::ActionTypeFilter::Physical :
|
||||
( attackType == Common::AttackType::Magical ? Common::ActionTypeFilter::Magical : Common::ActionTypeFilter::Unknown ) );
|
||||
if( reflectDmg.first > 0 )
|
||||
{
|
||||
m_effectBuilder->damage( actor, m_pSource, reflectDmg.first, reflectDmg.second, Common::ActionEffectResultFlag::Reflected, getExecutionDelay() + victimCounter * 100 );
|
||||
}
|
||||
|
||||
auto absorb = Math::CalcStats::calcAbsorbHP( m_pSource, dmg.first );
|
||||
if( absorb > 0 )
|
||||
{
|
||||
if( absorb > actor->getHp() )
|
||||
absorb = actor->getHp();
|
||||
m_effectBuilder->heal( actor, m_pSource, absorb, Common::ActionHitSeverityType::NormalHeal, Common::ActionEffectResultFlag::EffectOnSource, getExecutionDelay() + victimCounter * 100 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( dodged )
|
||||
{
|
||||
m_effectBuilder->dodge( actor, actor );
|
||||
}
|
||||
else
|
||||
{
|
||||
// todo: no effect or invulnerable
|
||||
}
|
||||
}
|
||||
|
||||
if( !dodged )
|
||||
{
|
||||
if( ( !isComboAction() || isCorrectCombo() ) )
|
||||
{
|
||||
if ( !m_actionData->preservesCombo ) // this matches retail packet, on all standalone actions even casts.
|
||||
{
|
||||
m_effectBuilder->startCombo( actor, getId() ); // this is on all targets hit
|
||||
}
|
||||
}
|
||||
|
||||
if( m_lutEntry.bonusEffect & Common::ActionBonusEffect::SelfHeal )
|
||||
{
|
||||
if( checkActionBonusRequirement() )
|
||||
{
|
||||
auto heal = calcHealing( m_lutEntry.getSelfHealPotency() );
|
||||
heal.first = Math::CalcStats::applyHealingReceiveMultiplier( *m_pSource, heal.first );
|
||||
m_effectBuilder->heal( actor, m_pSource, heal.first, heal.second, Common::ActionEffectResultFlag::EffectOnSource );
|
||||
}
|
||||
}
|
||||
|
||||
if( validVictimCounter == 0 )
|
||||
{
|
||||
if( isCorrectCombo() )
|
||||
m_effectBuilder->comboSucceed( actor );
|
||||
|
||||
if( m_isAutoAttack && m_pSource->isPlayer() )
|
||||
{
|
||||
if( auto player = m_pSource->getAsPlayer() )
|
||||
{
|
||||
if( player->getClass() == Common::ClassJob::Paladin )
|
||||
{
|
||||
player->gaugePldSetOath( std::min( 100, player->gaugePldGetOath() + 5 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( m_lutEntry.bonusEffect & Common::ActionBonusEffect::GainMPPercentage )
|
||||
{
|
||||
if( checkActionBonusRequirement() )
|
||||
m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.getMPGainPercentage() / 100, Common::ActionEffectResultFlag::EffectOnSource );
|
||||
}
|
||||
|
||||
if( m_lutEntry.bonusEffect & Common::ActionBonusEffect::GainJobResource )
|
||||
{
|
||||
if( checkActionBonusRequirement() )
|
||||
{
|
||||
switch( m_lutEntry.getAffectedJob() )
|
||||
{
|
||||
case Common::ClassJob::Marauder:
|
||||
case Common::ClassJob::Warrior:
|
||||
{
|
||||
player->gaugeWarSetIb( std::min( 100, player->gaugeWarGetIb() + m_lutEntry.getJobResourceGain() ) );
|
||||
break;
|
||||
}
|
||||
case Common::ClassJob::Darkknight:
|
||||
{
|
||||
player->gaugeDrkSetBlood( std::min( 100, player->gaugeDrkGetBlood() + m_lutEntry.getJobResourceGain() ) );
|
||||
break;
|
||||
}
|
||||
case Common::ClassJob::Gunbreaker:
|
||||
{
|
||||
player->gaugeGnbSetAmmo( std::min( 2, player->gaugeGnbGetAmmo() + m_lutEntry.getJobResourceGain() ) );
|
||||
break;
|
||||
}
|
||||
case Common::ClassJob::Samurai:
|
||||
{
|
||||
player->gaugeSamSetKenki( std::min( 100, player->gaugeSamGetKenki() + m_lutEntry.getJobResourceGain() ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( m_lutEntry.bonusEffect & Common::ActionBonusEffect::GainJobTimer )
|
||||
{
|
||||
if( checkActionBonusRequirement() )
|
||||
{
|
||||
switch( m_lutEntry.getAffectedJob() )
|
||||
{
|
||||
case Common::ClassJob::Darkknight:
|
||||
{
|
||||
player->gaugeDrkSetDarkSideTimer( std::min( 60000, player->gaugeDrkGetDarkSideTimer() + m_lutEntry.getJobTimerGain() ), true );
|
||||
break;
|
||||
}
|
||||
case Common::ClassJob::Dragoon:
|
||||
{
|
||||
player->gaugeDrgSetDragonTimer( std::min( 30000, player->gaugeDrgGetDragonTimer() + m_lutEntry.getJobTimerGain() ), true );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
validVictimCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
if( m_lutEntry.healPotency > 0 )
|
||||
{
|
||||
auto heal = calcHealing( m_lutEntry.healPotency );
|
||||
heal.first = Math::CalcStats::applyHealingReceiveMultiplier( *actor, heal.first );
|
||||
m_effectBuilder->heal( actor, actor, heal.first, heal.second, Common::ActionEffectResultFlag::None, getExecutionDelay() + victimCounter * 100 );
|
||||
}
|
||||
|
||||
if( m_lutEntry.targetStatus != 0 )
|
||||
{
|
||||
if( !isComboAction() || isCorrectCombo() )
|
||||
{
|
||||
if( m_lutEntry.curePotency > 0 ) // actions with self heal
|
||||
{
|
||||
auto heal = calcHealing( m_lutEntry.curePotency );
|
||||
m_effectBuilder->heal( actor, m_pSource, heal.first, heal.second, Common::ActionEffectResultFlag::EffectOnSource );
|
||||
}
|
||||
|
||||
if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP )
|
||||
{
|
||||
m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionEffectResultFlag::EffectOnSource );
|
||||
shouldRestoreMP = false;
|
||||
}
|
||||
|
||||
if ( !m_actionData->preservesCombo ) // we need something like m_actionData->hasNextComboAction
|
||||
{
|
||||
m_effectBuilder->startCombo( actor, getId() ); // this is on all targets hit
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( m_lutEntry.curePotency > 0 )
|
||||
{
|
||||
auto heal = calcHealing( m_lutEntry.curePotency );
|
||||
m_effectBuilder->heal( actor, actor, heal.first, heal.second );
|
||||
|
||||
if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP )
|
||||
{
|
||||
m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionEffectResultFlag::EffectOnSource );
|
||||
shouldRestoreMP = false;
|
||||
}
|
||||
}
|
||||
else if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP )
|
||||
{
|
||||
m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionEffectResultFlag::EffectOnSource );
|
||||
shouldRestoreMP = false;
|
||||
m_effectBuilder->applyStatusEffect( actor, m_pSource, m_lutEntry.targetStatus, m_lutEntry.targetStatusDuration, m_lutEntry.targetStatusParam, getExecutionDelay() + victimCounter * 100 );
|
||||
}
|
||||
}
|
||||
|
||||
if( m_lutEntry.selfStatus != 0 )
|
||||
{
|
||||
if( !isComboAction() || isCorrectCombo() )
|
||||
m_effectBuilder->applyStatusEffect( m_pSource, m_pSource, m_lutEntry.selfStatus, m_lutEntry.selfStatusDuration, m_lutEntry.selfStatusParam );
|
||||
}
|
||||
|
||||
scriptMgr.onBeforeBuildEffect( *this, victimCounter, validVictimCounter );
|
||||
m_effectBuilder->buildAndSendPackets();
|
||||
scriptMgr.onAfterBuildEffect( *this );
|
||||
|
||||
// at this point we're done with it and no longer need it
|
||||
m_effectBuilder.reset();
|
||||
|
@ -547,6 +731,12 @@ void Action::Action::buildEffects()
|
|||
|
||||
bool Action::Action::preCheck()
|
||||
{
|
||||
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
|
||||
scriptMgr.onBeforePreCheck( *this );
|
||||
|
||||
if( isInterrupted() )
|
||||
return false;
|
||||
|
||||
if( auto player = m_pSource->getAsPlayer() )
|
||||
{
|
||||
if( !playerPreCheck( *player ) )
|
||||
|
@ -619,6 +809,9 @@ void Action::Action::setAdditionalData( uint32_t data )
|
|||
|
||||
bool Action::Action::isCorrectCombo() const
|
||||
{
|
||||
if( m_shouldAlwaysCombo )
|
||||
return true;
|
||||
|
||||
auto lastActionId = m_pSource->getLastComboActionId();
|
||||
|
||||
if( lastActionId == 0 )
|
||||
|
@ -634,6 +827,11 @@ bool Action::Action::isComboAction() const
|
|||
return m_actionData->actionCombo != 0;
|
||||
}
|
||||
|
||||
void Sapphire::World::Action::Action::setAlwaysCombo()
|
||||
{
|
||||
m_shouldAlwaysCombo = true;
|
||||
}
|
||||
|
||||
bool Action::Action::primaryCostCheck( bool subtractCosts )
|
||||
{
|
||||
switch( m_primaryCostType )
|
||||
|
@ -666,6 +864,168 @@ bool Action::Action::primaryCostCheck( bool subtractCosts )
|
|||
return true;
|
||||
}
|
||||
|
||||
case Common::ActionPrimaryCostType::StatusEffect:
|
||||
{
|
||||
auto statusEntry = m_pSource->getStatusEffectById( m_primaryCost );
|
||||
|
||||
if( !statusEntry.second )
|
||||
return false;
|
||||
|
||||
if( subtractCosts )
|
||||
m_pSource->removeStatusEffect( statusEntry.first );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case Common::ActionPrimaryCostType::WARGauge:
|
||||
{
|
||||
auto pPlayer = m_pSource->getAsPlayer();
|
||||
if( pPlayer )
|
||||
{
|
||||
auto ib = pPlayer->gaugeWarGetIb();
|
||||
if( ib >= m_primaryCost )
|
||||
{
|
||||
if( subtractCosts )
|
||||
pPlayer->gaugeWarSetIb( ib - m_primaryCost );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
case Common::ActionPrimaryCostType::PLDGauge:
|
||||
{
|
||||
auto pPlayer = m_pSource->getAsPlayer();
|
||||
if( pPlayer )
|
||||
{
|
||||
auto oath = pPlayer->gaugePldGetOath();
|
||||
if( oath >= m_primaryCost )
|
||||
{
|
||||
if( subtractCosts )
|
||||
pPlayer->gaugePldSetOath( oath - m_primaryCost );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
case Common::ActionPrimaryCostType::WHMBloodLily:
|
||||
{
|
||||
auto pPlayer = m_pSource->getAsPlayer();
|
||||
if( pPlayer )
|
||||
{
|
||||
auto bloodLily = pPlayer->gaugeWhmGetBloodLily();
|
||||
if( bloodLily >= m_primaryCost )
|
||||
{
|
||||
if( subtractCosts )
|
||||
pPlayer->gaugeWhmSetLilies( pPlayer->gaugeWhmGetLily(), bloodLily - m_primaryCost );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
case Common::ActionPrimaryCostType::WHMLily:
|
||||
{
|
||||
auto pPlayer = m_pSource->getAsPlayer();
|
||||
if( pPlayer )
|
||||
{
|
||||
auto lily = pPlayer->gaugeWhmGetLily();
|
||||
if( lily >= m_primaryCost )
|
||||
{
|
||||
if( subtractCosts )
|
||||
{
|
||||
lily -= m_primaryCost;
|
||||
auto bloodLily = pPlayer->gaugeWhmGetBloodLily();
|
||||
if( pPlayer->getLevel() >= 74 )
|
||||
{
|
||||
bloodLily = std::min( 3, bloodLily + m_primaryCost );
|
||||
}
|
||||
pPlayer->gaugeWhmSetLilies( lily, bloodLily );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
case Common::ActionPrimaryCostType::DRKGauge:
|
||||
{
|
||||
auto pPlayer = m_pSource->getAsPlayer();
|
||||
if( pPlayer )
|
||||
{
|
||||
auto blood = pPlayer->gaugeDrkGetBlood();
|
||||
if( blood >= m_primaryCost )
|
||||
{
|
||||
if( subtractCosts )
|
||||
pPlayer->gaugeDrkSetBlood( blood - m_primaryCost );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
case Common::ActionPrimaryCostType::GNBAmmo:
|
||||
{
|
||||
auto pPlayer = m_pSource->getAsPlayer();
|
||||
if( pPlayer )
|
||||
{
|
||||
auto ammo = pPlayer->gaugeGnbGetAmmo();
|
||||
if( ammo >= m_primaryCost )
|
||||
{
|
||||
if( subtractCosts )
|
||||
pPlayer->gaugeGnbSetAmmo( ammo - m_primaryCost );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
case Common::ActionPrimaryCostType::SAMKenki:
|
||||
{
|
||||
auto pPlayer = m_pSource->getAsPlayer();
|
||||
if( pPlayer )
|
||||
{
|
||||
auto kenki = pPlayer->gaugeSamGetKenki();
|
||||
if( kenki >= m_primaryCost )
|
||||
{
|
||||
if( subtractCosts )
|
||||
pPlayer->gaugeSamSetKenki( kenki - m_primaryCost );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
case Common::ActionPrimaryCostType::SAMSen:
|
||||
{
|
||||
auto pPlayer = m_pSource->getAsPlayer();
|
||||
if( pPlayer )
|
||||
{
|
||||
// trust the client and assume the action is correct, you can cheat this by performing one sen midare :)
|
||||
if( pPlayer->gaugeSamHasAnySen() )
|
||||
{
|
||||
if( subtractCosts )
|
||||
pPlayer->gaugeSamSetSen( Common::SamSen::None );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
case Common::ActionPrimaryCostType::SAMMeditation:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// free casts, likely just pure ogcds
|
||||
case Common::ActionPrimaryCostType::None:
|
||||
{
|
||||
|
@ -751,10 +1111,12 @@ void Action::Action::addDefaultActorFilters()
|
|||
break;
|
||||
}
|
||||
|
||||
// case Common::CastType::RectangularAOE:
|
||||
// {
|
||||
// break;
|
||||
// }
|
||||
case Common::CastType::RectangularAOE:
|
||||
{
|
||||
auto filter = std::make_shared< World::Util::ActorFilterInRange >( m_pos, m_effectRange );
|
||||
addActorFilter( filter );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
|
@ -775,18 +1137,18 @@ bool Action::Action::preFilterActor( Sapphire::Entity::Actor& actor ) const
|
|||
if( kind != ObjKind::BattleNpc && kind != ObjKind::Player )
|
||||
return false;
|
||||
|
||||
if( !m_canTargetSelf && chara->getId() == m_pSource->getId() )
|
||||
if( m_lutEntry.damagePotency > 0 && chara->getId() == m_pSource->getId() ) // !m_canTargetSelf
|
||||
return false;
|
||||
|
||||
if( ( m_lutEntry.potency > 0 || m_lutEntry.curePotency > 0 ) && !chara->isAlive() ) // !m_canTargetDead not working for aoe
|
||||
if( ( m_lutEntry.damagePotency > 0 || m_lutEntry.healPotency > 0 ) && !chara->isAlive() ) // !m_canTargetDead not working for aoe
|
||||
return false;
|
||||
|
||||
if( m_lutEntry.potency > 0 && m_pSource->getObjKind() == chara->getObjKind() ) // !m_canTargetFriendly not working for aoe
|
||||
if( m_lutEntry.damagePotency > 0 && m_pSource->getObjKind() == chara->getObjKind() ) // !m_canTargetFriendly not working for aoe
|
||||
return false;
|
||||
|
||||
if( ( m_lutEntry.potency == 0 && m_lutEntry.curePotency > 0 ) && m_pSource->getObjKind() != chara->getObjKind() ) // !m_canTargetHostile not working for aoe
|
||||
if( ( m_lutEntry.damagePotency == 0 && m_lutEntry.healPotency > 0 ) && m_pSource->getObjKind() != chara->getObjKind() ) // !m_canTargetHostile not working for aoe
|
||||
return false;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -807,11 +1169,128 @@ Sapphire::Entity::CharaPtr Action::Action::getHitChara()
|
|||
|
||||
bool Action::Action::hasValidLutEntry() const
|
||||
{
|
||||
return m_lutEntry.potency != 0 || m_lutEntry.comboPotency != 0 || m_lutEntry.flankPotency != 0 || m_lutEntry.frontPotency != 0 ||
|
||||
m_lutEntry.rearPotency != 0 || m_lutEntry.curePotency != 0 || m_lutEntry.restoreMPPercentage != 0;
|
||||
return m_lutEntry.damagePotency != 0 || m_lutEntry.healPotency != 0 || m_lutEntry.selfStatus != 0 ||
|
||||
m_lutEntry.targetStatus != 0 || m_lutEntry.bonusEffect != 0;
|
||||
}
|
||||
|
||||
float Action::Action::getAnimationLock()
|
||||
{
|
||||
switch( static_cast< Common::ActionCategory >( m_actionData->actionCategory ) )
|
||||
{
|
||||
case Common::ActionCategory::Item:
|
||||
{
|
||||
return 1.1f;
|
||||
}
|
||||
case Common::ActionCategory::Mount:
|
||||
{
|
||||
return 0.1f;
|
||||
}
|
||||
}
|
||||
return hasCastTime() ? 0.1f : 0.6f;
|
||||
}
|
||||
|
||||
Action::EffectBuilderPtr Action::Action::getEffectbuilder()
|
||||
{
|
||||
return m_effectBuilder;
|
||||
}
|
||||
|
||||
Data::ActionPtr Action::Action::getActionData()
|
||||
{
|
||||
return m_actionData;
|
||||
}
|
||||
|
||||
Action::ActionEntry& Action::Action::getActionEntry()
|
||||
{
|
||||
return m_lutEntry;
|
||||
}
|
||||
|
||||
void Action::Action::setAutoAttack()
|
||||
{
|
||||
m_isAutoAttack = true;
|
||||
}
|
||||
|
||||
void Action::Action::disableGenericHandler()
|
||||
{
|
||||
m_disableGenericHandler = true;
|
||||
}
|
||||
|
||||
bool Action::Action::isPhysical() const
|
||||
{
|
||||
return isAttackTypePhysical( static_cast< Common::AttackType >( m_actionData->attackType ) );
|
||||
}
|
||||
|
||||
bool Action::Action::isMagical() const
|
||||
{
|
||||
return isAttackTypeMagical( static_cast< Common::AttackType >( m_actionData->attackType ) );
|
||||
}
|
||||
|
||||
bool Action::Action::isGCD() const
|
||||
{
|
||||
auto actionCategory = static_cast< Common::ActionCategory >( m_actionData->actionCategory );
|
||||
return actionCategory == Common::ActionCategory::Weaponskill || actionCategory == Common::ActionCategory::Spell;
|
||||
}
|
||||
|
||||
bool Action::Action::isWeaponSkill() const
|
||||
{
|
||||
auto actionCategory = static_cast< Common::ActionCategory >( m_actionData->actionCategory );
|
||||
return actionCategory == Common::ActionCategory::Weaponskill;
|
||||
}
|
||||
|
||||
bool Action::Action::isAttackTypePhysical( Common::AttackType attackType )
|
||||
{
|
||||
return attackType == Common::AttackType::Physical;
|
||||
}
|
||||
|
||||
bool Action::Action::isAttackTypeMagical( Common::AttackType attackType )
|
||||
{
|
||||
return attackType == Common::AttackType::Magical;
|
||||
}
|
||||
|
||||
ActionTypeFilter Sapphire::World::Action::Action::getActionTypeFilterFromAttackType( AttackType attackType )
|
||||
{
|
||||
switch( attackType )
|
||||
{
|
||||
case AttackType::Physical:
|
||||
return ActionTypeFilter::Physical;
|
||||
case AttackType::Magical:
|
||||
return ActionTypeFilter::Magical;
|
||||
case AttackType::Slashing:
|
||||
return static_cast< ActionTypeFilter >( static_cast< uint32_t >( ActionTypeFilter::Slashing ) + static_cast< uint32_t >( ActionTypeFilter::Physical ) );
|
||||
case AttackType::Piercing:
|
||||
return static_cast< ActionTypeFilter >( static_cast< uint32_t >( ActionTypeFilter::Piercing ) + static_cast< uint32_t >( ActionTypeFilter::Physical ) );
|
||||
case AttackType::Blunt:
|
||||
return static_cast< ActionTypeFilter >( static_cast< uint32_t >( ActionTypeFilter::Blunt ) + static_cast< uint32_t >( ActionTypeFilter::Physical ) );
|
||||
default:
|
||||
return ActionTypeFilter::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
void Action::Action::setPrimaryCost( Common::ActionPrimaryCostType type, uint16_t cost )
|
||||
{
|
||||
m_primaryCostType = type;
|
||||
m_primaryCost = cost;
|
||||
}
|
||||
|
||||
bool Action::Action::checkActionBonusRequirement()
|
||||
{
|
||||
if( !m_pSource->isPlayer() )
|
||||
return false;
|
||||
|
||||
if( m_lutEntry.bonusRequirement & Common::ActionBonusEffectRequirement::RequireCorrectCombo )
|
||||
{
|
||||
if( !isCorrectCombo() )
|
||||
return false;
|
||||
}
|
||||
if( m_lutEntry.bonusRequirement & Common::ActionBonusEffectRequirement::RequireCorrectPositional )
|
||||
{
|
||||
// todo
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t Action::Action::getExecutionDelay() const
|
||||
{
|
||||
// let's see how 3.x is going to do it
|
||||
return 600;
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
#include "Util/ActorFilter.h"
|
||||
#include "ForwardsZone.h"
|
||||
#include "EffectBuilder.h"
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
namespace Sapphire::Data
|
||||
{
|
||||
|
@ -40,7 +41,6 @@ namespace Sapphire::World::Action
|
|||
|
||||
bool isInterrupted() const;
|
||||
Common::ActionInterruptType getInterruptType() const;
|
||||
void setInterrupted( Common::ActionInterruptType type );
|
||||
|
||||
uint32_t getCastTime() const;
|
||||
void setCastTime( uint32_t castTime );
|
||||
|
@ -52,6 +52,14 @@ namespace Sapphire::World::Action
|
|||
|
||||
bool isComboAction() const;
|
||||
|
||||
void setAlwaysCombo();
|
||||
|
||||
void setAutoAttack();
|
||||
|
||||
void disableGenericHandler();
|
||||
|
||||
bool checkActionBonusRequirement();
|
||||
|
||||
/*!
|
||||
* @brief Checks if a chara has enough resources available to cast the action (tp/mp/etc)
|
||||
* @return true if they have the required resources
|
||||
|
@ -118,6 +126,22 @@ namespace Sapphire::World::Action
|
|||
*/
|
||||
Entity::CharaPtr getHitChara();
|
||||
|
||||
Data::ActionPtr getActionData();
|
||||
ActionEntry& getActionEntry();
|
||||
float getAnimationLock();
|
||||
|
||||
void setPrimaryCost( Common::ActionPrimaryCostType type, uint16_t cost );
|
||||
|
||||
bool isPhysical() const;
|
||||
bool isMagical() const;
|
||||
bool isGCD() const;
|
||||
bool isWeaponSkill() const;
|
||||
uint64_t getExecutionDelay() const;
|
||||
|
||||
static bool isAttackTypePhysical( Common::AttackType attackType );
|
||||
static bool isAttackTypeMagical( Common::AttackType attackType );
|
||||
static ActionTypeFilter getActionTypeFilterFromAttackType( AttackType attackType );
|
||||
|
||||
/*!
|
||||
* @brief Starts the cast. Finishes it immediately if there is no cast time (weaponskills).
|
||||
*/
|
||||
|
@ -133,7 +157,7 @@ namespace Sapphire::World::Action
|
|||
*
|
||||
* m_interruptType will have the reason why the action was interrupted (eg. damage, movement, ...)
|
||||
*/
|
||||
virtual void interrupt();
|
||||
virtual void interrupt( Common::ActionInterruptType type = Common::ActionInterruptType::RegularInterrupt );
|
||||
|
||||
/*!
|
||||
* @brief Called on each player update tick
|
||||
|
@ -180,6 +204,9 @@ namespace Sapphire::World::Action
|
|||
bool m_canTargetFriendly;
|
||||
bool m_canTargetHostile;
|
||||
bool m_canTargetDead;
|
||||
bool m_isAutoAttack;
|
||||
bool m_disableGenericHandler;
|
||||
bool m_shouldAlwaysCombo;
|
||||
|
||||
Common::ActionInterruptType m_interruptType;
|
||||
|
||||
|
|
|
@ -12,9 +12,9 @@ bool ActionLut::validEntryExists( uint16_t actionId )
|
|||
|
||||
const auto& entry = it->second;
|
||||
|
||||
// if all of the fields are 0, it's not 'valid' due to parse error or no useful data in the tooltip
|
||||
return entry.potency != 0 || entry.comboPotency != 0 || entry.flankPotency != 0 || entry.frontPotency != 0 ||
|
||||
entry.rearPotency != 0 || entry.curePotency != 0;
|
||||
// if all of these fields are 0, it's not 'valid' due to parse error or no useful data
|
||||
return entry.damagePotency != 0 || entry.healPotency != 0 || entry.selfStatus != 0 ||
|
||||
entry.targetStatus != 0 || entry.bonusEffect != 0;
|
||||
}
|
||||
|
||||
const ActionEntry& ActionLut::getEntry( uint16_t actionId )
|
||||
|
@ -24,4 +24,408 @@ const ActionEntry& ActionLut::getEntry( uint16_t actionId )
|
|||
assert( it != m_actionLut.end() );
|
||||
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
bool ActionLut::validStatusEffectExists( uint16_t statusId )
|
||||
{
|
||||
auto it = m_statusEffectTable.find( statusId );
|
||||
|
||||
if( it == m_statusEffectTable.end() )
|
||||
return false;
|
||||
|
||||
const auto& entry = it->second;
|
||||
|
||||
return entry.getType() != StatusEffectType::Invalid;
|
||||
}
|
||||
|
||||
const StatusEffectEntry& ActionLut::getStatusEffectEntry( uint16_t statusId )
|
||||
{
|
||||
auto it = m_statusEffectTable.find( statusId );
|
||||
|
||||
assert( it != m_statusEffectTable.end() );
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
Sapphire::World::Action::StatusEffectEntry::StatusEffectEntry( uint32_t type, int32_t v1, int32_t v2, int32_t v3, int32_t v4 )
|
||||
{
|
||||
init( static_cast< StatusEffectType >( type ), v1, v2, v3, v4 );
|
||||
}
|
||||
|
||||
void Sapphire::World::Action::StatusEffectEntry::init( StatusEffectType type, int32_t v1, int32_t v2, int32_t v3, int32_t v4 )
|
||||
{
|
||||
effectType = static_cast< uint32_t >( type );
|
||||
effectValue1 = v1;
|
||||
effectValue2 = v2;
|
||||
effectValue3 = v3;
|
||||
effectValue4 = v4;
|
||||
}
|
||||
|
||||
StatusEffectType Sapphire::World::Action::StatusEffectEntry::getType() const
|
||||
{
|
||||
return static_cast< StatusEffectType >( effectType );
|
||||
}
|
||||
|
||||
ActionTypeFilter Sapphire::World::Action::StatusEffectEntry::getActionTypeFilter() const
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::Dot:
|
||||
case StatusEffectType::Hot:
|
||||
case StatusEffectType::DamageMultiplier:
|
||||
case StatusEffectType::DamageReceiveMultiplier:
|
||||
case StatusEffectType::CritDHRateBonus:
|
||||
case StatusEffectType::DamageDealtTrigger:
|
||||
case StatusEffectType::DamageReceiveTrigger:
|
||||
return static_cast< ActionTypeFilter >( effectValue1 );
|
||||
default:
|
||||
return ActionTypeFilter::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t StatusEffectEntry::getMPRestoreTick() const
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::MPRestore:
|
||||
return effectValue1 * 10;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Sapphire::World::Action::StatusEffectEntry::getDotHotPotency() const
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::Dot:
|
||||
case StatusEffectType::Hot:
|
||||
return effectValue2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Sapphire::World::Action::StatusEffectEntry::getOutgoingDamageMultiplier() const
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::DamageMultiplier:
|
||||
return effectValue2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Sapphire::World::Action::StatusEffectEntry::getIncomingDamageMultiplier() const
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::DamageReceiveMultiplier:
|
||||
return effectValue2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Sapphire::World::Action::StatusEffectEntry::getOutgoingHealMultiplier() const
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::HealCastMultiplier:
|
||||
return effectValue2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Sapphire::World::Action::StatusEffectEntry::getIncomingHealMultiplier() const
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::HealReceiveMultiplier:
|
||||
return effectValue2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Sapphire::World::Action::StatusEffectEntry::getPotencyMultiplier() const
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::PotencyMultiplier:
|
||||
return effectValue4;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Sapphire::World::Action::StatusEffectEntry::getGCDBasedMPRestorePercentage() const
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::MPRestorePerGCD:
|
||||
return effectValue1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Sapphire::World::Action::StatusEffectEntry::getRemainingCharges() const
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::AlwaysCombo:
|
||||
case StatusEffectType::InstantCast:
|
||||
case StatusEffectType::PotencyMultiplier:
|
||||
return effectValue1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void Sapphire::World::Action::StatusEffectEntry::setRemainingCharges( int32_t charges )
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::AlwaysCombo:
|
||||
case StatusEffectType::InstantCast:
|
||||
case StatusEffectType::PotencyMultiplier:
|
||||
effectValue1 = charges;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Sapphire::World::Action::StatusEffectEntry::getRemainingShield() const
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::Shield:
|
||||
return effectValue1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Sapphire::World::Action::StatusEffectEntry::setRemainingShield( int32_t shield )
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::Shield:
|
||||
effectValue1 = shield;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Sapphire::World::Action::StatusEffectEntry::getHasteBonus() const
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::Haste:
|
||||
return effectValue1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Sapphire::World::Action::StatusEffectEntry::getBlockRateBonus() const
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::BlockParryRateBonus:
|
||||
return effectValue2;
|
||||
case StatusEffectType::DamageReceiveMultiplier:
|
||||
return effectValue3;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Sapphire::World::Action::StatusEffectEntry::getParryRateBonus() const
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::BlockParryRateBonus:
|
||||
return effectValue3;
|
||||
case StatusEffectType::DamageReceiveMultiplier:
|
||||
return effectValue4;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Sapphire::World::Action::StatusEffectEntry::getCritRateBonus() const
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::CritDHRateBonus:
|
||||
return effectValue2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Sapphire::World::Action::StatusEffectEntry::getDirectHitRateBonus() const
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::CritDHRateBonus:
|
||||
return effectValue3;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
StatusEffectTriggerResult Sapphire::World::Action::StatusEffectEntry::getTriggerResult() const
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::DamageDealtTrigger:
|
||||
case StatusEffectType::DamageReceiveTrigger:
|
||||
return static_cast< StatusEffectTriggerResult >( effectValue3 );
|
||||
default:
|
||||
return StatusEffectTriggerResult::None;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Sapphire::World::Action::StatusEffectEntry::getTriggerValue() const
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::DamageDealtTrigger:
|
||||
case StatusEffectType::DamageReceiveTrigger:
|
||||
return effectValue2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ActionTypeFilter Sapphire::World::Action::StatusEffectEntry::getTriggerDamageType() const
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::DamageDealtTrigger:
|
||||
case StatusEffectType::DamageReceiveTrigger:
|
||||
if( effectValue4 == 0 ) // data missing in lut, default to physical
|
||||
return ActionTypeFilter::Physical;
|
||||
else
|
||||
return static_cast< ActionTypeFilter >( effectValue4 );
|
||||
default:
|
||||
return ActionTypeFilter::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
bool Sapphire::World::Action::StatusEffectEntry::canApplyToAction( uint32_t actionId, uint8_t actionCategory ) const
|
||||
{
|
||||
switch( static_cast< StatusEffectType >( effectType ) )
|
||||
{
|
||||
case StatusEffectType::AlwaysCombo:
|
||||
case StatusEffectType::InstantCast:
|
||||
case StatusEffectType::PotencyMultiplier:
|
||||
case StatusEffectType::MPRestorePerGCD:
|
||||
if ( effectValue2 != 0 )
|
||||
{
|
||||
if( actionId != effectValue2 && actionId != effectValue3 && actionId != effectValue4 )
|
||||
return false;
|
||||
}
|
||||
else if ( effectValue3 != 0 )
|
||||
{
|
||||
uint8_t cat1 = static_cast< uint8_t >( effectValue3 );
|
||||
uint8_t cat2 = static_cast< uint8_t >( effectValue3 >> 8 );
|
||||
uint8_t cat3 = static_cast< uint8_t >( effectValue3 >> 16 );
|
||||
uint8_t cat4 = static_cast< uint8_t >( effectValue3 >> 24 );
|
||||
bool passed = false;
|
||||
if( cat1 != 0 && actionCategory == cat1 )
|
||||
passed = true;
|
||||
else if ( cat2 != 0 && actionCategory == cat2 )
|
||||
passed = true;
|
||||
else if ( cat3 != 0 && actionCategory == cat3 )
|
||||
passed = true;
|
||||
else if ( cat4 != 0 && actionCategory == cat4 )
|
||||
passed = true;
|
||||
if( !passed )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Sapphire::World::Action::ActionEntry::ActionEntry( uint16_t dp, uint16_t dcp, uint16_t ddp, uint16_t hp, uint16_t ss, uint32_t ssd, uint16_t ssp, uint16_t ts, uint32_t tsd, uint16_t tsp, uint8_t be, uint8_t br, uint32_t bdu32 )
|
||||
{
|
||||
damagePotency = dp;
|
||||
damageComboPotency = dcp;
|
||||
damageDirectionalPotency = ddp;
|
||||
healPotency = hp;
|
||||
selfStatus = ss;
|
||||
selfStatusDuration = ssd;
|
||||
selfStatusParam = ssp;
|
||||
targetStatus = ts;
|
||||
targetStatusDuration = tsd;
|
||||
targetStatusParam = tsp;
|
||||
bonusEffect = be;
|
||||
bonusRequirement = br;
|
||||
bonusDataUInt32 = bdu32;
|
||||
}
|
||||
|
||||
uint32_t Sapphire::World::Action::ActionEntry::getRawBonusData() const
|
||||
{
|
||||
return bonusDataUInt32;
|
||||
}
|
||||
|
||||
uint8_t Sapphire::World::Action::ActionEntry::getDamageFallOffPercentage() const
|
||||
{
|
||||
if( bonusEffect & ActionBonusEffect::DamageFallOff )
|
||||
return bonusDataByte1;
|
||||
return 100;
|
||||
}
|
||||
|
||||
uint16_t Sapphire::World::Action::ActionEntry::getSelfHealPotency() const
|
||||
{
|
||||
if( bonusEffect & ActionBonusEffect::SelfHeal )
|
||||
return bonusDataUInt16L;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t Sapphire::World::Action::ActionEntry::getMPGainPercentage() const
|
||||
{
|
||||
if( bonusEffect & ActionBonusEffect::GainMPPercentage )
|
||||
return bonusDataUInt16L;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ClassJob Sapphire::World::Action::ActionEntry::getAffectedJob() const
|
||||
{
|
||||
if( bonusEffect & ActionBonusEffect::GainJobResource ||
|
||||
bonusEffect & ActionBonusEffect::GainJobTimer )
|
||||
return static_cast< Common::ClassJob >( bonusDataByte3 );
|
||||
return ClassJob::Adventurer;
|
||||
}
|
||||
|
||||
uint8_t Sapphire::World::Action::ActionEntry::getJobResourceGain() const
|
||||
{
|
||||
if( bonusEffect & ActionBonusEffect::GainJobResource )
|
||||
return bonusDataByte4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t Sapphire::World::Action::ActionEntry::getJobTimerGain() const
|
||||
{
|
||||
if( bonusEffect & ActionBonusEffect::GainJobTimer )
|
||||
return bonusDataUInt16L;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t Sapphire::World::Action::ActionEntry::getCritRateBonus() const
|
||||
{
|
||||
if( bonusEffect & ActionBonusEffect::CritBonus )
|
||||
return bonusDataUInt16L;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t Sapphire::World::Action::ActionEntry::getDirectHitRateBonus() const
|
||||
{
|
||||
if( bonusEffect & ActionBonusEffect::DHBonus )
|
||||
return bonusDataUInt16L;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -2,29 +2,110 @@
|
|||
#define SAPPHIRE_ACTIONLUT_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include <Common.h>
|
||||
|
||||
using namespace Sapphire::Common;
|
||||
|
||||
namespace Sapphire::World::Action
|
||||
{
|
||||
struct ActionEntry
|
||||
{
|
||||
uint16_t potency;
|
||||
uint16_t comboPotency;
|
||||
uint16_t flankPotency;
|
||||
uint16_t frontPotency;
|
||||
uint16_t rearPotency;
|
||||
uint16_t curePotency;
|
||||
uint16_t restoreMPPercentage;
|
||||
public:
|
||||
uint16_t damagePotency;
|
||||
uint16_t damageComboPotency;
|
||||
uint16_t damageDirectionalPotency;
|
||||
uint16_t healPotency;
|
||||
uint16_t selfStatus;
|
||||
uint32_t selfStatusDuration;
|
||||
uint16_t selfStatusParam;
|
||||
uint16_t targetStatus;
|
||||
uint32_t targetStatusDuration;
|
||||
uint16_t targetStatusParam;
|
||||
uint8_t bonusEffect;
|
||||
uint8_t bonusRequirement;
|
||||
private:
|
||||
union
|
||||
{
|
||||
uint32_t bonusDataUInt32;
|
||||
struct
|
||||
{
|
||||
uint16_t bonusDataUInt16L;
|
||||
uint16_t bonusDataUInt16H;
|
||||
};
|
||||
struct
|
||||
{
|
||||
uint8_t bonusDataByte1;
|
||||
uint8_t bonusDataByte2;
|
||||
uint8_t bonusDataByte3;
|
||||
uint8_t bonusDataByte4;
|
||||
};
|
||||
};
|
||||
public:
|
||||
ActionEntry() = default;
|
||||
ActionEntry( uint16_t dp, uint16_t dcp, uint16_t ddp, uint16_t hp, uint16_t ss, uint32_t ssd, uint16_t ssp, uint16_t ts, uint32_t tsd, uint16_t tsp, uint8_t be, uint8_t br, uint32_t bdu32 );
|
||||
uint32_t getRawBonusData() const;
|
||||
uint8_t getDamageFallOffPercentage() const; // as the result percentage for 2nd (or more) victims, not the percentage to subtract from 100%
|
||||
uint16_t getSelfHealPotency() const;
|
||||
uint16_t getMPGainPercentage() const;
|
||||
ClassJob getAffectedJob() const;
|
||||
uint8_t getJobResourceGain() const;
|
||||
uint16_t getJobTimerGain() const;
|
||||
uint16_t getCritRateBonus() const;
|
||||
uint16_t getDirectHitRateBonus() const;
|
||||
};
|
||||
|
||||
struct StatusEffectEntry
|
||||
{
|
||||
private:
|
||||
uint32_t effectType;
|
||||
int32_t effectValue1;
|
||||
int32_t effectValue2;
|
||||
int32_t effectValue3;
|
||||
int32_t effectValue4;
|
||||
public:
|
||||
StatusEffectEntry() = default;
|
||||
StatusEffectEntry( uint32_t type, int32_t v1, int32_t v2, int32_t v3, int32_t v4 );
|
||||
void init( StatusEffectType type, int32_t v1, int32_t v2, int32_t v3, int32_t v4 );
|
||||
StatusEffectType getType() const;
|
||||
ActionTypeFilter getActionTypeFilter() const;
|
||||
int32_t getMPRestoreTick() const;
|
||||
int32_t getDotHotPotency() const;
|
||||
int32_t getOutgoingDamageMultiplier() const;
|
||||
int32_t getIncomingDamageMultiplier() const;
|
||||
int32_t getOutgoingHealMultiplier() const;
|
||||
int32_t getIncomingHealMultiplier() const;
|
||||
int32_t getPotencyMultiplier() const;
|
||||
int32_t getGCDBasedMPRestorePercentage() const;
|
||||
int32_t getRemainingCharges() const; // -1 is infinite charges
|
||||
void setRemainingCharges( int32_t charges );
|
||||
int32_t getRemainingShield() const;
|
||||
void setRemainingShield( int32_t shield );
|
||||
int32_t getHasteBonus() const;
|
||||
int32_t getBlockRateBonus() const;
|
||||
int32_t getParryRateBonus() const;
|
||||
int32_t getCritRateBonus() const;
|
||||
int32_t getDirectHitRateBonus() const;
|
||||
StatusEffectTriggerResult getTriggerResult() const;
|
||||
int32_t getTriggerValue() const;
|
||||
ActionTypeFilter getTriggerDamageType() const;
|
||||
|
||||
bool canApplyToAction( uint32_t actionId, uint8_t actionCategory ) const;
|
||||
};
|
||||
|
||||
class ActionLut
|
||||
{
|
||||
public:
|
||||
using Lut = std::unordered_map< uint16_t, ActionEntry >;
|
||||
using StatusEffectTable = std::unordered_map< uint16_t, StatusEffectEntry >;
|
||||
|
||||
static bool validEntryExists( uint16_t actionId );
|
||||
static const ActionEntry& getEntry( uint16_t actionId );
|
||||
|
||||
static bool validStatusEffectExists( uint16_t statusId );
|
||||
static const StatusEffectEntry& getStatusEffectEntry( uint16_t statusId );
|
||||
|
||||
static Lut m_actionLut;
|
||||
static StatusEffectTable m_statusEffectTable;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -19,18 +19,12 @@ using namespace Sapphire::Network::Packets;
|
|||
EffectBuilder::EffectBuilder( Entity::CharaPtr source, uint32_t actionId, uint16_t sequence ) :
|
||||
m_sourceChara( std::move( source ) ),
|
||||
m_actionId( actionId ),
|
||||
m_animationLock( 0.6f ),
|
||||
m_sequence( sequence )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
uint64_t EffectBuilder::getResultDelayMs()
|
||||
{
|
||||
// todo: actually figure this retarded shit out
|
||||
|
||||
return Common::Util::getTimeMs() + 850;
|
||||
}
|
||||
|
||||
void EffectBuilder::moveToResultList( Entity::CharaPtr& chara, EffectResultPtr result )
|
||||
{
|
||||
auto it = m_resolvedEffects.find( chara->getId() );
|
||||
|
@ -49,27 +43,48 @@ void EffectBuilder::moveToResultList( Entity::CharaPtr& chara, EffectResultPtr r
|
|||
it->second->push_back( std::move( result ) );
|
||||
}
|
||||
|
||||
void EffectBuilder::heal( Entity::CharaPtr& effectTarget, Entity::CharaPtr& healingTarget, uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag )
|
||||
void EffectBuilder::heal( Entity::CharaPtr& effectTarget, Entity::CharaPtr& healingTarget, uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag, uint64_t resultDelayMs )
|
||||
{
|
||||
EffectResultPtr nextResult = make_EffectResult( healingTarget, getResultDelayMs() );
|
||||
EffectResultPtr nextResult = make_EffectResult( healingTarget, Common::Util::getTimeMs() + resultDelayMs );
|
||||
nextResult->heal( amount, severity, flag );
|
||||
moveToResultList( effectTarget, nextResult );
|
||||
}
|
||||
|
||||
void EffectBuilder::restoreMP( Entity::CharaPtr& target, Entity::CharaPtr& restoringTarget, uint32_t amount, Common::ActionEffectResultFlag flag )
|
||||
void EffectBuilder::restoreMP( Entity::CharaPtr& target, Entity::CharaPtr& restoringTarget, uint32_t amount, Common::ActionEffectResultFlag flag, uint64_t resultDelayMs )
|
||||
{
|
||||
EffectResultPtr nextResult = make_EffectResult( restoringTarget, getResultDelayMs() ); // restore mp source actor
|
||||
EffectResultPtr nextResult = make_EffectResult( restoringTarget, Common::Util::getTimeMs() + resultDelayMs );
|
||||
nextResult->restoreMP( amount, flag );
|
||||
moveToResultList( target, nextResult );
|
||||
}
|
||||
|
||||
void EffectBuilder::damage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag )
|
||||
void EffectBuilder::dodge( Entity::CharaPtr& effectTarget, Entity::CharaPtr& dodgingTarget, Common::ActionEffectResultFlag flag )
|
||||
{
|
||||
EffectResultPtr nextResult = make_EffectResult( damagingTarget, getResultDelayMs() );
|
||||
EffectResultPtr nextResult = make_EffectResult( dodgingTarget, 0 );
|
||||
nextResult->dodge( flag );
|
||||
moveToResultList( effectTarget, nextResult );
|
||||
}
|
||||
|
||||
void EffectBuilder::damage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag, uint64_t resultDelayMs )
|
||||
{
|
||||
EffectResultPtr nextResult = make_EffectResult( damagingTarget, Common::Util::getTimeMs() + resultDelayMs );
|
||||
nextResult->damage( amount, severity, flag );
|
||||
moveToResultList( effectTarget, nextResult );
|
||||
}
|
||||
|
||||
void EffectBuilder::blockedDamage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount, uint16_t rate, Common::ActionEffectResultFlag flag, uint64_t resultDelayMs )
|
||||
{
|
||||
EffectResultPtr nextResult = make_EffectResult( damagingTarget, Common::Util::getTimeMs() + resultDelayMs );
|
||||
nextResult->blockedDamage( amount, rate, flag );
|
||||
moveToResultList( effectTarget, nextResult );
|
||||
}
|
||||
|
||||
void EffectBuilder::parriedDamage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount, uint16_t rate, Common::ActionEffectResultFlag flag, uint64_t resultDelayMs )
|
||||
{
|
||||
EffectResultPtr nextResult = make_EffectResult( damagingTarget, Common::Util::getTimeMs() + resultDelayMs );
|
||||
nextResult->parriedDamage( amount, rate, flag );
|
||||
moveToResultList( effectTarget, nextResult );
|
||||
}
|
||||
|
||||
void EffectBuilder::startCombo( Entity::CharaPtr& target, uint16_t actionId )
|
||||
{
|
||||
EffectResultPtr nextResult = make_EffectResult( target, 0 );
|
||||
|
@ -84,25 +99,51 @@ void EffectBuilder::comboSucceed( Entity::CharaPtr& target )
|
|||
moveToResultList( target, nextResult );
|
||||
}
|
||||
|
||||
void EffectBuilder::applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint8_t param )
|
||||
void EffectBuilder::applyStatusEffect( Entity::CharaPtr& target, Entity::CharaPtr& source, uint16_t statusId, uint32_t duration, uint16_t param, uint64_t resultDelayMs )
|
||||
{
|
||||
EffectResultPtr nextResult = make_EffectResult( target, 0 );
|
||||
nextResult->applyStatusEffect( statusId, param );
|
||||
EffectResultPtr nextResult = make_EffectResult( target, source, Common::Util::getTimeMs() + resultDelayMs );
|
||||
nextResult->applyStatusEffect( statusId, duration, param );
|
||||
moveToResultList( target, nextResult );
|
||||
}
|
||||
|
||||
void EffectBuilder::mount( Entity::CharaPtr& target, uint16_t mountId )
|
||||
void EffectBuilder::applyStatusEffect( Entity::CharaPtr& target, Entity::CharaPtr& source, StatusEffect::StatusEffectPtr pStatusEffect, uint64_t resultDelayMs )
|
||||
{
|
||||
EffectResultPtr nextResult = make_EffectResult( target, getResultDelayMs() );
|
||||
EffectResultPtr nextResult = make_EffectResult( target, source, Common::Util::getTimeMs() + resultDelayMs );
|
||||
nextResult->applyStatusEffect( pStatusEffect );
|
||||
moveToResultList( target, nextResult );
|
||||
}
|
||||
|
||||
void EffectBuilder::statusNoEffect( Entity::CharaPtr& target, uint16_t statusId )
|
||||
{
|
||||
EffectResultPtr nextResult = make_EffectResult( target, 0 );
|
||||
nextResult->statusNoEffect( statusId );
|
||||
moveToResultList( target, nextResult );
|
||||
}
|
||||
|
||||
void EffectBuilder::mount( Entity::CharaPtr& target, uint16_t mountId, uint64_t resultDelayMs )
|
||||
{
|
||||
EffectResultPtr nextResult = make_EffectResult( target, Common::Util::getTimeMs() + resultDelayMs );
|
||||
nextResult->mount( mountId );
|
||||
moveToResultList( target, nextResult );
|
||||
}
|
||||
|
||||
void Sapphire::World::Action::EffectBuilder::provoke( Entity::CharaPtr& target )
|
||||
{
|
||||
EffectResultPtr nextResult = make_EffectResult( target, 0 );
|
||||
nextResult->provoke();
|
||||
moveToResultList( target, nextResult );
|
||||
}
|
||||
|
||||
void EffectBuilder::setAnimationLock( float animationLock )
|
||||
{
|
||||
m_animationLock = animationLock;
|
||||
}
|
||||
|
||||
void EffectBuilder::buildAndSendPackets()
|
||||
{
|
||||
auto targetCount = m_resolvedEffects.size();
|
||||
Logger::debug( "EffectBuilder result: " );
|
||||
Logger::debug( "Targets afflicted: {}", targetCount );
|
||||
//Logger::debug( "EffectBuilder result: " );
|
||||
//Logger::debug( "Targets afflicted: {}", targetCount );
|
||||
|
||||
auto globalSequence = m_sourceChara->getCurrentTerritory()->getNextEffectSequence();
|
||||
|
||||
|
@ -183,6 +224,7 @@ std::shared_ptr< FFXIVPacketBase > EffectBuilder::buildNextEffectPacket( uint32_
|
|||
pHeader->effectCount = static_cast< uint8_t >( remainingTargetCount > packetSize ? packetSize : remainingTargetCount );
|
||||
pHeader->sourceSequence = m_sequence;
|
||||
pHeader->globalSequence = globalSequence;
|
||||
pHeader->animationLockTime = m_animationLock;
|
||||
|
||||
uint8_t targetIndex = 0;
|
||||
for( auto it = m_resolvedEffects.begin(); it != m_resolvedEffects.end(); )
|
||||
|
@ -191,7 +233,7 @@ std::shared_ptr< FFXIVPacketBase > EffectBuilder::buildNextEffectPacket( uint32_
|
|||
assert( !resultList->empty() );
|
||||
auto firstResult = resultList->data()[ 0 ];
|
||||
pEffectTargetId[ targetIndex ] = firstResult->getTarget()->getId();
|
||||
Logger::debug( " - id: {}", pEffectTargetId[ targetIndex ] );
|
||||
//Logger::debug( " - id: {}", pEffectTargetId[ targetIndex ] );
|
||||
|
||||
for( auto i = 0; i < resultList->size(); i++ )
|
||||
{
|
||||
|
@ -220,13 +262,14 @@ std::shared_ptr< FFXIVPacketBase > EffectBuilder::buildNextEffectPacket( uint32_
|
|||
auto resultList = m_resolvedEffects.begin()->second;
|
||||
assert( !resultList->empty() );
|
||||
auto firstResult = resultList->data()[ 0 ];
|
||||
Logger::debug( " - id: {}", firstResult->getTarget()->getId() );
|
||||
//Logger::debug( " - id: {}", firstResult->getTarget()->getId() );
|
||||
|
||||
auto seq = m_sourceChara->getCurrentTerritory()->getNextEffectSequence();
|
||||
|
||||
auto effectPacket = std::make_shared< Server::EffectPacket >( m_sourceChara->getId(), firstResult->getTarget()->getId(), m_actionId );
|
||||
effectPacket->setRotation( Common::Util::floatToUInt16Rot( m_sourceChara->getRot() ) );
|
||||
effectPacket->setSequence( seq, m_sequence );
|
||||
effectPacket->data().animationLockTime = m_animationLock;
|
||||
|
||||
for( int i = 0; i < resultList->size(); i++ )
|
||||
{
|
||||
|
@ -254,6 +297,7 @@ std::shared_ptr< FFXIVPacketBase > EffectBuilder::buildNextEffectPacket( uint32_
|
|||
effectPacket->data().effectCount = 0;
|
||||
effectPacket->data().sourceSequence = m_sequence;
|
||||
effectPacket->data().globalSequence = globalSequence;
|
||||
effectPacket->data().animationLockTime = m_animationLock;
|
||||
|
||||
return effectPacket;
|
||||
}
|
||||
|
|
|
@ -11,37 +11,50 @@ namespace Sapphire::World::Action
|
|||
public:
|
||||
EffectBuilder( Entity::CharaPtr source, uint32_t actionId, uint16_t sequence );
|
||||
|
||||
void setAnimationLock( float animationLock );
|
||||
|
||||
void heal( Entity::CharaPtr& effectTarget, Entity::CharaPtr& healingTarget, uint32_t amount,
|
||||
Common::ActionHitSeverityType severity = Common::ActionHitSeverityType::NormalHeal,
|
||||
Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None);
|
||||
Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None, uint64_t resultDelayMs = 600 );
|
||||
|
||||
void restoreMP( Entity::CharaPtr& effectTarget, Entity::CharaPtr& restoringTarget, uint32_t amount,
|
||||
Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None);
|
||||
Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None, uint64_t resultDelayMs = 600 );
|
||||
|
||||
void dodge( Entity::CharaPtr& effectTarget, Entity::CharaPtr& dodgingTarget, Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None );
|
||||
|
||||
void damage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount,
|
||||
Common::ActionHitSeverityType severity = Common::ActionHitSeverityType::NormalDamage,
|
||||
Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None);
|
||||
Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None, uint64_t resultDelayMs = 600 );
|
||||
|
||||
void blockedDamage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount, uint16_t rate,
|
||||
Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None, uint64_t resultDelayMs = 600 );
|
||||
|
||||
void parriedDamage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount, uint16_t rate,
|
||||
Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None, uint64_t resultDelayMs = 600 );
|
||||
|
||||
void startCombo( Entity::CharaPtr& target, uint16_t actionId );
|
||||
|
||||
void comboSucceed( Entity::CharaPtr& target );
|
||||
|
||||
void applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint8_t param );
|
||||
void applyStatusEffect( Entity::CharaPtr& target, Entity::CharaPtr& source, uint16_t statusId, uint32_t duration, uint16_t param, uint64_t resultDelayMs = 500 );
|
||||
void applyStatusEffect( Entity::CharaPtr& target, Entity::CharaPtr& source, StatusEffect::StatusEffectPtr pStatusEffect, uint64_t resultDelayMs = 500 );
|
||||
|
||||
void mount( Entity::CharaPtr& target, uint16_t mountId );
|
||||
void statusNoEffect( Entity::CharaPtr& target, uint16_t statusId );
|
||||
|
||||
void mount( Entity::CharaPtr& target, uint16_t mountId, uint64_t resultDelayMs = 600 );
|
||||
|
||||
void provoke( Entity::CharaPtr& target );
|
||||
|
||||
void buildAndSendPackets();
|
||||
|
||||
|
||||
private:
|
||||
void moveToResultList( Entity::CharaPtr& chara, EffectResultPtr result );
|
||||
|
||||
uint64_t getResultDelayMs();
|
||||
|
||||
std::shared_ptr< Sapphire::Network::Packets::FFXIVPacketBase > buildNextEffectPacket( uint32_t globalSequence );
|
||||
|
||||
private:
|
||||
uint32_t m_actionId;
|
||||
float m_animationLock;
|
||||
uint16_t m_sequence;
|
||||
|
||||
Entity::CharaPtr m_sourceChara;
|
||||
|
|
|
@ -9,19 +9,32 @@ using namespace Sapphire;
|
|||
using namespace Sapphire::World::Action;
|
||||
|
||||
|
||||
EffectResult::EffectResult( Entity::CharaPtr target, uint64_t runAfter ) :
|
||||
EffectResult::EffectResult( Entity::CharaPtr target, Entity::CharaPtr source, uint64_t runAfter ) :
|
||||
m_target( std::move( target ) ),
|
||||
m_source( std::move( source ) ),
|
||||
m_delayMs( runAfter ),
|
||||
m_type( Common::ActionEffectType::Nothing ),
|
||||
m_value( 0 ),
|
||||
m_statusDuration( 0 ),
|
||||
m_param0( 0 ),
|
||||
m_param1( 0 ),
|
||||
m_type( Common::ActionEffectType::Nothing ),
|
||||
m_param2( 0 ),
|
||||
m_flag( Common::ActionEffectResultFlag::None )
|
||||
m_flag( Common::ActionEffectResultFlag::None ),
|
||||
m_pPreBuiltStatusEffect( nullptr )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
EffectResult::EffectResult( Entity::CharaPtr target, uint64_t delayMs ) :
|
||||
EffectResult::EffectResult( std::move( target ), nullptr, delayMs )
|
||||
{
|
||||
}
|
||||
|
||||
Entity::CharaPtr EffectResult::getSource() const
|
||||
{
|
||||
return m_source;
|
||||
}
|
||||
|
||||
Entity::CharaPtr EffectResult::getTarget() const
|
||||
{
|
||||
return m_target;
|
||||
|
@ -37,6 +50,13 @@ uint64_t EffectResult::getDelay()
|
|||
return m_delayMs;
|
||||
}
|
||||
|
||||
void EffectResult::dodge( Common::ActionEffectResultFlag flag )
|
||||
{
|
||||
m_flag = flag;
|
||||
|
||||
m_type = Common::ActionEffectType::Miss;
|
||||
}
|
||||
|
||||
void EffectResult::damage( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag )
|
||||
{
|
||||
m_param0 = static_cast< uint8_t >( severity );
|
||||
|
@ -46,6 +66,24 @@ void EffectResult::damage( uint32_t amount, Common::ActionHitSeverityType severi
|
|||
m_type = Common::ActionEffectType::Damage;
|
||||
}
|
||||
|
||||
void EffectResult::blockedDamage( uint32_t amount, uint16_t rate, Common::ActionEffectResultFlag flag )
|
||||
{
|
||||
m_value = amount;
|
||||
m_flag = flag;
|
||||
m_param2 = rate;
|
||||
|
||||
m_type = Common::ActionEffectType::BlockedDamage;
|
||||
}
|
||||
|
||||
void EffectResult::parriedDamage( uint32_t amount, uint16_t rate, Common::ActionEffectResultFlag flag )
|
||||
{
|
||||
m_value = amount;
|
||||
m_flag = flag;
|
||||
m_param2 = rate;
|
||||
|
||||
m_type = Common::ActionEffectType::ParriedDamage;
|
||||
}
|
||||
|
||||
void EffectResult::heal( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag )
|
||||
{
|
||||
m_param1 = static_cast< uint8_t >( severity );
|
||||
|
@ -77,14 +115,31 @@ void EffectResult::comboSucceed()
|
|||
m_type = Common::ActionEffectType::ComboSucceed;
|
||||
}
|
||||
|
||||
void EffectResult::applyStatusEffect( uint16_t statusId, uint8_t param )
|
||||
void EffectResult::applyStatusEffect( uint16_t statusId, uint32_t duration, uint16_t param )
|
||||
{
|
||||
m_value = statusId;
|
||||
m_statusDuration = duration;
|
||||
m_param2 = param;
|
||||
|
||||
m_type = Common::ActionEffectType::ApplyStatusEffectTarget;
|
||||
}
|
||||
|
||||
void EffectResult::applyStatusEffect( StatusEffect::StatusEffectPtr pStatusEffect )
|
||||
{
|
||||
m_value = pStatusEffect->getId();
|
||||
m_param2 = pStatusEffect->getParam();
|
||||
m_pPreBuiltStatusEffect = std::move( pStatusEffect );
|
||||
|
||||
m_type = Common::ActionEffectType::ApplyStatusEffectTarget;
|
||||
}
|
||||
|
||||
void EffectResult::statusNoEffect( uint16_t statusId )
|
||||
{
|
||||
m_value = statusId;
|
||||
|
||||
m_type = Common::ActionEffectType::StatusNoEffect;
|
||||
}
|
||||
|
||||
void EffectResult::mount( uint16_t mountId )
|
||||
{
|
||||
m_value = mountId;
|
||||
|
@ -93,6 +148,11 @@ void EffectResult::mount( uint16_t mountId )
|
|||
m_type = Common::ActionEffectType::Mount;
|
||||
}
|
||||
|
||||
void Sapphire::World::Action::EffectResult::provoke()
|
||||
{
|
||||
m_type = Common::ActionEffectType::Provoke;
|
||||
}
|
||||
|
||||
Common::EffectEntry EffectResult::buildEffectEntry() const
|
||||
{
|
||||
Common::EffectEntry entry{};
|
||||
|
@ -110,7 +170,7 @@ Common::EffectEntry EffectResult::buildEffectEntry() const
|
|||
}
|
||||
entry.param0 = m_param0;
|
||||
entry.param1 = m_param1;
|
||||
entry.param2 = m_param2;
|
||||
entry.param2 = static_cast< uint8_t >( m_param2 );
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
@ -120,6 +180,8 @@ void EffectResult::execute()
|
|||
switch( m_type )
|
||||
{
|
||||
case Common::ActionEffectType::Damage:
|
||||
case Common::ActionEffectType::BlockedDamage:
|
||||
case Common::ActionEffectType::ParriedDamage:
|
||||
{
|
||||
m_target->takeDamage( m_value );
|
||||
break;
|
||||
|
@ -137,6 +199,38 @@ void EffectResult::execute()
|
|||
break;
|
||||
}
|
||||
|
||||
case Common::ActionEffectType::ApplyStatusEffectTarget:
|
||||
case Common::ActionEffectType::ApplyStatusEffectSource:
|
||||
{
|
||||
//refreshing old buff
|
||||
for( auto const& entry : m_target->getStatusEffectMap() )
|
||||
{
|
||||
auto statusEffect = entry.second;
|
||||
if( statusEffect->getId() == m_value && statusEffect->getSrcActorId() == m_source->getId() )
|
||||
{
|
||||
if( m_pPreBuiltStatusEffect )
|
||||
{
|
||||
statusEffect->refresh( m_pPreBuiltStatusEffect->getEffectEntry() );
|
||||
}
|
||||
else
|
||||
{
|
||||
statusEffect->refresh();
|
||||
}
|
||||
m_target->sendStatusEffectUpdate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( m_pPreBuiltStatusEffect )
|
||||
{
|
||||
m_target->addStatusEffect( m_pPreBuiltStatusEffect );
|
||||
}
|
||||
else
|
||||
m_target->addStatusEffectById( m_value, m_statusDuration, *m_source, m_param2 );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Common::ActionEffectType::Mount:
|
||||
{
|
||||
auto pPlayer = m_target->getAsPlayer();
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include <ForwardsZone.h>
|
||||
#include <Common.h>
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
namespace Sapphire::World::Action
|
||||
{
|
||||
/*!
|
||||
|
@ -13,16 +15,24 @@ namespace Sapphire::World::Action
|
|||
class EffectResult
|
||||
{
|
||||
public:
|
||||
explicit EffectResult( Entity::CharaPtr target, Entity::CharaPtr source, uint64_t delayMs );
|
||||
explicit EffectResult( Entity::CharaPtr target, uint64_t delayMs );
|
||||
|
||||
void dodge( Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None );
|
||||
void damage( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None );
|
||||
void blockedDamage( uint32_t amount, uint16_t rate, Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None );
|
||||
void parriedDamage( uint32_t amount, uint16_t rate, Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None );
|
||||
void heal( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None );
|
||||
void restoreMP( uint32_t amount, Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None );
|
||||
void startCombo( uint16_t actionId );
|
||||
void comboSucceed();
|
||||
void applyStatusEffect( uint16_t statusId, uint8_t param );
|
||||
void applyStatusEffect( uint16_t statusId, uint32_t duration, uint16_t param );
|
||||
void applyStatusEffect( StatusEffect::StatusEffectPtr pStatusEffect );
|
||||
void statusNoEffect( uint16_t statusId );
|
||||
void mount( uint16_t mountId );
|
||||
void provoke();
|
||||
|
||||
Entity::CharaPtr getSource() const;
|
||||
Entity::CharaPtr getTarget() const;
|
||||
|
||||
uint32_t getValue() const;
|
||||
|
@ -36,16 +46,20 @@ namespace Sapphire::World::Action
|
|||
private:
|
||||
uint64_t m_delayMs;
|
||||
|
||||
Entity::CharaPtr m_source;
|
||||
Entity::CharaPtr m_target;
|
||||
|
||||
Common::ActionEffectType m_type;
|
||||
|
||||
uint8_t m_param0;
|
||||
uint8_t m_param1;
|
||||
uint8_t m_param2;
|
||||
uint16_t m_param2;
|
||||
|
||||
uint32_t m_value;
|
||||
uint32_t m_statusDuration;
|
||||
Common::ActionEffectResultFlag m_flag;
|
||||
|
||||
StatusEffect::StatusEffectPtr m_pPreBuiltStatusEffect;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -90,11 +90,13 @@ void Action::EventAction::execute()
|
|||
|
||||
}
|
||||
|
||||
void Action::EventAction::interrupt()
|
||||
void Action::EventAction::interrupt( Common::ActionInterruptType type )
|
||||
{
|
||||
if( !m_pSource )
|
||||
return;
|
||||
|
||||
m_interruptType = type;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
|
||||
void execute() override;
|
||||
|
||||
void interrupt() override;
|
||||
void interrupt( Common::ActionInterruptType type = Common::ActionInterruptType::RegularInterrupt ) override;
|
||||
|
||||
private:
|
||||
uint32_t m_eventId;
|
||||
|
|
|
@ -64,7 +64,7 @@ void ItemAction::execute()
|
|||
}
|
||||
}
|
||||
|
||||
void ItemAction::interrupt()
|
||||
void ItemAction::interrupt( Common::ActionInterruptType type )
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace Sapphire::World::Action
|
|||
|
||||
void execute() override;
|
||||
|
||||
void interrupt() override;
|
||||
void interrupt( Common::ActionInterruptType type = Common::ActionInterruptType::RegularInterrupt ) override;
|
||||
|
||||
private:
|
||||
void handleVFXItem();
|
||||
|
|
|
@ -679,9 +679,14 @@ void Sapphire::Entity::BNpc::setFlag( uint32_t flag )
|
|||
m_flags |= flag;
|
||||
}
|
||||
|
||||
/*!
|
||||
TODO: this only solves attacks from melee classes.
|
||||
Will have to be extended for ranged attacks.
|
||||
|
||||
\param ActorPtr the autoAttack is performed on
|
||||
*/
|
||||
void Sapphire::Entity::BNpc::autoAttack( CharaPtr pTarget )
|
||||
{
|
||||
|
||||
uint64_t tick = Util::getTimeMs();
|
||||
|
||||
// todo: this needs to use the auto attack delay for the equipped weapon
|
||||
|
@ -689,23 +694,20 @@ void Sapphire::Entity::BNpc::autoAttack( CharaPtr pTarget )
|
|||
{
|
||||
pTarget->onActionHostile( getAsChara() );
|
||||
m_lastAttack = tick;
|
||||
srand( static_cast< uint32_t >( tick ) );
|
||||
|
||||
auto damage = Math::CalcStats::calcAutoAttackDamage( *this );
|
||||
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
|
||||
auto actionData = exdData.get< Data::Action >( 7 );
|
||||
assert( actionData );
|
||||
auto action = World::Action::make_Action( getAsChara(), 7, 0, actionData );
|
||||
|
||||
auto effectPacket = std::make_shared< Server::EffectPacket >( getId(), pTarget->getId(), 7 );
|
||||
effectPacket->setRotation( Util::floatToUInt16Rot( getRot() ) );
|
||||
Common::EffectEntry effectEntry{};
|
||||
effectEntry.value = static_cast< int16_t >( damage.first );
|
||||
effectEntry.effectType = ActionEffectType::Damage;
|
||||
effectEntry.param0 = static_cast< uint8_t >( damage.second );
|
||||
effectEntry.param2 = 0x71;
|
||||
effectPacket->addEffect( effectEntry );
|
||||
|
||||
sendToInRangeSet( effectPacket );
|
||||
|
||||
pTarget->takeDamage( static_cast< uint16_t >( damage.first ) );
|
||||
action->setTargetId( pTarget->getId() );
|
||||
action->setPos( getPos() );
|
||||
action->setAutoAttack();
|
||||
|
||||
if( action->init() )
|
||||
{
|
||||
action->start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -391,8 +391,6 @@ uint8_t Sapphire::Entity::Chara::getLevel() const
|
|||
Let an actor take damage and perform necessary steps
|
||||
according to resulting hp, propagates new hp value to players
|
||||
in range
|
||||
TODO: eventually this needs to distinguish between physical and
|
||||
magical dmg and take status effects into account
|
||||
|
||||
\param amount of damage to be taken
|
||||
*/
|
||||
|
@ -483,43 +481,8 @@ uint32_t Sapphire::Entity::Chara::getBonusStat( Common::BaseParam bonus ) const
|
|||
return m_bonusStats[ static_cast< uint8_t >( bonus ) ];
|
||||
}
|
||||
|
||||
/*!
|
||||
Autoattack prototype implementation
|
||||
TODO: move the check if the autoAttack can be performed to the callee
|
||||
also rename autoAttack to autoAttack as that is more elaborate
|
||||
On top of that, this only solves attacks from melee classes.
|
||||
Will have to be extended for ranged attacks.
|
||||
|
||||
\param ActorPtr the autoAttack is performed on
|
||||
*/
|
||||
void Sapphire::Entity::Chara::autoAttack( CharaPtr pTarget )
|
||||
{
|
||||
|
||||
uint64_t tick = Util::getTimeMs();
|
||||
|
||||
// todo: this needs to use the auto attack delay for the equipped weapon
|
||||
if( ( tick - m_lastAttack ) > 2500 )
|
||||
{
|
||||
pTarget->onActionHostile( getAsChara() );
|
||||
m_lastAttack = tick;
|
||||
srand( static_cast< uint32_t >( tick ) );
|
||||
|
||||
auto damage = static_cast< uint16_t >( 10 + rand() % 12 );
|
||||
|
||||
auto effectPacket = std::make_shared< Server::EffectPacket >( getId(), pTarget->getId(), 7 );
|
||||
effectPacket->setRotation( Util::floatToUInt16Rot( getRot() ) );
|
||||
Common::EffectEntry effectEntry{};
|
||||
effectEntry.value = static_cast< int16_t >( damage );
|
||||
effectEntry.effectType = ActionEffectType::Damage;
|
||||
effectEntry.param0 = static_cast< uint8_t >( ActionHitSeverityType::NormalDamage );
|
||||
effectEntry.param2 = 0x71;
|
||||
effectPacket->addEffect( effectEntry );
|
||||
|
||||
sendToInRangeSet( effectPacket );
|
||||
|
||||
pTarget->takeDamage( damage );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*! \param StatusEffectPtr to be applied to the actor */
|
||||
|
@ -551,28 +514,49 @@ void Sapphire::Entity::Chara::addStatusEffect( StatusEffect::StatusEffectPtr pEf
|
|||
status.index = static_cast< uint8_t >( nextSlot );
|
||||
status.param = pEffect->getParam();
|
||||
|
||||
sendToInRangeSet( statusEffectAdd, isPlayer() );
|
||||
float totalShieldValue = 0;
|
||||
for( auto effectIt : m_statusEffectMap )
|
||||
{
|
||||
auto statusEffect = effectIt.second;
|
||||
totalShieldValue += statusEffect->getEffectEntry().getRemainingShield();
|
||||
}
|
||||
|
||||
if( totalShieldValue > 0 )
|
||||
{
|
||||
totalShieldValue /= getMaxHp();
|
||||
totalShieldValue *= 100;
|
||||
statusEffectAdd->data().shieldPercentage = static_cast< uint8_t >( std::min( 255.0f, totalShieldValue ) );
|
||||
}
|
||||
|
||||
sendToInRangeSet( statusEffectAdd, true );
|
||||
sendStatusEffectUpdate(); // although client buff displays correctly without this but retail sends it so we do it as well
|
||||
}
|
||||
|
||||
/*! \param StatusEffectPtr to be applied to the actor */
|
||||
void Sapphire::Entity::Chara::addStatusEffectById( uint32_t id, int32_t duration, Entity::Chara& source, uint16_t param )
|
||||
void Sapphire::Entity::Chara::addStatusEffectById( uint32_t id, int32_t duration, Entity::Chara& source, uint16_t param, bool sendActorControl )
|
||||
{
|
||||
auto effect = StatusEffect::make_StatusEffect( id, source.getAsChara(), getAsChara(), duration, 3000 );
|
||||
effect->setParam( param );
|
||||
if( sendActorControl )
|
||||
{
|
||||
auto p = makeActorControl( getId(), Network::ActorControl::StatusEffectGain, id, 0, 0, 0 );
|
||||
sendToInRangeSet( p, true );
|
||||
}
|
||||
addStatusEffect( effect );
|
||||
}
|
||||
|
||||
/*! \param StatusEffectPtr to be applied to the actor */
|
||||
void Sapphire::Entity::Chara::addStatusEffectByIdIfNotExist( uint32_t id, int32_t duration, Entity::Chara& source,
|
||||
uint16_t param )
|
||||
void Sapphire::Entity::Chara::addStatusEffectByIdIfNotExist( uint32_t id, int32_t duration, Entity::Chara& source, uint16_t param, bool sendActorControl )
|
||||
{
|
||||
if( hasStatusEffect( id ) )
|
||||
if( getStatusEffectById( id ).second )
|
||||
return;
|
||||
|
||||
auto effect = StatusEffect::make_StatusEffect( id, source.getAsChara(), getAsChara(), duration, 3000 );
|
||||
effect->setParam( param );
|
||||
if( sendActorControl )
|
||||
{
|
||||
auto p = makeActorControl( getId(), Network::ActorControl::StatusEffectGain, id, 0, 0, 0 );
|
||||
sendToInRangeSet( p, true );
|
||||
}
|
||||
addStatusEffect( effect );
|
||||
|
||||
}
|
||||
|
||||
int8_t Sapphire::Entity::Chara::getStatusEffectFreeSlot()
|
||||
|
@ -593,34 +577,35 @@ void Sapphire::Entity::Chara::statusEffectFreeSlot( uint8_t slotId )
|
|||
m_statusEffectFreeSlotQueue.push( slotId );
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Chara::removeSingleStatusEffectById( uint32_t id )
|
||||
void Sapphire::Entity::Chara::removeSingleStatusEffectById( uint32_t id, bool sendStatusList )
|
||||
{
|
||||
for( auto effectIt : m_statusEffectMap )
|
||||
{
|
||||
if( effectIt.second->getId() == id )
|
||||
{
|
||||
removeStatusEffect( effectIt.first );
|
||||
removeStatusEffect( effectIt.first, sendStatusList );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Chara::removeStatusEffect( uint8_t effectSlotId )
|
||||
void Sapphire::Entity::Chara::removeStatusEffect( uint8_t effectSlotId, bool sendStatusList )
|
||||
{
|
||||
auto pEffectIt = m_statusEffectMap.find( effectSlotId );
|
||||
if( pEffectIt == m_statusEffectMap.end() )
|
||||
return;
|
||||
|
||||
statusEffectFreeSlot( effectSlotId );
|
||||
|
||||
auto pEffect = pEffectIt->second;
|
||||
pEffect->removeStatus();
|
||||
|
||||
sendToInRangeSet( makeActorControl( getId(), StatusEffectLose, pEffect->getId() ), isPlayer() );
|
||||
statusEffectFreeSlot( effectSlotId );
|
||||
|
||||
m_statusEffectMap.erase( effectSlotId );
|
||||
|
||||
sendStatusEffectUpdate();
|
||||
pEffect->removeStatus();
|
||||
|
||||
sendToInRangeSet( makeActorControl( getId(), StatusEffectLose, pEffect->getId() ), true );
|
||||
if( sendStatusList )
|
||||
sendStatusEffectUpdate();
|
||||
}
|
||||
|
||||
std::map< uint8_t, Sapphire::StatusEffect::StatusEffectPtr > Sapphire::Entity::Chara::getStatusEffectMap() const
|
||||
|
@ -652,7 +637,6 @@ void Sapphire::Entity::Chara::sendStatusEffectUpdate()
|
|||
{
|
||||
uint64_t currentTimeMs = Util::getTimeMs();
|
||||
|
||||
|
||||
auto statusEffectList = makeZonePacket< FFXIVIpcStatusEffectList >( getId() );
|
||||
statusEffectList->data().classId = static_cast< uint8_t >( getClass() );
|
||||
statusEffectList->data().level = getLevel();
|
||||
|
@ -662,18 +646,56 @@ void Sapphire::Entity::Chara::sendStatusEffectUpdate()
|
|||
statusEffectList->data().max_hp = getMaxHp();
|
||||
statusEffectList->data().max_mp = getMaxMp();
|
||||
uint8_t slot = 0;
|
||||
float totalShieldValue = 0;
|
||||
for( auto effectIt : m_statusEffectMap )
|
||||
{
|
||||
float timeLeft = static_cast< float >( effectIt.second->getDuration() -
|
||||
( currentTimeMs - effectIt.second->getStartTimeMs() ) ) / 1000;
|
||||
auto statusEffect = effectIt.second;
|
||||
totalShieldValue += statusEffect->getEffectEntry().getRemainingShield();
|
||||
|
||||
float timeLeft = static_cast< float >( statusEffect->getDuration() -
|
||||
( currentTimeMs - statusEffect->getStartTimeMs() ) ) / 1000;
|
||||
statusEffectList->data().effect[ slot ].duration = timeLeft;
|
||||
statusEffectList->data().effect[ slot ].effect_id = effectIt.second->getId();
|
||||
statusEffectList->data().effect[ slot ].sourceActorId = effectIt.second->getSrcActorId();
|
||||
statusEffectList->data().effect[ slot ].effect_id = statusEffect->getId();
|
||||
statusEffectList->data().effect[ slot ].param = statusEffect->getParam();
|
||||
statusEffectList->data().effect[ slot ].sourceActorId = statusEffect->getSrcActorId();
|
||||
slot++;
|
||||
}
|
||||
|
||||
sendToInRangeSet( statusEffectList, isPlayer() );
|
||||
if( totalShieldValue > 0 )
|
||||
{
|
||||
totalShieldValue /= getMaxHp();
|
||||
totalShieldValue *= 100;
|
||||
statusEffectList->data().shieldPercentage = static_cast< uint8_t >( std::min( 255.0f, totalShieldValue ) );
|
||||
}
|
||||
|
||||
sendToInRangeSet( statusEffectList, true );
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Chara::sendShieldUpdate()
|
||||
{
|
||||
auto pPacket = makeZonePacket< FFXIVIpcEffectResult >( getId() );
|
||||
|
||||
pPacket->data().actor_id = getId();
|
||||
pPacket->data().current_hp = getHp();
|
||||
pPacket->data().current_mp = static_cast< uint16_t >( getMp() );
|
||||
pPacket->data().max_hp = getMaxHp();
|
||||
pPacket->data().classId = static_cast< uint8_t >( getClass() );
|
||||
|
||||
float totalShieldValue = 0;
|
||||
for( auto effectIt : m_statusEffectMap )
|
||||
{
|
||||
auto statusEffect = effectIt.second;
|
||||
totalShieldValue += statusEffect->getEffectEntry().getRemainingShield();
|
||||
}
|
||||
|
||||
if( totalShieldValue > 0 )
|
||||
{
|
||||
totalShieldValue /= getMaxHp();
|
||||
totalShieldValue *= 100;
|
||||
pPacket->data().shieldPercentage = static_cast< uint8_t >( std::min( 255.0f, totalShieldValue ) );
|
||||
}
|
||||
|
||||
sendToInRangeSet( pPacket, true );
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Chara::updateStatusEffects()
|
||||
|
@ -690,7 +712,7 @@ void Sapphire::Entity::Chara::updateStatusEffects()
|
|||
uint32_t duration = effect->getDuration();
|
||||
uint32_t tickRate = effect->getTickRate();
|
||||
|
||||
if( duration > 0 && ( currentTimeMs - startTime ) > duration )
|
||||
if( effect->isMarkedToRemove() || ( duration > 0 && ( currentTimeMs - startTime ) > duration ) )
|
||||
{
|
||||
// remove status effect
|
||||
removeStatusEffect( effectIndex );
|
||||
|
@ -706,9 +728,16 @@ void Sapphire::Entity::Chara::updateStatusEffects()
|
|||
}
|
||||
}
|
||||
|
||||
bool Sapphire::Entity::Chara::hasStatusEffect( uint32_t id )
|
||||
std::pair< uint8_t, Sapphire::StatusEffect::StatusEffectPtr > Sapphire::Entity::Chara::getStatusEffectById( uint32_t id )
|
||||
{
|
||||
return m_statusEffectMap.find( id ) != m_statusEffectMap.end();
|
||||
for( auto effectIt : m_statusEffectMap )
|
||||
{
|
||||
if( effectIt.second->getId() == id && !effectIt.second->isMarkedToRemove() )
|
||||
{
|
||||
return std::make_pair( effectIt.first, effectIt.second );
|
||||
}
|
||||
}
|
||||
return std::make_pair( 0, nullptr );
|
||||
}
|
||||
|
||||
int64_t Sapphire::Entity::Chara::getLastUpdateTime() const
|
||||
|
@ -871,6 +900,10 @@ uint32_t Sapphire::Entity::Chara::getStatValue( Sapphire::Common::BaseParam base
|
|||
case Common::BaseParam::Haste:
|
||||
{
|
||||
value = m_baseStats.haste;
|
||||
for( auto const& statusIt : m_statusEffectMap )
|
||||
{
|
||||
value -= statusIt.second->getEffectEntry().getHasteBonus();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -912,6 +945,40 @@ uint32_t Sapphire::Entity::Chara::getStatValue( Sapphire::Common::BaseParam base
|
|||
return value + getBonusStat( baseParam );
|
||||
}
|
||||
|
||||
float Sapphire::Entity::Chara::applyShieldProtection( float damage )
|
||||
{
|
||||
float remainingDamage = damage;
|
||||
bool shieldChanged = false;
|
||||
|
||||
for( auto const& entry : getStatusEffectMap() )
|
||||
{
|
||||
auto status = entry.second;
|
||||
auto effectEntry = status->getEffectEntry();
|
||||
|
||||
if( effectEntry.getType() == Sapphire::Common::StatusEffectType::Shield )
|
||||
{
|
||||
shieldChanged = true;
|
||||
if( remainingDamage < effectEntry.getRemainingShield() )
|
||||
{
|
||||
effectEntry.setRemainingShield( effectEntry.getRemainingShield() - static_cast< int32_t >( remainingDamage ) );
|
||||
status->replaceEffectEntry( effectEntry );
|
||||
remainingDamage = 0;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
remainingDamage -= effectEntry.getRemainingShield();
|
||||
status->markToRemove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( shieldChanged )
|
||||
sendShieldUpdate();
|
||||
|
||||
return remainingDamage;
|
||||
}
|
||||
|
||||
uint32_t Sapphire::Entity::Chara::getVisualEffect()
|
||||
{
|
||||
return m_effect;
|
||||
|
@ -957,7 +1024,9 @@ void Sapphire::Entity::Chara::onTick()
|
|||
|
||||
if( thisTickDmg != 0 )
|
||||
{
|
||||
takeDamage( thisTickDmg );
|
||||
thisTickDmg = applyShieldProtection( thisTickDmg );
|
||||
if( thisTickDmg > 0 )
|
||||
takeDamage( thisTickDmg );
|
||||
sendToInRangeSet( makeActorControl( getId(), HPFloatingText, 0,
|
||||
static_cast< uint8_t >( ActionEffectType::Damage ), thisTickDmg ), true );
|
||||
}
|
||||
|
|
|
@ -149,13 +149,13 @@ namespace Sapphire::Entity
|
|||
/// Status effect functions
|
||||
void addStatusEffect( StatusEffect::StatusEffectPtr pEffect );
|
||||
|
||||
void removeStatusEffect( uint8_t effectSlotId );
|
||||
void removeStatusEffect( uint8_t effectSlotId, bool sendStatusList = true );
|
||||
|
||||
void removeSingleStatusEffectById( uint32_t id );
|
||||
void removeSingleStatusEffectById( uint32_t id, bool sendStatusList = true );
|
||||
|
||||
void updateStatusEffects();
|
||||
|
||||
bool hasStatusEffect( uint32_t id );
|
||||
std::pair< uint8_t, StatusEffect::StatusEffectPtr > getStatusEffectById( uint32_t id );
|
||||
|
||||
int8_t getStatusEffectFreeSlot();
|
||||
|
||||
|
@ -169,19 +169,19 @@ namespace Sapphire::Entity
|
|||
|
||||
void sendStatusEffectUpdate();
|
||||
|
||||
void sendShieldUpdate();
|
||||
|
||||
/*! return a const pointer to the look array */
|
||||
const uint8_t* getLookArray() const;
|
||||
|
||||
const uint32_t* getModelArray() const;
|
||||
|
||||
// add a status effect by id
|
||||
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, bool sendActorControl = false );
|
||||
|
||||
// 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, bool sendActorControl = false );
|
||||
|
||||
// remove a status effect by id
|
||||
void removeSingleStatusEffectFromId( uint32_t id );
|
||||
/// End Status Effect Functions
|
||||
|
||||
std::string getName() const;
|
||||
|
@ -291,6 +291,8 @@ namespace Sapphire::Entity
|
|||
|
||||
Common::BaseParam getPrimaryStat() const;
|
||||
|
||||
float applyShieldProtection( float damage );
|
||||
|
||||
uint32_t getVisualEffect();
|
||||
void setVisualEffect( uint32_t effect, bool sendPacket = true );
|
||||
void sendVisualEffect();
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include "Manager/TerritoryMgr.h"
|
||||
#include "Manager/RNGMgr.h"
|
||||
#include "Manager/MapMgr.h"
|
||||
#include <Manager/PlayerMgr.h>
|
||||
#include "Manager/EventMgr.h"
|
||||
|
||||
#include "Territory/Territory.h"
|
||||
#include "Territory/ZonePosition.h"
|
||||
|
@ -236,6 +238,10 @@ Sapphire::Common::OnlineStatus Sapphire::Entity::Player::getOnlineStatus() const
|
|||
void Sapphire::Entity::Player::setOnlineStatusMask( uint64_t status )
|
||||
{
|
||||
m_onlineStatus = status;
|
||||
sendToInRangeSet( makeActorControl( getId(), SetStatusIcon, static_cast< uint8_t >( getOnlineStatus() ) ), true );
|
||||
auto statusPacket = makeZonePacket< FFXIVIpcSetOnlineStatus >( getId() );
|
||||
statusPacket->data().onlineStatusFlags = status;
|
||||
queuePacket( statusPacket );
|
||||
}
|
||||
|
||||
uint64_t Sapphire::Entity::Player::getOnlineStatusMask() const
|
||||
|
@ -414,10 +420,30 @@ void Sapphire::Entity::Player::teleport( uint16_t aetheryteId, uint8_t type )
|
|||
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::forceZoneing( uint32_t zoneId )
|
||||
void Sapphire::Entity::Player::forceZoneing( uint32_t zoneId, float x, float y, float z, float r, bool showZoneName )
|
||||
{
|
||||
m_queuedZoneing = std::make_shared< QueuedZoning >( zoneId, getPos(), Util::getTimeMs(), 0.f );
|
||||
//performZoning( zoneId, Common::ZoneingType::None, getPos() );
|
||||
if( zoneId == 0 )
|
||||
{
|
||||
zoneId = getCurrentTerritory()->getTerritoryTypeId();
|
||||
x = m_pos.x;
|
||||
y = m_pos.y;
|
||||
z = m_pos.z;
|
||||
r = m_rot;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( x == FLT_MAX )
|
||||
x = m_pos.x;
|
||||
if( y == FLT_MAX )
|
||||
y = m_pos.y;
|
||||
if( z == FLT_MAX )
|
||||
z = m_pos.z;
|
||||
if( r == FLT_MAX )
|
||||
r = m_rot;
|
||||
}
|
||||
Common::FFXIVARR_POSITION3 pos { x, y, z };
|
||||
m_queuedZoneing = std::make_shared< QueuedZoning >( zoneId, pos, Util::getTimeMs(), r );
|
||||
prepareZoning( showZoneName ? zoneId : 0, true, 1, 0 );
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::returnToHomepoint()
|
||||
|
@ -465,12 +491,16 @@ bool Sapphire::Entity::Player::setInstance( TerritoryPtr instance )
|
|||
auto currentZone = getCurrentTerritory();
|
||||
|
||||
// zoning within the same zone won't cause the prev data to be overwritten
|
||||
if( instance->getTerritoryTypeId() != m_territoryTypeId )
|
||||
if( instance->getTerritoryTypeId() != m_territoryTypeId && ( teriMgr.isDefaultTerritory( currentZone->getTerritoryTypeId() ) || teriMgr.isHousingTerritory( currentZone->getTerritoryTypeId() ) ) )
|
||||
{
|
||||
m_prevPos = m_pos;
|
||||
m_prevRot = m_rot;
|
||||
m_prevTerritoryTypeId = currentZone->getTerritoryTypeId();
|
||||
m_prevTerritoryId = getTerritoryId();
|
||||
// never returning to a BeforeTrialDung zone.
|
||||
if( currentZone->getTerritoryTypeInfo()->territoryIntendedUse != Sapphire::World::Manager::TerritoryMgr::TerritoryIntendedUse::BeforeTrialDung )
|
||||
{
|
||||
m_prevPos = m_pos;
|
||||
m_prevRot = m_rot;
|
||||
m_prevTerritoryTypeId = currentZone->getTerritoryTypeId();
|
||||
m_prevTerritoryId = getTerritoryId();
|
||||
}
|
||||
}
|
||||
|
||||
return teriMgr.movePlayer( instance, getAsPlayer() );
|
||||
|
@ -486,12 +516,16 @@ bool Sapphire::Entity::Player::setInstance( TerritoryPtr instance, Common::FFXIV
|
|||
auto currentZone = getCurrentTerritory();
|
||||
|
||||
// zoning within the same zone won't cause the prev data to be overwritten
|
||||
if( instance->getTerritoryTypeId() != m_territoryTypeId )
|
||||
if( instance->getTerritoryTypeId() != m_territoryTypeId && ( teriMgr.isDefaultTerritory( currentZone->getTerritoryTypeId() ) || teriMgr.isHousingTerritory( currentZone->getTerritoryTypeId() ) ) )
|
||||
{
|
||||
m_prevPos = m_pos;
|
||||
m_prevRot = m_rot;
|
||||
m_prevTerritoryTypeId = currentZone->getTerritoryTypeId();
|
||||
m_prevTerritoryId = getTerritoryId();
|
||||
// never returning to a BeforeTrialDung zone.
|
||||
if( currentZone->getTerritoryTypeInfo()->territoryIntendedUse != Sapphire::World::Manager::TerritoryMgr::TerritoryIntendedUse::BeforeTrialDung )
|
||||
{
|
||||
m_prevPos = m_pos;
|
||||
m_prevRot = m_rot;
|
||||
m_prevTerritoryTypeId = currentZone->getTerritoryTypeId();
|
||||
m_prevTerritoryId = getTerritoryId();
|
||||
}
|
||||
}
|
||||
|
||||
m_pos = pos;
|
||||
|
@ -797,15 +831,7 @@ void Sapphire::Entity::Player::gainLevel()
|
|||
m_hp = getMaxHp();
|
||||
m_mp = getMaxMp();
|
||||
|
||||
auto effectListPacket = makeZonePacket< FFXIVIpcStatusEffectList >( getId() );
|
||||
effectListPacket->data().classId = static_cast< uint8_t > ( getClass() );
|
||||
effectListPacket->data().level1 = getLevel();
|
||||
effectListPacket->data().level = getLevel();
|
||||
effectListPacket->data().current_hp = getMaxHp();
|
||||
effectListPacket->data().current_mp = getMaxMp();
|
||||
effectListPacket->data().max_hp = getMaxHp();
|
||||
effectListPacket->data().max_mp = getMaxMp();
|
||||
sendToInRangeSet( effectListPacket, true );
|
||||
sendStatusEffectUpdate();
|
||||
|
||||
sendToInRangeSet( makeActorControl( getId(), LevelUpEffect, static_cast< uint8_t >( getClass() ),
|
||||
getLevel(), getLevel() - 1 ), true );
|
||||
|
@ -987,6 +1013,13 @@ void Sapphire::Entity::Player::spawn( Entity::PlayerPtr pTarget )
|
|||
Logger::debug( "[{0}] Spawning {1} for {2}", pTarget->getId(), getName(), pTarget->getName() );
|
||||
|
||||
pTarget->queuePacket( std::make_shared< PlayerSpawnPacket >( *getAsPlayer(), *pTarget ) );
|
||||
if( m_effect > 0 )
|
||||
{
|
||||
auto effect = makeZonePacket< FFXIVIpcCharaVisualEffect >( pTarget->getId() );
|
||||
effect->setSourceActor( getId() );
|
||||
effect->data().id = m_effect;
|
||||
pTarget->queuePacket( effect );
|
||||
}
|
||||
}
|
||||
|
||||
// despawn
|
||||
|
@ -1080,7 +1113,6 @@ bool Sapphire::Entity::Player::hasStateFlag( Common::PlayerStateFlag flag ) cons
|
|||
|
||||
void Sapphire::Entity::Player::setStateFlag( Common::PlayerStateFlag flag )
|
||||
{
|
||||
auto prevOnlineStatus = getOnlineStatus();
|
||||
int32_t iFlag = static_cast< uint32_t >( flag );
|
||||
|
||||
uint16_t index;
|
||||
|
@ -1089,13 +1121,6 @@ void Sapphire::Entity::Player::setStateFlag( Common::PlayerStateFlag flag )
|
|||
|
||||
m_stateFlags[ index ] |= value;
|
||||
sendStateFlags();
|
||||
|
||||
auto newOnlineStatus = getOnlineStatus();
|
||||
|
||||
if( prevOnlineStatus != newOnlineStatus )
|
||||
sendToInRangeSet( makeActorControl( getId(), SetStatusIcon,
|
||||
static_cast< uint8_t >( getOnlineStatus() ) ), true );
|
||||
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::setStateFlags( std::vector< Common::PlayerStateFlag > flags )
|
||||
|
@ -1116,8 +1141,6 @@ void Sapphire::Entity::Player::unsetStateFlag( Common::PlayerStateFlag flag )
|
|||
if( !hasStateFlag( flag ) )
|
||||
return;
|
||||
|
||||
auto prevOnlineStatus = getOnlineStatus();
|
||||
|
||||
int32_t iFlag = static_cast< uint32_t >( flag );
|
||||
|
||||
uint16_t index;
|
||||
|
@ -1126,11 +1149,6 @@ void Sapphire::Entity::Player::unsetStateFlag( Common::PlayerStateFlag flag )
|
|||
|
||||
m_stateFlags[ index ] ^= value;
|
||||
sendStateFlags();
|
||||
|
||||
auto newOnlineStatus = getOnlineStatus();
|
||||
|
||||
if( prevOnlineStatus != newOnlineStatus )
|
||||
sendToInRangeSet( makeActorControl( getId(), SetStatusIcon, static_cast< uint8_t >( getOnlineStatus() ) ), true );
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::update( uint64_t tickCount )
|
||||
|
@ -1164,6 +1182,7 @@ void Sapphire::Entity::Player::update( uint64_t tickCount )
|
|||
if( !isAlive() )
|
||||
return;
|
||||
|
||||
int64_t interval = tickCount - m_lastUpdate;
|
||||
m_lastUpdate = tickCount;
|
||||
|
||||
if( !checkAction() )
|
||||
|
@ -1205,6 +1224,63 @@ void Sapphire::Entity::Player::update( uint64_t tickCount )
|
|||
}
|
||||
}
|
||||
|
||||
switch( getClass() )
|
||||
{
|
||||
case Common::ClassJob::Conjurer:
|
||||
case Common::ClassJob::Whitemage:
|
||||
{
|
||||
if( hasStateFlag( Common::PlayerStateFlag::InCombat ) )
|
||||
{
|
||||
if( gaugeWhmGetLily() < 3 )
|
||||
{
|
||||
auto lilyTimer = gaugeWhmGetLilyTimer();
|
||||
lilyTimer += interval;
|
||||
if( lilyTimer >= 30000 )
|
||||
{
|
||||
gaugeWhmSetLilyTimer( 0 ); // packet is sent together with following lily update
|
||||
gaugeWhmSetLilies( gaugeWhmGetLily() + 1, gaugeWhmGetBloodLily() );
|
||||
}
|
||||
else
|
||||
{
|
||||
gaugeWhmSetLilyTimer( lilyTimer ); // don't send any packet, the client increases the timer by itself
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gaugeWhmSetLilyTimer( 0 );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Common::ClassJob::Darkknight:
|
||||
{
|
||||
auto dsTimer = gaugeDrkGetDarkSideTimer();
|
||||
if( dsTimer > interval )
|
||||
dsTimer -= interval;
|
||||
else
|
||||
dsTimer = 0;
|
||||
gaugeDrkSetDarkSideTimer( dsTimer );
|
||||
|
||||
auto shadowTimer = gaugeDrkGetShadowTimer();
|
||||
if( shadowTimer > interval )
|
||||
shadowTimer -= interval;
|
||||
else
|
||||
shadowTimer = 0;
|
||||
gaugeDrkSetShadowTimer( shadowTimer );
|
||||
break;
|
||||
}
|
||||
case Common::ClassJob::Dragoon:
|
||||
{
|
||||
auto drTimer = gaugeDrgGetDragonTimer();
|
||||
if( drTimer > interval )
|
||||
drTimer -= interval;
|
||||
else
|
||||
drTimer = 0;
|
||||
gaugeDrgSetDragonTimer( drTimer );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Chara::update( tickCount );
|
||||
}
|
||||
|
||||
|
@ -1529,13 +1605,17 @@ void Sapphire::Entity::Player::onMobAggro( BNpcPtr pBNpc )
|
|||
{
|
||||
hateListAdd( pBNpc );
|
||||
queuePacket( makeActorControl( getId(), ToggleAggro, 1 ) );
|
||||
setStateFlag( Common::PlayerStateFlag::InCombat );
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::onMobDeaggro( BNpcPtr pBNpc )
|
||||
{
|
||||
hateListRemove( pBNpc );
|
||||
if( m_actorIdTohateSlotMap.empty() )
|
||||
{
|
||||
queuePacket( makeActorControl( getId(), ToggleAggro ) );
|
||||
unsetStateFlag( Common::PlayerStateFlag::InCombat );
|
||||
}
|
||||
}
|
||||
|
||||
bool Sapphire::Entity::Player::isLogin() const
|
||||
|
@ -1677,52 +1757,30 @@ uint32_t Sapphire::Entity::Player::getPersistentEmote() const
|
|||
|
||||
void Sapphire::Entity::Player::autoAttack( CharaPtr pTarget )
|
||||
{
|
||||
|
||||
auto mainWeap = getItemAt( Common::GearSet0, Common::GearSetSlot::MainHand );
|
||||
|
||||
pTarget->onActionHostile( getAsChara() );
|
||||
//uint64_t tick = Util::getTimeMs();
|
||||
//srand(static_cast< uint32_t >(tick));
|
||||
|
||||
auto& RNGMgr = Common::Service< World::Manager::RNGMgr >::ref();
|
||||
auto variation = static_cast< uint32_t >( RNGMgr.getRandGenerator< float >( 0, 3 ).next() );
|
||||
|
||||
auto damage = Math::CalcStats::calcAutoAttackDamage( *this );
|
||||
|
||||
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
|
||||
World::Action::ActionPtr action;
|
||||
if( getClass() == ClassJob::Machinist || getClass() == ClassJob::Bard || getClass() == ClassJob::Archer )
|
||||
{
|
||||
auto effectPacket = std::make_shared< Server::EffectPacket >( getId(), pTarget->getId(), 8 );
|
||||
effectPacket->setRotation( Util::floatToUInt16Rot( getRot() ) );
|
||||
|
||||
Common::EffectEntry entry{};
|
||||
entry.value = damage.first;
|
||||
entry.effectType = Common::ActionEffectType::Damage;
|
||||
entry.param0 = static_cast< uint8_t >( damage.second );
|
||||
entry.param2 = 0x72;
|
||||
|
||||
effectPacket->addEffect( entry );
|
||||
|
||||
sendToInRangeSet( effectPacket, true );
|
||||
auto actionData = exdData.get< Data::Action >( 8 );
|
||||
assert( actionData );
|
||||
action = World::Action::make_Action( getAsChara(), 8, 0, actionData );
|
||||
}
|
||||
else
|
||||
{
|
||||
auto effectPacket = std::make_shared< Server::EffectPacket >( getId(), pTarget->getId(), 7 );
|
||||
effectPacket->setRotation( Util::floatToUInt16Rot( getRot() ) );
|
||||
|
||||
Common::EffectEntry entry{};
|
||||
entry.value = damage.first;
|
||||
entry.effectType = Common::ActionEffectType::Damage;
|
||||
entry.param0 = static_cast< uint8_t >( damage.second );
|
||||
entry.param2 = 0x73;
|
||||
|
||||
effectPacket->addEffect( entry );
|
||||
|
||||
sendToInRangeSet( effectPacket, true );
|
||||
|
||||
auto actionData = exdData.get< Data::Action >( 7 );
|
||||
assert( actionData );
|
||||
action = World::Action::make_Action( getAsChara(), 7, 0, actionData );
|
||||
}
|
||||
|
||||
pTarget->takeDamage( damage.first );
|
||||
action->setTargetId( pTarget->getId() );
|
||||
action->setPos( getPos() );
|
||||
action->setAutoAttack();
|
||||
|
||||
if( action->init() )
|
||||
{
|
||||
action->start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -2216,7 +2274,7 @@ void Sapphire::Entity::Player::updateHuntingLog( uint16_t id )
|
|||
const auto maxRank = 4;
|
||||
auto& pExdData = Common::Service< Data::ExdDataGenerated >::ref();
|
||||
|
||||
auto& logEntry = m_huntingLogEntries[ static_cast< uint8_t >( getClass() ) - 1 ];
|
||||
auto logEntryIndex = static_cast< uint8_t >( getClass() ) - 1;
|
||||
|
||||
bool logChanged = false;
|
||||
|
||||
|
@ -2225,6 +2283,10 @@ void Sapphire::Entity::Player::updateHuntingLog( uint16_t id )
|
|||
auto classJobInfo = pExdData.get< Sapphire::Data::ClassJob >( currentClass );
|
||||
if( !classJobInfo )
|
||||
return;
|
||||
if( classJobInfo->classJobParent > 0 )
|
||||
logEntryIndex = classJobInfo->classJobParent;
|
||||
|
||||
auto& logEntry = m_huntingLogEntries[ logEntryIndex ];
|
||||
|
||||
bool allSectionsComplete = true;
|
||||
for( int i = 1; i <= 10; ++i )
|
||||
|
@ -2301,7 +2363,7 @@ bool Sapphire::Entity::Player::hasQueuedAction() const
|
|||
|
||||
void Sapphire::Entity::Player::setQueuedAction( Sapphire::World::Action::ActionPtr pAction )
|
||||
{
|
||||
m_pQueuedAction = std::move( pAction ); // overwrite previous queued action if any
|
||||
m_pQueuedAction = std::move( pAction );
|
||||
}
|
||||
|
||||
bool Sapphire::Entity::Player::checkAction()
|
||||
|
@ -2374,4 +2436,281 @@ void Sapphire::Entity::Player::gaugeSetRaw( uint8_t* pData )
|
|||
{
|
||||
std::memcpy( &m_gauge, pData, 15 );
|
||||
sendActorGauge();
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::gaugeWarSetIb( uint8_t value )
|
||||
{
|
||||
assert( value >= 0 && value <= 100 );
|
||||
auto oldValue = gaugeWarGetIb();
|
||||
if( ( oldValue == 0 && value != 0 ) ||
|
||||
( oldValue != 0 && value == 0 ) )
|
||||
{
|
||||
if( m_effect == 0 && value != 0 )
|
||||
setVisualEffect( 7, true );
|
||||
else if ( m_effect == 7 && value == 0 )
|
||||
setVisualEffect( 0, true );
|
||||
}
|
||||
m_gauge.war.beastGauge = value;
|
||||
if( oldValue != value )
|
||||
sendActorGauge();
|
||||
}
|
||||
|
||||
uint8_t Sapphire::Entity::Player::gaugeWarGetIb()
|
||||
{
|
||||
return m_gauge.war.beastGauge;
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::gaugePldSetOath( uint8_t value )
|
||||
{
|
||||
assert( value >= 0 && value <= 100 );
|
||||
auto oldValue = gaugePldGetOath();
|
||||
m_gauge.pld.oathGauge = value;
|
||||
if( oldValue != value )
|
||||
sendActorGauge();
|
||||
}
|
||||
|
||||
uint8_t Sapphire::Entity::Player::gaugePldGetOath()
|
||||
{
|
||||
return m_gauge.pld.oathGauge;
|
||||
}
|
||||
|
||||
uint8_t Sapphire::Entity::Player::gaugeWhmGetLily()
|
||||
{
|
||||
return m_gauge.whm.lilies;
|
||||
}
|
||||
|
||||
uint8_t Sapphire::Entity::Player::gaugeWhmGetBloodLily()
|
||||
{
|
||||
return m_gauge.whm.bloodLilies;
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::gaugeWhmSetLilies( uint8_t liles, uint8_t bloodLilies )
|
||||
{
|
||||
assert( liles >= 0 && liles <= 3 );
|
||||
assert( bloodLilies >= 0 && bloodLilies <= 3 );
|
||||
m_gauge.whm.lilies = liles;
|
||||
m_gauge.whm.bloodLilies = bloodLilies;
|
||||
sendActorGauge();
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::gaugeWhmSetLilyTimer( uint16_t value, bool sendPacket )
|
||||
{
|
||||
assert( value >= 0 && value <= 30000 );
|
||||
m_gauge.whm.lilyTimer = value;
|
||||
if( sendPacket )
|
||||
sendActorGauge();
|
||||
}
|
||||
|
||||
uint16_t Sapphire::Entity::Player::gaugeWhmGetLilyTimer()
|
||||
{
|
||||
return m_gauge.whm.lilyTimer;
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::gaugeDrkSetBlood( uint8_t value )
|
||||
{
|
||||
assert( value >= 0 && value <= 100 );
|
||||
auto oldValue = gaugeDrkGetBlood();
|
||||
m_gauge.drk.blood = value;
|
||||
if( oldValue != value )
|
||||
sendActorGauge();
|
||||
}
|
||||
|
||||
uint8_t Sapphire::Entity::Player::gaugeDrkGetBlood()
|
||||
{
|
||||
return m_gauge.drk.blood;
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::gaugeDrkSetDarkArts( bool value )
|
||||
{
|
||||
auto oldValue = gaugeDrkGetDarkArts();
|
||||
m_gauge.drk.darkArts = value ? 1 : 0;
|
||||
if( oldValue != value )
|
||||
sendActorGauge();
|
||||
}
|
||||
|
||||
bool Sapphire::Entity::Player::gaugeDrkGetDarkArts()
|
||||
{
|
||||
return m_gauge.drk.darkArts > 0;
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::gaugeDrkSetDarkSideTimer( uint16_t value, bool sendPacket )
|
||||
{
|
||||
assert( value >= 0 && value <= 60000 );
|
||||
auto oldValue = gaugeDrkGetDarkSideTimer();
|
||||
m_gauge.drk.darksideTimer = value;
|
||||
if( ( oldValue == 0 && value != 0 ) ||
|
||||
( oldValue != 0 && value == 0 ) )
|
||||
{
|
||||
if( m_effect == 0 && value != 0 )
|
||||
{
|
||||
setVisualEffect( 22, true );
|
||||
sendDebug( "{0}", gaugeDrkGetDarkSideTimer() );
|
||||
}
|
||||
else if ( m_effect == 22 && value == 0 )
|
||||
{
|
||||
setVisualEffect( 0, true );
|
||||
sendDebug( "{0}", gaugeDrkGetDarkSideTimer() );
|
||||
}
|
||||
}
|
||||
if( sendPacket )
|
||||
sendActorGauge();
|
||||
}
|
||||
|
||||
uint16_t Sapphire::Entity::Player::gaugeDrkGetDarkSideTimer()
|
||||
{
|
||||
return m_gauge.drk.darksideTimer;
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::gaugeDrkSetShadowTimer( uint16_t value, bool sendPacket )
|
||||
{
|
||||
assert( value >= 0 && value <= 60000 );
|
||||
m_gauge.drk.shadowTimer = value;
|
||||
if( sendPacket )
|
||||
sendActorGauge();
|
||||
}
|
||||
|
||||
uint16_t Sapphire::Entity::Player::gaugeDrkGetShadowTimer()
|
||||
{
|
||||
return m_gauge.drk.shadowTimer;
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::gaugeGnbSetAmmo( uint8_t value )
|
||||
{
|
||||
assert( value >= 0 && value <= 2 );
|
||||
auto oldValue = gaugeGnbGetAmmo();
|
||||
m_gauge.gnb.ammo = value;
|
||||
if( oldValue != value )
|
||||
sendActorGauge();
|
||||
}
|
||||
|
||||
uint8_t Sapphire::Entity::Player::gaugeGnbGetAmmo()
|
||||
{
|
||||
return m_gauge.gnb.ammo;
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::gaugeGnbSetComboStep( uint8_t value )
|
||||
{
|
||||
assert( value >= 0 && value <= 2 );
|
||||
auto oldValue = gaugeGnbGetComboStep();
|
||||
m_gauge.gnb.ammoComboStep = value;
|
||||
if( oldValue != value )
|
||||
sendActorGauge();
|
||||
}
|
||||
|
||||
uint8_t Sapphire::Entity::Player::gaugeGnbGetComboStep()
|
||||
{
|
||||
return m_gauge.gnb.ammoComboStep;
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::gaugeDrgSetDragonTimer( uint16_t value, bool sendPacket )
|
||||
{
|
||||
assert( value >= 0 && value <= 30000 );
|
||||
auto oldValue = gaugeDrgGetDragonTimer();
|
||||
m_gauge.drg.dragonTimer = value;
|
||||
if( ( oldValue == 0 && value != 0 ) ||
|
||||
( oldValue != 0 && value == 0 ) )
|
||||
{
|
||||
if( m_effect == 0 && value != 0 )
|
||||
{
|
||||
setVisualEffect( 21, true );
|
||||
}
|
||||
else if ( m_effect == 34 && value == 0 )
|
||||
{
|
||||
gaugeDrgSetDragonState( Sapphire::Common::DrgState::BloodOfTheDragon );
|
||||
setVisualEffect( 21, true );
|
||||
gaugeDrgSetDragonTimer( 30000, true );
|
||||
}
|
||||
else if ( m_effect == 21 && value == 0 )
|
||||
{
|
||||
gaugeDrgSetDragonState( Sapphire::Common::DrgState::None );
|
||||
setVisualEffect( 0, true );
|
||||
}
|
||||
}
|
||||
if( sendPacket )
|
||||
sendActorGauge();
|
||||
}
|
||||
|
||||
uint16_t Sapphire::Entity::Player::gaugeDrgGetDragonTimer()
|
||||
{
|
||||
return m_gauge.drg.dragonTimer;
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::gaugeDrgSetDragonState( Sapphire::Common::DrgState value )
|
||||
{
|
||||
auto oldValue = gaugeDrgGetDragonStateRaw();
|
||||
m_gauge.drg.dragonState = value;
|
||||
if( oldValue != value )
|
||||
sendActorGauge();
|
||||
}
|
||||
|
||||
bool Sapphire::Entity::Player::gaugeDrgGetDragonState( Sapphire::Common::DrgState state )
|
||||
{
|
||||
return ( static_cast< uint8_t >( m_gauge.drg.dragonState ) & static_cast< uint8_t >( state ) ) > 0;
|
||||
}
|
||||
|
||||
Sapphire::Common::DrgState Sapphire::Entity::Player::gaugeDrgGetDragonStateRaw()
|
||||
{
|
||||
return m_gauge.drg.dragonState;
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::gaugeDrgSetEyes( uint8_t value )
|
||||
{
|
||||
assert( value >= 0 && value <= 2 );
|
||||
auto oldValue = gaugeDrgGetEyes();
|
||||
m_gauge.drg.eyes = value;
|
||||
if( oldValue != value )
|
||||
sendActorGauge();
|
||||
}
|
||||
|
||||
uint8_t Sapphire::Entity::Player::gaugeDrgGetEyes()
|
||||
{
|
||||
return m_gauge.drg.eyes;
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::gaugeSamSetKenki( uint8_t value )
|
||||
{
|
||||
assert( value >= 0 && value <= 100 );
|
||||
auto oldValue = gaugeSamGetKenki();
|
||||
m_gauge.sam.kenki = value;
|
||||
if( oldValue != value )
|
||||
sendActorGauge();
|
||||
}
|
||||
|
||||
uint8_t Sapphire::Entity::Player::gaugeSamGetKenki()
|
||||
{
|
||||
return m_gauge.sam.kenki;
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::gaugeSamSetSen( Sapphire::Common::SamSen type, bool value )
|
||||
{
|
||||
auto sen = static_cast< uint8_t >( type );
|
||||
auto current = static_cast< uint8_t >( gaugeSamGetSenRaw() );
|
||||
if( value )
|
||||
current |= sen;
|
||||
else
|
||||
current &= ~sen;
|
||||
gaugeSamSetSen( static_cast< Sapphire::Common::SamSen >( current ) );
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::gaugeSamSetSen( Sapphire::Common::SamSen value )
|
||||
{
|
||||
auto oldValue = gaugeSamGetSenRaw();
|
||||
m_gauge.sam.sen = value;
|
||||
if( oldValue != value )
|
||||
sendActorGauge();
|
||||
}
|
||||
|
||||
bool Sapphire::Entity::Player::gaugeSamGetSen( Sapphire::Common::SamSen type )
|
||||
{
|
||||
return ( static_cast< uint8_t >( m_gauge.sam.sen ) & static_cast< uint8_t >( type ) ) > 0;
|
||||
}
|
||||
|
||||
Sapphire::Common::SamSen Sapphire::Entity::Player::gaugeSamGetSenRaw()
|
||||
{
|
||||
return m_gauge.sam.sen;
|
||||
}
|
||||
|
||||
bool Sapphire::Entity::Player::gaugeSamHasAnySen()
|
||||
{
|
||||
return static_cast< uint8_t >( m_gauge.sam.sen ) > 0;
|
||||
}
|
|
@ -511,7 +511,7 @@ namespace Sapphire::Entity
|
|||
/*! gets the players territoryTypeId */
|
||||
uint32_t getTerritoryTypeId() const;
|
||||
|
||||
void forceZoneing( uint32_t zoneId );
|
||||
void forceZoneing( uint32_t zoneId = 0, float x = FLT_MAX, float y = FLT_MAX, float z = FLT_MAX, float r = FLT_MAX, bool showZoneName = false );
|
||||
|
||||
/*! return player to preset homepoint */
|
||||
void returnToHomepoint();
|
||||
|
@ -1016,6 +1016,48 @@ namespace Sapphire::Entity
|
|||
void sendActorGauge();
|
||||
void gaugeSetRaw( uint8_t* pData );
|
||||
|
||||
void gaugeWarSetIb( uint8_t value );
|
||||
uint8_t gaugeWarGetIb();
|
||||
|
||||
void gaugePldSetOath( uint8_t value );
|
||||
uint8_t gaugePldGetOath();
|
||||
|
||||
uint8_t gaugeWhmGetLily();
|
||||
uint8_t gaugeWhmGetBloodLily();
|
||||
void gaugeWhmSetLilies( uint8_t liles, uint8_t bloodLilies );
|
||||
void gaugeWhmSetLilyTimer( uint16_t value, bool sendPacket = false );
|
||||
uint16_t gaugeWhmGetLilyTimer();
|
||||
|
||||
void gaugeDrkSetBlood( uint8_t value );
|
||||
uint8_t gaugeDrkGetBlood();
|
||||
void gaugeDrkSetDarkArts( bool value );
|
||||
bool gaugeDrkGetDarkArts();
|
||||
void gaugeDrkSetDarkSideTimer( uint16_t value, bool sendPacket = false );
|
||||
uint16_t gaugeDrkGetDarkSideTimer();
|
||||
void gaugeDrkSetShadowTimer( uint16_t value, bool sendPacket = false );
|
||||
uint16_t gaugeDrkGetShadowTimer();
|
||||
|
||||
void gaugeGnbSetAmmo( uint8_t value );
|
||||
uint8_t gaugeGnbGetAmmo();
|
||||
void gaugeGnbSetComboStep( uint8_t value );
|
||||
uint8_t gaugeGnbGetComboStep();
|
||||
|
||||
void gaugeDrgSetDragonTimer ( uint16_t value, bool sendPacket = false );
|
||||
uint16_t gaugeDrgGetDragonTimer();
|
||||
void gaugeDrgSetDragonState ( Sapphire::Common::DrgState value );
|
||||
bool gaugeDrgGetDragonState( Common::DrgState state );
|
||||
Common::DrgState gaugeDrgGetDragonStateRaw();
|
||||
void Sapphire::Entity::Player::gaugeDrgSetEyes( uint8_t value );
|
||||
uint8_t Sapphire::Entity::Player::gaugeDrgGetEyes();
|
||||
|
||||
void gaugeSamSetKenki( uint8_t value );
|
||||
uint8_t gaugeSamGetKenki();
|
||||
void gaugeSamSetSen( Common::SamSen type, bool value );
|
||||
void gaugeSamSetSen( Common::SamSen value );
|
||||
bool gaugeSamGetSen( Common::SamSen type );
|
||||
Common::SamSen gaugeSamGetSenRaw();
|
||||
bool gaugeSamHasAnySen();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setPosAndNotifyClient( float x, float y, float z, float rot );
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include "Script/ScriptMgr.h"
|
||||
#include "Actor/Player.h"
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
#include <Exd/ExdDataGenerated.h>
|
||||
|
||||
#include <Network/PacketWrappers/EffectPacket.h>
|
||||
|
@ -107,6 +109,14 @@ void World::Manager::ActionMgr::bootstrapAction( Entity::Player& player,
|
|||
Action::ActionPtr currentAction,
|
||||
Data::Action& actionData )
|
||||
{
|
||||
for( const auto& statusIt : player.getStatusEffectMap() )
|
||||
{
|
||||
statusIt.second->onBeforeActionStart( currentAction.get() );
|
||||
}
|
||||
|
||||
if( currentAction->isInterrupted() )
|
||||
return;
|
||||
|
||||
if( !currentAction->preCheck() )
|
||||
{
|
||||
player.sendDebug( "preCheck failed" );
|
||||
|
|
|
@ -636,6 +636,8 @@ bool Sapphire::World::Manager::HousingMgr::initHouseModels( Entity::Player& play
|
|||
|
||||
auto pItem = invMgr.createItem( player, static_cast< uint32_t >( item.second ) );
|
||||
|
||||
player.sendDebug( "container: {}, slot: {}, uid: {}", destContainer.first, item.first, pItem->getUId() );
|
||||
|
||||
container->setItem( static_cast< uint8_t >( item.first ), pItem );
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,10 @@
|
|||
|
||||
#include "Inventory/Item.h"
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
#include "Action/Action.h"
|
||||
|
||||
#include "CalcStats.h"
|
||||
|
||||
using namespace Sapphire::Math;
|
||||
|
@ -104,9 +108,7 @@ const int levelTable[81][6] =
|
|||
{ 340, 380, 3300, 3600, 569, 569 },
|
||||
};
|
||||
|
||||
std::random_device CalcStats::dev;
|
||||
std::mt19937 CalcStats::rng( dev() );
|
||||
std::uniform_int_distribution< std::mt19937::result_type > CalcStats::range100( 0, 99 );
|
||||
std::unique_ptr< RandGenerator< float > > CalcStats::rnd = nullptr;
|
||||
|
||||
/*
|
||||
Class used for battle-related formulas and calculations.
|
||||
|
@ -181,16 +183,42 @@ uint32_t CalcStats::calculateMaxHp( PlayerPtr pPlayer )
|
|||
return result;
|
||||
}
|
||||
|
||||
float CalcStats::dodgeProbability( const Sapphire::Entity::Chara& chara )
|
||||
{
|
||||
// dummy value: 5% for players.
|
||||
return chara.isPlayer() ? 5 : 0;
|
||||
}
|
||||
|
||||
float CalcStats::blockProbability( const Chara& chara )
|
||||
{
|
||||
auto level = chara.getLevel();
|
||||
auto blockRate = static_cast< float >( chara.getStatValue( Common::BaseParam::BlockRate ) );
|
||||
auto levelVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] );
|
||||
|
||||
return std::floor( ( 30 * blockRate ) / levelVal + 10 );
|
||||
float result = std::floor( ( 30 * blockRate ) / levelVal + 10 );
|
||||
|
||||
for( auto const& entry : chara.getStatusEffectMap() )
|
||||
{
|
||||
result += entry.second->getEffectEntry().getBlockRateBonus();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
float CalcStats::directHitProbability( const Chara& chara )
|
||||
float CalcStats::parryProbability( const Sapphire::Entity::Chara& chara )
|
||||
{
|
||||
// dummy value: 10% for players.
|
||||
float result = chara.isPlayer() ? 10 : 0;
|
||||
|
||||
for( auto const& entry : chara.getStatusEffectMap() )
|
||||
{
|
||||
result += entry.second->getEffectEntry().getParryRateBonus();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
float CalcStats::directHitProbability( const Chara& chara, Sapphire::Common::CritDHBonusFilter filter )
|
||||
{
|
||||
const auto& baseStats = chara.getStats();
|
||||
auto level = chara.getLevel();
|
||||
|
@ -200,10 +228,23 @@ float CalcStats::directHitProbability( const Chara& chara )
|
|||
auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] );
|
||||
auto subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] );
|
||||
|
||||
return std::floor( 550.f * ( dhRate - subVal ) / divVal ) / 10.f;
|
||||
auto result = std::floor( 550.f * ( dhRate - subVal ) / divVal ) / 10.f;
|
||||
|
||||
for( auto const& entry : chara.getStatusEffectMap() )
|
||||
{
|
||||
auto status = entry.second;
|
||||
auto effectEntry = status->getEffectEntry();
|
||||
if( effectEntry.getType() != Common::StatusEffectType::CritDHRateBonus )
|
||||
continue;
|
||||
if( static_cast< int32_t >( effectEntry.getActionTypeFilter() ) & static_cast< int32_t >( filter ) )
|
||||
{
|
||||
result += effectEntry.getDirectHitRateBonus();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
float CalcStats::criticalHitProbability( const Chara& chara )
|
||||
float CalcStats::criticalHitProbability( const Chara& chara, Sapphire::Common::CritDHBonusFilter filter )
|
||||
{
|
||||
const auto& baseStats = chara.getStats();
|
||||
auto level = chara.getLevel();
|
||||
|
@ -213,7 +254,20 @@ float CalcStats::criticalHitProbability( const Chara& chara )
|
|||
auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] );
|
||||
auto subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] );
|
||||
|
||||
return std::floor( 200.f * ( chRate - subVal ) / divVal + 50.f ) / 10.f;
|
||||
auto result = std::floor( 200.f * ( chRate - subVal ) / divVal + 50.f ) / 10.f;
|
||||
|
||||
for( auto const& entry : chara.getStatusEffectMap() )
|
||||
{
|
||||
auto status = entry.second;
|
||||
auto effectEntry = status->getEffectEntry();
|
||||
if( effectEntry.getType() != Common::StatusEffectType::CritDHRateBonus )
|
||||
continue;
|
||||
if( static_cast< int32_t >( effectEntry.getActionTypeFilter() ) & static_cast< int32_t >( filter ) )
|
||||
{
|
||||
result += effectEntry.getCritRateBonus();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
@ -222,15 +276,8 @@ float CalcStats::potency( uint16_t potency )
|
|||
return potency / 100.f;
|
||||
}
|
||||
|
||||
float CalcStats::autoAttackPotency( const Sapphire::Entity::Chara& chara )
|
||||
float CalcStats::autoAttackPotency( const Sapphire::Entity::Chara& chara, uint32_t aaPotency )
|
||||
{
|
||||
uint32_t aaPotency = AUTO_ATTACK_POTENCY;
|
||||
|
||||
if( chara.getRole() == Common::Role::RangedPhysical )
|
||||
{
|
||||
aaPotency = RANGED_AUTO_ATTACK_POTENCY;
|
||||
}
|
||||
|
||||
float autoAttackDelay = 2.5f;
|
||||
// fetch actual auto attack delay if its a player
|
||||
if( chara.isPlayer() )
|
||||
|
@ -328,6 +375,29 @@ float CalcStats::healingMagicPower( const Sapphire::Entity::Chara& chara )
|
|||
return calcAttackPower( chara, chara.getStatValue( Common::BaseParam::HealingMagicPotency ) );
|
||||
}
|
||||
|
||||
float CalcStats::getWeaponDamage( CharaPtr chara )
|
||||
{
|
||||
auto wepDmg = chara->getLevel();
|
||||
|
||||
if( auto player = chara->getAsPlayer() )
|
||||
{
|
||||
auto item = player->getEquippedWeapon();
|
||||
assert( item );
|
||||
|
||||
auto role = player->getRole();
|
||||
if( role == Common::Role::RangedMagical || role == Common::Role::Healer )
|
||||
{
|
||||
wepDmg = item->getMagicalDmg();
|
||||
}
|
||||
else
|
||||
{
|
||||
wepDmg = item->getPhysicalDmg();
|
||||
}
|
||||
}
|
||||
|
||||
return wepDmg;
|
||||
}
|
||||
|
||||
float CalcStats::determination( const Sapphire::Entity::Chara& chara )
|
||||
{
|
||||
auto level = chara.getLevel();
|
||||
|
@ -409,6 +479,11 @@ float CalcStats::blockStrength( const Sapphire::Entity::Chara& chara )
|
|||
return std::floor( ( 30 * blockStrength ) / levelVal + 10 ) / 100.f;
|
||||
}
|
||||
|
||||
float CalcStats::parryStrength( const Sapphire::Entity::Chara& chara )
|
||||
{
|
||||
return 0.15f;
|
||||
}
|
||||
|
||||
float CalcStats::autoAttack( const Sapphire::Entity::Chara& chara )
|
||||
{
|
||||
// todo: default values for NPCs, not sure what we should have here
|
||||
|
@ -428,6 +503,11 @@ float CalcStats::autoAttack( const Sapphire::Entity::Chara& chara )
|
|||
autoAttackDelay = pItem->getDelay() / 1000.f;
|
||||
weaponDamage = pItem->getWeaponDmg();
|
||||
}
|
||||
else
|
||||
{
|
||||
// dummy value for BNpc
|
||||
weaponDamage = chara.getLevel() * 3;
|
||||
}
|
||||
|
||||
auto level = chara.getLevel();
|
||||
auto mainVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::MAIN ] );
|
||||
|
@ -442,12 +522,12 @@ float CalcStats::healingMagicPotency( const Sapphire::Entity::Chara& chara )
|
|||
return std::floor( 100.f * ( chara.getStatValue( Common::BaseParam::HealingMagicPotency ) - 292.f ) / 264.f + 100.f ) / 100.f;
|
||||
}
|
||||
|
||||
std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcAutoAttackDamage( const Sapphire::Entity::Chara& chara )
|
||||
std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcAutoAttackDamage( const Sapphire::Entity::Chara& chara, uint32_t ptc )
|
||||
{
|
||||
// D = ⌊ f(ptc) × f(aa) × f(ap) × f(det) × f(tnc) × traits ⌋ × f(ss) ⌋ ×
|
||||
// f(chr) ⌋ × f(dhr) ⌋ × rand[ 0.95, 1.05 ] ⌋ × buff_1 ⌋ × buff... ⌋
|
||||
|
||||
auto pot = autoAttackPotency( chara );
|
||||
auto pot = autoAttackPotency( chara, ptc );
|
||||
auto aa = autoAttack( chara );
|
||||
auto ap = getPrimaryAttackPower( chara );
|
||||
auto det = determination( chara );
|
||||
|
@ -464,13 +544,13 @@ std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcAutoA
|
|||
|
||||
factor = std::floor( factor * speed( chara ) );
|
||||
|
||||
if( criticalHitProbability( chara ) > range100( rng ) )
|
||||
if( criticalHitProbability( chara, Common::CritDHBonusFilter::Damage ) > getRandomNumber0To100() )
|
||||
{
|
||||
factor *= criticalHitBonus( chara );
|
||||
hitType = Sapphire::Common::ActionHitSeverityType::CritDamage;
|
||||
}
|
||||
|
||||
if( directHitProbability( chara ) > range100( rng ) )
|
||||
if( directHitProbability( chara, Common::CritDHBonusFilter::Damage ) > getRandomNumber0To100() )
|
||||
{
|
||||
factor *= 1.25f;
|
||||
hitType = hitType == Sapphire::Common::ActionHitSeverityType::CritDamage ?
|
||||
|
@ -478,25 +558,42 @@ std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcAutoA
|
|||
Sapphire::Common::ActionHitSeverityType::DirectHitDamage;
|
||||
}
|
||||
|
||||
factor *= 1.0f + ( ( range100( rng ) - 50.0f ) / 1000.0f );
|
||||
factor *= 1.0f + ( ( getRandomNumber0To100() - 50.0f ) / 1000.0f );
|
||||
|
||||
// todo: buffs
|
||||
|
||||
constexpr auto format = "auto attack: pot: {} aa: {} ap: {} det: {} ten: {} = {}";
|
||||
|
||||
if( auto player = const_cast< Entity::Chara& >( chara ).getAsPlayer() )
|
||||
for( auto const& entry : chara.getStatusEffectMap() )
|
||||
{
|
||||
player->sendDebug( format, pot, aa, ap, det, ten, factor );
|
||||
auto status = entry.second;
|
||||
auto effectEntry = status->getEffectEntry();
|
||||
if( effectEntry.getType() != Common::StatusEffectType::DamageMultiplier )
|
||||
continue;
|
||||
if( static_cast< int32_t >( effectEntry.getActionTypeFilter() ) & static_cast< int32_t >( Common::ActionTypeFilter::Physical ) )
|
||||
{
|
||||
factor *= 1.0f + ( effectEntry.getOutgoingDamageMultiplier() / 100.0f );
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if( chara.isPlayer() )
|
||||
{
|
||||
Logger::debug( format, pot, aa, ap, det, ten, factor );
|
||||
auto player = const_cast< Entity::Chara& >( chara ).getAsPlayer();
|
||||
switch( player->getClass() )
|
||||
{
|
||||
case Common::ClassJob::Darkknight:
|
||||
{
|
||||
if( player->gaugeDrkGetDarkSideTimer() > 0 )
|
||||
{
|
||||
factor *= 1.1f;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
constexpr auto format = "auto attack: pot: {} aa: {} ap: {} det: {} ten: {} = {}";
|
||||
player->sendDebug( format, pot, aa, ap, det, ten, factor );
|
||||
}
|
||||
|
||||
return std::pair( factor, hitType );
|
||||
}
|
||||
|
||||
std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcActionDamage( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg )
|
||||
float CalcStats::calcDamageBaseOnPotency( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg )
|
||||
{
|
||||
// D = ⌊ f(pot) × f(wd) × f(ap) × f(det) × f(tnc) × traits ⌋
|
||||
// × f(chr) ⌋ × f(dhr) ⌋ × rand[ 0.95, 1.05 ] ⌋ buff_1 ⌋ × buff_1 ⌋ × buff... ⌋
|
||||
|
@ -511,25 +608,6 @@ std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcActio
|
|||
ten = tenacity( chara );
|
||||
|
||||
auto factor = std::floor( pot * wd * ap * det * ten );
|
||||
Sapphire::Common::ActionHitSeverityType hitType = Sapphire::Common::ActionHitSeverityType::NormalDamage;
|
||||
|
||||
if( criticalHitProbability( chara ) > range100( rng ) )
|
||||
{
|
||||
factor *= criticalHitBonus( chara );
|
||||
hitType = Sapphire::Common::ActionHitSeverityType::CritDamage;
|
||||
}
|
||||
|
||||
if( directHitProbability( chara ) > range100( rng ) )
|
||||
{
|
||||
factor *= 1.25f;
|
||||
hitType = hitType == Sapphire::Common::ActionHitSeverityType::CritDamage ?
|
||||
Sapphire::Common::ActionHitSeverityType::CritDirectHitDamage :
|
||||
Sapphire::Common::ActionHitSeverityType::DirectHitDamage;
|
||||
}
|
||||
|
||||
factor *= 1.0f + ( ( range100( rng ) - 50.0f ) / 1000.0f );
|
||||
|
||||
// todo: buffs
|
||||
|
||||
constexpr auto format = "dmg: pot: {} ({}) wd: {} ({}) ap: {} det: {} ten: {} = {}";
|
||||
|
||||
|
@ -537,27 +615,185 @@ std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcActio
|
|||
{
|
||||
player->sendDebug( format, pot, ptc, wd, wepDmg, ap, det, ten, factor );
|
||||
}
|
||||
else
|
||||
|
||||
return factor;
|
||||
}
|
||||
|
||||
float CalcStats::calcHealBaseOnPotency( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg )
|
||||
{
|
||||
// reused damage formula just for testing
|
||||
auto pot = potency( static_cast< uint16_t >( ptc ) );
|
||||
auto wd = weaponDamage( chara, wepDmg );
|
||||
auto ap = getPrimaryAttackPower( chara );
|
||||
auto det = determination( chara );
|
||||
|
||||
auto factor = std::floor( pot * wd * ap * det );
|
||||
|
||||
constexpr auto format = "heal: pot: {} ({}) wd: {} ({}) ap: {} det: {} = {}";
|
||||
|
||||
if( auto player = const_cast< Entity::Chara& >( chara ).getAsPlayer() )
|
||||
{
|
||||
Logger::debug( format, pot, ptc, wd, wepDmg, ap, det, ten, factor );
|
||||
player->sendDebug( format, pot, ptc, wd, wepDmg, ap, det, factor );
|
||||
}
|
||||
|
||||
return factor;
|
||||
}
|
||||
|
||||
std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcActionDamage( Sapphire::World::Action::Action* pAction, const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg )
|
||||
{
|
||||
auto factor = calcDamageBaseOnPotency( chara, ptc, wepDmg );
|
||||
Sapphire::Common::ActionHitSeverityType hitType = Sapphire::Common::ActionHitSeverityType::NormalDamage;
|
||||
|
||||
auto critProb = criticalHitProbability( chara, Common::CritDHBonusFilter::Damage );
|
||||
if( pAction )
|
||||
{
|
||||
auto lutEntry = pAction->getActionEntry();
|
||||
if( lutEntry.bonusEffect & Common::ActionBonusEffect::CritBonus )
|
||||
{
|
||||
if( pAction->checkActionBonusRequirement() )
|
||||
{
|
||||
critProb += lutEntry.getCritRateBonus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( critProb > getRandomNumber0To100() )
|
||||
{
|
||||
factor *= criticalHitBonus( chara );
|
||||
hitType = Sapphire::Common::ActionHitSeverityType::CritDamage;
|
||||
}
|
||||
|
||||
auto dhProb = directHitProbability( chara, Common::CritDHBonusFilter::Damage );
|
||||
if( pAction )
|
||||
{
|
||||
auto lutEntry = pAction->getActionEntry();
|
||||
if( lutEntry.bonusEffect & Common::ActionBonusEffect::DHBonus )
|
||||
{
|
||||
if( pAction->checkActionBonusRequirement() )
|
||||
{
|
||||
dhProb += lutEntry.getDirectHitRateBonus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( dhProb > getRandomNumber0To100() )
|
||||
{
|
||||
factor *= 1.25f;
|
||||
hitType = hitType == Sapphire::Common::ActionHitSeverityType::CritDamage ?
|
||||
Sapphire::Common::ActionHitSeverityType::CritDirectHitDamage :
|
||||
Sapphire::Common::ActionHitSeverityType::DirectHitDamage;
|
||||
}
|
||||
|
||||
factor *= 1.0f + ( ( getRandomNumber0To100() - 50.0f ) / 1000.0f );
|
||||
|
||||
Common::ActionTypeFilter actionTypeFilter = Common::ActionTypeFilter::Physical;
|
||||
if( pAction && pAction->isMagical() )
|
||||
{
|
||||
actionTypeFilter = Common::ActionTypeFilter::Magical;
|
||||
}
|
||||
|
||||
for( auto const& entry : chara.getStatusEffectMap() )
|
||||
{
|
||||
auto status = entry.second;
|
||||
auto effectEntry = status->getEffectEntry();
|
||||
if( effectEntry.getType() != Common::StatusEffectType::DamageMultiplier )
|
||||
continue;
|
||||
|
||||
if( static_cast< int32_t >( effectEntry.getActionTypeFilter() ) & static_cast< int32_t >( actionTypeFilter ) )
|
||||
{
|
||||
factor *= 1.0f + ( effectEntry.getOutgoingDamageMultiplier() / 100.0f );
|
||||
}
|
||||
}
|
||||
|
||||
if( chara.isPlayer() )
|
||||
{
|
||||
auto player = const_cast< Entity::Chara& >( chara ).getAsPlayer();
|
||||
switch( player->getClass() )
|
||||
{
|
||||
case Common::ClassJob::Darkknight:
|
||||
{
|
||||
if( player->gaugeDrkGetDarkSideTimer() > 0 )
|
||||
{
|
||||
factor *= 1.1f;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::pair( factor, hitType );
|
||||
}
|
||||
|
||||
std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcActionHealing( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg )
|
||||
float CalcStats::applyDamageReceiveMultiplier( const Sapphire::Entity::Chara& chara, float originalDamage, Common::ActionTypeFilter typeFilter )
|
||||
{
|
||||
// lol just for testing
|
||||
auto factor = std::floor( ptc * ( wepDmg / 10.0f ) + ptc );
|
||||
if( typeFilter == ActionTypeFilter::Unknown )
|
||||
return originalDamage;
|
||||
|
||||
float damage = originalDamage;
|
||||
|
||||
for( auto const& entry : chara.getStatusEffectMap() )
|
||||
{
|
||||
auto status = entry.second;
|
||||
auto effectEntry = status->getEffectEntry();
|
||||
if( effectEntry.getType() != Common::StatusEffectType::DamageReceiveMultiplier )
|
||||
continue;
|
||||
|
||||
if( static_cast< int32_t >( effectEntry.getActionTypeFilter() ) & static_cast< int32_t >( typeFilter ) )
|
||||
{
|
||||
damage *= ( 1.0f + ( effectEntry.getIncomingDamageMultiplier() / 100.0f ) );
|
||||
}
|
||||
}
|
||||
return damage;
|
||||
}
|
||||
|
||||
float CalcStats::applyHealingReceiveMultiplier( const Sapphire::Entity::Chara& chara, float originalHeal )
|
||||
{
|
||||
float heal = originalHeal;
|
||||
for( auto const& entry : chara.getStatusEffectMap() )
|
||||
{
|
||||
heal *= ( 1.0f + ( entry.second->getEffectEntry().getIncomingHealMultiplier() / 100.0f ) );
|
||||
}
|
||||
return heal;
|
||||
}
|
||||
|
||||
std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcActionHealing( Sapphire::World::Action::Action* pAction, const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg )
|
||||
{
|
||||
auto factor = calcHealBaseOnPotency( chara, ptc, wepDmg );
|
||||
Sapphire::Common::ActionHitSeverityType hitType = Sapphire::Common::ActionHitSeverityType::NormalHeal;
|
||||
|
||||
if( criticalHitProbability( chara ) > range100( rng ) )
|
||||
auto critProb = criticalHitProbability( chara, Common::CritDHBonusFilter::Heal );
|
||||
if( pAction )
|
||||
{
|
||||
auto lutEntry = pAction->getActionEntry();
|
||||
if( lutEntry.bonusEffect & Common::ActionBonusEffect::CritBonus )
|
||||
{
|
||||
if( pAction->checkActionBonusRequirement() )
|
||||
{
|
||||
critProb += lutEntry.getCritRateBonus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( critProb > getRandomNumber0To100() )
|
||||
{
|
||||
factor *= criticalHitBonus( chara );
|
||||
hitType = Sapphire::Common::ActionHitSeverityType::CritHeal;
|
||||
}
|
||||
|
||||
factor *= 1.0f + ( ( range100( rng ) - 50.0f ) / 1000.0f );
|
||||
factor *= 1.0f + ( ( getRandomNumber0To100() - 50.0f ) / 1000.0f );
|
||||
|
||||
for( auto const& entry : chara.getStatusEffectMap() )
|
||||
{
|
||||
auto status = entry.second;
|
||||
auto effectEntry = status->getEffectEntry();
|
||||
if( effectEntry.getType() != Common::StatusEffectType::HealCastMultiplier )
|
||||
continue;
|
||||
|
||||
if( pAction->isGCD() ) // must be a "cast"
|
||||
{
|
||||
factor *= 1.0f + ( effectEntry.getOutgoingHealMultiplier() / 100.0f );
|
||||
}
|
||||
}
|
||||
|
||||
return std::pair( factor, hitType );
|
||||
}
|
||||
|
@ -565,4 +801,81 @@ std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcActio
|
|||
uint32_t CalcStats::primaryStatValue( const Sapphire::Entity::Chara& chara )
|
||||
{
|
||||
return chara.getStatValue( chara.getPrimaryStat() );
|
||||
}
|
||||
|
||||
std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcDamageReflect( Sapphire::Entity::CharaPtr pCharaAttacker, Sapphire::Entity::CharaPtr pCharaVictim, float damage, Sapphire::Common::ActionTypeFilter filter )
|
||||
{
|
||||
for( auto const& entry : pCharaVictim->getStatusEffectMap() )
|
||||
{
|
||||
auto status = entry.second;
|
||||
auto effectEntry = status->getEffectEntry();
|
||||
|
||||
if( effectEntry.getType() == Common::StatusEffectType::DamageReceiveTrigger && ( static_cast< int32_t >( effectEntry.getActionTypeFilter() ) & static_cast< int32_t >( filter ) ) )
|
||||
{
|
||||
if( effectEntry.getTriggerResult() == Common::StatusEffectTriggerResult::ReflectDamage )
|
||||
{
|
||||
auto wepDmg = Sapphire::Math::CalcStats::getWeaponDamage( pCharaVictim );
|
||||
auto damage = Sapphire::Math::CalcStats::calcActionDamage( nullptr, *pCharaVictim, effectEntry.getTriggerValue(), wepDmg );
|
||||
damage.first = Math::CalcStats::applyDamageReceiveMultiplier( *pCharaAttacker, damage.first, effectEntry.getTriggerDamageType() );
|
||||
|
||||
return damage;
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::pair< float, Sapphire::Common::ActionHitSeverityType >( 0, Sapphire::Common::ActionHitSeverityType::NormalDamage );
|
||||
}
|
||||
|
||||
float CalcStats::calcAbsorbHP( Sapphire::Entity::CharaPtr pChara, float damage )
|
||||
{
|
||||
float result = 0;
|
||||
for( auto const& entry : pChara->getStatusEffectMap() )
|
||||
{
|
||||
auto status = entry.second;
|
||||
auto effectEntry = status->getEffectEntry();
|
||||
|
||||
if( effectEntry.getType() == Common::StatusEffectType::DamageDealtTrigger )
|
||||
{
|
||||
if( effectEntry.getTriggerResult() == Common::StatusEffectTriggerResult::AbsorbHP )
|
||||
{
|
||||
result += damage * effectEntry.getTriggerValue() / 100.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CalcStats::calcDodge( const Sapphire::Entity::Chara& chara )
|
||||
{
|
||||
if( dodgeProbability( chara ) > getRandomNumber0To100() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float CalcStats::calcBlock( const Sapphire::Entity::Chara& chara, float damage )
|
||||
{
|
||||
if( blockProbability( chara ) > getRandomNumber0To100() )
|
||||
{
|
||||
return damage * blockStrength( chara );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
float CalcStats::calcParry( const Sapphire::Entity::Chara& chara, float damage )
|
||||
{
|
||||
if( parryProbability( chara ) > getRandomNumber0To100() )
|
||||
{
|
||||
return damage * parryStrength( chara );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
float CalcStats::getRandomNumber0To100()
|
||||
{
|
||||
if( !rnd )
|
||||
{
|
||||
rnd = std::make_unique< RandGenerator< float > >( Service< RNGMgr >::ref().getRandGenerator< float >( 0, 100 ) );
|
||||
}
|
||||
return rnd->next();
|
||||
}
|
|
@ -1,37 +1,39 @@
|
|||
#ifndef _CALCSTATS_H
|
||||
#define _CALCSTATS_H
|
||||
|
||||
#include <random>
|
||||
#include <Common.h>
|
||||
#include "Forwards.h"
|
||||
#include "Manager/RNGMgr.h"
|
||||
|
||||
namespace Sapphire::Math
|
||||
{
|
||||
using namespace Sapphire::World::Manager;
|
||||
|
||||
class CalcStats
|
||||
{
|
||||
public:
|
||||
static const uint32_t AUTO_ATTACK_POTENCY = 110;
|
||||
static const uint32_t RANGED_AUTO_ATTACK_POTENCY = 100;
|
||||
|
||||
static float calculateBaseStat( const Entity::Chara& chara );
|
||||
|
||||
static uint32_t calculateMaxHp( Sapphire::Entity::PlayerPtr pPlayer );
|
||||
|
||||
static float dodgeProbability( const Sapphire::Entity::Chara& chara );
|
||||
|
||||
/*!
|
||||
* @brief Calculates the probability of a block happening
|
||||
*/
|
||||
static float blockProbability( const Sapphire::Entity::Chara& chara );
|
||||
|
||||
static float parryProbability( const Sapphire::Entity::Chara& chara );
|
||||
|
||||
/*!
|
||||
* @brief Calculates the probability of a direct hit happening
|
||||
*/
|
||||
static float directHitProbability( const Sapphire::Entity::Chara& chara );
|
||||
static float directHitProbability( const Sapphire::Entity::Chara& chara, Sapphire::Common::CritDHBonusFilter filter );
|
||||
|
||||
/*!
|
||||
* @brief Calculates the probability of a critical hit happening
|
||||
*/
|
||||
static float criticalHitProbability( const Sapphire::Entity::Chara& chara );
|
||||
static float criticalHitProbability( const Sapphire::Entity::Chara& chara, Sapphire::Common::CritDHBonusFilter filter );
|
||||
|
||||
/*!
|
||||
* @brief Calculates the contribution of potency to damage output.
|
||||
|
@ -40,7 +42,7 @@ namespace Sapphire::Math
|
|||
*/
|
||||
static float potency( uint16_t potency );
|
||||
|
||||
static float autoAttackPotency( const Sapphire::Entity::Chara& chara );
|
||||
static float autoAttackPotency( const Sapphire::Entity::Chara& chara, uint32_t aaPotency );
|
||||
|
||||
/*!
|
||||
* @brief Weapon damage is the contribution the weapon's damage rating
|
||||
|
@ -64,6 +66,8 @@ namespace Sapphire::Math
|
|||
|
||||
static float healingMagicPower( const Sapphire::Entity::Chara& chara );
|
||||
|
||||
static float getWeaponDamage( Sapphire::Entity::CharaPtr chara );
|
||||
|
||||
/*!
|
||||
* @brief Calculates determinations contribution to damage and healing output.
|
||||
*
|
||||
|
@ -116,6 +120,8 @@ namespace Sapphire::Math
|
|||
*/
|
||||
static float blockStrength( const Sapphire::Entity::Chara& chara );
|
||||
|
||||
static float parryStrength( const Sapphire::Entity::Chara& chara );
|
||||
|
||||
static float autoAttack( const Sapphire::Entity::Chara& chara );
|
||||
|
||||
/*!
|
||||
|
@ -129,13 +135,33 @@ namespace Sapphire::Math
|
|||
|
||||
////////////////////////////////////////////
|
||||
|
||||
static std::pair< float, Common::ActionHitSeverityType > calcAutoAttackDamage( const Sapphire::Entity::Chara& chara );
|
||||
static float calcDamageBaseOnPotency( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg );
|
||||
|
||||
static std::pair< float, Common::ActionHitSeverityType > calcActionDamage( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg );
|
||||
static float calcHealBaseOnPotency( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg );
|
||||
|
||||
static std::pair< float, Common::ActionHitSeverityType > calcActionHealing( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg );
|
||||
static std::pair< float, Common::ActionHitSeverityType > calcAutoAttackDamage( const Sapphire::Entity::Chara& chara, uint32_t ptc );
|
||||
|
||||
static std::pair< float, Common::ActionHitSeverityType > calcActionDamage( World::Action::Action* pAction, const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg );
|
||||
|
||||
static float applyDamageReceiveMultiplier( const Sapphire::Entity::Chara& chara, float originalDamage, Common::ActionTypeFilter typeFilter );
|
||||
|
||||
static float applyHealingReceiveMultiplier( const Sapphire::Entity::Chara& chara, float originalHeal );
|
||||
|
||||
static std::pair< float, Common::ActionHitSeverityType > calcActionHealing( World::Action::Action* pAction, const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg );
|
||||
|
||||
static uint32_t primaryStatValue( const Sapphire::Entity::Chara& chara );
|
||||
|
||||
static std::pair< float, Sapphire::Common::ActionHitSeverityType > calcDamageReflect( Sapphire::Entity::CharaPtr pCharaAttacker, Sapphire::Entity::CharaPtr pCharaVictim, float damage, Sapphire::Common::ActionTypeFilter filter );
|
||||
|
||||
static float calcAbsorbHP( Sapphire::Entity::CharaPtr pChara, float damage );
|
||||
|
||||
static bool calcDodge( const Sapphire::Entity::Chara& chara );
|
||||
|
||||
static float calcBlock( const Sapphire::Entity::Chara& chara, float damage );
|
||||
|
||||
static float calcParry( const Sapphire::Entity::Chara& chara, float damage );
|
||||
|
||||
static float getRandomNumber0To100();
|
||||
private:
|
||||
|
||||
/*!
|
||||
|
@ -145,9 +171,7 @@ namespace Sapphire::Math
|
|||
*/
|
||||
static float calcAttackPower( const Sapphire::Entity::Chara& chara, uint32_t attackPower );
|
||||
|
||||
static std::random_device dev;
|
||||
static std::mt19937 rng;
|
||||
static std::uniform_int_distribution< std::mt19937::result_type > range100;
|
||||
static std::unique_ptr< RandGenerator< float > > rnd;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ void Sapphire::Network::GameConnection::clientTriggerHandler( const Packets::FFX
|
|||
case ClientTriggerType::CastCancel: // Cancel cast
|
||||
{
|
||||
if( player.getCurrentAction() )
|
||||
player.getCurrentAction()->setInterrupted( Common::ActionInterruptType::RegularInterrupt );
|
||||
player.getCurrentAction()->interrupt();
|
||||
break;
|
||||
}
|
||||
case ClientTriggerType::Examine:
|
||||
|
|
|
@ -256,19 +256,12 @@ void Sapphire::Network::GameConnection::gm1Handler( const Packets::FFXIVARR_PACK
|
|||
{
|
||||
targetPlayer->setOnlineStatusMask( param1 );
|
||||
|
||||
auto statusPacket = makeZonePacket< FFXIVIpcSetOnlineStatus >( player.getId() );
|
||||
statusPacket->data().onlineStatusFlags = param1;
|
||||
queueOutPacket( statusPacket );
|
||||
|
||||
auto searchInfoPacket = makeZonePacket< FFXIVIpcSetSearchInfo >( player.getId() );
|
||||
searchInfoPacket->data().onlineStatusFlags = param1;
|
||||
searchInfoPacket->data().selectRegion = targetPlayer->getSearchSelectRegion();
|
||||
strcpy( searchInfoPacket->data().searchMessage, targetPlayer->getSearchMessage() );
|
||||
targetPlayer->queuePacket( searchInfoPacket );
|
||||
|
||||
targetPlayer->sendToInRangeSet( makeActorControl( player.getId(), SetStatusIcon,
|
||||
static_cast< uint8_t >( player.getOnlineStatus() ) ),
|
||||
true );
|
||||
player.sendNotice( "Icon for {0} was set to {1}", targetPlayer->getName(), param1 );
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -91,18 +91,11 @@ void Sapphire::Network::GameConnection::setSearchInfoHandler( const Packets::FFX
|
|||
// mark player as new adventurer
|
||||
player.setNewAdventurer( true );
|
||||
|
||||
auto statusPacket = makeZonePacket< FFXIVIpcSetOnlineStatus >( player.getId() );
|
||||
statusPacket->data().onlineStatusFlags = status;
|
||||
queueOutPacket( statusPacket );
|
||||
|
||||
auto searchInfoPacket = makeZonePacket< FFXIVIpcSetSearchInfo >( player.getId() );
|
||||
searchInfoPacket->data().onlineStatusFlags = status;
|
||||
searchInfoPacket->data().selectRegion = player.getSearchSelectRegion();
|
||||
strcpy( searchInfoPacket->data().searchMessage, player.getSearchMessage() );
|
||||
queueOutPacket( searchInfoPacket );
|
||||
|
||||
player.sendToInRangeSet( makeActorControl( player.getId(), SetStatusIcon,
|
||||
static_cast< uint8_t >( player.getOnlineStatus() ) ), true );
|
||||
}
|
||||
|
||||
void Sapphire::Network::GameConnection::reqSearchInfoHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket,
|
||||
|
@ -184,7 +177,7 @@ void Sapphire::Network::GameConnection::updatePositionHandler( const Packets::FF
|
|||
Entity::Player& player )
|
||||
{
|
||||
// if the player is marked for zoning we no longer want to update his pos
|
||||
if( player.isMarkedForZoning() )
|
||||
if( player.isMarkedForZoning() || !player.isLoadingComplete() )
|
||||
return;
|
||||
|
||||
const auto updatePositionPacket = ZoneChannelPacket< Client::FFXIVIpcUpdatePosition >( inPacket );
|
||||
|
@ -200,7 +193,7 @@ void Sapphire::Network::GameConnection::updatePositionHandler( const Packets::FF
|
|||
player.setPos( updatePositionPacket.data().position );
|
||||
|
||||
if( ( player.getCurrentAction() != nullptr ) && bPosChanged )
|
||||
player.getCurrentAction()->setInterrupted( Common::ActionInterruptType::RegularInterrupt );
|
||||
player.getCurrentAction()->interrupt();
|
||||
|
||||
// if no one is in range, don't bother trying to send a position update
|
||||
if( !player.hasInRangeActor() )
|
||||
|
@ -416,6 +409,7 @@ void Sapphire::Network::GameConnection::finishLoadingHandler( const Packets::FFX
|
|||
player.setIsLogin( false );
|
||||
}
|
||||
|
||||
player.setVisualEffect( 0, false );
|
||||
// spawn the player for himself
|
||||
player.spawn( player.getAsPlayer() );
|
||||
|
||||
|
|
|
@ -76,6 +76,10 @@ namespace Sapphire::ScriptAPI
|
|||
{
|
||||
}
|
||||
|
||||
void ActionScript::onBeforePreCheck( Sapphire::World::Action::Action& action )
|
||||
{
|
||||
}
|
||||
|
||||
void ActionScript::onStart( Sapphire::World::Action::Action& action )
|
||||
{
|
||||
}
|
||||
|
@ -84,6 +88,14 @@ namespace Sapphire::ScriptAPI
|
|||
{
|
||||
}
|
||||
|
||||
void ActionScript::onBeforeBuildEffect( Sapphire::World::Action::Action& action, uint8_t victimCounter, uint8_t validVictimCounter )
|
||||
{
|
||||
}
|
||||
|
||||
void ActionScript::onAfterBuildEffect( Sapphire::World::Action::Action& action )
|
||||
{
|
||||
}
|
||||
|
||||
void ActionScript::onInterrupt( Sapphire::World::Action::Action& action )
|
||||
{
|
||||
}
|
||||
|
|
|
@ -3,7 +3,11 @@
|
|||
|
||||
#include <string>
|
||||
#include <Event/EventHandler.h>
|
||||
#include "Manager/EventMgr.h"
|
||||
#include "Manager/PlayerMgr.h"
|
||||
#include "Manager/TerritoryMgr.h"
|
||||
#include "ForwardsZone.h"
|
||||
#include <Service.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define EXPORT __declspec( dllexport )
|
||||
|
@ -125,10 +129,16 @@ namespace Sapphire::ScriptAPI
|
|||
public:
|
||||
explicit ActionScript( uint32_t actionId );
|
||||
|
||||
virtual void onBeforePreCheck( Sapphire::World::Action::Action& action );
|
||||
|
||||
virtual void onStart( Sapphire::World::Action::Action& action );
|
||||
|
||||
virtual void onExecute( Sapphire::World::Action::Action& action );
|
||||
|
||||
virtual void onBeforeBuildEffect( Sapphire::World::Action::Action& action, uint8_t victimCounter, uint8_t validVictimCounter );
|
||||
|
||||
virtual void onAfterBuildEffect( Sapphire::World::Action::Action& action );
|
||||
|
||||
virtual void onInterrupt( Sapphire::World::Action::Action& action );
|
||||
};
|
||||
|
||||
|
|
|
@ -329,6 +329,18 @@ bool Sapphire::Scripting::ScriptMgr::onEObjHit( Sapphire::Entity::Player& player
|
|||
return didCallScript;
|
||||
}
|
||||
|
||||
bool Sapphire::Scripting::ScriptMgr::onBeforePreCheck( World::Action::Action& action )
|
||||
{
|
||||
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::ActionScript >( action.getId() );
|
||||
|
||||
if( script )
|
||||
{
|
||||
script->onBeforePreCheck( action );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Sapphire::Scripting::ScriptMgr::onExecute( World::Action::Action& action )
|
||||
{
|
||||
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::ActionScript >( action.getId() );
|
||||
|
@ -341,6 +353,30 @@ bool Sapphire::Scripting::ScriptMgr::onExecute( World::Action::Action& action )
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Sapphire::Scripting::ScriptMgr::onBeforeBuildEffect( World::Action::Action& action, uint8_t victimCounter, uint8_t validVictimCounter )
|
||||
{
|
||||
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::ActionScript >( action.getId() );
|
||||
|
||||
if( script )
|
||||
{
|
||||
script->onBeforeBuildEffect( action, victimCounter, validVictimCounter );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
bool Sapphire::Scripting::ScriptMgr::onAfterBuildEffect( World::Action::Action& action )
|
||||
{
|
||||
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::ActionScript >( action.getId() );
|
||||
|
||||
if( script )
|
||||
{
|
||||
script->onAfterBuildEffect( action );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Sapphire::Scripting::ScriptMgr::onInterrupt( World::Action::Action& action )
|
||||
{
|
||||
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::ActionScript >( action.getId() );
|
||||
|
|
|
@ -71,12 +71,18 @@ namespace Sapphire::Scripting
|
|||
|
||||
bool onEObjHit( Entity::Player& player, uint64_t actorId, uint32_t actionId );
|
||||
|
||||
bool onBeforePreCheck( World::Action::Action& action );
|
||||
|
||||
bool onStart( World::Action::Action& action );
|
||||
|
||||
bool onInterrupt( World::Action::Action& action );
|
||||
|
||||
bool onExecute( World::Action::Action& action );
|
||||
|
||||
bool onBeforeBuildEffect( World::Action::Action& action, uint8_t victimCounter, uint8_t validVictimCounter );
|
||||
|
||||
bool onAfterBuildEffect( World::Action::Action& action );
|
||||
|
||||
bool onStatusReceive( Entity::CharaPtr pActor, uint32_t effectId );
|
||||
|
||||
bool onStatusTick( Entity::CharaPtr pActor, Sapphire::StatusEffect::StatusEffect& effect );
|
||||
|
|
|
@ -6,11 +6,16 @@
|
|||
#include <algorithm>
|
||||
#include <Service.h>
|
||||
|
||||
#include "Actor/Player.h"
|
||||
#include "Actor/Chara.h"
|
||||
#include "Actor/Actor.h"
|
||||
|
||||
#include "Action/Action.h"
|
||||
|
||||
#include "Script/ScriptMgr.h"
|
||||
|
||||
#include "Math/CalcStats.h"
|
||||
|
||||
#include "StatusEffect.h"
|
||||
|
||||
using namespace Sapphire::Common;
|
||||
|
@ -25,7 +30,11 @@ Sapphire::StatusEffect::StatusEffect::StatusEffect( uint32_t id, Entity::CharaPt
|
|||
m_duration( duration ),
|
||||
m_startTime( 0 ),
|
||||
m_tickRate( tickRate ),
|
||||
m_lastTick( 0 )
|
||||
m_lastTick( 0 ),
|
||||
m_value( 0 ),
|
||||
m_cachedSourceCrit( 0 ),
|
||||
m_cachedSourceCritBonus( 0 ),
|
||||
m_markToRemove( false )
|
||||
{
|
||||
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
|
||||
auto entry = exdData.get< Sapphire::Data::Status >( id );
|
||||
|
@ -40,8 +49,12 @@ Sapphire::StatusEffect::StatusEffect::StatusEffect( uint32_t id, Entity::CharaPt
|
|||
Util::eraseAll( m_name, '-' );
|
||||
Util::eraseAll( m_name, '(' );
|
||||
Util::eraseAll( m_name, ')' );
|
||||
}
|
||||
|
||||
if( Sapphire::World::Action::ActionLut::validStatusEffectExists( id ) )
|
||||
m_effectEntry = Sapphire::World::Action::ActionLut::getStatusEffectEntry( id );
|
||||
else
|
||||
m_effectEntry.init( Common::StatusEffectType::Invalid, 0, 0, 0, 0 );
|
||||
}
|
||||
|
||||
Sapphire::StatusEffect::StatusEffect::~StatusEffect()
|
||||
{
|
||||
|
@ -54,9 +67,40 @@ void Sapphire::StatusEffect::StatusEffect::registerTickEffect( uint8_t type, uin
|
|||
|
||||
std::pair< uint8_t, uint32_t > Sapphire::StatusEffect::StatusEffect::getTickEffect()
|
||||
{
|
||||
auto thisTick = m_currTickEffect;
|
||||
m_currTickEffect = std::make_pair( 0, 0 );
|
||||
return thisTick;
|
||||
switch( m_effectEntry.getType() )
|
||||
{
|
||||
case Common::StatusEffectType::Dot:
|
||||
{
|
||||
auto value = m_value;
|
||||
if( m_cachedSourceCrit > Sapphire::Math::CalcStats::getRandomNumber0To100() )
|
||||
{
|
||||
value *= m_cachedSourceCritBonus;
|
||||
}
|
||||
value *= 1.0f + ( ( Sapphire::Math::CalcStats::getRandomNumber0To100() - 50.0f ) / 1000.0f );
|
||||
m_currTickEffect = std::make_pair( 1, value );
|
||||
break;
|
||||
}
|
||||
|
||||
case Common::StatusEffectType::Hot:
|
||||
{
|
||||
auto value = m_value;
|
||||
if( m_cachedSourceCrit > Sapphire::Math::CalcStats::getRandomNumber0To100() )
|
||||
{
|
||||
value *= m_cachedSourceCritBonus;
|
||||
}
|
||||
value *= 1.0f + ( ( Sapphire::Math::CalcStats::getRandomNumber0To100() - 50.0f ) / 1000.0f );
|
||||
m_currTickEffect = std::make_pair( 2, value );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
m_currTickEffect = std::make_pair( 0, 0 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return m_currTickEffect;
|
||||
}
|
||||
|
||||
void Sapphire::StatusEffect::StatusEffect::onTick()
|
||||
|
@ -65,6 +109,10 @@ void Sapphire::StatusEffect::StatusEffect::onTick()
|
|||
|
||||
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
|
||||
scriptMgr.onStatusTick( m_targetActor, *this );
|
||||
|
||||
auto mp = m_effectEntry.getMPRestoreTick();
|
||||
if( mp > 0 )
|
||||
m_targetActor->restoreMP( mp );
|
||||
}
|
||||
|
||||
uint32_t Sapphire::StatusEffect::StatusEffect::getSrcActorId() const
|
||||
|
@ -86,32 +134,90 @@ void Sapphire::StatusEffect::StatusEffect::applyStatus()
|
|||
{
|
||||
m_startTime = Util::getTimeMs();
|
||||
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
|
||||
|
||||
// this is only right when an action is being used by the player
|
||||
// else you probably need to use an actorcontrol
|
||||
|
||||
//GamePacketNew< FFXIVIpcEffect > effectPacket( m_sourceActor->getId() );
|
||||
//effectPacket.data().targetId = m_sourceActor->getId();
|
||||
//effectPacket.data().actionAnimationId = 3;
|
||||
//effectPacket.data().unknown_3 = 1;
|
||||
//effectPacket.data().actionTextId = 3;
|
||||
//effectPacket.data().unknown_5 = 1;
|
||||
//effectPacket.data().unknown_6 = 321;
|
||||
//effectPacket.data().rotation = ( uint16_t ) ( 0x8000 * ( ( m_sourceActor->getPos().getR() + 3.1415926 ) ) / 3.1415926 );
|
||||
//effectPacket.data().effectTargetId = m_sourceActor->getId();
|
||||
//effectPacket.data().effects[4].unknown_1 = 17;
|
||||
//effectPacket.data().effects[4].bonusPercent = 30;
|
||||
//effectPacket.data().effects[4].param1 = m_id;
|
||||
//effectPacket.data().effects[4].unknown_5 = 0x80;
|
||||
//m_sourceActor->sendToInRangeSet( effectPacket, true );
|
||||
|
||||
scriptMgr.onStatusReceive( m_targetActor, m_id );
|
||||
|
||||
switch( m_effectEntry.getType() )
|
||||
{
|
||||
case Common::StatusEffectType::Dot:
|
||||
{
|
||||
auto wepDmg = Sapphire::Math::CalcStats::getWeaponDamage( m_sourceActor );
|
||||
auto damage = Sapphire::Math::CalcStats::calcDamageBaseOnPotency( *m_sourceActor, m_effectEntry.getDotHotPotency(), wepDmg );
|
||||
|
||||
for( auto const& entry : m_sourceActor->getStatusEffectMap() )
|
||||
{
|
||||
auto status = entry.second;
|
||||
auto effectEntry = status->getEffectEntry();
|
||||
if( effectEntry.getType() != Common::StatusEffectType::DamageMultiplier )
|
||||
continue;
|
||||
if( static_cast< int32_t >( effectEntry.getActionTypeFilter() ) & static_cast< int32_t >( m_effectEntry.getActionTypeFilter() ) )
|
||||
{
|
||||
damage *= 1.0f + ( effectEntry.getOutgoingDamageMultiplier() / 100.0f );
|
||||
}
|
||||
}
|
||||
|
||||
m_value = Sapphire::Math::CalcStats::applyDamageReceiveMultiplier( *m_targetActor, damage, m_effectEntry.getActionTypeFilter() );
|
||||
m_cachedSourceCrit = Sapphire::Math::CalcStats::criticalHitProbability( *m_sourceActor, Common::CritDHBonusFilter::Damage );
|
||||
m_cachedSourceCritBonus = Sapphire::Math::CalcStats::criticalHitBonus( *m_sourceActor );
|
||||
break;
|
||||
}
|
||||
|
||||
case Common::StatusEffectType::Hot:
|
||||
{
|
||||
auto wepDmg = Sapphire::Math::CalcStats::getWeaponDamage( m_sourceActor );
|
||||
auto heal = Sapphire::Math::CalcStats::calcHealBaseOnPotency( *m_sourceActor, m_effectEntry.getDotHotPotency(), wepDmg );
|
||||
|
||||
for( auto const& entry : m_sourceActor->getStatusEffectMap() )
|
||||
{
|
||||
auto status = entry.second;
|
||||
auto effectEntry = status->getEffectEntry();
|
||||
if( effectEntry.getType() != Common::StatusEffectType::HealCastMultiplier )
|
||||
continue;
|
||||
heal *= 1.0f + ( effectEntry.getOutgoingHealMultiplier() / 100.0f );
|
||||
}
|
||||
|
||||
m_value = Sapphire::Math::CalcStats::applyHealingReceiveMultiplier( *m_targetActor, heal );
|
||||
m_cachedSourceCrit = Sapphire::Math::CalcStats::criticalHitProbability( *m_sourceActor, Common::CritDHBonusFilter::Heal );
|
||||
m_cachedSourceCritBonus = Sapphire::Math::CalcStats::criticalHitBonus( *m_sourceActor );
|
||||
break;
|
||||
}
|
||||
|
||||
case Common::StatusEffectType::Haste:
|
||||
{
|
||||
auto pPlayer = m_targetActor->getAsPlayer();
|
||||
if( pPlayer )
|
||||
pPlayer->sendStats();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sapphire::StatusEffect::StatusEffect::removeStatus()
|
||||
{
|
||||
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
|
||||
scriptMgr.onStatusTimeOut( m_targetActor, m_id );
|
||||
|
||||
switch( m_effectEntry.getType() )
|
||||
{
|
||||
case Common::StatusEffectType::Haste:
|
||||
{
|
||||
auto pPlayer = m_targetActor->getAsPlayer();
|
||||
if( pPlayer )
|
||||
pPlayer->sendStats();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// hardcoded for now, TODO: add m_statusForceRemoveReason so a proper check for "shield completely absorbed" is possible
|
||||
if( m_markToRemove && m_id == 1178 )
|
||||
{
|
||||
if( auto drk = m_sourceActor->getAsPlayer() )
|
||||
{
|
||||
if( drk->getClass() == Common::ClassJob::Darkknight )
|
||||
{
|
||||
drk->gaugeDrkSetDarkArts( true );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Sapphire::StatusEffect::StatusEffect::getId() const
|
||||
|
@ -149,7 +255,140 @@ void Sapphire::StatusEffect::StatusEffect::setParam( uint16_t param )
|
|||
m_param = param;
|
||||
}
|
||||
|
||||
void Sapphire::StatusEffect::StatusEffect::setStacks( uint8_t stacks )
|
||||
{
|
||||
if( ( m_param & 0x00FF ) != 0x00FF )
|
||||
{
|
||||
m_param = stacks;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t Sapphire::StatusEffect::StatusEffect::getStacks()
|
||||
{
|
||||
if( ( m_param & 0x00FF ) == 0x00FF )
|
||||
return 0;
|
||||
return static_cast< uint8_t >( m_param & 0x00FF );
|
||||
}
|
||||
|
||||
const std::string& Sapphire::StatusEffect::StatusEffect::getName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
const Sapphire::World::Action::StatusEffectEntry& Sapphire::StatusEffect::StatusEffect::getEffectEntry() const
|
||||
{
|
||||
return m_effectEntry;
|
||||
}
|
||||
|
||||
void Sapphire::StatusEffect::StatusEffect::replaceEffectEntry( Sapphire::World::Action::StatusEffectEntry entryOverride )
|
||||
{
|
||||
m_effectEntry = entryOverride;
|
||||
}
|
||||
|
||||
void Sapphire::StatusEffect::StatusEffect::onBeforeActionStart( Sapphire::World::Action::Action* action )
|
||||
{
|
||||
// todo: add script function for this if needed
|
||||
switch( m_effectEntry.getType() )
|
||||
{
|
||||
case Common::StatusEffectType::InstantCast:
|
||||
{
|
||||
if( action->hasCastTime() && applyToAction( action ) )
|
||||
action->setCastTime( 0 );
|
||||
break;
|
||||
}
|
||||
case Common::StatusEffectType::AlwaysCombo:
|
||||
{
|
||||
if( applyToAction( action ) )
|
||||
action->setAlwaysCombo();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sapphire::StatusEffect::StatusEffect::onActionExecute( Sapphire::World::Action::Action* action )
|
||||
{
|
||||
// todo: add script function for this if needed
|
||||
switch( m_effectEntry.getType() )
|
||||
{
|
||||
case Common::StatusEffectType::PotencyMultiplier:
|
||||
{
|
||||
if( applyToAction( action ) )
|
||||
{
|
||||
action->getActionEntry().damagePotency *= 1.0 + ( m_effectEntry.getPotencyMultiplier() / 100.0 );
|
||||
action->getActionEntry().damageComboPotency *= 1.0 + ( m_effectEntry.getPotencyMultiplier() / 100.0 );
|
||||
action->getActionEntry().damageDirectionalPotency *= 1.0 + ( m_effectEntry.getPotencyMultiplier() / 100.0 );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Sapphire::StatusEffect::StatusEffect::isMarkedToRemove()
|
||||
{
|
||||
return m_markToRemove;
|
||||
}
|
||||
|
||||
void Sapphire::StatusEffect::StatusEffect::markToRemove()
|
||||
{
|
||||
m_markToRemove = true;
|
||||
}
|
||||
|
||||
void Sapphire::StatusEffect::StatusEffect::refresh()
|
||||
{
|
||||
m_value = 0;
|
||||
m_cachedSourceCritBonus = 0;
|
||||
m_cachedSourceCrit = 0;
|
||||
applyStatus();
|
||||
}
|
||||
|
||||
void Sapphire::StatusEffect::StatusEffect::refresh( Sapphire::World::Action::StatusEffectEntry newEntry )
|
||||
{
|
||||
m_effectEntry = newEntry;
|
||||
refresh();
|
||||
}
|
||||
|
||||
bool Sapphire::StatusEffect::StatusEffect::onActionHitTarget( World::Action::Action* action, Entity::CharaPtr victim, int victimCounter )
|
||||
{
|
||||
switch( m_effectEntry.getType() )
|
||||
{
|
||||
case Common::StatusEffectType::MPRestorePerGCD:
|
||||
{
|
||||
if( victimCounter == 1 && action->isGCD() ) // only restore mp on first victim in case of aoe
|
||||
{
|
||||
if( applyToAction( action ) )
|
||||
{
|
||||
float restored = 0.01f * m_targetActor->getMaxMp() * m_effectEntry.getGCDBasedMPRestorePercentage();
|
||||
action->getEffectbuilder()->restoreMP( victim, m_targetActor, static_cast< uint32_t >( restored ), Sapphire::Common::ActionEffectResultFlag::EffectOnSource );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Sapphire::StatusEffect::StatusEffect::applyToAction( World::Action::Action* action )
|
||||
{
|
||||
if( m_effectEntry.getRemainingCharges() == 0 )
|
||||
return false;
|
||||
|
||||
if( !m_effectEntry.canApplyToAction( action->getId(), action->getActionData()->actionCategory ) )
|
||||
return false;
|
||||
|
||||
if( m_effectEntry.getRemainingCharges() > 0 )
|
||||
{
|
||||
if( m_effectEntry.getRemainingCharges() == getStacks() )
|
||||
{
|
||||
// if stacks equal to remaining charges, assume it is synced
|
||||
setStacks( getStacks() - 1 );
|
||||
m_targetActor->sendStatusEffectUpdate();
|
||||
}
|
||||
m_effectEntry.setRemainingCharges( m_effectEntry.getRemainingCharges() - 1 );
|
||||
if( m_effectEntry.getRemainingCharges() == 0 )
|
||||
{
|
||||
markToRemove();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "Forwards.h"
|
||||
|
||||
#include "Action/ActionLut.h"
|
||||
|
||||
namespace Sapphire {
|
||||
namespace StatusEffect {
|
||||
|
||||
|
@ -17,6 +19,12 @@ public:
|
|||
|
||||
void onTick();
|
||||
|
||||
void onBeforeActionStart( World::Action::Action* action );
|
||||
|
||||
void onActionExecute( World::Action::Action* action );
|
||||
|
||||
bool onActionHitTarget( World::Action::Action* action, Entity::CharaPtr victim, int victimCounter );
|
||||
|
||||
void applyStatus();
|
||||
|
||||
void removeStatus();
|
||||
|
@ -41,13 +49,30 @@ public:
|
|||
|
||||
void setParam( uint16_t param );
|
||||
|
||||
void setStacks( uint8_t stacks );
|
||||
|
||||
uint8_t getStacks();
|
||||
|
||||
void registerTickEffect( uint8_t type, uint32_t param );
|
||||
|
||||
std::pair< uint8_t, uint32_t > getTickEffect();
|
||||
|
||||
const std::string& getName() const;
|
||||
|
||||
const Sapphire::World::Action::StatusEffectEntry& getEffectEntry() const;
|
||||
|
||||
void replaceEffectEntry( Sapphire::World::Action::StatusEffectEntry entryOverride );
|
||||
|
||||
bool isMarkedToRemove();
|
||||
|
||||
void markToRemove();
|
||||
|
||||
void refresh();
|
||||
void refresh( Sapphire::World::Action::StatusEffectEntry newEntry );
|
||||
|
||||
private:
|
||||
bool applyToAction( Sapphire::World::Action::Action* action );
|
||||
|
||||
uint32_t m_id;
|
||||
Entity::CharaPtr m_sourceActor;
|
||||
Entity::CharaPtr m_targetActor;
|
||||
|
@ -58,7 +83,11 @@ private:
|
|||
uint16_t m_param;
|
||||
std::string m_name;
|
||||
std::pair< uint8_t, uint32_t > m_currTickEffect;
|
||||
|
||||
Sapphire::World::Action::StatusEffectEntry m_effectEntry;
|
||||
uint32_t m_value;
|
||||
float m_cachedSourceCrit;
|
||||
float m_cachedSourceCritBonus;
|
||||
bool m_markToRemove;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue