diff --git a/common/ruletypes.h b/common/ruletypes.h index 99c798546..f113834fa 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -412,6 +412,7 @@ RULE_BOOL(Spells, DOTsScaleWithSpellDmg, false, "Allow SpellDmg stat to affect D RULE_BOOL(Spells, HOTsScaleWithHealAmt, false, "Allow HealAmt stat to affect HoT spells") RULE_BOOL(Spells, CompoundLifetapHeals, true, "True: Lifetap heals calculate damage bonuses and then heal bonuses. False: Lifetaps heal using the amount damaged to mob.") RULE_BOOL(Spells, UseFadingMemoriesMaxLevel, false, "Enables to limit field in spell data to set the max level that over which an NPC will ignore fading memories effect and not lose aggro.") +RULE_BOOL(Spells, FixBeaconHeading, false, "Beacon spells use casters heading to fix live bug. False: Live like heading always 0.") RULE_CATEGORY_END() RULE_CATEGORY(Combat) diff --git a/common/spdat.cpp b/common/spdat.cpp index e0af5f0f4..64e9adc9b 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -364,10 +364,19 @@ bool IsImprovedDamageSpell(uint16 spell_id) bool IsAEDurationSpell(uint16 spell_id) { - if (IsValidSpell(spell_id) && - (spells[spell_id].target_type == ST_AETarget || spells[spell_id].target_type == ST_UndeadAE) && - spells[spell_id].aoe_duration != 0) + /* + There are plenty of spells with aoe_duration set at single digit numbers, but these + do not act as duration effects. + */ + if (IsValidSpell(spell_id) && + spells[spell_id].aoe_duration >= 2500 && + ( spells[spell_id].target_type == ST_AETarget || + spells[spell_id].target_type == ST_UndeadAE || + spells[spell_id].target_type == ST_AECaster || + spells[spell_id].target_type == ST_Ring) + ) { return true; + } return false; } diff --git a/zone/beacon.cpp b/zone/beacon.cpp index 60b572dd2..1563b151b 100644 --- a/zone/beacon.cpp +++ b/zone/beacon.cpp @@ -52,10 +52,10 @@ extern Zone* zone; // if lifetime is 0 this is a permanent beacon.. not sure if that'll be // useful for anything -Beacon::Beacon(Mob *at_mob, int lifetime) +Beacon::Beacon(const glm::vec4 &in_pos, int lifetime) :Mob ( - nullptr, nullptr, 0, 0, 0, INVISIBLE_MAN, 0, BT_NoTarget, 0, 0, 0, 0, 0, at_mob->GetPosition(), 0, 0, 0, + nullptr, nullptr, 0, 0, 0, INVISIBLE_MAN, 0, BT_NoTarget, 0, 0, 0, 0, 0, in_pos, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, EQ::TintProfile(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false ), remove_timer(lifetime), @@ -121,16 +121,19 @@ bool Beacon::Process() void Beacon::AELocationSpell(Mob *caster, uint16 cast_spell_id, int16 resist_adjust) { - if(!IsValidSpell(cast_spell_id) || !caster) + if (!IsValidSpell(cast_spell_id) || !caster) { return; + } caster_id = caster->GetID(); spell_id = cast_spell_id; this->resist_adjust = resist_adjust; spell_iterations = spells[spell_id].aoe_duration / 2500; spell_iterations = spell_iterations < 1 ? 1 : spell_iterations; // at least 1 - if (spells[spell_id].aoe_max_targets) + if (spells[spell_id].aoe_max_targets) { max_targets = spells[spell_id].aoe_max_targets; + } + spell_timer.Start(2500); spell_timer.Trigger(); } diff --git a/zone/beacon.h b/zone/beacon.h index e5880c981..2ff25dd4d 100644 --- a/zone/beacon.h +++ b/zone/beacon.h @@ -30,7 +30,7 @@ struct ExtraAttackOptions; class Beacon : public Mob { public: - Beacon(Mob *at_mob, int lifetime); + Beacon(const glm::vec4 &in_pos, int lifetime); ~Beacon(); //abstract virtual function implementations requird by base abstract class diff --git a/zone/spells.cpp b/zone/spells.cpp index 22e7edb63..76585d463 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -704,13 +704,16 @@ bool Mob::DoCastingChecksOnTarget(bool check_on_casting, int32 spell_id, Mob *sp } if (check_on_casting){ - + + if (spells[spell_id].target_type == ST_AEClientV1 || + spells[spell_id].target_type == ST_AECaster || + spells[spell_id].target_type == ST_Ring || + spells[spell_id].target_type == ST_Beam) { + return true; + } + if (!spell_target) { - if (IsGroupSpell(spell_id) || - spells[spell_id].target_type == ST_AEClientV1 || - spells[spell_id].target_type == ST_AECaster || - spells[spell_id].target_type == ST_Ring || - spells[spell_id].target_type == ST_Beam) { + if (IsGroupSpell(spell_id)){ return true; } else if (spells[spell_id].target_type == ST_Self) { @@ -728,7 +731,6 @@ bool Mob::DoCastingChecksOnTarget(bool check_on_casting, int32 spell_id, Mob *sp if (!spell_target){ return false; } - /* Spells that use caster_restriction field which requires specific conditions on target to be met before casting. [Insufficient mana first] @@ -2278,12 +2280,25 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui LogSpells("Spell [{}]: target type [{}], target [{}], AE center [{}]", spell_id, CastAction, spell_target?spell_target->GetName():"NONE", ae_center?ae_center->GetName():"NONE"); // if a spell has the AEDuration flag, it becomes an AE on target - // spell that's recast every 2500 msec for AEDuration msec. There are - // spells of all kinds of target types that do this, strangely enough - // TODO: finish this + // spell that's recast every 2500 msec for AEDuration msec. if(IsAEDurationSpell(spell_id)) { // the spells are AE target, but we aim them on a beacon - Mob *beacon_loc = spell_target ? spell_target : this; + glm::vec4 beacon_loc; + if (spells[spell_id].target_type == ST_Ring) { + beacon_loc = glm::vec4{ GetTargetRingX(),GetTargetRingY(), GetTargetRingZ(), GetHeading()}; + } + else { + if (spell_target) { + beacon_loc = spell_target->GetPosition(); + } + else { + beacon_loc = GetPosition(); + } + } + // live has a bug where the heading is always north + if (!RuleB(Spells, FixBeaconHeading)) { + beacon_loc.w = 0.0f; + } auto beacon = new Beacon(beacon_loc, spells[spell_id].aoe_duration); entity_list.AddBeacon(beacon); LogSpells("Spell [{}]: AE duration beacon created, entity id [{}]", spell_id, beacon->GetName());