[Rule] Add rule for NPC Level Based Buff Restrictions. (#2708)

* [Rule] Add rule for NPC Level Based Buff Restrictions.

* Erroneous whitespace

* Change prefix on log message

* Added log message when GM casts
This commit is contained in:
Paul Coene 2023-01-15 16:30:42 -05:00 committed by GitHub
parent 9e3539295b
commit ca0ae3cb98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 63 additions and 43 deletions

View File

@ -426,6 +426,7 @@ RULE_BOOL(Spells, BuffsFadeOnDeath, true, "Disable to keep buffs from fading on
RULE_BOOL(Spells, IllusionsAlwaysPersist, false, "Allows Illusions to persist beyond death and zoning always.") RULE_BOOL(Spells, IllusionsAlwaysPersist, false, "Allows Illusions to persist beyond death and zoning always.")
RULE_BOOL(Spells, UseItemCastMessage, false, "Enable to use the \"item begins to glow\" messages when casting from an item.") RULE_BOOL(Spells, UseItemCastMessage, false, "Enable to use the \"item begins to glow\" messages when casting from an item.")
RULE_BOOL(Spells, TargetsTargetRequiresCombatRange, true, "Disable to remove combat range requirement from Target's Target Spell Target Type") RULE_BOOL(Spells, TargetsTargetRequiresCombatRange, true, "Disable to remove combat range requirement from Target's Target Spell Target Type")
RULE_BOOL(Spells, NPCBuffLevelRestrictions, false, "Impose BuffLevelRestrictions on NPCs if true")
RULE_CATEGORY_END() RULE_CATEGORY_END()
RULE_CATEGORY(Combat) RULE_CATEGORY(Combat)

View File

@ -560,7 +560,6 @@ public:
inline virtual int32 GetDelayDeath() const { return aabonuses.DelayDeath + spellbonuses.DelayDeath + itembonuses.DelayDeath + 11; } inline virtual int32 GetDelayDeath() const { return aabonuses.DelayDeath + spellbonuses.DelayDeath + itembonuses.DelayDeath + 11; }
virtual bool CheckFizzle(uint16 spell_id); virtual bool CheckFizzle(uint16 spell_id);
virtual bool CheckSpellLevelRestriction(uint16 spell_id);
virtual int GetCurrentBuffSlots() const; virtual int GetCurrentBuffSlots() const;
virtual int GetCurrentSongSlots() const; virtual int GetCurrentSongSlots() const;
virtual int GetCurrentDiscSlots() const { return 1; } virtual int GetCurrentDiscSlots() const { return 1; }

View File

@ -373,7 +373,7 @@ public:
bool DoCastingChecksZoneRestrictions(bool check_on_casting, int32 spell_id); bool DoCastingChecksZoneRestrictions(bool check_on_casting, int32 spell_id);
bool DoCastingChecksOnTarget(bool check_on_casting, int32 spell_id, Mob* spell_target); bool DoCastingChecksOnTarget(bool check_on_casting, int32 spell_id, Mob* spell_target);
virtual bool CheckFizzle(uint16 spell_id); virtual bool CheckFizzle(uint16 spell_id);
virtual bool CheckSpellLevelRestriction(uint16 spell_id); virtual bool CheckSpellLevelRestriction(Mob *caster, uint16 spell_id);
virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster); virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster);
virtual float GetAOERange(uint16 spell_id); virtual float GetAOERange(uint16 spell_id);
void InterruptSpell(uint16 spellid = SPELL_UNKNOWN); void InterruptSpell(uint16 spellid = SPELL_UNKNOWN);

View File

@ -735,11 +735,11 @@ bool ZoneDatabase::GetBasePetItems(int32 equipmentset, uint32 *items) {
return true; return true;
} }
bool Pet::CheckSpellLevelRestriction(uint16 spell_id) bool Pet::CheckSpellLevelRestriction(Mob *caster, uint16 spell_id)
{ {
auto owner = GetOwner(); auto owner = GetOwner();
if (owner) if (owner)
return owner->CheckSpellLevelRestriction(spell_id); return owner->CheckSpellLevelRestriction(caster, spell_id);
return true; return true;
} }

View File

@ -7,7 +7,7 @@ struct NPCType;
class Pet : public NPC { class Pet : public NPC {
public: public:
Pet(NPCType *type_data, Mob *owner, PetType type, uint16 spell_id, int16 power); Pet(NPCType *type_data, Mob *owner, PetType type, uint16 spell_id, int16 power);
virtual bool CheckSpellLevelRestriction(uint16 spell_id); virtual bool CheckSpellLevelRestriction(Mob *caster, uint16 spell_id);
}; };

View File

@ -734,10 +734,6 @@ bool Mob::DoCastingChecksOnTarget(bool check_on_casting, int32 spell_id, Mob *sp
*/ */
bool ignore_on_casting = false; bool ignore_on_casting = false;
bool ignore_if_npc_or_gm = false;
if (!IsClient() || (IsClient() && CastToClient()->GetGM())) {
ignore_if_npc_or_gm = true;
}
if (check_on_casting) { if (check_on_casting) {
if (spells[spell_id].target_type == ST_AEClientV1 || if (spells[spell_id].target_type == ST_AEClientV1 ||
@ -803,15 +799,9 @@ bool Mob::DoCastingChecksOnTarget(bool check_on_casting, int32 spell_id, Mob *sp
/* /*
Prevent buffs from being cast on targets who don't meet level restriction Prevent buffs from being cast on targets who don't meet level restriction
*/ */
if (!ignore_if_npc_or_gm && RuleB(Spells, BuffLevelRestrictions)) {
// casting_spell_targetid is guaranteed to be what we went, check for ST_Self for now should work though if (!spell_target->CheckSpellLevelRestriction(this, spell_id)) {
if (spells[spell_id].target_type != ST_Self && !spell_target->CheckSpellLevelRestriction(spell_id)) { return false;
LogSpells("Spell [{}] failed: recipient did not meet the level restrictions", spell_id);
if (!IsBardSong(spell_id)) {
MessageString(Chat::SpellFailure, SPELL_TOO_POWERFUL);
}
return false;
}
} }
/* /*
Prevents buff from being cast based on tareget ing PC OR NPC (1 = PCs, 2 = NPCs) Prevents buff from being cast based on tareget ing PC OR NPC (1 = PCs, 2 = NPCs)
@ -1161,6 +1151,9 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid)
} }
} }
LogSpells("Interrupt: casting_spell_id [{}] casting_spell_slot [{}]",
casting_spell_id, (int) casting_spell_slot);
if(casting_spell_id && IsNPC()) { if(casting_spell_id && IsNPC()) {
CastToNPC()->AI_Event_SpellCastFinished(false, static_cast<uint16>(casting_spell_slot)); CastToNPC()->AI_Event_SpellCastFinished(false, static_cast<uint16>(casting_spell_slot));
} }
@ -1275,8 +1268,9 @@ void Mob::StopCastSpell(int32 spell_id, bool send_spellbar_enable)
-AA that fail at CastSpell because they never get timer set. -AA that fail at CastSpell because they never get timer set.
-Instant cast items that fail at CastSpell because they never get timer set. -Instant cast items that fail at CastSpell because they never get timer set.
*/ */
if (casting_spell_id && IsNPC()) { // Often called before spell_id and slot are set. For NPCs always update AI
CastToNPC()->AI_Event_SpellCastFinished(false, static_cast<uint16>(casting_spell_slot)); if (IsNPC()) {
CastToNPC()->AI_Event_SpellCastFinished(false, 1);
} }
if (send_spellbar_enable) { if (send_spellbar_enable) {
@ -3210,29 +3204,60 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
// spells 1-50: no restrictons // spells 1-50: no restrictons
// 51-65: SpellLevel/2+15 // 51-65: SpellLevel/2+15
// 66+ Group Spells 62, Single Target 61 // 66+ Group Spells 62, Single Target 61
bool Mob::CheckSpellLevelRestriction(uint16 spell_id) bool Mob::CheckSpellLevelRestriction(Mob *caster, uint16 spell_id)
{ {
return true; bool check_for_restrictions = false;
} bool can_cast = true;
bool Client::CheckSpellLevelRestriction(uint16 spell_id) if (!caster) {
{ LogSpells("[CheckSpellLevelRestriction] No caster");
int SpellLevel = GetMinLevel(spell_id); return false;
}
// Only check for beneficial buffs if (caster->IsClient() && caster->CastToClient()->GetGM()) {
if (IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id)) { LogSpells("[CheckSpellLevelRestriction] GM casting - No restrictions");
if (SpellLevel > 65) { return true;
if (IsGroupSpell(spell_id) && GetLevel() < 62) }
return false;
else if (GetLevel() < 61) // NON GM clients might be restricted by rule setting
return false; if (caster->IsClient()) {
} else if (SpellLevel > 50) { // 51-65 if (RuleB(Spells, BuffLevelRestrictions)) {
if (GetLevel() < (SpellLevel / 2 + 15)) check_for_restrictions = true;
return false; }
}
// NPCS might be restricted by rule setting
else if (RuleB(Spells, NPCBuffLevelRestrictions)) {
check_for_restrictions = true;
}
if (check_for_restrictions) {
int spell_level = GetMinLevel(spell_id);
// Only check for beneficial buffs
if (IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id)) {
if (spell_level > 65) {
if (IsGroupSpell(spell_id) && GetLevel() < 62) {
can_cast = false;
}
else if (GetLevel() < 61) {
can_cast = false;
}
} else if (spell_level > 50) { // 51-65
if (GetLevel() < (spell_level / 2 + 15)) {
can_cast = false;
}
}
} }
} }
return true; if (!can_cast) {
LogSpells("Spell [{}] failed: recipient did not meet the level restrictions", spell_id);
if (!IsBardSong(spell_id)) {
caster->MessageString(Chat::SpellFailure, SPELL_TOO_POWERFUL);
}
}
return can_cast;
} }
uint32 Mob::GetFirstBuffSlot(bool disc, bool song) uint32 Mob::GetFirstBuffSlot(bool disc, bool song)
@ -4175,12 +4200,7 @@ bool Mob::SpellOnTarget(
} }
// make sure spelltar is high enough level for the buff // make sure spelltar is high enough level for the buff
if (RuleB(Spells, BuffLevelRestrictions) && !spelltar->CheckSpellLevelRestriction(spell_id)) { if (!spelltar->CheckSpellLevelRestriction(this, spell_id)) {
LogSpells("Spell [{}] failed: recipient did not meet the level restrictions", spell_id);
if (!IsBardSong(spell_id)) {
MessageString(Chat::SpellFailure, SPELL_TOO_POWERFUL);
}
safe_delete(action_packet); safe_delete(action_packet);
return false; return false;
} }