diff --git a/changelog.txt b/changelog.txt index d93a000ca..aac15127e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,12 @@ 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 Uleat: PlayerLogMerchantTransactions does not support partial stack purchase logging at this time == 11/12/2014 == 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 feaddaf84..410269813 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 @@ -131,10 +131,13 @@ 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 +/* 48 */ // UNKNOWN +/* 49 */ // NOT USED +/* 50 */ ST_TargetAENoPlayersPets = 0x32, } SpellTargetType; typedef enum { @@ -719,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/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/effects.cpp b/zone/effects.cpp index 02060c10b..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) { @@ -764,6 +768,12 @@ 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_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()); @@ -815,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++; } } 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/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/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 2e5e16f46..6c6119831 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); @@ -1610,9 +1611,48 @@ 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: { if(!spell_target) { @@ -1628,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) || @@ -1803,6 +1844,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; @@ -2033,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; } @@ -2099,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 } @@ -2107,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 } @@ -2126,54 +2188,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: { @@ -3239,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); @@ -4688,7 +4716,7 @@ void NPC::Stun(int duration) { void NPC::UnStun() { Mob::UnStun(); - SetRunAnimSpeed(this->GetRunspeed()); + SetRunAnimSpeed(static_cast(GetRunspeed())); SendPosition(); } @@ -5385,3 +5413,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 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.