From fabe93e548a24cbb03eefbfc0bb91465608fab34 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 13 Nov 2014 02:25:18 -0500 Subject: [PATCH 1/5] Implemented target type (44) 'Beams' (which projects an AE infront of caster with a specified length and width). Clean up of target type direction code, implemented use of aemaxtargets field for it. --- changelog.txt | 3 + common/spdat.h | 2 +- zone/common.h | 1 + zone/mob.cpp | 2 + zone/mob.h | 10 ++- zone/npc.cpp | 13 +++- zone/spells.cpp | 198 +++++++++++++++++++++++++++++++++++++----------- 7 files changed, 178 insertions(+), 51 deletions(-) diff --git a/changelog.txt b/changelog.txt index b10dfbec7..b80128505 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 11/13/2014 == +Kayen: Implemented target type (44) 'Beams' (which projects an AE infront of caster with a specified length and width). + == 11/12/2014 == Uleat: Changed 'GMTrainee' struct to reflect the actual client hard-coded max skill count (100) - applies to all currently supported clients (6.2->RoF) diff --git a/common/spdat.h b/common/spdat.h index feaddaf84..83af3cd73 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -131,7 +131,7 @@ typedef enum { /* 41 */ ST_Group = 0x29, /* 42 */ ST_Directional = 0x2a, //ae around this target between two angles /* 43 */ ST_GroupClientAndPet = 0x2b, -/* 44 */ //ST_Beam = 0x2c, //like directional but facing in front of you always +/* 44 */ ST_Beam = 0x2c, /* 45 */ ST_Ring = 0x2d, /* 46 */ ST_TargetsTarget = 0x2e, // uses the target of your target /* 47 */ ST_PetMaster = 0x2f, // uses the master as target diff --git a/zone/common.h b/zone/common.h index fb89dfe77..64b6e38af 100644 --- a/zone/common.h +++ b/zone/common.h @@ -495,6 +495,7 @@ typedef enum { GroupSpell, // causes effect to caster + target's group CAHateList, // causes effect to all people on caster's hate list within some range DirectionalAE, + Beam, TargetRing, CastActUnknown } CastAction_type; diff --git a/zone/mob.cpp b/zone/mob.cpp index 0b028dd75..b0dac9dff 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -300,6 +300,8 @@ Mob::Mob(const char* in_name, held = false; nocast = false; focused = false; + _IsTempPet = false; + pet_owner_client = false; attacked_count = 0; mezzed = false; diff --git a/zone/mob.h b/zone/mob.h index 94b8ed930..7bd873d98 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -235,7 +235,9 @@ public: void ResourceTap(int32 damage, uint16 spell_id); void TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker); bool CheckSpellCategory(uint16 spell_id, int category_id, int effect_id); - + void CalcDestFromHeading(float heading, float distance, float MaxZDiff, float StartX, float StartY, float &dX, float &dY, float &dZ); + void BeamDirectional(uint16 spell_id, int16 resist_adjust); + void ConeDirectional(uint16 spell_id, int16 resist_adjust); //Buff void BuffProcess(); @@ -674,6 +676,10 @@ public: bool HadTempPets() const { return(hasTempPet); } void TempPets(bool i) { hasTempPet = i; } bool HasPetAffinity() { if (aabonuses.GivePetGroupTarget || itembonuses.GivePetGroupTarget || spellbonuses.GivePetGroupTarget) return true; return false; } + inline bool IsPetOwnerClient() const { return pet_owner_client; } + inline void SetPetOwnerClient(bool value) { pet_owner_client = value; } + inline bool IsTempPet() const { return _IsTempPet; } + inline void SetTempPet(bool value) { _IsTempPet = value; } inline const bodyType GetBodyType() const { return bodytype; } inline const bodyType GetOrigBodyType() const { return orig_bodytype; } @@ -1225,6 +1231,8 @@ protected: //temppet bool hasTempPet; + bool _IsTempPet; + bool pet_owner_client; //Flags regular and pets as belonging to a client EGNode *_egnode; //the EG node we are in float tarx; diff --git a/zone/npc.cpp b/zone/npc.cpp index c9d3e1879..699f81384 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -1847,6 +1847,7 @@ void NPC::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) Client *c = entity_list.GetClientByID(GetSwarmOwner()); if(c) { SetAllowBeneficial(1); //Allow client cast swarm pets to be heal/buffed. + SetPetOwnerClient(true); //This is a hack to allow CLIENT swarm pets NOT to be targeted with F8. Warning: Will turn name 'Yellow'! if (RuleB(Pets, SwarmPetNotTargetableWithHotKey)) ns->spawn.IsMercenary = 1; @@ -1862,16 +1863,20 @@ void NPC::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) ns->spawn.is_pet = 1; if (!IsCharmed() && GetOwnerID()) { Client *c = entity_list.GetClientByID(GetOwnerID()); - if(c) + if(c){ + SetPetOwnerClient(true); sprintf(ns->spawn.lastName, "%s's Pet", c->GetName()); + } } else if (GetSwarmOwner()) { ns->spawn.bodytype = 11; if(!IsCharmed()) { Client *c = entity_list.GetClientByID(GetSwarmOwner()); - if(c) + if(c){ + SetPetOwnerClient(true); sprintf(ns->spawn.lastName, "%s's Pet", c->GetName()); + } } } } @@ -1880,8 +1885,10 @@ void NPC::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) ns->spawn.is_pet = 1; if (!IsCharmed() && GetOwnerID()) { Client *c = entity_list.GetClientByID(GetOwnerID()); - if(c) + if(c){ + SetPetOwnerClient(true); sprintf(ns->spawn.lastName, "%s's Pet", c->GetName()); + } } } else ns->spawn.is_pet = 0; diff --git a/zone/spells.cpp b/zone/spells.cpp index 2e5e16f46..e5178faa3 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -369,6 +369,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot, spell.targettype == ST_Self || spell.targettype == ST_AECaster || spell.targettype == ST_Ring || + spell.targettype == ST_Beam || spell.targettype == ST_TargetOptional) && target_id == 0) { mlog(SPELLS__CASTING, "Spell %d auto-targeted the caster. Group? %d, target type %d", spell_id, IsGroupSpell(spell_id), spell.targettype); @@ -1803,6 +1804,14 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce break; } + case ST_Beam: + { + CastAction = Beam; + spell_target = nullptr; + ae_center = nullptr; + break; + } + case ST_Ring: { CastAction = TargetRing; @@ -2126,54 +2135,15 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 case DirectionalAE: { - float angle_start = spells[spell_id].directional_start + (GetHeading() * 360.0f / 256.0f); - float angle_end = spells[spell_id].directional_end + (GetHeading() * 360.0f / 256.0f); - - while(angle_start > 360.0f) - angle_start -= 360.0f; - - while(angle_end > 360.0f) - angle_end -= 360.0f; - - std::list targets_in_range; - std::list::iterator iter; - - entity_list.GetTargetsForConeArea(this, spells[spell_id].min_range, spells[spell_id].aoerange, spells[spell_id].aoerange / 2, targets_in_range); - iter = targets_in_range.begin(); - while(iter != targets_in_range.end()) - { - float heading_to_target = (CalculateHeadingToTarget((*iter)->GetX(), (*iter)->GetY()) * 360.0f / 256.0f); - while(heading_to_target < 0.0f) - heading_to_target += 360.0f; - - while(heading_to_target > 360.0f) - heading_to_target -= 360.0f; - - if(angle_start > angle_end) - { - if((heading_to_target >= angle_start && heading_to_target <= 360.0f) || - (heading_to_target >= 0.0f && heading_to_target <= angle_end)) - { - if(CheckLosFN((*iter)) || spells[spell_id].npc_no_los){ - (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); - SpellOnTarget(spell_id, (*iter), false, true, resist_adjust); - } - } - } - else - { - if(heading_to_target >= angle_start && heading_to_target <= angle_end) - { - if(CheckLosFN((*iter)) || spells[spell_id].npc_no_los){ - (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); - SpellOnTarget(spell_id, (*iter), false, true, resist_adjust); - } - } - } - ++iter; - } + ConeDirectional(spell_id, resist_adjust); break; } + + case Beam: + { + BeamDirectional(spell_id, resist_adjust); + break; + } case TargetRing: { @@ -5385,3 +5355,139 @@ void Client::SendSpellAnim(uint16 targetid, uint16 spell_id) app.priority = 1; entity_list.QueueCloseClients(this, &app); } + +void Mob::CalcDestFromHeading(float heading, float distance, float MaxZDiff, float StartX, float StartY, float &dX, float &dY, float &dZ) +{ + if (!distance) { return; } + if (!MaxZDiff) { MaxZDiff = 5; } + + float ReverseHeading = 256 - heading; + float ConvertAngle = ReverseHeading * 1.40625f; + if (ConvertAngle <= 270) + ConvertAngle = ConvertAngle + 90; + else + ConvertAngle = ConvertAngle - 270; + + float Radian = ConvertAngle * (3.1415927f / 180.0f); + + float CircleX = distance * cos(Radian); + float CircleY = distance * sin(Radian); + dX = CircleX + StartX; + dY = CircleY + StartY; + dZ = FindGroundZ(dX, dY, MaxZDiff); +} + +void Mob::BeamDirectional(uint16 spell_id, int16 resist_adjust) +{ + int maxtarget_count = 0; + bool beneficial_targets = false; + + if (IsBeneficialSpell(spell_id) && IsClient()) + beneficial_targets = true; + + std::list targets_in_range; + std::list::iterator iter; + + entity_list.GetTargetsForConeArea(this, spells[spell_id].min_range, spells[spell_id].range, spells[spell_id].range / 2, targets_in_range); + iter = targets_in_range.begin(); + + float dX = 0; + float dY = 0; + float dZ = 0; + + CalcDestFromHeading(GetHeading(), spells[spell_id].range, 5, GetX(), GetY(), dX, dY, dZ); + dZ = GetZ(); + + //FIND SLOPE: Put it into the form y = mx + b + float m = (dY - GetY()) / (dX - GetX()); + float b = (GetY() * dX - dY * GetX()) / (dX - GetX()); + + while(iter != targets_in_range.end()) + { + if (!(*iter) || (beneficial_targets && ((*iter)->IsNPC() && !(*iter)->IsPetOwnerClient())) + || (*iter)->BehindMob(this, (*iter)->GetX(),(*iter)->GetY())){ + ++iter; + continue; + } + + //# shortest distance from line to target point + float d = abs( (*iter)->GetY() - m * (*iter)->GetX() - b) / sqrt(m * m + 1); + + if (d <= spells[spell_id].aoerange) + { + if(CheckLosFN((*iter)) || spells[spell_id].npc_no_los) { + (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); + SpellOnTarget(spell_id, (*iter), false, true, resist_adjust); + maxtarget_count++; + } + + if (maxtarget_count >= spells[spell_id].aemaxtargets) + return; + } + ++iter; + } +} + +void Mob::ConeDirectional(uint16 spell_id, int16 resist_adjust) +{ + int maxtarget_count = 0; + bool beneficial_targets = false; + + if (IsBeneficialSpell(spell_id) && IsClient()) + beneficial_targets = true; + + float angle_start = spells[spell_id].directional_start + (GetHeading() * 360.0f / 256.0f); + float angle_end = spells[spell_id].directional_end + (GetHeading() * 360.0f / 256.0f); + + while(angle_start > 360.0f) + angle_start -= 360.0f; + + while(angle_end > 360.0f) + angle_end -= 360.0f; + + std::list targets_in_range; + std::list::iterator iter; + + entity_list.GetTargetsForConeArea(this, spells[spell_id].min_range, spells[spell_id].aoerange, spells[spell_id].aoerange / 2, targets_in_range); + iter = targets_in_range.begin(); + + while(iter != targets_in_range.end()){ + + if (!(*iter) || (beneficial_targets && ((*iter)->IsNPC() && !(*iter)->IsPetOwnerClient()))){ + ++iter; + continue; + } + + float heading_to_target = (CalculateHeadingToTarget((*iter)->GetX(), (*iter)->GetY()) * 360.0f / 256.0f); + + while(heading_to_target < 0.0f) + heading_to_target += 360.0f; + + while(heading_to_target > 360.0f) + heading_to_target -= 360.0f; + + if(angle_start > angle_end){ + if((heading_to_target >= angle_start && heading_to_target <= 360.0f) || (heading_to_target >= 0.0f && heading_to_target <= angle_end)){ + if(CheckLosFN((*iter)) || spells[spell_id].npc_no_los){ + (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); + SpellOnTarget(spell_id,(*iter), false, true, resist_adjust); + maxtarget_count++; + } + } + } + else{ + if(heading_to_target >= angle_start && heading_to_target <= angle_end){ + if(CheckLosFN((*iter)) || spells[spell_id].npc_no_los) { + (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); + SpellOnTarget(spell_id, (*iter), false, true, resist_adjust); + maxtarget_count++; + } + } + } + + if (maxtarget_count >= spells[spell_id].aemaxtargets) + return; + + ++iter; + } +} \ No newline at end of file From 738fa380471eda76788aafc4aa958dada53ddee4 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 13 Nov 2014 02:46:22 -0500 Subject: [PATCH 2/5] Implemented target type (50) which excludes players pets from target AE's. --- common/spdat.h | 3 +++ zone/effects.cpp | 2 ++ zone/spells.cpp | 1 + 3 files changed, 6 insertions(+) diff --git a/common/spdat.h b/common/spdat.h index 83af3cd73..b71b3c2ad 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -135,6 +135,9 @@ typedef enum { /* 45 */ ST_Ring = 0x2d, /* 46 */ ST_TargetsTarget = 0x2e, // uses the target of your target /* 47 */ ST_PetMaster = 0x2f, // uses the master as target +/* 48 */ // UNKNOWN +/* 49 */ // NOT USED +/* 50 */ ST_TargetAENoPlayersPets = 0x32, } SpellTargetType; typedef enum { diff --git a/zone/effects.cpp b/zone/effects.cpp index 02060c10b..9888258d9 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -764,6 +764,8 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ continue; if (curmob == caster && !affect_caster) //watch for caster too continue; + if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && curmob->IsPetOwnerClient()) + continue; if (spells[spell_id].targettype == ST_Ring) { dist_targ = curmob->DistNoRoot(caster->GetTargetRingX(), caster->GetTargetRingY(), caster->GetTargetRingZ()); diff --git a/zone/spells.cpp b/zone/spells.cpp index e5178faa3..e6cab8422 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1614,6 +1614,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce case ST_UndeadAE: //should only affect undead... case ST_TargetAETap: case ST_AETarget: + case ST_TargetAENoPlayersPets: { if(!spell_target) { From 352d6fd83c436b467d2cf555fbe8173fed6922ee Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 13 Nov 2014 05:19:01 -0500 Subject: [PATCH 3/5] Support for all remaining known spell target types. Implemented target type (32) AE Target HateList Implemented target type (36) Area Client Only Implemented target type (37) Area PC Only Implemented target type (39) Group No Pet --- changelog.txt | 4 +++ common/spdat.h | 2 +- zone/effects.cpp | 4 +++ zone/groups.cpp | 4 +-- zone/hate_list.cpp | 11 +++++--- zone/hate_list.h | 2 +- zone/raids.cpp | 4 +-- zone/spells.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++---- 8 files changed, 78 insertions(+), 15 deletions(-) diff --git a/changelog.txt b/changelog.txt index b80128505..d72b4ca23 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,10 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 11/13/2014 == Kayen: Implemented target type (44) 'Beams' (which projects an AE infront of caster with a specified length and width). +Kayen: Implemented target type (32) AE Target HateList +Kayen: Implemented target type (36) Area Client Only +Kayen: Implemented target type (37) Area PC Only +Kayen: Implemented target type (39) Group No Pet == 11/12/2014 == Uleat: Changed 'GMTrainee' struct to reflect the actual client hard-coded max skill count (100) - applies to all currently supported clients (6.2->RoF) diff --git a/common/spdat.h b/common/spdat.h index b71b3c2ad..da2e4486c 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -119,7 +119,7 @@ typedef enum { /* 29 */ // NOT USED /* 30 */ // NOT USED /* 31 */ // NOT USED -/* 32 */ ST_AECaster2 = 0x20, //ae caster hatelist maybe? +/* 32 */ ST_AETargetHateList = 0x20, /* 33 */ ST_HateList = 0x21, /* 34 */ ST_LDoNChest_Cursed = 0x22, /* 35 */ ST_Muramite = 0x23, //only works on special muramites diff --git a/zone/effects.cpp b/zone/effects.cpp index 9888258d9..16024c169 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -766,6 +766,10 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ continue; if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && curmob->IsPetOwnerClient()) continue; + if (spells[spell_id].targettype == ST_AreaClientOnly && !curmob->IsClient()) + continue; + if (spells[spell_id].targettype == ST_AreaNPCOnly && !curmob->IsNPC()) + continue; if (spells[spell_id].targettype == ST_Ring) { dist_targ = curmob->DistNoRoot(caster->GetTargetRingX(), caster->GetTargetRingY(), caster->GetTargetRingZ()); diff --git a/zone/groups.cpp b/zone/groups.cpp index e80650f08..ae51f51c9 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -693,7 +693,7 @@ void Group::CastGroupSpell(Mob* caster, uint16 spell_id) { if(members[z] == caster) { caster->SpellOnTarget(spell_id, caster); #ifdef GROUP_BUFF_PETS - if(caster->GetPet() && caster->HasPetAffinity() && !caster->GetPet()->IsCharmed()) + if(spells[spell_id].targettype != ST_GroupNoPets && caster->GetPet() && caster->HasPetAffinity() && !caster->GetPet()->IsCharmed()) caster->SpellOnTarget(spell_id, caster->GetPet()); #endif } @@ -704,7 +704,7 @@ void Group::CastGroupSpell(Mob* caster, uint16 spell_id) { members[z]->CalcSpellPowerDistanceMod(spell_id, distance); caster->SpellOnTarget(spell_id, members[z]); #ifdef GROUP_BUFF_PETS - if(members[z]->GetPet() && members[z]->HasPetAffinity() && !members[z]->GetPet()->IsCharmed()) + if(spells[spell_id].targettype != ST_GroupNoPets && members[z]->GetPet() && members[z]->HasPetAffinity() && !members[z]->GetPet()->IsCharmed()) caster->SpellOnTarget(spell_id, members[z]->GetPet()); #endif } else diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index e9117db98..2ee904453 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -557,12 +557,15 @@ int HateList::AreaRampage(Mob *caster, Mob *target, int count, ExtraAttackOption return ret; } -void HateList::SpellCast(Mob *caster, uint32 spell_id, float range) +void HateList::SpellCast(Mob *caster, uint32 spell_id, float range, Mob* ae_center) { if(!caster) - { return; - } + + Mob* center = caster; + + if (ae_center) + center = ae_center; //this is slower than just iterating through the list but avoids //crashes when people kick the bucket in the middle of this call @@ -578,7 +581,7 @@ void HateList::SpellCast(Mob *caster, uint32 spell_id, float range) tHateEntry *h = (*iterator); if(range > 0) { - dist_targ = caster->DistNoRoot(*h->ent); + dist_targ = center->DistNoRoot(*h->ent); if(dist_targ <= range && dist_targ >= min_range2) { id_list.push_back(h->ent->GetID()); diff --git a/zone/hate_list.h b/zone/hate_list.h index a3e6c6a88..4ea8d0bd6 100644 --- a/zone/hate_list.h +++ b/zone/hate_list.h @@ -63,7 +63,7 @@ public: int AreaRampage(Mob *caster, Mob *target, int count, ExtraAttackOptions *opts); - void SpellCast(Mob *caster, uint32 spell_id, float range); + void SpellCast(Mob *caster, uint32 spell_id, float range, Mob *ae_center = nullptr); bool IsEmpty(); void PrintToClient(Client *c); diff --git a/zone/raids.cpp b/zone/raids.cpp index 041b92ad4..d1e8db464 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -478,7 +478,7 @@ void Raid::CastGroupSpell(Mob* caster, uint16 spellid, uint32 gid) if(members[x].member == caster) { caster->SpellOnTarget(spellid, caster); #ifdef GROUP_BUFF_PETS - if(caster->GetPet() && caster->HasPetAffinity() && !caster->GetPet()->IsCharmed()) + if(spells[spellid].targettype != ST_GroupNoPets && caster->GetPet() && caster->HasPetAffinity() && !caster->GetPet()->IsCharmed()) caster->SpellOnTarget(spellid, caster->GetPet()); #endif } @@ -489,7 +489,7 @@ void Raid::CastGroupSpell(Mob* caster, uint16 spellid, uint32 gid) if(distance <= range2){ caster->SpellOnTarget(spellid, members[x].member); #ifdef GROUP_BUFF_PETS - if(members[x].member->GetPet() && members[x].member->HasPetAffinity() && !members[x].member->GetPet()->IsCharmed()) + if(spells[spellid].targettype != ST_GroupNoPets && members[x].member->GetPet() && members[x].member->HasPetAffinity() && !members[x].member->GetPet()->IsCharmed()) caster->SpellOnTarget(spellid, members[x].member->GetPet()); #endif } diff --git a/zone/spells.cpp b/zone/spells.cpp index e6cab8422..5b8bfef39 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1611,7 +1611,45 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce break; } + case ST_AETargetHateList: + { + if (spells[spell_id].range > 0) + { + if(!spell_target) + return false; + + ae_center = spell_target; + CastAction = AETarget; + } + else { + spell_target = nullptr; + ae_center = this; + CastAction = CAHateList; + } + break; + } + + case ST_AreaClientOnly: + case ST_AreaNPCOnly: + { + if (spells[spell_id].range > 0) + { + if(!spell_target) + return false; + + ae_center = spell_target; + CastAction = AETarget; + } + else { + spell_target = nullptr; + ae_center = this; + CastAction = AECaster; + } + break; + } + case ST_UndeadAE: //should only affect undead... + case ST_SummonedAE: case ST_TargetAETap: case ST_AETarget: case ST_TargetAENoPlayersPets: @@ -1630,6 +1668,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce // Group spells case ST_GroupTeleport: case ST_Group: + case ST_GroupNoPets: { if(IsClient() && CastToClient()->TGB() && IsTGBCompatibleSpell(spell_id)) { if( (!target) || @@ -2043,15 +2082,28 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 } else { // regular PB AE or targeted AE spell - spell_target is null if PB if(spell_target) // this must be an AETarget spell - { + { + bool cast_on_target = true; + if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && spell_target->IsPetOwnerClient()) + cast_on_target = false; + if (spells[spell_id].targettype == ST_AreaClientOnly && !spell_target->IsClient()) + cast_on_target = false; + if (spells[spell_id].targettype == ST_AreaNPCOnly && !spell_target->IsNPC()) + cast_on_target = false; + // affect the target too - SpellOnTarget(spell_id, spell_target, false, true, resist_adjust); + if (cast_on_target) + SpellOnTarget(spell_id, spell_target, false, true, resist_adjust); } if(ae_center && ae_center == this && IsBeneficialSpell(spell_id)) SpellOnTarget(spell_id, this); bool affect_caster = !IsNPC(); //NPC AE spells do not affect the NPC caster - entity_list.AESpell(this, ae_center, spell_id, affect_caster, resist_adjust); + + if (spells[spell_id].targettype == ST_AETargetHateList) + hate_list.SpellCast(this, spell_id, spells[spell_id].aoerange, ae_center); + else + entity_list.AESpell(this, ae_center, spell_id, affect_caster, resist_adjust); } break; } @@ -2109,7 +2161,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 SpellOnTarget(spell_id, this); #ifdef GROUP_BUFF_PETS //pet too - if (GetPet() && HasPetAffinity() && !GetPet()->IsCharmed()) + if (spells[spell_id].targettype != ST_GroupNoPets && GetPet() && HasPetAffinity() && !GetPet()->IsCharmed()) SpellOnTarget(spell_id, GetPet()); #endif } @@ -2117,7 +2169,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 SpellOnTarget(spell_id, spell_target); #ifdef GROUP_BUFF_PETS //pet too - if (spell_target->GetPet() && HasPetAffinity() && !spell_target->GetPet()->IsCharmed()) + if (spells[spell_id].targettype != ST_GroupNoPets && spell_target->GetPet() && HasPetAffinity() && !spell_target->GetPet()->IsCharmed()) SpellOnTarget(spell_id, spell_target->GetPet()); #endif } From b32f59a40db33a6d8396dcdd474bca4a74ce7f8f Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 13 Nov 2014 06:00:01 -0500 Subject: [PATCH 4/5] Further support for spell field 'aemaxtargets' --- zone/effects.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 16024c169..7506f99cf 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -752,7 +752,11 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ bool bad = IsDetrimentalSpell(spell_id); bool isnpc = caster->IsNPC(); - const int MAX_TARGETS_ALLOWED = 4; + int MAX_TARGETS_ALLOWED = 4; + + if (spells[spell_id].aemaxtargets) + MAX_TARGETS_ALLOWED = spells[spell_id].aemaxtargets; + int iCounter = 0; for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { @@ -821,10 +825,13 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); } } else { - caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); + if (spells[spell_id].aemaxtargets && iCounter < spells[spell_id].aemaxtargets) + caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); + if (!spells[spell_id].aemaxtargets) + caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); } - if (!isnpc) //npcs are not target limited... + if (!isnpc || spells[spell_id].aemaxtargets) //npcs are not target limited (unless casting a spell with a target limit)... iCounter++; } } From 563a39c2d904a08942148ec9425cd6727acd2eb0 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 13 Nov 2014 21:45:19 -0500 Subject: [PATCH 5/5] Spell field defined that determines if must be sneaking to use this ability. This is handled by client under normal conditions, however if you force cast this effect in situations like 'procs' you can cause zone crashes, therefore a failsafe check is added in 'SpellOnTarget' --- changelog.txt | 2 +- common/shareddb.cpp | 1 + common/spdat.h | 2 +- zone/spells.cpp | 7 ++++++- zone/string_ids.h | 1 + 5 files changed, 10 insertions(+), 3 deletions(-) diff --git a/changelog.txt b/changelog.txt index d72b4ca23..52e445090 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,6 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- -== 11/13/2014 == +== 11/14/2014 == Kayen: Implemented target type (44) 'Beams' (which projects an AE infront of caster with a specified length and width). Kayen: Implemented target type (32) AE Target HateList Kayen: Implemented target type (36) Area Client Only diff --git a/common/shareddb.cpp b/common/shareddb.cpp index c679cd19c..1f30a3481 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1564,6 +1564,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].NimbusEffect = atoi(row[193]); sp[tempid].directional_start = static_cast(atoi(row[194])); sp[tempid].directional_end = static_cast(atoi(row[195])); + sp[tempid].sneak = atoi(row[196]) != 0; sp[tempid].not_extendable = atoi(row[197]) != 0; sp[tempid].suspendable = atoi(row[200]) != 0; sp[tempid].viral_range = atoi(row[201]); diff --git a/common/spdat.h b/common/spdat.h index da2e4486c..410269813 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -722,7 +722,7 @@ struct SPDat_Spell_Struct /* 193 */ int NimbusEffect; /* 194 */ float directional_start; //Cone Start Angle: /* 195 */ float directional_end; // Cone End Angle: -/* 196 */ +/* 196 */ bool sneak; // effect can only be used if sneaking (rogue 'Daggerfall' ect) /* 197 */ bool not_extendable; /* 198- 199 */ /* 200 */ bool suspendable; // buff is suspended in suspended buff zones diff --git a/zone/spells.cpp b/zone/spells.cpp index 5b8bfef39..6c6119831 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3262,6 +3262,11 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r if(spelltar->IsClient() && spelltar->CastToClient()->IsHoveringForRespawn()) return false; + if (spells[spell_id].sneak && IsClient() && !CastToClient()->sneaking){ + Message_StringID(13, SNEAK_RESTRICT); + return false;//Fail Safe, this can cause a zone crash certain situations if you try to apply sneak effects when not sneaking. + } + if(IsDetrimentalSpell(spell_id) && !IsAttackAllowed(spelltar) && !IsResurrectionEffects(spell_id)) { if(!IsClient() || !CastToClient()->GetGM()) { Message_StringID(MT_SpellFailure, SPELL_NO_HOLD); @@ -4711,7 +4716,7 @@ void NPC::Stun(int duration) { void NPC::UnStun() { Mob::UnStun(); - SetRunAnimSpeed(this->GetRunspeed()); + SetRunAnimSpeed(static_cast(GetRunspeed())); SendPosition(); } diff --git a/zone/string_ids.h b/zone/string_ids.h index 41e0960b0..a12b8640e 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -338,6 +338,7 @@ #define STRIKETHROUGH_STRING 9078 //You strike through your opponent's defenses! #define SPELL_REFLECT 9082 //%1's spell has been reflected by %2. #define NEW_SPELLS_AVAIL 9149 //You have new spells available to you. Check the merchants near your guild master. +#define SNEAK_RESTRICT 9240 //You can not use this ability because you have not been hidden for long enough. #define PET_NOW_FOCUSING 9254 //Focusing on one target, Master. #define PET_NOT_FOCUSING 9263 //No longer focusing on one target, Master. #define PET_NOT_CASTING 9264 //Not casting spells, Master.