diff --git a/common/ruletypes.h b/common/ruletypes.h index 3cfda60ae..7c4273161 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -399,6 +399,7 @@ RULE_BOOL(Spells, ItemExtraSpellAmtCalcAsPercent, false, "Apply the Item stats S RULE_BOOL(Spells, AllowItemTGB, false, "Target group buff (/tgb) doesn't work with items on live, custom servers want it though") RULE_BOOL(Spells, NPCInnateProcOverride, true, "NPC innate procs override the target type to single target") RULE_BOOL(Spells, OldRainTargets, false, "Use old incorrectly implemented maximum targets for rains") +RULE_REAL(Spells, CallOfTheHeroAggroClearDist, 250.0, "Distance at which CoTH will wipe aggro. To disable and always enable aggro wipe on any distance of CoTH, set to 0.") RULE_BOOL(Spells, NPCSpellPush, false, "Enable spell push on NPCs") RULE_BOOL(Spells, July242002PetResists, true, "Enable Pets using PCs resist change from July 24 2002") RULE_INT(Spells, AOEMaxTargets, 0, "Max number of targets a Targeted AOE spell can cast on. Set to 0 for no limit.") diff --git a/zone/entity.cpp b/zone/entity.cpp index 16b3e6dc8..459797738 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2091,6 +2091,14 @@ Group *EntityList::GetGroupByID(uint32 group_id) return nullptr; } +bool EntityList::IsInSameGroupOrRaidGroup(Client *client1, Client *client2) { + Group* group = entity_list.GetGroupByClient(client1); + Raid* raid = entity_list.GetRaidByClient(client1); + + return (group && group->IsGroupMember(client2)) + || (raid && raid->IsRaidMember(client2->GetName()) && raid->GetGroup(client1) == raid->GetGroup(client2)); +} + Group *EntityList::GetGroupByClient(Client *client) { std::list ::iterator iterator; diff --git a/zone/entity.h b/zone/entity.h index f06e26119..c0efa6886 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -193,6 +193,7 @@ public: NPC* GetRandomNPC(const glm::vec3& location, float distance, NPC* exclude_npc = nullptr); Mob* GetRandomMob(const glm::vec3& location, float distance, Mob* exclude_mob = nullptr); Group *GetGroupByMob(Mob* mob); + bool IsInSameGroupOrRaidGroup(Client *client1, Client *client2); Group *GetGroupByClient(Client* client); Group *GetGroupByID(uint32 id); Group *GetGroupByLeaderName(const char* leader); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 8382d03a1..096b6cd48 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2130,20 +2130,30 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_SummonPC: { - if (!caster) + if (!caster) { break; + } + if (IsClient()) { + if (caster->IsClient()) { + if (!entity_list.IsInSameGroupOrRaidGroup(caster->CastToClient(), CastToClient())) { + caster->Message(Chat::Red, "Your target must be a group member for this spell."); + break; + } + + // clear aggro when summoned in zone and further than aggro clear distance rule. + if (RuleR(Spells, CallOfTheHeroAggroClearDist) == 0 || caster->CalculateDistance(GetX(), GetY(), GetZ()) >= RuleR(Spells, CallOfTheHeroAggroClearDist)) { + entity_list.ClearAggro(this); + } + } + CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), caster->GetX(), caster->GetY(), caster->GetZ(), caster->GetHeading(), 2, SummonPC); Message(Chat::Yellow, "You have been summoned!"); - // only for beneficial spells like Call of the Hero - // This clear probably isn't actually needed, but need to investigate more - if (IsBeneficialSpell(spell_id)) - entity_list.ClearAggro(this); - } else + } else { caster->Message(Chat::Red, "This spell can only be cast on players."); - + } break; }