From b65d3c85b668f85370a3db625b8a6a852ecc5f6e Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 2 Aug 2014 10:42:11 -0400 Subject: [PATCH 01/10] Implemented support for spells_new fields InCombat, OutofCombat Required SQL to rename and add new fields to spells_new table. --- changelog.txt | 9 +++++++++ common/shareddb.cpp | 12 ++++++++++-- common/spdat.h | 27 ++++++++++++++++---------- zone/StringIDs.h | 4 ++++ zone/spells.cpp | 46 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 86 insertions(+), 12 deletions(-) diff --git a/changelog.txt b/changelog.txt index 464669fd5..2847b92e6 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,14 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 08/02/2014 == +Kayen: Implemented spell_news fields +- npc_no_los (check if LOS is required for spells) +- InCombat, OutofCombat - Used together to restrict spells to only be cast while +in/out of combat (beneficial) or if target is in/out of combat (detrimental). +Many new fields added and defined for future development. + +Required SQL: utils/sql/git/required/2014_08_02_spells_new.sql + == 07/31/2014 == Uleat: More inventory slot constant conversions. This should be the bulk of everything..but, due to the size of the server code, there may be some hidden ones. (client_packet.cpp and the client translators still need a thorough review.) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index b93b57710..585576f17 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1726,6 +1726,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].EndurCost=atoi(row[166]); sp[tempid].EndurTimerIndex=atoi(row[167]); + sp[tempid].IsDisciplineBuff = atoi(row[168]) != 0; sp[tempid].HateAdded=atoi(row[173]); sp[tempid].EndurUpkeep=atoi(row[174]); sp[tempid].numhitstype = atoi(row[175]); @@ -1745,15 +1746,22 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].directional_end = (float)atoi(row[195]); sp[tempid].not_extendable = atoi(row[197]) != 0; sp[tempid].suspendable = atoi(row[200]) != 0; + sp[tempid].viral_range = atoi(row[201]); sp[tempid].spellgroup=atoi(row[207]); + sp[tempid].rank = atoi(row[208]); sp[tempid].powerful_flag=atoi(row[209]); sp[tempid].CastRestriction = atoi(row[211]); sp[tempid].AllowRest = atoi(row[212]) != 0; - sp[tempid].NotOutofCombat = atoi(row[213]) != 0; - sp[tempid].NotInCombat = atoi(row[214]) != 0; + sp[tempid].InCombat = atoi(row[213]) != 0; + sp[tempid].OutofCombat = atoi(row[214]) != 0; sp[tempid].aemaxtargets = atoi(row[218]); sp[tempid].maxtargets = atoi(row[219]); sp[tempid].persistdeath = atoi(row[224]) != 0; + sp[tempid].min_dist = atof(row[227]); + sp[tempid].min_dist_mod = atof(row[228]); + sp[tempid].max_dist = atof(row[229]); + sp[tempid].max_dist_mod = atof(row[230]); + sp[tempid].min_range = atoi(row[231]); sp[tempid].DamageShieldType = 0; } mysql_free_result(result); diff --git a/common/spdat.h b/common/spdat.h index 927550fc6..510817121 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -697,8 +697,8 @@ struct SPDat_Spell_Struct /* 164 */ // for most spells this appears to mimic ResistDiff /* 166 */ int EndurCost; /* 167 */ int8 EndurTimerIndex; -/* 168 */ //int IsDisciplineBuff; //Will goto the combat window when cast -/* 169 */ +/* 168 */ bool IsDisciplineBuff; //Will goto the combat window when cast +/* 169 - 172*/ //These are zero for ALL spells /* 173 */ int HateAdded; /* 174 */ int EndurUpkeep; /* 175 */ int numhitstype; // defines which type of behavior will tick down the numhit counter. @@ -721,23 +721,30 @@ struct SPDat_Spell_Struct /* 197 */ bool not_extendable; /* 198- 199 */ /* 200 */ bool suspendable; // buff is suspended in suspended buff zones -/* 201 - 202 */ +/* 201 */ int viral_range; +/* 202 */ /* 203 */ //int songcap; // individual song cap (how live currently does it, not implemented) /* 204 - 206 */ /* 207 */ int spellgroup; -/* 208 */ // int rank - increments AA effects with same name +/* 208 */ int rank; //increments AA effects with same name /* 209 */ int powerful_flag; // Need more investigation to figure out what to call this, for now we know -1 makes charm spells not break before their duration is complete, it does alot more though /* 210 */ // bool DurationFrozen; ??? /* 211 */ int CastRestriction; //Various restriction categories for spells most seem targetable race related but have also seen others for instance only castable if target hp 20% or lower or only if target out of combat /* 212 */ bool AllowRest; -/* 213 */ bool NotOutofCombat; //Fail if cast out of combat -/* 214 */ bool NotInCombat; //Fail if cast in combat +/* 213 */ bool InCombat; //Allow spell if target is in combat +/* 214 */ bool OutofCombat; //Allow spell if target is out of combat /* 215 - 217 */ -/* 219 */ int aemaxtargets; // -/* 219 */ int maxtargets; // is used for beam and ring spells for target # limits (not implemented) -/* 220 - 223 */ +/* 218 */ int aemaxtargets; //Is used for various AE effects +/* 219 */ int maxtargets; //Is used for beam and ring spells for target # limits (not implemented) +/* 220 - 223 */ /* 224 */ bool persistdeath; // buff doesn't get stripped on death -/* 225 - 236 */ // Not in DB +/* 225 - 226 */ +/* 227 */ float min_dist; //spell power modified by distance from caster (Min Distance) +/* 228 */ float min_dist_mod; //spell power modified by distance from caster (Modifier at Min Distance) +/* 229 */ float max_dist; //spell power modified by distance from caster (Max Distance) +/* 230 */ float max_dist_mod; //spell power modified by distance from caster (Modifier at Max Distance) +/* 231 */ int min_range; //Min casting range +/* 232 - 236 */ uint8 DamageShieldType; // This field does not exist in spells_us.txt }; diff --git a/zone/StringIDs.h b/zone/StringIDs.h index 9a222d276..5e5d16bb2 100644 --- a/zone/StringIDs.h +++ b/zone/StringIDs.h @@ -316,6 +316,10 @@ #define PET_NOT_FOCUSING 9263 //No longer focusing on one target, Master. #define PET_NOT_CASTING 9264 //Not casting spells, Master. #define PET_CASTING 9291 //Casting spells normally, Master. +#define NO_CAST_IN_COMBAT 9190 //You can not cast this spell while in combat. +#define NO_CAST_OUT_OF_COMBAT 9191 //You can not cast this spell while out of combat. +#define NO_ABILITY_IN_COMBAT 9192 //You can not use this ability while in combat. +#define NO_ABILITY_OUT_OF_COMBAT 9194 //You can not use this ability while out of combat. #define AE_RAMPAGE 11015 //%1 goes on a WILD RAMPAGE! #define FACE_ACCEPTED 12028 //Facial features accepted. #define SPELL_LEVEL_TO_LOW 12048 //You will have to achieve level %1 before you can scribe the %2. diff --git a/zone/spells.cpp b/zone/spells.cpp index 305156b63..6757a7ba6 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1384,6 +1384,52 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce return false; } + //Must be out of combat. (If Beneficial checks casters combat state, Deterimental checks targets) + if (!spells[spell_id].InCombat && spells[spell_id].OutofCombat){ + if (IsDetrimentalSpell(spell_id)) { + if ( (spell_target->IsNPC() && spell_target->IsEngaged()) || + (spell_target->IsClient() && spell_target->CastToClient()->GetAggroCount())){ + Message_StringID(13,SPELL_NO_EFFECT); //Unsure correct string + return false; + } + } + + else if (IsBeneficialSpell(spell_id)) { + if ( (IsNPC() && IsEngaged()) || + (IsClient() && CastToClient()->GetAggroCount())){ + if (IsDiscipline(spell_id)) + Message_StringID(13,NO_ABILITY_IN_COMBAT); + else + Message_StringID(13,NO_CAST_IN_COMBAT); + + return false; + } + } + } + + //Must be in combat. (If Beneficial checks casters combat state, Deterimental checks targets) + else if (spells[spell_id].InCombat && !spells[spell_id].OutofCombat){ + if (IsDetrimentalSpell(spell_id)) { + if ( (spell_target->IsNPC() && !spell_target->IsEngaged()) || + (spell_target->IsClient() && !spell_target->CastToClient()->GetAggroCount())){ + Message_StringID(13,SPELL_NO_EFFECT); //Unsure correct string + return false; + } + } + + else if (IsBeneficialSpell(spell_id)) { + if ( (IsNPC() && !IsEngaged()) || + (IsClient() && !CastToClient()->GetAggroCount())){ + if (IsDiscipline(spell_id)) + Message_StringID(13,NO_ABILITY_OUT_OF_COMBAT); + else + Message_StringID(13,NO_CAST_OUT_OF_COMBAT); + + return false; + } + } + } + switch (targetType) { // single target spells From 9c92c5dbe4af5a57a7b4af82c8768bcf2a688a5b Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 2 Aug 2014 10:43:48 -0400 Subject: [PATCH 02/10] SQL --- .../sql/git/required/2014_08_02_spells_new.sql | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 utils/sql/git/required/2014_08_02_spells_new.sql diff --git a/utils/sql/git/required/2014_08_02_spells_new.sql b/utils/sql/git/required/2014_08_02_spells_new.sql new file mode 100644 index 000000000..f4e74158a --- /dev/null +++ b/utils/sql/git/required/2014_08_02_spells_new.sql @@ -0,0 +1,18 @@ +-- spells new talbe update +ALTER TABLE `spells_new` CHANGE `NotOutofCombat` `InCombat` INT(11) NOT NULL DEFAULT '0'; +ALTER TABLE `spells_new` CHANGE `NotInCombat` `OutofCombat` INT(11) NOT NULL DEFAULT '0'; +ALTER TABLE `spells_new` CHANGE `field201` `viral_range` INT(11) NOT NULL DEFAULT '0'; +ALTER TABLE `spells_new` CHANGE `field218` `aemaxtargets` INT(11) NOT NULL DEFAULT '0'; +ALTER TABLE `spells_new` ADD `field225` int( 11 ) NOT NULL DEFAULT '0' AFTER `persistdeath`; +ALTER TABLE `spells_new` ADD `field226` int( 11 ) NOT NULL DEFAULT '0' AFTER `field225`; +ALTER TABLE `spells_new` ADD `min_dist` float( 0 ) NOT NULL DEFAULT '0' AFTER `field226`; +ALTER TABLE `spells_new` ADD `min_dist_mod` float( 0 ) NOT NULL DEFAULT '0' AFTER `min_dist`; +ALTER TABLE `spells_new` ADD `max_dist` float( 0 ) NOT NULL DEFAULT '0' AFTER `min_dist_mod`; +ALTER TABLE `spells_new` ADD `max_dist_mod` float( 0 ) NOT NULL DEFAULT '0' AFTER `max_dist`; +ALTER TABLE `spells_new` ADD `min_range` int( 11 ) NOT NULL DEFAULT '0' AFTER `max_dist_mod`; +ALTER TABLE `spells_new` ADD `field232` int( 11 ) NOT NULL DEFAULT '0' AFTER `min_range`; +ALTER TABLE `spells_new` ADD `field233` int( 11 ) NOT NULL DEFAULT '0' AFTER `field232`; +ALTER TABLE `spells_new` ADD `field234` int( 11 ) NOT NULL DEFAULT '0' AFTER `field233`; +ALTER TABLE `spells_new` ADD `field235` int( 11 ) NOT NULL DEFAULT '0' AFTER `field234`; +ALTER TABLE `spells_new` ADD `field236` int( 11 ) NOT NULL DEFAULT '0' AFTER `field235`; + From 52d92b71814e6e364e0d5ada187c71f53238ab9c Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 2 Aug 2014 21:10:44 -0400 Subject: [PATCH 03/10] Implemented broad support for fields min_dist, min_dist_mod, max_dist, max_dist_mod - Scales spell power based on targets distance from caster. This implemented in a broad way to function with spells that would make sense to scale. Some work will still be needed on this. Be aware if making custom spells not everything will work and certain effects just should not be included (use common sense). --- changelog.txt | 4 +++- common/spdat.cpp | 9 +++++++++ common/spdat.h | 1 + zone/effects.cpp | 7 ++++++- zone/groups.cpp | 1 + zone/hate_list.cpp | 6 +++++- zone/mob.cpp | 1 + zone/mob.h | 4 ++++ zone/spell_effects.cpp | 31 ++++++++++++++++++++++++++++++- zone/spells.cpp | 10 ++++++++-- 10 files changed, 68 insertions(+), 6 deletions(-) diff --git a/changelog.txt b/changelog.txt index 2847b92e6..85fa4ecb5 100644 --- a/changelog.txt +++ b/changelog.txt @@ -5,7 +5,9 @@ Kayen: Implemented spell_news fields - npc_no_los (check if LOS is required for spells) - InCombat, OutofCombat - Used together to restrict spells to only be cast while in/out of combat (beneficial) or if target is in/out of combat (detrimental). -Many new fields added and defined for future development. +-min_dist, min_dist_mod, max_dist, max_dist_mod - Scales spell power based on targets distance from caster. +*This will require further work to fully implement but will work with 90% of live spells as is. +*If making custom spells do not include effects that can't be scaled (like a spell trigger) Required SQL: utils/sql/git/required/2014_08_02_spells_new.sql diff --git a/common/spdat.cpp b/common/spdat.cpp index 75f1e9b88..8e93f4e1f 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1040,6 +1040,15 @@ bool IsCastonFadeDurationSpell(uint16 spell_id) return false; } +bool IsPowerDistModSpell(uint16 spell_id) +{ + if (IsValidSpell(spell_id) && + (spells[spell_id].max_dist_mod || spells[spell_id].min_dist_mod) && spells[spell_id].max_dist > spells[spell_id].min_dist) + return true; + + return false; +} + uint32 GetPartialMeleeRuneReduction(uint32 spell_id) { for (int i = 0; i < EFFECT_COUNT; ++i) diff --git a/common/spdat.h b/common/spdat.h index 510817121..ec068585c 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -845,6 +845,7 @@ bool IsPersistDeathSpell(uint16 spell_id); bool IsSuspendableSpell(uint16 spell_id); uint32 GetMorphTrigger(uint32 spell_id); bool IsCastonFadeDurationSpell(uint16 spell_id); +bool IsPowerDistModSpell(uint16 spell_id); uint32 GetPartialMeleeRuneReduction(uint32 spell_id); uint32 GetPartialMagicRuneReduction(uint32 spell_id); uint32 GetPartialMeleeRuneAmount(uint32 spell_id); diff --git a/zone/effects.cpp b/zone/effects.cpp index c298974db..27cccd0a9 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -740,6 +740,7 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ float dist = caster->GetAOERange(spell_id); float dist2 = dist * dist; + float dist_targ = 0; bool bad = IsDetrimentalSpell(spell_id); bool isnpc = caster->IsNPC(); @@ -755,7 +756,9 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ continue; if (curmob == caster && !affect_caster) //watch for caster too continue; - if (center->DistNoRoot(*curmob) > dist2) //make sure they are in range + + dist_targ = center->DistNoRoot(*curmob); + if (dist_targ > dist2) //make sure they are in range continue; if (isnpc && curmob->IsNPC()) { //check npc->npc casting FACTION_VALUE f = curmob->GetReverseFactionCon(caster); @@ -786,6 +789,8 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ continue; } + curmob->CalcSpellPowerDistanceMod(spell_id, dist_targ); + //if we get here... cast the spell. if (IsTargetableAESpell(spell_id) && bad) { if (iCounter < MAX_TARGETS_ALLOWED) { diff --git a/zone/groups.cpp b/zone/groups.cpp index 6aee5f38b..c9f6e9127 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -674,6 +674,7 @@ void Group::CastGroupSpell(Mob* caster, uint16 spell_id) { { distance = caster->DistNoRoot(*members[z]); if(distance <= range2) { + 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()) diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index bb39d224a..f3de52544 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -567,20 +567,24 @@ void HateList::SpellCast(Mob *caster, uint32 spell_id, float range) //So keep a list of entity ids and look up after std::list id_list; range = range * range; + float dist_targ = 0; auto iterator = list.begin(); while (iterator != list.end()) { tHateEntry *h = (*iterator); if(range > 0) { - if(caster->DistNoRoot(*h->ent) <= range) + dist_targ = caster->DistNoRoot(*h->ent); + if(dist_targ <= range) { id_list.push_back(h->ent->GetID()); + h->ent->CalcSpellPowerDistanceMod(spell_id, dist_targ); } } else { id_list.push_back(h->ent->GetID()); + h->ent->CalcSpellPowerDistanceMod(spell_id, 0, caster); } ++iterator; } diff --git a/zone/mob.cpp b/zone/mob.cpp index a55390050..24b691e36 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -182,6 +182,7 @@ Mob::Mob(const char* in_name, has_numhits = false; has_MGB = false; has_ProjectIllusion = false; + SpellPowerDistanceMod = 0; if(in_aa_title>0) aa_title = in_aa_title; diff --git a/zone/mob.h b/zone/mob.h index 1793e0864..15f1728ec 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -615,6 +615,9 @@ public: bool ImprovedTaunt(); bool TryRootFadeByDamage(int buffslot, Mob* attacker); int16 GetSlowMitigation() const {return slow_mitigation;} + void CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster = nullptr); + inline int16 GetSpellPowerDistanceMod() const { return SpellPowerDistanceMod; }; + inline void SetSpellPowerDistanceMod(int16 value) { SpellPowerDistanceMod = value; }; void ModSkillDmgTaken(SkillUseTypes skill_num, int value); int16 GetModSkillDmgTaken(const SkillUseTypes skill_num); @@ -1114,6 +1117,7 @@ protected: bool has_numhits; bool has_MGB; bool has_ProjectIllusion; + int16 SpellPowerDistanceMod; // Bind wound Timer bindwound_timer; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 0ee88a154..864bf004c 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -186,6 +186,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) buffs[buffslot].numhits = numhit; } + bool _IsPowerDistModSpell = false; + if (IsPowerDistModSpell(spell_id)) + _IsPowerDistModSpell = true; + else + SetSpellPowerDistanceMod(0); + // iterate through the effects in the spell for (i = 0; i < EFFECT_COUNT; i++) { @@ -198,6 +204,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if(spell_id == SPELL_LAY_ON_HANDS && caster && caster->GetAA(aaImprovedLayOnHands)) effect_value = GetMaxHP(); + if (_IsPowerDistModSpell) + effect_value += (effect_value*(GetSpellPowerDistanceMod()/100)/100); + #ifdef SPELL_EFFECT_SPAM effect_desc[0] = 0; #endif @@ -6444,4 +6453,24 @@ bool Mob::CheckSpellCategory(uint16 spell_id, int category_id, int effect_id){ return false; } - \ No newline at end of file + +void Mob::CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster) +{ + if (IsPowerDistModSpell(spell_id)){ + + float distance = 0; + + if (caster && !range) + distance = caster->CalculateDistance(GetX(), GetY(), GetZ()); + else + distance = sqrt(range); + + float dm_range = spells[spell_id].max_dist - spells[spell_id].min_dist; + float dm_mod_interval = spells[spell_id].max_dist_mod - spells[spell_id].min_dist_mod; + float dist_from_min = distance - spells[spell_id].min_dist; + float mod = spells[spell_id].min_dist_mod + (dist_from_min * (dm_mod_interval/dm_range)); + mod *= 100.0f; + + SetSpellPowerDistanceMod(static_cast(mod)); + } +} \ No newline at end of file diff --git a/zone/spells.cpp b/zone/spells.cpp index 6757a7ba6..f5bd8f09d 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1915,6 +1915,8 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 Message_StringID(13, TARGET_OUT_OF_RANGE); return(false); } + + spell_target->CalcSpellPowerDistanceMod(spell_id, dist2); } // @@ -2114,16 +2116,20 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 if((heading_to_target >= angle_start && heading_to_target <= 360.0f) || (heading_to_target >= 0.0f && heading_to_target <= angle_end)) { - if(CheckLosFN(spell_target)) + if(CheckLosFN((*iter)) || spells[spell_id].npc_no_los){ + (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); SpellOnTarget(spell_id, spell_target, false, true, resist_adjust); + } } } else { if(heading_to_target >= angle_start && heading_to_target <= angle_end) { - if(CheckLosFN((*iter))) + if(CheckLosFN((*iter)) || spells[spell_id].npc_no_los){ + (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); SpellOnTarget(spell_id, (*iter), false, true, resist_adjust); + } } } ++iter; From ef982b9ce236d83ebe5a5e576e2eadbd9ddb3d85 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 3 Aug 2014 08:21:47 -0400 Subject: [PATCH 04/10] Implemented 'min_range' field, sets a min range that you must be away from target for spell to land. --- changelog.txt | 1 + common/shareddb.cpp | 6 +++--- common/spdat.h | 2 +- zone/StringIDs.h | 1 + 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/changelog.txt b/changelog.txt index 85fa4ecb5..16d63a05e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -8,6 +8,7 @@ in/out of combat (beneficial) or if target is in/out of combat (detrimental). -min_dist, min_dist_mod, max_dist, max_dist_mod - Scales spell power based on targets distance from caster. *This will require further work to fully implement but will work with 90% of live spells as is. *If making custom spells do not include effects that can't be scaled (like a spell trigger) +- min_rage sets minimum distance range that must be away from target. Required SQL: utils/sql/git/required/2014_08_02_spells_new.sql diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 585576f17..a64fef404 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1742,8 +1742,8 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].viral_targets = atoi(row[191]); sp[tempid].viral_timer = atoi(row[192]); sp[tempid].NimbusEffect = atoi(row[193]); - sp[tempid].directional_start = (float)atoi(row[194]); - sp[tempid].directional_end = (float)atoi(row[195]); + sp[tempid].directional_start = static_cast(atoi(row[194])); + sp[tempid].directional_end = static_cast(atoi(row[195])); sp[tempid].not_extendable = atoi(row[197]) != 0; sp[tempid].suspendable = atoi(row[200]) != 0; sp[tempid].viral_range = atoi(row[201]); @@ -1761,7 +1761,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].min_dist_mod = atof(row[228]); sp[tempid].max_dist = atof(row[229]); sp[tempid].max_dist_mod = atof(row[230]); - sp[tempid].min_range = atoi(row[231]); + sp[tempid].min_range = static_cast(atoi(row[231])); sp[tempid].DamageShieldType = 0; } mysql_free_result(result); diff --git a/common/spdat.h b/common/spdat.h index ec068585c..aaf29e61a 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -743,7 +743,7 @@ struct SPDat_Spell_Struct /* 228 */ float min_dist_mod; //spell power modified by distance from caster (Modifier at Min Distance) /* 229 */ float max_dist; //spell power modified by distance from caster (Max Distance) /* 230 */ float max_dist_mod; //spell power modified by distance from caster (Modifier at Max Distance) -/* 231 */ int min_range; //Min casting range +/* 231 */ float min_range; //Min casting range /* 232 - 236 */ uint8 DamageShieldType; // This field does not exist in spells_us.txt }; diff --git a/zone/StringIDs.h b/zone/StringIDs.h index 5e5d16bb2..b1e13edca 100644 --- a/zone/StringIDs.h +++ b/zone/StringIDs.h @@ -249,6 +249,7 @@ #define CORPSEDRAG_BEGIN 4064 //You begin to drag %1. #define CORPSEDRAG_STOPALL 4065 //You stop dragging the corpses. #define CORPSEDRAG_STOP 4066 //You stop dragging the corpse. +#define TARGET_TOO_CLOSE 4602 //You are too close to your target. Get farther away. #define WHOALL_NO_RESULTS 5029 //There are no players in EverQuest that match those who filters. #define PETITION_NO_DELETE 5053 //You do not have a petition in the queue. #define PETITION_DELETED 5054 //Your petition was successfully deleted. From f3710856ad59e6e3c456a5d4429119b9f1f64fad Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 3 Aug 2014 16:04:55 -0400 Subject: [PATCH 05/10] min_range field --- zone/effects.cpp | 3 +++ zone/entity.cpp | 10 +++++----- zone/entity.h | 2 +- zone/groups.cpp | 3 ++- zone/hate_list.cpp | 3 ++- zone/spell_effects.cpp | 4 ++-- zone/spells.cpp | 9 ++++++++- 7 files changed, 23 insertions(+), 11 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 27cccd0a9..1b1eeb2ea 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -740,6 +740,7 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ float dist = caster->GetAOERange(spell_id); float dist2 = dist * dist; + float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; float dist_targ = 0; bool bad = IsDetrimentalSpell(spell_id); @@ -760,6 +761,8 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ dist_targ = center->DistNoRoot(*curmob); if (dist_targ > dist2) //make sure they are in range continue; + if (dist_targ < min_range2) //make sure they are in range + continue; if (isnpc && curmob->IsNPC()) { //check npc->npc casting FACTION_VALUE f = curmob->GetReverseFactionCon(caster); if (bad) { diff --git a/zone/entity.cpp b/zone/entity.cpp index 3d316585d..5ff19847f 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -4538,7 +4538,7 @@ Mob *EntityList::GetClosestMobByBodyType(Mob *sender, bodyType BodyType) return ClosestMob; } -void EntityList::GetTargetsForConeArea(Mob *start, uint32 radius, uint32 height, std::list &m_list) +void EntityList::GetTargetsForConeArea(Mob *start, float min_radius, float radius, float height, std::list &m_list) { auto it = mob_list.begin(); while (it != mob_list.end()) { @@ -4547,15 +4547,15 @@ void EntityList::GetTargetsForConeArea(Mob *start, uint32 radius, uint32 height, ++it; continue; } - int32 x_diff = ptr->GetX() - start->GetX(); - int32 y_diff = ptr->GetY() - start->GetY(); - int32 z_diff = ptr->GetZ() - start->GetZ(); + float x_diff = ptr->GetX() - start->GetX(); + float y_diff = ptr->GetY() - start->GetY(); + float z_diff = ptr->GetZ() - start->GetZ(); x_diff *= x_diff; y_diff *= y_diff; z_diff *= z_diff; - if ((x_diff + y_diff) <= (radius * radius)) + if ((x_diff + y_diff) <= (radius * radius) && (x_diff + y_diff) >= (min_radius * min_radius)) if(z_diff <= (height * height)) m_list.push_back(ptr); diff --git a/zone/entity.h b/zone/entity.h index 8b94b0e0a..c1ba0936c 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -406,7 +406,7 @@ public: void GetObjectList(std::list &o_list); void GetDoorsList(std::list &d_list); void GetSpawnList(std::list &d_list); - void GetTargetsForConeArea(Mob *start, uint32 radius, uint32 height, std::list &m_list); + void GetTargetsForConeArea(Mob *start, float min_radius, float radius, float height, std::list &m_list); void DepopAll(int NPCTypeID, bool StartSpawnTimer = true); diff --git a/zone/groups.cpp b/zone/groups.cpp index c9f6e9127..d948eb476 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -658,6 +658,7 @@ void Group::CastGroupSpell(Mob* caster, uint16 spell_id) { range = caster->GetAOERange(spell_id); float range2 = range*range; + float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; // caster->SpellOnTarget(spell_id, caster); @@ -673,7 +674,7 @@ void Group::CastGroupSpell(Mob* caster, uint16 spell_id) { else if(members[z] != nullptr) { distance = caster->DistNoRoot(*members[z]); - if(distance <= range2) { + if(distance <= range2 && distance >= min_range2) { members[z]->CalcSpellPowerDistanceMod(spell_id, distance); caster->SpellOnTarget(spell_id, members[z]); #ifdef GROUP_BUFF_PETS diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index f3de52544..1b585ca32 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -567,6 +567,7 @@ void HateList::SpellCast(Mob *caster, uint32 spell_id, float range) //So keep a list of entity ids and look up after std::list id_list; range = range * range; + float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; float dist_targ = 0; auto iterator = list.begin(); while (iterator != list.end()) @@ -575,7 +576,7 @@ void HateList::SpellCast(Mob *caster, uint32 spell_id, float range) if(range > 0) { dist_targ = caster->DistNoRoot(*h->ent); - if(dist_targ <= range) + if(dist_targ <= range && dist_targ >= min_range2) { id_list.push_back(h->ent->GetID()); h->ent->CalcSpellPowerDistanceMod(spell_id, dist_targ); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 864bf004c..2312614bc 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -205,7 +205,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) effect_value = GetMaxHP(); if (_IsPowerDistModSpell) - effect_value += (effect_value*(GetSpellPowerDistanceMod()/100)/100); + effect_value = effect_value*(GetSpellPowerDistanceMod()/100); #ifdef SPELL_EFFECT_SPAM effect_desc[0] = 0; @@ -6471,6 +6471,6 @@ void Mob::CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster) float mod = spells[spell_id].min_dist_mod + (dist_from_min * (dm_mod_interval/dm_range)); mod *= 100.0f; - SetSpellPowerDistanceMod(static_cast(mod)); + SetSpellPSetSpellPowerDistanceMod(static_cast(mod)); } } \ No newline at end of file diff --git a/zone/spells.cpp b/zone/spells.cpp index f5bd8f09d..ca6d80f6f 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1909,12 +1909,19 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 //casting a spell on somebody but ourself, make sure they are in range float dist2 = DistNoRoot(*spell_target); float range2 = range * range; + float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; if(dist2 > range2) { //target is out of range. mlog(SPELLS__CASTING, "Spell %d: Spell target is out of range (squared: %f > %f)", spell_id, dist2, range2); Message_StringID(13, TARGET_OUT_OF_RANGE); return(false); } + else if (dist2 < min_range2){ + //target is too close range. + mlog(SPELLS__CASTING, "Spell %d: Spell target is too close (squared: %f < %f)", spell_id, dist2, min_range2); + Message_StringID(13, TARGET_TOO_CLOSE); + return(false); + } spell_target->CalcSpellPowerDistanceMod(spell_id, dist2); } @@ -2100,7 +2107,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 std::list targets_in_range; std::list::iterator iter; - entity_list.GetTargetsForConeArea(this, spells[spell_id].aoerange, spells[spell_id].aoerange / 2, targets_in_range); + 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()) { From 4b7fa0654c00b0fd4d30d182ca752db82ca20908 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 3 Aug 2014 16:32:36 -0400 Subject: [PATCH 06/10] fix --- zone/spell_effects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 2312614bc..dc4de2409 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -204,7 +204,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if(spell_id == SPELL_LAY_ON_HANDS && caster && caster->GetAA(aaImprovedLayOnHands)) effect_value = GetMaxHP(); - if (_IsPowerDistModSpell) + if (_IsPowerDistModSpell && GetSpellPowerDistanceMod()) effect_value = effect_value*(GetSpellPowerDistanceMod()/100); #ifdef SPELL_EFFECT_SPAM From 92a1abbbee67ce6507c27be147edbcd821db3bdc Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 3 Aug 2014 16:36:44 -0400 Subject: [PATCH 07/10] fix --- zone/spell_effects.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index dc4de2409..078827b78 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -186,10 +186,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) buffs[buffslot].numhits = numhit; } - bool _IsPowerDistModSpell = false; - if (IsPowerDistModSpell(spell_id)) - _IsPowerDistModSpell = true; - else + if (!IsPowerDistModSpell(spell_id)) SetSpellPowerDistanceMod(0); // iterate through the effects in the spell @@ -204,7 +201,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if(spell_id == SPELL_LAY_ON_HANDS && caster && caster->GetAA(aaImprovedLayOnHands)) effect_value = GetMaxHP(); - if (_IsPowerDistModSpell && GetSpellPowerDistanceMod()) + if (GetSpellPowerDistanceMod()) effect_value = effect_value*(GetSpellPowerDistanceMod()/100); #ifdef SPELL_EFFECT_SPAM From aab3cac29c493ee7f831f89a7da27a0219fc0011 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 6 Aug 2014 06:49:29 -0400 Subject: [PATCH 08/10] fix --- zone/spell_effects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 078827b78..9f6a3866b 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -6468,6 +6468,6 @@ void Mob::CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster) float mod = spells[spell_id].min_dist_mod + (dist_from_min * (dm_mod_interval/dm_range)); mod *= 100.0f; - SetSpellPSetSpellPowerDistanceMod(static_cast(mod)); + SetSpellPowerDistanceMod(static_cast(mod)); } } \ No newline at end of file From 2aec190afc0dd714f5120727d4f82de1ca6baa8f Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 8 Aug 2014 13:42:14 -0400 Subject: [PATCH 09/10] Support for spells_new field 'uninterruptable' --- common/shareddb.cpp | 2 +- zone/spell_effects.cpp | 2 +- zone/spells.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index a64fef404..7c7a99efa 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1710,7 +1710,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { for (y = 0; y < 16; y++) sp[tempid].deities[y]=atoi(row[126+y]); - sp[tempid].uninterruptable=atoi(row[146]); + sp[tempid].uninterruptable=atoi(row[146]) != 0; sp[tempid].ResistDiff=atoi(row[147]); sp[tempid].dot_stacking_exempt=atoi(row[148]); sp[tempid].RecourseLink = atoi(row[150]); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 9f6a3866b..8a45cf391 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2711,7 +2711,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if (buffslot >= 0) break; - if(IsCasting() && MakeRandomInt(0, 100) <= spells[spell_id].base[i]) + if(!spells[spell_id].uninterruptable && IsCasting() && MakeRandomInt(0, 100) <= spells[spell_id].base[i]) InterruptSpell(); break; diff --git a/zone/spells.cpp b/zone/spells.cpp index ca6d80f6f..e9d3eb55a 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1034,7 +1034,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot, mlog(SPELLS__CASTING, "Checking Interruption: spell x: %f spell y: %f cur x: %f cur y: %f channelchance %f channeling skill %d\n", GetSpellX(), GetSpellY(), GetX(), GetY(), channelchance, GetSkill(SkillChanneling)); - if(MakeRandomFloat(0, 100) > channelchance) { + if(!spells[spell_id].uninterruptable && MakeRandomFloat(0, 100) > channelchance) { mlog(SPELLS__CASTING_ERR, "Casting of %d canceled: interrupted.", spell_id); InterruptSpell(); return; @@ -4684,7 +4684,7 @@ void Mob::Stun(int duration) if(stunned && stunned_timer.GetRemainingTime() > uint32(duration)) return; - if(casting_spell_id) { + if(IsValidSpell(casting_spell_id) && !spells[casting_spell_id].uninterruptable) { int persistent_casting = spellbonuses.PersistantCasting + itembonuses.PersistantCasting; if(IsClient()) persistent_casting += aabonuses.PersistantCasting; From b7be8fb6257500dd3f5c5ee42ae1a71bca33d2e2 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 8 Aug 2014 15:53:53 -0400 Subject: [PATCH 10/10] fix --- zone/spells.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index e9d3eb55a..b7b5dd169 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4685,11 +4685,9 @@ void Mob::Stun(int duration) return; if(IsValidSpell(casting_spell_id) && !spells[casting_spell_id].uninterruptable) { - int persistent_casting = spellbonuses.PersistantCasting + itembonuses.PersistantCasting; - if(IsClient()) - persistent_casting += aabonuses.PersistantCasting; + int persistent_casting = spellbonuses.PersistantCasting + itembonuses.PersistantCasting + aabonuses.PersistantCasting; - if(MakeRandomInt(1,99) > persistent_casting) + if(MakeRandomInt(0,99) > persistent_casting) InterruptSpell(); }