diff --git a/changelog.txt b/changelog.txt index 08fc5dd27..e2805115e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,11 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +Kayen: Further refinements to root, charm, mez and fear behaviors - See commit message for full details + +New rule for 'Fear' break chance, and updates to default settings of various rules. +Optional SQL: utils/sql/git/optional/2014_03_19_RulesUpdates.sql + + == 03/18/2014 == Uleat: Fixed the name/account discrepancy in the Client::SummonItem() code as well as the origin of the mistake (thanks K_K!) Uleat: Condensed and rearranged certain snippets of code in SummonItem(). Added a 'augslotvisible' check to validation check. diff --git a/common/ruletypes.h b/common/ruletypes.h index 83db6c3f0..fd50d1514 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -290,17 +290,18 @@ RULE_BOOL ( Spells, NPCIgnoreBaseImmunity, true) // Whether or not NPCs get to i RULE_REAL ( Spells, AvgSpellProcsPerMinute, 6.0) //Adjust rate for sympathetic spell procs RULE_INT ( Spells, ResistFalloff, 67) //Max that level that will adjust our resist chance based on level modifiers RULE_INT ( Spells, CharismaEffectiveness, 10) // Deterimes how much resist modification charisma applies to charm/pacify checks. Default 10 CHA = -1 resist mod. -RULE_INT ( Spells, CharismaEffectivenessCap, 200) // Deterimes how much resist modification charisma applies to charm/pacify checks. Default 10 CHA = -1 resist mod. +RULE_INT ( Spells, CharismaEffectivenessCap, 255) // Deterimes how much resist modification charisma applies to charm/pacify checks. Default 10 CHA = -1 resist mod. RULE_BOOL ( Spells, CharismaCharmDuration, false) // Allow CHA resist mod to extend charm duration. RULE_INT ( Spells, CharmBreakCheckChance, 25) //Determines chance for a charm break check to occur each buff tick. RULE_INT ( Spells, MaxCastTimeReduction, 50) //Max percent your spell cast time can be reduced by spell haste -RULE_INT ( Spells, RootBreakFromSpells, 20) //Chance for root to break when cast on. +RULE_INT ( Spells, RootBreakFromSpells, 55) //Chance for root to break when cast on. RULE_INT ( Spells, DeathSaveCharismaMod, 3) //Determines how much charisma effects chance of death save firing. RULE_INT ( Spells, DivineInterventionHeal, 8000) //Divine intervention heal amount. RULE_BOOL ( Spells, AdditiveBonusValues, false) //Allow certain bonuses to be calculated by adding together the value from each item, instead of taking the highest value. (ie Add together all Cleave Effects) RULE_BOOL ( Spells, UseCHAScribeHack, false) //ScribeSpells and TrainDiscs quest functions will ignore entries where field 12 is CHA. What's the best way to do this? RULE_BOOL ( Spells, BuffLevelRestrictions, true) //Buffs will not land on low level toons like live -RULE_INT ( Spells, RootBreakCheckChance, 40) //Determines chance for a root break check to occur each buff tick. +RULE_INT ( Spells, RootBreakCheckChance, 70) //Determines chance for a root break check to occur each buff tick. +RULE_INT ( Spells, FearBreakCheckChance, 70) //Determines chance for a fear break check to occur each buff tick. RULE_CATEGORY_END() RULE_CATEGORY( Combat ) diff --git a/common/spdat.cpp b/common/spdat.cpp index 0f4387b83..c75acc056 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -390,7 +390,7 @@ bool IsPartialCapableSpell(uint16 spell_id) if (spells[spell_id].no_partial_resist) return false; - if (IsPureNukeSpell(spell_id) || IsFearSpell(spell_id)) + if (IsPureNukeSpell(spell_id)) return true; return false; diff --git a/utils/sql/git/optional/2014_03_19_RulesUpdates.sql b/utils/sql/git/optional/2014_03_19_RulesUpdates.sql new file mode 100644 index 000000000..d360528e6 --- /dev/null +++ b/utils/sql/git/optional/2014_03_19_RulesUpdates.sql @@ -0,0 +1,6 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:FearBreakCheckChance', '70', 'Chance for fear to do a resist check each tick. Decrease for longer fears.'); + +-- Updates rule value if server is using the OLD DEFAULT values +UPDATE rule_values SET rule_value = 55 WHERE rule_name LIKE 'Spells:RootBreakFromSpells' AND rule_value = 20; +UPDATE rule_values SET rule_value = 70 WHERE rule_name LIKE 'Spells:RootBreakCheckChance' AND rule_value = 40; +UPDATE rule_values SET rule_value = 255 WHERE rule_name LIKE 'Spells:CharismaResistCap' AND rule_value = 200; diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 71956a535..144f7363e 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -1398,7 +1398,7 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) { /* Charm formula is correct based on over 50 hours of personal live parsing - Kayen - Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 200 CHA + Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 255 CHA (min ~ 75 CHA) Charisma DOES NOT extend charm durations. Base effect value of charm spells in the spell file DOES NOT effect duration OR resist rate (unclear if does anything) Charm has a lower limit of 5% chance to break per tick, regardless of resist modifiers / level difference. diff --git a/zone/attack.cpp b/zone/attack.cpp index ccbc3a8f8..be099adc6 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3680,7 +3680,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons if(spell_id != SPELL_UNKNOWN && !iBuffTic) { //see if root will break if (IsRooted() && !FromDamageShield) // neotoyko: only spells cancel root - TryRootFadeByDamage(buffslot); + TryRootFadeByDamage(buffslot, attacker); } else if(spell_id == SPELL_UNKNOWN) { @@ -4548,34 +4548,43 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, float chance) } } -bool Mob::TryRootFadeByDamage(int buffslot) -{ - /*Dev Quote 2010: http://forums.station.sony.com/eq/posts/list.m?topic_id=161443 - The Viscid Roots AA does the following: Reduces the chance for root to break by X percent. - There is no distinction of any kind between the caster inflicted damage, or anyone - else's damage. There is also no distinction between Direct and DOT damage in the root code. - There is however, a provision that if the damage inflicted is greater than 500 per hit, the - chance to break root is increased. My guess is when this code was put in place, the devs at - the time couldn't imagine DOT damage getting that high. - */ - - /* General Mechanics - - Check buffslot to make sure damage from a root does not cancel the root - - If multiple roots on target, always and only checks first root slot and if broken only removes that slots root. - - Only roots on determental spells can be broken by damage. - */ - - if (!spellbonuses.Root[0] || spellbonuses.Root[1] < 0) - return false; - - if (IsDetrimentalSpell(spellbonuses.Root[1]) && spellbonuses.Root[1] != buffslot){ - - int BreakChance = RuleI(Spells, RootBreakFromSpells); +bool Mob::TryRootFadeByDamage(int buffslot, Mob* attacker) { + + /*Dev Quote 2010: http://forums.station.sony.com/eq/posts/list.m?topic_id=161443 + The Viscid Roots AA does the following: Reduces the chance for root to break by X percent. + There is no distinction of any kind between the caster inflicted damage, or anyone + else's damage. There is also no distinction between Direct and DOT damage in the root code. + + /* General Mechanics + - Check buffslot to make sure damage from a root does not cancel the root + - If multiple roots on target, always and only checks first root slot and if broken only removes that slots root. + - Only roots on determental spells can be broken by damage. + - Root break chance values obtained from live parses. + */ + + if (!attacker || !spellbonuses.Root[0] || spellbonuses.Root[1] < 0) + return false; + + if (IsDetrimentalSpell(spellbonuses.Root[1]) && spellbonuses.Root[1] != buffslot){ + + int BreakChance = RuleI(Spells, RootBreakFromSpells); - BreakChance -= BreakChance*buffs[spellbonuses.Root[1]].RootBreakChance/100; + BreakChance -= BreakChance*buffs[spellbonuses.Root[1]].RootBreakChance/100; + int level_diff = attacker->GetLevel() - GetLevel(); - if (BreakChance < 1) - BreakChance = 1; + //Use baseline if level difference <= 1 (ie. If target is (1) level less than you, or equal or greater level) + + if (level_diff == 2) + BreakChance = (BreakChance * 80) /100; //Decrease by 20%; + + else if (level_diff >= 3 && level_diff <= 20) + BreakChance = (BreakChance * 60) /100; //Decrease by 40%; + + else if (level_diff > 21) + BreakChance = (BreakChance * 20) /100; //Decrease by 80%; + + if (BreakChance < 1) + BreakChance = 1; if (MakeRandomInt(0, 99) < BreakChance) { diff --git a/zone/mob.h b/zone/mob.h index c771d57b7..c8453d301 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -191,7 +191,7 @@ public: virtual int32 GetActSpellDuration(uint16 spell_id, int32 duration){ return duration;} virtual int32 GetActSpellCasttime(uint16 spell_id, int32 casttime); float ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override = false, - int resist_override = 0, bool CharismaCheck = false, bool CharmTick = false); + int resist_override = 0, bool CharismaCheck = false, bool CharmTick = false, bool IsRoot = false); uint16 GetSpecializeSkillValue(uint16 spell_id) const; void SendSpellBarDisable(); void SendSpellBarEnable(uint16 spellid); @@ -593,7 +593,7 @@ public: void MeleeLifeTap(int32 damage); bool PassCastRestriction(bool UseCastRestriction = true, int16 value = 0, bool IsDamage = true); bool ImprovedTaunt(); - bool TryRootFadeByDamage(int buffslot); + bool TryRootFadeByDamage(int buffslot, Mob* attacker); void ModSkillDmgTaken(SkillUseTypes skill_num, int value); int16 GetModSkillDmgTaken(const SkillUseTypes skill_num); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index cebd548dd..7fd2f4332 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -830,14 +830,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Fear: %+i", effect_value); #endif - //use resistance value for duration... - buffs[buffslot].ticsremaining = ((buffs[buffslot].ticsremaining * partial) / 100); - if(IsClient()) { if(buffs[buffslot].ticsremaining > RuleI(Character, MaxFearDurationForPlayerCharacter)) buffs[buffslot].ticsremaining = RuleI(Character, MaxFearDurationForPlayerCharacter); } + if(RuleB(Combat, EnableFearPathing)){ if(IsClient()) @@ -3327,6 +3325,22 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste if (MakeRandomInt(0, 99) < RuleI(Spells, RootBreakCheckChance)){ + float resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, 0,0,0,0,true); + + if(resist_check == 100) + break; + else + if(!TryFadeEffect(slot)) + BuffFadeBySlot(slot); + } + + break; + } + + case SE_Fear: + { + if (MakeRandomInt(0, 99) < RuleI(Spells, FearBreakCheckChance)){ + float resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster); if(resist_check == 100) diff --git a/zone/spells.cpp b/zone/spells.cpp index 332906883..86d21ea7a 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3409,7 +3409,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r // not all unresistable, so changing this to only check certain spells if(IsResistableSpell(spell_id)) { - if (IsCharmSpell(spell_id)) + if (IsCharmSpell(spell_id) || IsMezSpell(spell_id) || IsFearSpell(spell_id)) spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust,true); else spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust); @@ -4040,7 +4040,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) // pvp_resist_base // pvp_resist_calc // pvp_resist_cap -float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override, int resist_override, bool CharismaCheck, bool CharmTick) +float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override, int resist_override, bool CharismaCheck, bool CharmTick, bool IsRoot) { if(!caster) @@ -4079,8 +4079,10 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use } //Check for fear resist + bool IsFear = false; if(IsFearSpell(spell_id)) { + IsFear = true; int fear_resist_bonuses = CalcFearResistChance(); if(MakeRandomInt(0, 99) < fear_resist_bonuses) { @@ -4089,7 +4091,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use } } - if (!CharismaCheck){ + if (!CharmTick){ //Check for Spell Effect specific resistance chances (ie AA Mental Fortitude) int se_resist_bonuses = GetSpellEffectResistChance(spell_id); @@ -4232,15 +4234,38 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use if (CharismaCheck) { - //Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 200 CHA - //'Lull' spells only check charisma if inital cast is resisted to see if mob will aggro, same modifier/cap as above. - //Charisma DOES NOT extend charm durations. + /* + Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 255 CHA (min ~ 75 cha) + Charisma less than ~ 75 gives a postive modifier to resist checks at approximate ratio of -10 CHA = +6 Resist. + Mez spells do same initial resist check as a above. + Lull spells only check charisma if inital cast is resisted to see if mob will aggro, same modifier/cap as above. + Charisma DOES NOT extend charm durations. + Fear resist chance is given a -20 resist modifier if CHA is < 100, from 100-255 it progressively reduces the negative mod to 0. + Fears verse undead DO NOT apply a charisma modifer. (Note: unknown Base1 values defined in undead fears do not effect duration). + */ int16 charisma = caster->GetCHA(); - if (charisma > RuleI(Spells, CharismaEffectivenessCap)) - charisma = RuleI(Spells, CharismaEffectivenessCap); + if (IsFear && (spells[spell_id].targettype != 10)){ - resist_modifier -= charisma/RuleI(Spells, CharismaEffectiveness); + if (charisma < 100) + resist_modifier -= 20; + + else if (charisma <= 255) + resist_modifier += (charisma - 100)/8; + } + + else { + + if (charisma >= 75){ + + if (charisma > RuleI(Spells, CharismaEffectivenessCap)) + charisma = RuleI(Spells, CharismaEffectivenessCap); + + resist_modifier -= (charisma - 75)/RuleI(Spells, CharismaEffectiveness); + } + else + resist_modifier += ((75 - charisma)/10) * 6; //Increase Resist Chance + } } //Lull spells DO NOT use regular resists on initial cast, instead they use a flat +15 modifier. Live parses confirm this. @@ -4266,10 +4291,26 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use resist_chance = spells[spell_id].MinResist; } - //Charm can not have less than 5% chance to fail. - if (CharmTick && (resist_chance < 10)) - resist_chance = 10; - + //Average charm duration agianst mobs with 0% chance to resist on LIVE is ~ 68 ticks. + //Minimum resist chance should be caclulated factoring in the RuleI(Spells, CharmBreakCheckChance) + if (CharmTick) { + + int min_charmbreakchance = ((100/RuleI(Spells, CharmBreakCheckChance))/66 * 100)*2; + + if (resist_chance < min_charmbreakchance) + resist_chance = min_charmbreakchance; + } + + //Average root duration agianst mobs with 0% chance to resist on LIVE is ~ 22 ticks (6% resist chance). + //Minimum resist chance should be caclulated factoring in the RuleI(Spells, RootBreakCheckChance) + if (IsRoot) { + + int min_rootbreakchance = ((100/RuleI(Spells, RootBreakCheckChance))/22 * 100)*2; + + if (resist_chance < min_rootbreakchance) + resist_chance = min_rootbreakchance; + } + //Finally our roll int roll = MakeRandomInt(0, 200); if(roll > resist_chance)