diff --git a/common/races.h b/common/races.h index 050b2fd78..d34d9b754 100644 --- a/common/races.h +++ b/common/races.h @@ -45,6 +45,7 @@ #define TIGER 63 #define ELEMENTAL 75 #define ALLIGATOR 91 +#define OGGOK_CITIZEN 93 #define EYE_OF_ZOMM 108 #define WOLF_ELEMENTAL 120 #define INVISIBLE_MAN 127 diff --git a/common/spdat.h b/common/spdat.h index cb6e49fc9..2734f6078 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -446,7 +446,7 @@ typedef enum { #define SE_IncreaseRunSpeedCap 290 // implemented[AA] - increases run speed over the hard cap #define SE_Purify 291 // implemented - Removes determental effects #define SE_StrikeThrough2 292 // implemented[AA] - increasing chance of bypassing an opponent's special defenses, such as dodge, block, parry, and riposte. -#define SE_FrontalStunResist 293 // implemented[AA] - Reduce chance to be stunned from front. +#define SE_FrontalStunResist 293 // implemented[AA] - Reduce chance to be stunned from front. -- live descriptions sounds like this isn't limited to frontal anymore #define SE_CriticalSpellChance 294 // implemented - increase chance to critical hit and critical damage modifier. //#define SE_ReduceTimerSpecial 295 // not used #define SE_FcSpellVulnerability 296 // implemented - increase in incoming spell damage diff --git a/zone/attack.cpp b/zone/attack.cpp index 54f98c97a..d4d8f141c 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -23,6 +23,7 @@ #include "../common/skills.h" #include "../common/spdat.h" #include "../common/string_util.h" +#include "../common/data_verification.h" #include "queryserv.h" #include "quest_parser_collection.h" #include "string_ids.h" @@ -3169,68 +3170,63 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons BuffFadeByEffect(SE_Mez); } - //check stun chances if bashing - if (damage > 0 && ((skill_used == EQEmu::skills::SkillBash || skill_used == EQEmu::skills::SkillKick) && attacker)) { - // NPCs can stun with their bash/kick as soon as they receive it. - // Clients can stun mobs under level 56 with their kick when they get level 55 or greater. - // Clients have a chance to stun if the mob is 56+ - - // Calculate the chance to stun - int stun_chance = 0; - if (!GetSpecialAbility(UNSTUNABLE)) { - if (attacker->IsNPC()) { - stun_chance = RuleI(Combat, NPCBashKickStunChance); - } else if (attacker->IsClient()) { - // Less than base immunity - // Client vs. Client always uses the chance - if (!IsClient() && GetLevel() <= RuleI(Spells, BaseImmunityLevel)) { - if (skill_used == EQEmu::skills::SkillBash) // Bash always will - stun_chance = 100; - else if (attacker->GetLevel() >= RuleI(Combat, ClientStunLevel)) - stun_chance = 100; // only if you're over level 55 and using kick - } else { // higher than base immunity or Client vs. Client - // not sure on this number, use same as NPC for now - if (skill_used == EQEmu::skills::SkillKick && attacker->GetLevel() < RuleI(Combat, ClientStunLevel)) - stun_chance = RuleI(Combat, NPCBashKickStunChance); - else if (skill_used == EQEmu::skills::SkillBash) - stun_chance = RuleI(Combat, NPCBashKickStunChance) + - attacker->spellbonuses.StunBashChance + - attacker->itembonuses.StunBashChance + - attacker->aabonuses.StunBashChance; - } - } + // broken up for readability + // This is based on what the client is doing + // We had a bunch of stuff like BaseImmunityLevel checks, which I think is suppose to just be for spells + // This is missing some merc checks, but those mostly just skipped the spell bonuses I think ... + bool can_stun = false; + int stunbash_chance = 0; // bonus + if (attacker) { + if (skill_used == EQEmu::skills::SkillBash) { + can_stun = true; + if (attacker->IsClient()) + stunbash_chance = attacker->spellbonuses.StunBashChance + + attacker->itembonuses.StunBashChance + + attacker->aabonuses.StunBashChance; + } else if (skill_used == EQEmu::skills::SkillKick && + (attacker->GetLevel() > 55 || attacker->IsNPC()) && GetClass() == WARRIOR) { + can_stun = true; } - if (stun_chance && zone->random.Roll(stun_chance)) { - // Passed stun, try to resist now - int stun_resist = itembonuses.StunResist + spellbonuses.StunResist; - int frontal_stun_resist = itembonuses.FrontalStunResist + spellbonuses.FrontalStunResist; - - Log.Out(Logs::Detail, Logs::Combat, "Stun passed, checking resists. Was %d chance.", stun_chance); - if (IsClient()) { - stun_resist += aabonuses.StunResist; - frontal_stun_resist += aabonuses.FrontalStunResist; - } - - // frontal stun check for ogres/bonuses - if (((GetBaseRace() == OGRE && IsClient()) || - (frontal_stun_resist && zone->random.Roll(frontal_stun_resist))) && - !attacker->BehindMob(this, attacker->GetX(), attacker->GetY())) { - Log.Out(Logs::Detail, Logs::Combat, "Frontal stun resisted. %d chance.", frontal_stun_resist); - - } else { - // Normal stun resist check. - if (stun_resist && zone->random.Roll(stun_resist)) { + if ((GetBaseRace() == OGRE || GetBaseRace() == OGGOK_CITIZEN) && + !attacker->BehindMob(this, attacker->GetX(), attacker->GetY())) + can_stun = false; + if (GetSpecialAbility(UNSTUNABLE)) + can_stun = false; + } + if (can_stun) { + int bashsave_roll = zone->random.Int(0, 100); + if (bashsave_roll > 98 || bashsave_roll > (55 - stunbash_chance)) { + // did stun -- roll other resists + // SE_FrontalStunResist description says any angle now a days + int stun_resist2 = spellbonuses.FrontalStunResist + itembonuses.FrontalStunResist + + aabonuses.FrontalStunResist; + if (zone->random.Int(1, 100) > stun_resist2) { + // stun resist 2 failed + // time to check SE_StunResist and mod2 stun resist + int stun_resist = + spellbonuses.StunResist + itembonuses.StunResist + aabonuses.StunResist; + if (zone->random.Int(0, 100) >= stun_resist) { + // did stun + // nothing else to check! + Stun(2000); // straight 2 seconds every time + } else { + // stun resist passed! if (IsClient()) Message_StringID(MT_Stun, SHAKE_OFF_STUN); - Log.Out(Logs::Detail, Logs::Combat, "Stun Resisted. %d chance.", stun_resist); - } else { - Log.Out(Logs::Detail, Logs::Combat, "Stunned. %d resist chance.", stun_resist); - Stun(zone->random.Int(0, 2) * 1000); // 0-2 seconds } + } else { + // stun resist 2 passed! + if (IsClient()) + Message_StringID(MT_Stun, AVOID_STUNNING_BLOW); } } else { - Log.Out(Logs::Detail, Logs::Combat, "Stun failed. %d chance.", stun_chance); + // main stun failed -- extra interrupt roll + if (IsCasting() && + !EQEmu::ValueWithin(casting_spell_id, 859, 1023)) // these spells are excluded + // 90% chance >< -- stun immune won't reach this branch though :( + if (zone->random.Int(0, 9) > 1) + InterruptSpell(); } } diff --git a/zone/string_ids.h b/zone/string_ids.h index be05b1eb2..e1fb4de7c 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -297,6 +297,7 @@ #define ADVENTURE_COMPLETE 5147 //You received %1 points for successfully completing the adventure. #define SUCCOR_FAIL 5169 //The portal collapes before you can escape! #define PET_ATTACKING 5501 //%1 tells you, 'Attacking %2 Master.' +#define AVOID_STUNNING_BLOW 5753 //You avoid the stunning blow. #define FATAL_BOW_SHOT 5745 //%1 performs a FATAL BOW SHOT!! #define MELEE_SILENCE 5806 //You *CANNOT* use this melee ability, you are suffering from amnesia! #define DISCIPLINE_REUSE_MSG 5807 //You can use the ability %1 again in %2 hour(s) %3 minute(s) %4 seconds. @@ -354,7 +355,7 @@ #define HIT_NON_MELEE 9073 //%1 hit %2 for %3 points of non-melee damage. #define GLOWS_BLUE 9074 //Your %1 glows blue. #define GLOWS_RED 9075 //Your %1 glows red. -#define SHAKE_OFF_STUN 9077 +#define SHAKE_OFF_STUN 9077 //You shake off the stun effect! #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.