diff --git a/common/shareddb.cpp b/common/shareddb.cpp index fd55b3766..fc628ea6f 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1700,6 +1700,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].pvpresistcalc=atoi(row[178]); sp[tempid].pvpresistcap=atoi(row[179]); sp[tempid].spell_category=atoi(row[180]); + sp[tempid].pcnpc_only_flag=atoi(row[183]); sp[tempid].cast_not_standing = atoi(row[184]) != 0; sp[tempid].can_mgb=atoi(row[185]); sp[tempid].dispel_flag = atoi(row[186]); diff --git a/common/spdat.h b/common/spdat.h index edb750f2a..ab00451e0 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -735,7 +735,7 @@ struct SPDat_Spell_Struct /* 180 */ int spell_category; // -- GLOBAL_GROUP /* 181 */ //int pvp_duration; // buffdurationformula for PvP -- PVP_DURATION /* 182 */ //int pvp_duration_cap; // buffduration for PvP -- PVP_DURATION_CAP -/* 183 */ //int pcnpc_only_flag; // valid values are 0, 1 = PCs (and mercs), and 2 = NPCs (and not mercs) -- PCNPC_ONLY_FLAG +/* 183 */ int pcnpc_only_flag; // valid values are 0, 1 = PCs (and mercs), and 2 = NPCs (and not mercs) -- PCNPC_ONLY_FLAG /* 184 */ bool cast_not_standing; // this is checked in the client's EQ_Spell::IsCastWhileInvisSpell, this also blocks SE_InterruptCasting from affecting this spell -- CAST_NOT_STANDING /* 185 */ bool can_mgb; // 0=no, -1 or 1 = yes -- CAN_MGB /* 186 */ int dispel_flag; // -- NO_DISPELL diff --git a/zone/effects.cpp b/zone/effects.cpp index 3e3f7a4bc..0cec65877 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -778,6 +778,11 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ continue; if (spells[spell_id].targettype == ST_AreaNPCOnly && !curmob->IsNPC()) continue; + // check PC/NPC only flag 1 = PCs, 2 = NPCs + if (spells[spell_id].pcnpc_only_flag == 1 && !curmob->IsClient() && !curmob->IsMerc()) + continue; + if (spells[spell_id].pcnpc_only_flag == 2 && (curmob->IsClient() || curmob->IsMerc())) + continue; if (spells[spell_id].targettype == ST_Ring) { dist_targ = DistanceSquared(static_cast(curmob->GetPosition()), caster->GetTargetRingLocation()); diff --git a/zone/entity.cpp b/zone/entity.cpp index 2bbcdef20..2ff83980e 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -4655,7 +4655,7 @@ Mob *EntityList::GetClosestMobByBodyType(Mob *sender, bodyType BodyType) return ClosestMob; } -void EntityList::GetTargetsForConeArea(Mob *start, float min_radius, float radius, float height, std::list &m_list) +void EntityList::GetTargetsForConeArea(Mob *start, float min_radius, float radius, float height, int pcnpc, std::list &m_list) { auto it = mob_list.begin(); while (it != mob_list.end()) { @@ -4664,6 +4664,14 @@ void EntityList::GetTargetsForConeArea(Mob *start, float min_radius, float radiu ++it; continue; } + // check PC/NPC only flag 1 = PCs, 2 = NPCs + if (pcnpc == 1 && !ptr->IsClient() && !ptr->IsMerc()) { + ++it; + continue; + } else if (pcnpc == 2 && (ptr->IsClient() || ptr->IsMerc())) { + ++it; + continue; + } float x_diff = ptr->GetX() - start->GetX(); float y_diff = ptr->GetY() - start->GetY(); float z_diff = ptr->GetZ() - start->GetZ(); diff --git a/zone/entity.h b/zone/entity.h index d2141d1b9..9a532cdb1 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -451,7 +451,7 @@ public: void GetObjectList(std::list &o_list); void GetDoorsList(std::list &d_list); void GetSpawnList(std::list &d_list); - void GetTargetsForConeArea(Mob *start, float min_radius, float radius, float height, std::list &m_list); + void GetTargetsForConeArea(Mob *start, float min_radius, float radius, float height, int pcnpc, std::list &m_list); void DepopAll(int NPCTypeID, bool StartSpawnTimer = true); diff --git a/zone/spells.cpp b/zone/spells.cpp index 3b232f9cd..814f01be6 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3405,6 +3405,17 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r if(!IsValidSpell(spell_id)) return false; + // these target types skip pcnpc only check (according to dev quotes) + // other AE spells this is redundant, oh well + // 1 = PCs, 2 = NPCs + if (spells[spell_id].pcnpc_only_flag && spells[spell_id].targettype != ST_AETargetHateList && + spells[spell_id].targettype != ST_HateList) { + if (spells[spell_id].pcnpc_only_flag == 1 && !spelltar->IsClient() && !spelltar->IsMerc()) + return false; + else if (spells[spell_id].pcnpc_only_flag == 2 && (spelltar->IsClient() || spelltar->IsMerc())) + return false; + } + uint16 caster_level = level_override > 0 ? level_override : GetCasterLevel(spell_id); Log.Out(Logs::Detail, Logs::Spells, "Casting spell %d on %s with effective caster level %d", spell_id, spelltar->GetName(), caster_level); @@ -5623,7 +5634,7 @@ void Mob::BeamDirectional(uint16 spell_id, int16 resist_adjust) std::list targets_in_range; entity_list.GetTargetsForConeArea(this, spells[spell_id].min_range, spells[spell_id].range, - spells[spell_id].range / 2, targets_in_range); + spells[spell_id].range / 2, spells[spell_id].pcnpc_only_flag, targets_in_range); auto iter = targets_in_range.begin(); float dX = 0; @@ -5698,7 +5709,7 @@ void Mob::ConeDirectional(uint16 spell_id, int16 resist_adjust) std::list targets_in_range; entity_list.GetTargetsForConeArea(this, spells[spell_id].min_range, spells[spell_id].aoerange, - spells[spell_id].aoerange / 2, targets_in_range); + spells[spell_id].aoerange / 2, spells[spell_id].pcnpc_only_flag, targets_in_range); auto iter = targets_in_range.begin(); while (iter != targets_in_range.end()) {