[Feature] Implemented /shield ability and related affects (#1494)

* shield ability initial work

* updates

* update

* updates

* Update client_process.cpp

* major updates

optimized
pet support
perl support

* updates

* minor update

* fix merge error

* requested changes

* variable fix

* optimization

* minor update

* Revert "optimization"

This reverts commit 27e11e758b.

* fix

reset variables on shield_target if shielder dies or zones during shielding.

* edge case fix

Catch and fix situations where shield target doesn't have shielder variable cleared. Can occur if shielder . uses ability when target is not in combat then zones.

* combined packet and mob function

Shield now uses a common pathway through ShieldAbility, added parameters to perl function

* Addressing formatting for Kayen

* Fix function typo

Co-authored-by: Akkadius <akkadius1@gmail.com>
This commit is contained in:
KayenEQ
2021-08-15 23:59:10 -04:00
committed by GitHub
parent 9c62bf3c2f
commit d40d21121a
15 changed files with 363 additions and 191 deletions
+50 -6
View File
@@ -1655,9 +1655,7 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill
int exploss = 0;
LogCombat("Fatal blow dealt by [{}] with [{}] damage, spell [{}], skill [{}]", killerMob ? killerMob->GetName() : "Unknown", damage, spell, attack_skill);
/*
#1: Send death packet to everyone
*/
// #1: Send death packet to everyone
uint8 killed_level = GetLevel();
SendLogoutPackets();
@@ -1684,13 +1682,12 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill
app.priority = 6;
entity_list.QueueClients(this, &app);
/*
#2: figure out things that affect the player dying and mark them dead
*/
// #2: figure out things that affect the player dying and mark them dead
InterruptSpell();
SetPet(0);
SetHorseId(0);
ShieldAbilityClearVariables();
dead = true;
if (GetMerc()) {
@@ -2252,6 +2249,8 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy
Log(Logs::Detail, Logs::Attack, "%s Mobs currently Aggro %i", __FUNCTION__, zone->MobsAggroCount());
}
ShieldAbilityClearVariables();
SetHP(0);
SetPet(0);
@@ -5278,9 +5277,54 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac
hit.damage_done += (hit.damage_done * pct_damage_reduction / 100) + (defender->GetFcDamageAmtIncoming(this, 0, true, hit.skill)) + defender->GetPositionalDmgTakenAmt(this);
if (defender->GetShielderID()) {
DoShieldDamageOnShielder(defender, hit.damage_done, hit.skill);
hit.damage_done -= hit.damage_done * defender->GetShieldTargetMitigation() / 100; //Default shielded takes 50 pct damage
}
CheckNumHitsRemaining(NumHit::OutgoingHitSuccess);
}
void Mob::DoShieldDamageOnShielder(Mob *shield_target, int hit_damage_done, EQ::skills::SkillType skillInUse)
{
if (!shield_target) {
return;
}
Mob *shielder = entity_list.GetMob(shield_target->GetShielderID());
if (!shielder) {
shield_target->SetShielderID(0);
shield_target->SetShieldTargetMitigation(0);
return;
}
if (shield_target->CalculateDistance(shielder->GetX(), shielder->GetY(), shielder->GetZ()) > static_cast<float>(shielder->GetMaxShielderDistance())) {
shielder->SetShieldTargetID(0);
shielder->SetShielderMitigation(0);
shielder->SetShielderMaxDistance(0);
shielder->shield_timer.Disable();
shield_target->SetShielderID(0);
shield_target->SetShieldTargetMitigation(0);
return; //Too far away, no message is given thoughh.
}
int mitigation = shielder->GetShielderMitigation(); //Default shielder mitigates 25 pct of damage taken, this can be increased up to max 50 by equiping a shield item
if (shielder->IsClient() && shielder->HasShieldEquiped()) {
EQ::ItemInstance* inst = shielder->CastToClient()->GetInv().GetItem(EQ::invslot::slotSecondary);
if (inst) {
const EQ::ItemData* shield = inst->GetItem();
if (shield && shield->ItemType == EQ::item::ItemTypeShield) {
mitigation += shield->AC * 50 / 100; //1% increase per 2 AC
std::min(50, mitigation);//50 pct max mitigation bonus from /shield
}
}
}
hit_damage_done -= hit_damage_done * mitigation / 100;
shielder->Damage(this, hit_damage_done, SPELL_UNKNOWN, skillInUse, true, -1, false, m_specialattacks);
shielder->CheckNumHitsRemaining(NumHit::OutgoingHitSuccess);
}
void Mob::CommonBreakInvisibleFromCombat()
{
//break invis when you attack
+45 -4
View File
@@ -1633,6 +1633,23 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
if (newbon->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] < base1) {
newbon->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] = base1;
newbon->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_DMG_BONUS] = base2;
}
break;
}
case SE_ExtendedShielding:
{
if (newbon->ExtendedShielding < base1) {
newbon->ExtendedShielding = base1;
}
break;
}
case SE_ShieldDuration:
{
if (newbon->ShieldDuration < base1) {
newbon->ShieldDuration = base1;
}
break;
}
@@ -1650,10 +1667,6 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
break;
case SE_SecondaryForte:
break;
case SE_ExtendedShielding:
break;
case SE_ShieldDuration:
break;
case SE_ReduceApplyPoisonTime:
break;
case SE_NimbleEvasion:
@@ -3567,6 +3580,34 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
new_bonus->Pet_Add_Atk += effect_value;
break;
case SE_ExtendedShielding:
{
if (AdditiveWornBonus) {
new_bonus->ExtendedShielding += effect_value;
}
else if (effect_value < 0 && new_bonus->ExtendedShielding > effect_value){
new_bonus->ExtendedShielding = effect_value;
}
else if (effect_value > 0 && new_bonus->ExtendedShielding < effect_value){
new_bonus->ExtendedShielding = effect_value;
}
break;
}
case SE_ShieldDuration:
{
if (AdditiveWornBonus) {
new_bonus->ShieldDuration += effect_value;
}
else if (effect_value < 0 && new_bonus->ShieldDuration > effect_value){
new_bonus->ShieldDuration = effect_value;
}
else if (effect_value > 0 && new_bonus->ShieldDuration < effect_value){
new_bonus->ShieldDuration = effect_value;
}
break;
}
case SE_Worn_Endurance_Regen_Cap:
new_bonus->ItemEnduranceRegenCap += effect_value;
break;
-13
View File
@@ -138,7 +138,6 @@ Client::Client(EQStreamInterface* ieqs)
linkdead_timer(RuleI(Zone,ClientLinkdeadMS)),
dead_timer(2000),
global_channel_timer(1000),
shield_timer(500),
fishing_timer(8000),
endupkeep_timer(1000),
forget_timer(0),
@@ -200,7 +199,6 @@ Client::Client(EQStreamInterface* ieqs)
account_id = 0;
admin = 0;
lsaccountid = 0;
shield_target = nullptr;
guild_id = GUILD_NONE;
guildrank = 0;
GuildBanker = false;
@@ -236,7 +234,6 @@ Client::Client(EQStreamInterface* ieqs)
pQueuedSaveWorkID = 0;
position_update_same_count = 0;
fishing_timer.Disable();
shield_timer.Disable();
dead_timer.Disable();
camp_timer.Disable();
autosave_timer.Disable();
@@ -420,16 +417,6 @@ Client::~Client() {
}
}
if (shield_target) {
for (int y = 0; y < 2; y++) {
if (shield_target->shielder[y].shielder_id == GetID()) {
shield_target->shielder[y].shielder_id = 0;
shield_target->shielder[y].shielder_bonus = 0;
}
}
shield_target = nullptr;
}
if(GetTarget())
GetTarget()->IsTargeted(-1);
-1
View File
@@ -1804,7 +1804,6 @@ private:
Timer linkdead_timer;
Timer dead_timer;
Timer global_channel_timer;
Timer shield_timer;
Timer fishing_timer;
Timer endupkeep_timer;
Timer forget_timer; // our 2 min everybody forgets you timer
+36 -71
View File
@@ -12806,87 +12806,52 @@ void Client::Handle_OP_SetTitle(const EQApplicationPacket *app)
void Client::Handle_OP_Shielding(const EQApplicationPacket *app)
{
/*
/shield command mechanics
Warriors get this skill at level 30
Used by typing /shield while targeting a player
While active for the duration of 12 seconds baseline. The 'shield target' will take 50 pct less damage and
the 'shielder' will be hit with the damage taken by the 'shield target' after all applicable mitigiont is calculated,
the damage on the 'shielder' will be reduced by 25 percent, this reduction can be increased to 50 pct if equiping a shield.
You receive a 1% increase in mitigation for every 2 AC on the shield.
Shielder must stay with in a close distance (15 units) to your 'shield target'. If either move out of range, shield ends, no message given.
Both duration and shield range can be modified by AA.
Recast is 3 minutes.
For custom use cases, Mob::ShieldAbility can be used in quests with all parameters being altered. This functional
is also used for SPA 201 SE_PetShield, which functions in a simalar manner with pet shielding owner.
Note: If either the shielder or the shield target die all variables are reset on both.
*/
if (app->size != sizeof(Shielding_Struct)) {
LogError("OP size error: OP_Shielding expected:[{}] got:[{}]", sizeof(Shielding_Struct), app->size);
return;
}
if (GetClass() != WARRIOR)
{
if (GetLevel() < 30) { //Client gives message
return;
}
if (GetClass() != WARRIOR){
return;
}
if (shield_target)
{
entity_list.MessageCloseString(
this, false, 100, 0,
END_SHIELDING, GetName(), shield_target->GetName());
for (int y = 0; y < 2; y++)
{
if (shield_target->shielder[y].shielder_id == GetID())
{
shield_target->shielder[y].shielder_id = 0;
shield_target->shielder[y].shielder_bonus = 0;
}
}
pTimerType timer = pTimerShieldAbility;
if (!p_timers.Expired(&database, timer, false)) {
uint32 remain = p_timers.GetRemainingTime(timer);
Message(Chat::White, "You can use the ability /shield in %d minutes %d seconds.", ((remain) / 60), (remain % 60));
return;
}
Shielding_Struct* shield = (Shielding_Struct*)app->pBuffer;
shield_target = entity_list.GetMob(shield->target_id);
bool ack = false;
EQ::ItemInstance* inst = GetInv().GetItem(EQ::invslot::slotSecondary);
if (!shield_target)
return;
if (inst)
{
const EQ::ItemData* shield = inst->GetItem();
if (shield && shield->ItemType == EQ::item::ItemTypeShield)
{
for (int x = 0; x < 2; x++)
{
if (shield_target->shielder[x].shielder_id == 0)
{
entity_list.MessageCloseString(
this, false, 100, 0,
START_SHIELDING, GetName(), shield_target->GetName());
shield_target->shielder[x].shielder_id = GetID();
int shieldbonus = shield->AC * 2;
switch (GetAA(197))
{
case 1:
shieldbonus = shieldbonus * 115 / 100;
break;
case 2:
shieldbonus = shieldbonus * 125 / 100;
break;
case 3:
shieldbonus = shieldbonus * 150 / 100;
break;
}
shield_target->shielder[x].shielder_bonus = shieldbonus;
shield_timer.Start();
ack = true;
break;
}
}
}
else
{
Message(0, "You must have a shield equipped to shield a target!");
shield_target = 0;
return;
}
}
else
{
Message(0, "You must have a shield equipped to shield a target!");
shield_target = 0;
return;
}
if (!ack)
{
MessageString(Chat::White, ALREADY_SHIELDED);
shield_target = 0;
return;
if (ShieldAbility(shield->target_id, 15, 12000, 50, 25, true, false)) {
p_timers.Start(timer, SHIELD_ABILITY_RECAST_TIME);
}
return;
}
+3 -27
View File
@@ -464,33 +464,9 @@ bool Client::Process() {
if (gravity_timer.Check())
DoGravityEffect();
}
if (shield_timer.Check())
{
if (shield_target)
{
if (!CombatRange(shield_target))
{
entity_list.MessageCloseString(
this, false, 100, 0,
END_SHIELDING, GetCleanName(), shield_target->GetCleanName());
for (int y = 0; y < 2; y++)
{
if (shield_target->shielder[y].shielder_id == GetID())
{
shield_target->shielder[y].shielder_id = 0;
shield_target->shielder[y].shielder_bonus = 0;
}
}
shield_target = 0;
shield_timer.Disable();
}
}
else
{
shield_target = 0;
shield_timer.Disable();
}
if (shield_timer.Check()) {
ShieldAbilityFinish();
}
SpellProcess();
+6 -7
View File
@@ -22,8 +22,6 @@
#define SEE_POSITION 0.5f //ratio of GetSize() where NPCs try to see for LOS
#define CHECK_LOS_STEP 1.0f
#define MAX_SHIELDERS 2 //I dont know if this is based on a client limit
#define ARCHETYPE_HYBRID 1
#define ARCHETYPE_CASTER 2
#define ARCHETYPE_MELEE 3
@@ -109,6 +107,8 @@
#define WEAPON_STANCE_TYPE_MAX 2
#define SHIELD_ABILITY_RECAST_TIME 180
typedef enum { //focus types
focusSpellHaste = 1, //@Fc, SPA: 127, SE_IncreaseSpellHaste, On Caster, cast time mod pct, base: pct
focusSpellDuration, //@Fc, SPA: 128, SE_IncreaseSpellDuration, On Caster, spell duration mod pct, base: pct
@@ -553,8 +553,11 @@ struct StatBonuses {
int32 ItemEnduranceRegenCap; // modify endurance regen cap
int32 WeaponStance[WEAPON_STANCE_TYPE_MAX +1];// base = trigger spell id, base2 = 0 is 2h, 1 is shield, 2 is dual wield, [0]spid 2h, [1]spid shield, [2]spid DW
// AAs
int8 Packrat; //weight reduction for items, 1 point = 10%
int32 ShieldDuration; // extends duration of /shield ability
int32 ExtendedShielding; // extends range of /shield ability
int8 Packrat; // weight reduction for items, 1 point = 10%
uint8 BuffSlotIncrease; // Increases number of available buff slots
uint32 DelayDeath; // how far below 0 hp you can go
int8 BaseMovementSpeed; // Adjust base run speed, does not stack with other movement bonuses.
@@ -685,10 +688,6 @@ typedef struct
int level_override;
} tProc;
struct Shielders_Struct {
uint32 shielder_id;
uint16 shielder_bonus;
};
struct WeaponStance_Struct {
bool enabled;
+126 -9
View File
@@ -261,7 +261,6 @@ Mob::Mob(
MR = CR = FR = DR = PR = Corrup = PhR = 0;
ExtraHaste = 0;
bEnraged = false;
shield_target = nullptr;
current_mana = 0;
max_mana = 0;
hp_regen = in_hp_regen;
@@ -376,11 +375,13 @@ Mob::Mob(
silenced = false;
amnesiad = false;
inWater = false;
int m;
for (m = 0; m < MAX_SHIELDERS; m++) {
shielder[m].shielder_id = 0;
shielder[m].shielder_bonus = 0;
}
shield_timer.Disable();
m_shield_target_id = 0;
m_shielder_id = 0;
m_shield_target_mitigation = 0;
m_shielder_mitigation = 0;
m_shielder_max_distance = 0;
destructibleobject = false;
wandertype = 0;
@@ -3144,7 +3145,7 @@ int32 Mob::GetActSpellCasttime(uint16 spell_id, int32 casttime)
cast_reducer += cast_reducer_no_limit;
casttime = casttime * (100 - cast_reducer) / 100;
casttime -= cast_reducer_amt;
return std::max(casttime, 0);
}
@@ -4944,11 +4945,11 @@ int16 Mob::GetPositionalDmgAmt(Mob* defender)
if (back_arc_dmg_amt || front_arc_dmg_amt) {
if (BehindMob(defender, GetX(), GetY()))
total_amt = back_arc_dmg_amt;
total_amt = back_arc_dmg_amt;
else
total_amt = front_arc_dmg_amt;
}
return total_amt;
}
@@ -6196,6 +6197,122 @@ float Mob::GetDefaultRaceSize() const {
return GetRaceGenderDefaultHeight(race, gender);
}
bool Mob::ShieldAbility(uint32 target_id, int shielder_max_distance, int shield_duration, int shield_target_mitigation, int shielder_mitigation, bool use_aa, bool can_shield_npc)
{
Mob* shield_target = entity_list.GetMob(target_id);
if (!shield_target) {
return false;
}
if (!can_shield_npc && shield_target->IsNPC()) {
if (IsClient()) {
MessageString(Chat::White, SHIELD_TARGET_NPC);
}
return false;
}
if (shield_target->GetID() == GetID()) { //Client will give message "You can not shield yourself"
return false;
}
//Edge case situations. If 'Shield Target' still has Shielder set but Shielder is not in zone. Catch and fix here.
if (shield_target->GetShielderID() && !entity_list.GetMob(shield_target->GetShielderID())) {
shield_target->SetShielderID(0);
}
if (GetShielderID() && !entity_list.GetMob(GetShielderID())) {
SetShielderID(0);
}
//You have a shielder, or your 'Shield Target' already has a 'Shielder'
if (GetShielderID() || shield_target->GetShielderID()) {
if (IsClient()) {
MessageString(Chat::White, ALREADY_SHIELDED);
}
return false;
}
//You are being shielded or already have a 'Shield Target'
if (GetShieldTargetID() || shield_target->GetShieldTargetID()) {
if (IsClient()) {
MessageString(Chat::White, ALREADY_SHIELDING);
}
return false;
}
//AA to increase SPA 230 extended shielding (default live is 15 distance units)
if (use_aa) {
shielder_max_distance += aabonuses.ExtendedShielding + itembonuses.ExtendedShielding + spellbonuses.ExtendedShielding;
shielder_max_distance = std::max(shielder_max_distance, 0);
}
if (shield_target->CalculateDistance(GetX(), GetY(), GetZ()) > static_cast<float>(shielder_max_distance)) {
return false; //Live does not give a message when out of range.
}
entity_list.MessageCloseString(this, false, 100, 0, START_SHIELDING, GetCleanName(), shield_target->GetCleanName());
SetShieldTargetID(shield_target->GetID());
SetShielderMitigation(shield_target_mitigation);
SetShielderMaxDistance(shielder_max_distance);
shield_target->SetShielderID(GetID());
shield_target->SetShieldTargetMitigation(shield_target_mitigation);
//Calculate AA for adding time SPA 255 extend shield duration (Baseline ability is 12 seconds)
if (use_aa) {
shield_duration += (aabonuses.ShieldDuration + itembonuses.ShieldDuration + spellbonuses.ShieldDuration) * 1000;
shield_duration = std::max(shield_duration, 1); //Incase of negative modifiers lets just make min duration 1 ms.
}
shield_timer.Start(static_cast<uint32>(shield_duration));
return true;
}
void Mob::ShieldAbilityFinish()
{
Mob* shield_target = entity_list.GetMob(GetShieldTargetID());
if (shield_target) {
entity_list.MessageCloseString(this, false, 100, 0, END_SHIELDING, GetCleanName(), shield_target->GetCleanName());
shield_target->SetShielderID(0);
shield_target->SetShieldTargetMitigation(0);
}
SetShieldTargetID(0);
SetShielderMitigation(0);
SetShielderMaxDistance(0);
shield_timer.Disable();
}
void Mob::ShieldAbilityClearVariables()
{
//If 'shield target' dies
if (GetShielderID()){
Mob* shielder = entity_list.GetMob(GetShielderID());
if (shielder) {
shielder->SetShieldTargetID(0);
shielder->SetShielderMitigation(0);
shielder->SetShielderMaxDistance(0);
shielder->shield_timer.Disable();
}
SetShielderID(0);
SetShieldTargetMitigation(0);
}
//If 'shielder' dies
if (GetShieldTargetID()) {
Mob* shield_target = entity_list.GetMob(GetShieldTargetID());
if (shield_target) {
shield_target->SetShielderID(0);
shield_target->SetShieldTargetMitigation(0);
}
SetShieldTargetID(0);
SetShielderMitigation(0);
SetShielderMaxDistance(0);
shield_timer.Disable();
}
}
#ifdef BOTS
bool Mob::JoinHealRotationTargetPool(std::shared_ptr<HealRotation>* heal_rotation)
{
+26 -8
View File
@@ -843,11 +843,11 @@ public:
bool HarmonySpellLevelCheck(int32 spell_id, Mob* target = nullptr);
bool CanFocusUseRandomEffectivenessByType(focusType type);
int GetFocusRandomEffectivenessValue(int focus_base, int focus_base2, bool best_focus = 0);
bool TryDoubleMeleeRoundEffect();
bool GetUseDoubleMeleeRoundDmgBonus() const { return use_double_melee_round_dmg_bonus; }
inline void SetUseDoubleMeleeRoundDmgBonus(bool val) { use_double_melee_round_dmg_bonus = val; }
void CastSpellOnLand(Mob* caster, int32 spell_id);
void FocusProcLimitProcess();
bool ApplyFocusProcLimiter(int32 spell_id, int buffslot = -1);
@@ -1094,8 +1094,6 @@ public:
void InstillDoubt(Mob *who);
int16 GetResist(uint8 type) const;
Mob* GetShieldTarget() const { return shield_target; }
void SetShieldTarget(Mob* mob) { shield_target = mob; }
bool HasActiveSong() const { return(bardsong != 0); }
bool Charmed() const { return typeofpet == petCharmed; }
static uint32 GetLevelHP(uint8 tlevel);
@@ -1133,13 +1131,28 @@ public:
bool IsMoved() { return moved; }
void SetMoved(bool moveflag) { moved = moveflag; }
Shielders_Struct shielder[MAX_SHIELDERS];
Trade* trade;
bool ShieldAbility(uint32 target_id, int shielder_max_distance = 15, int shield_duration = 12000, int shield_target_mitigation = 50, int shielder_mitigation = 75, bool use_aa = false, bool can_shield_npc = true);
void DoShieldDamageOnShielder(Mob *shield_target, int hit_damage_done, EQ::skills::SkillType skillInUse);
void ShieldAbilityFinish();
void ShieldAbilityClearVariables();
inline uint32 GetShielderID() const { return m_shielder_id; }
inline void SetShielderID(uint32 val) { m_shielder_id = val; }
inline uint32 GetShieldTargetID() const { return m_shield_target_id; }
inline void SetShieldTargetID(uint32 val) { m_shield_target_id = val; }
inline int GetShieldTargetMitigation() const { return m_shield_target_mitigation; }
inline void SetShieldTargetMitigation(int val) { m_shield_target_mitigation = val; }
inline int GetShielderMitigation() const { return m_shielder_mitigation; }
inline void SetShielderMitigation(int val) { m_shielder_mitigation = val; }
inline int GetMaxShielderDistance() const { return m_shielder_max_distance; }
inline void SetShielderMaxDistance(int val) { m_shielder_max_distance = val; }
WeaponStance_Struct weaponstance;
bool IsWeaponStanceEnabled() const { return weaponstance.enabled; }
inline void SetWeaponStanceEnabled(bool val) { weaponstance.enabled = val; }
inline glm::vec4 GetCurrentWayPoint() const { return m_CurrentWayPoint; }
inline float GetCWPP() const { return(static_cast<float>(cur_wp_pause)); }
inline int GetCWP() const { return(cur_wp); }
@@ -1439,6 +1452,13 @@ protected:
Timer mana_timer;
Timer focus_proc_limit_timer;
Timer shield_timer;
uint32 m_shield_target_id;
uint32 m_shielder_id;
int m_shield_target_mitigation;
int m_shielder_mitigation;
int m_shielder_max_distance;
//spell casting vars
Timer spellend_timer;
uint16 casting_spell_id;
@@ -1485,8 +1505,6 @@ protected:
uint8 aa_title;
Mob* shield_target;
int ExtraHaste; // for the #haste command
bool mezzed;
bool stunned;
@@ -1626,7 +1644,7 @@ protected:
std::unordered_map<uint32, std::pair<uint32, uint32>> aa_ranks;
Timer aa_timers[aaTimerMax];
bool is_horse;
AuraMgr aura_mgr;
+4
View File
@@ -1128,6 +1128,10 @@ void Mob::AI_Process() {
if (focus_proc_limit_timer.Check())
FocusProcLimitProcess();
if (shield_timer.Check()) {
ShieldAbilityFinish();
}
auto npcSpawnPoint = CastToNPC()->GetSpawnPoint();
if (GetSpecialAbility(TETHER)) {
float tether_range = static_cast<float>(GetSpecialAbilityParam(TETHER, 0));
+46 -40
View File
@@ -4179,44 +4179,6 @@ XS(XS_Mob_GetResist) {
XSRETURN(1);
}
XS(XS_Mob_GetShieldTarget); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_GetShieldTarget) {
dXSARGS;
if (items != 1)
Perl_croak(aTHX_ "Usage: Mob::GetShieldTarget(THIS)"); // @categories Script Utility
{
Mob *THIS;
Mob *RETVAL;
VALIDATE_THIS_IS_MOB;
RETVAL = THIS->GetShieldTarget();
ST(0) = sv_newmortal();
sv_setref_pv(ST(0), "Mob", (void *) RETVAL);
}
XSRETURN(1);
}
XS(XS_Mob_SetShieldTarget); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_SetShieldTarget) {
dXSARGS;
if (items != 2)
Perl_croak(aTHX_ "Usage: Mob::SetShieldTarget(THIS, mob)"); // @categories Script Utility
{
Mob *THIS;
Mob *mob;
VALIDATE_THIS_IS_MOB;
if (sv_derived_from(ST(1), "Mob")) {
IV tmp = SvIV((SV *) SvRV(ST(1)));
mob = INT2PTR(Mob *, tmp);
} else
Perl_croak(aTHX_ "mob is not of type Mob");
if (mob == nullptr)
Perl_croak(aTHX_ "mob is nullptr, avoiding crash.");
THIS->SetShieldTarget(mob);
}
XSRETURN_EMPTY;
}
XS(XS_Mob_Charmed); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_Charmed) {
dXSARGS;
@@ -6301,6 +6263,51 @@ XS(XS_Mob_AddNimbusEffect) {
XSRETURN_EMPTY;
}
XS(XS_Mob_ShieldAbility); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_ShieldAbility) {
dXSARGS;
if (items < 2 || items > 6)
Perl_croak(aTHX_ "Usage: Mob::ShieldAbility(THIS, uint32 target_id, [int32 shielder__max_distance = 15], [int32 shield_duration = 12000], [int32 shield_target_mitigation= 50], [int32 shielder_mitigation = 50], [bool use_aa = false], bool [can_shield_npc = true]"); // @categories Spells and Disciplines
{
Mob *THIS;
uint32 target_id = (uint32)SvUV(ST(1));
int32 shielder_max_distance = (int32)SvUV(ST(2));
int32 shield_duration = (int32)SvUV(ST(3));
int32 shield_target_mitigation = (int32)SvUV(ST(4));
int32 shielder_mitigation = (int32)SvUV(ST(5));
bool use_aa = (bool)SvTRUE(ST(6));
bool can_shield_npc = (bool)SvTRUE(ST(7));
VALIDATE_THIS_IS_MOB;
if (items < 3) {
shielder_max_distance = 15;
}
if (items < 4) {
shield_duration = 12000;
}
if (items < 5) {
shield_target_mitigation = 50;
}
if (items < 6) {
shielder_mitigation = 50;
}
if (items < 7) {
use_aa = false;
}
if (items < 8) {
can_shield_npc = true;
}
THIS->ShieldAbility(target_id, shielder_max_distance, shield_duration, shield_duration, shield_duration, use_aa, can_shield_npc);
}
XSRETURN_EMPTY;
}
#ifdef BOTS
XS(XS_Mob_CastToBot); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_CastToBot)
@@ -6561,8 +6568,6 @@ XS(boot_Mob) {
newXSproto(strcpy(buf, "DontRootMeBefore"), XS_Mob_DontRootMeBefore, file, "$");
newXSproto(strcpy(buf, "DontSnareMeBefore"), XS_Mob_DontSnareMeBefore, file, "$");
newXSproto(strcpy(buf, "GetResist"), XS_Mob_GetResist, file, "$$");
newXSproto(strcpy(buf, "GetShieldTarget"), XS_Mob_GetShieldTarget, file, "$");
newXSproto(strcpy(buf, "SetShieldTarget"), XS_Mob_SetShieldTarget, file, "$$");
newXSproto(strcpy(buf, "Charmed"), XS_Mob_Charmed, file, "$");
newXSproto(strcpy(buf, "GetLevelHP"), XS_Mob_GetLevelHP, file, "$$");
newXSproto(strcpy(buf, "GetZoneID"), XS_Mob_GetZoneID, file, "$");
@@ -6672,6 +6677,7 @@ XS(boot_Mob) {
newXSproto(strcpy(buf, "CanRaceEquipItem"), XS_Mob_CanRaceEquipItem, file, "$$");
newXSproto(strcpy(buf, "RemoveAllNimbusEffects"), XS_Mob_RemoveAllNimbusEffects, file, "$");
newXSproto(strcpy(buf, "AddNimbusEffect"), XS_Mob_AddNimbusEffect, file, "$$");
newXSproto(strcpy(buf, "ShieldAbility"), XS_Mob_ShieldAbility, file, "$$$$$$$$");
#ifdef BOTS
newXSproto(strcpy(buf, "CastToBot"), XS_Mob_CastToBot, file, "$");
#endif
+13 -1
View File
@@ -2950,6 +2950,19 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
break;
}
case SE_PetShield: {
if (IsPet()) {
Mob* petowner = GetOwner();
if (petowner) {
int shield_duration = spells[spell_id].base[i] * 12 * 1000;
int shield_target_mitigation = spells[spell_id].base2[i] ? spells[spell_id].base2[i] : 50;
int shielder_mitigation = spells[spell_id].max[i] ? spells[spell_id].base2[i] : 50;
ShieldAbility(petowner->GetID(), 25, shield_duration, shield_target_mitigation, shielder_mitigation);
break;
}
}
}
case SE_Weapon_Stance: {
if (IsClient()) {
CastToClient()->ApplyWeaponsStance();
@@ -3170,7 +3183,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
case SE_LimitManaMax:
case SE_DoubleRangedAttack:
case SE_ShieldEquipDmgMod:
case SE_GroupShielding:
case SE_TriggerOnReqTarget:
case SE_LimitRace:
case SE_FcLimitUse:
+2
View File
@@ -287,7 +287,9 @@
#define SUSPEND_MINION_SUSPEND 3268 //%1 tells you, 'By your command, master.'
#define ONLY_SUMMONED_PETS 3269 //3269 This effect only works with summoned pets.
#define SUSPEND_MINION_FIGHTING 3270 //Your pet must be at peace, first.
#define SHIELD_TARGET_NPC 3278 //You must first target a living Player Character.
#define ALREADY_SHIELDED 3279 //Either you or your target is already being shielded.
#define ALREADY_SHIELDING 3280 //Either you or your target is already shielding another.
#define START_SHIELDING 3281 //%1 begins to use %2 as a living shield!
#define END_SHIELDING 3282 //%1 ceases protecting %2.
#define TRADESKILL_MISSING_ITEM 3455 //You are missing a %1.