diff --git a/changelog.txt b/changelog.txt index 1e371109d..a55917f97 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,17 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 07/5/2014 == +Kayen: Updated SE_Sanctuary - Adjust way hate lowering effect worked to be more accurate +Kayen: Updated SE_SympatheticProc - Revised proc rate formula to be accurate to live. +Sympathetic foci on items with proc rate mod will now benefit from that modifier. +Sympathetic foci can now be placed on AA's (This should always be slot1 in the AA) +Kayen: Implemented SE_IllusionPersistence- Allows illusions to last until you die or the illusion is forcibly removed. +Kayen: Added rule 'PreNerftBardAEDot' for SE_BardAEDot to allow it to once again do damage to moving targets. (Set to true) +Kayen: Completely revised SE_SkillProc, SE_LimitToSkill, SE_SkillProcSuccess to overall just work better and more accurately, AA support. + +Required SQL: utils/sql/git/required/2014_07_04_AA_Update.sql + + == 07/2/2014 == Kayen: Implemented SE_Sanctuary - Places caster at bottom hate list, effect fades if caster cast spell on targets other than self. Kayen: Implemented SE_ResourceTap - Coverts a percent of dmg from dmg spells(DD/DoT) to hp/mana/end. diff --git a/common/ruletypes.h b/common/ruletypes.h index 6f5fe0769..8c9ac543e 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -310,6 +310,7 @@ RULE_INT ( Spells, FRProjectileItem_SOF, 80684) // Item id for SOF clients for F RULE_INT ( Spells, FRProjectileItem_NPC, 80684) // Item id for NPC Fire 'spell projectile'. RULE_BOOL ( Spells, UseLiveSpellProjectileGFX, false) // Use spell projectile graphics set in the spells_new table (player_1). Server must be using UF+ spell file. RULE_BOOL ( Spells, FocusCombatProcs, false) //Allow all combat procs to receive focus effects. +RULE_BOOL ( Spells, PreNerfBardAEDoT, false) //Allow bard AOE dots to damage targets when moving. RULE_CATEGORY_END() RULE_CATEGORY( Combat ) diff --git a/common/spdat.h b/common/spdat.h index 02dca8ff7..69082d305 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -37,6 +37,8 @@ #define MAX_SPELL_TRIGGER 12 // One for each slot(only 6 for AA since AA use 2) #define MAX_RESISTABLE_EFFECTS 12 // Number of effects that are typcially checked agianst resists. #define MaxLimitInclude 16 //Number(x 0.5) of focus Limiters that have inclusive checks used when calcing focus effects +#define MAX_SKILL_PROCS 4 //Number of spells to check skill procs from. (This is arbitrary) [Single spell can have multiple proc checks] + const int Z_AGGRO=10; @@ -385,7 +387,7 @@ typedef enum { #define SE_ChannelChanceSpells 235 // implemented[AA] - chance to channel from SPELLS *No longer used on live. //#define SE_FreePet 236 // not used #define SE_GivePetGroupTarget 237 // implemented[AA] - (Pet Affinity) -//#define SE_IllusionPersistence 238 // *not implemented - lends persistence to your illusionary disguises, causing them to last until you die or the illusion is forcibly removed. +#define SE_IllusionPersistence 238 // implemented - lends persistence to your illusionary disguises, causing them to last until you die or the illusion is forcibly removed. //#define SE_FeignedCastOnChance 239 // *not implemented as bonus - ability gives you an increasing chance for your feigned deaths to not be revealed by spells cast upon you. //#define SE_StringUnbreakable 240 // not used [Likely related to above - you become immune to feign breaking on a resisted spell and have a good chance of feigning through a spell that successfully lands upon you.] #define SE_ImprovedReclaimEnergy 241 // implemented - increase the amount of mana returned to you when reclaiming your pet. @@ -576,7 +578,7 @@ typedef enum { //#define SE_IncreaseExtTargetWindow 426 // *not implmented[AA] - increases the capacity of your extended target window #define SE_SkillProc 427 // implemented - chance to proc when using a skill(ie taunt) #define SE_LimitToSkill 428 // implemented - limits what skills will effect a skill proc -#define SE_SkillProc2 429 // implemented - chance to proc when using a skill (most have hit limits) +#define SE_SkillProcSuccess 429 // implemented - chance to proc when tje skill in use successfully fires. //#define SE_PostEffect 430 // *not implemented - Fear of the Dark(27641) - Alters vision //#define SE_PostEffectData 431 // *not implemented - Fear of the Dark(27641) - Alters vision //#define SE_ExpandMaxActiveTrophyBen 432 // not used diff --git a/utils/sql/git/required/2014_07_04_AA_Updates.sql b/utils/sql/git/required/2014_07_04_AA_Updates.sql new file mode 100644 index 000000000..a943e8c15 --- /dev/null +++ b/utils/sql/git/required/2014_07_04_AA_Updates.sql @@ -0,0 +1,6 @@ +-- AA Permanent Illusion +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('158', '1', '238', '1', '0'); + +-- AA Sanctuary +INSERT INTO `altadv_vars` (`skill_id`, `name`, `cost`, `max_level`, `hotkey_sid`, `hotkey_sid2`, `title_sid`, `desc_sid`, `type`, `spellid`, `prereq_skill`, `prereq_minpoints`, `spell_type`, `spell_refresh`, `classes`, `berserker`, `class_type`, `cost_inc`, `aa_expansion`, `special_category`, `sof_type`, `sof_cost_inc`, `sof_max_level`, `sof_next_skill`, `clientver`, `account_time_required`, `sof_current_level`,`sof_next_id`,`level_inc`) VALUES ('1209', 'Sanctuary', '12', '1', '1209', '4294967295', '1209', '1209', '3', '5912', '0', '0', '14', '4320', '4', '0', '70', '0', '8', '4294967295', '3', '0', '1', '1209', '1', '0', '0', '0', '0'); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES ('1209', '0', '4320', '5912', '1', '0', '0', '0', '0', '0', '0', '0'); diff --git a/zone/attack.cpp b/zone/attack.cpp index fdbc46d24..37759bc9a 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1359,8 +1359,11 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b MeleeLifeTap(damage); - if (damage > 0) + if (damage > 0){ CheckNumHitsRemaining(NUMHIT_OutgoingHitSuccess); + if (HasSkillProcSuccess() && other && other->GetHP() > 0) + TrySkillProc(other, skillinuse, 0, true, Hand); + } //break invis when you attack if(invisible) { @@ -2018,9 +2021,12 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool if (!GetTarget()) return true; //We killed them - if(!bRiposte && other->GetHP() > 0 ) { + if(!bRiposte && other && other->GetHP() > 0) { TryWeaponProc(nullptr, weapon, other, Hand); //no weapon TrySpellProc(nullptr, weapon, other, Hand); + + if (damage > 0 && HasSkillProcSuccess()) + TrySkillProc(other, skillinuse, 0, true, Hand); } TriggerDefensiveProcs(nullptr, other, Hand, damage); @@ -3426,9 +3432,20 @@ bool Mob::HasDefensiveProcs() const bool Mob::HasSkillProcs() const { - for (int i = 0; i < MAX_PROCS; i++) - if (SkillProcs[i].spellID != SPELL_UNKNOWN) + + for(int i = 0; i < MAX_SKILL_PROCS; i++){ + if (spellbonuses.SkillProc[i] || itembonuses.SkillProc[i] || aabonuses.SkillProc[i]) return true; + } + return false; +} + +bool Mob::HasSkillProcSuccess() const +{ + for(int i = 0; i < MAX_SKILL_PROCS; i++){ + if (spellbonuses.SkillProcSuccess[i] || itembonuses.SkillProcSuccess[i] || aabonuses.SkillProcSuccess[i]) + return true; + } return false; } @@ -3911,22 +3928,7 @@ float Mob::GetProcChances(float ProcBonus, uint16 weapon_speed, uint16 hand) int mydex = GetDEX(); float ProcChance = 0.0f; - switch (hand) { - case 13: - weapon_speed = attack_timer.GetDuration(); - break; - case 14: - weapon_speed = attack_dw_timer.GetDuration(); - break; - case 11: - weapon_speed = ranged_timer.GetDuration(); - break; - } - - //calculate the weapon speed in ms, so we can use the rule to compare against. - // fast as a client can swing, so should be the floor of the proc chance - if (weapon_speed < RuleI(Combat, MinHastedDelay)) - weapon_speed = RuleI(Combat, MinHastedDelay); + weapon_speed = GetWeaponSpeedbyHand(hand); if (RuleB(Combat, AdjustProcPerMinute)) { ProcChance = (static_cast(weapon_speed) * @@ -3948,25 +3950,10 @@ float Mob::GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 w ProcBonus = 0; ProcChance = 0; - switch(hand){ - case 13: - weapon_speed = attack_timer.GetDuration(); - break; - case 14: - weapon_speed = attack_dw_timer.GetDuration(); - break; - case 11: - return 0; - break; - } + weapon_speed = GetWeaponSpeedbyHand(hand); - //calculate the weapon speed in ms, so we can use the rule to compare against. - //weapon_speed = ((int)(weapon_speed*(100.0f+attack_speed)*PermaHaste)); - if(weapon_speed < RuleI(Combat, MinHastedDelay)) // fast as a client can swing, so should be the floor of the proc chance - weapon_speed = RuleI(Combat, MinHastedDelay); - - ProcChance = ((float)weapon_speed * RuleR(Combat, AvgDefProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms - ProcBonus += float(myagi) * RuleR(Combat, DefProcPerMinAgiContrib) / 100.0f; + ProcChance = (static_cast(weapon_speed) * RuleR(Combat, AvgDefProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms + ProcBonus += static_cast(myagi) * RuleR(Combat, DefProcPerMinAgiContrib) / 100.0f; ProcChance = ProcChance + (ProcChance * ProcBonus); mlog(COMBAT__PROCS, "Defensive Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus); @@ -3981,13 +3968,9 @@ void Mob::TryDefensiveProc(const ItemInst* weapon, Mob *on, uint16 hand, int dam return; } - bool bSkillProc = HasSkillProcs(); bool bDefensiveProc = HasDefensiveProcs(); - if (!bDefensiveProc && !bSkillProc) - return; - - if (!bDefensiveProc && (bSkillProc && damage >= 0)) + if (!bDefensiveProc) return; float ProcChance, ProcBonus; @@ -4000,30 +3983,15 @@ void Mob::TryDefensiveProc(const ItemInst* weapon, Mob *on, uint16 hand, int dam if (bDefensiveProc){ for (int i = 0; i < MAX_PROCS; i++) { - if (DefensiveProcs[i].spellID != SPELL_UNKNOWN) { - int chance = ProcChance * (DefensiveProcs[i].chance); - if ((MakeRandomInt(0, 100) < chance)) { + if (IsValidSpell(DefensiveProcs[i].spellID)) { + float chance = ProcChance * (static_cast(DefensiveProcs[i].chance)/100.0f); + if ((MakeRandomFloat(0, 1) <= chance)) { ExecWeaponProc(nullptr, DefensiveProcs[i].spellID, on); CheckNumHitsRemaining(NUMHIT_DefensiveSpellProcs,0,DefensiveProcs[i].base_spellID); } } } } - - if (bSkillProc && damage < 0){ - - if (damage == -1) - TrySkillProc(on, SkillBlock, ProcChance); - - if (damage == -2) - TrySkillProc(on, SkillParry, ProcChance); - - if (damage == -3) - TrySkillProc(on, SkillRiposte, ProcChance); - - if (damage == -4) - TrySkillProc(on, SkillDodge, ProcChance); - } } void Mob::TryWeaponProc(const ItemInst* weapon_g, Mob *on, uint16 hand) { @@ -4032,7 +4000,7 @@ void Mob::TryWeaponProc(const ItemInst* weapon_g, Mob *on, uint16 hand) { LogFile->write(EQEMuLog::Error, "A null Mob object was passed to Mob::TryWeaponProc for evaluation!"); return; } - + if (!IsAttackAllowed(on)) { mlog(COMBAT__PROCS, "Preventing procing off of unattackable things."); return; @@ -4059,6 +4027,7 @@ void Mob::TryWeaponProc(const ItemInst* weapon_g, Mob *on, uint16 hand) { void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on, uint16 hand) { + if (!weapon) return; uint16 skillinuse = 28; @@ -4135,8 +4104,6 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on } } // TODO: Powersource procs - if (HasSkillProcs()) - TrySkillProc(on, skillinuse, ProcChance); return; } @@ -4217,6 +4184,14 @@ void Mob::TrySpellProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on, } } + if (HasSkillProcs() && hand != 11){ //We check ranged skill procs within the attack functions. + uint16 skillinuse = 28; + if (weapon) + skillinuse = GetSkillByItemType(weapon->ItemType); + + TrySkillProc(on, skillinuse, 0, false, hand); + } + return; } @@ -4560,7 +4535,7 @@ uint16 Mob::GetDamageTable(SkillUseTypes skillinuse) } } -void Mob::TrySkillProc(Mob *on, uint16 skill, float chance) +void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, uint16 hand, bool IsDefensive) { if (!on) { @@ -4569,17 +4544,174 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, float chance) return; } - for (int i = 0; i < MAX_PROCS; i++) { - if (SkillProcs[i].spellID != SPELL_UNKNOWN){ - if (PassLimitToSkill(SkillProcs[i].base_spellID,skill)){ - int ProcChance = chance * (float)SkillProcs[i].chance; - if ((MakeRandomInt(0, 100) < ProcChance)) { - ExecWeaponProc(nullptr, SkillProcs[i].spellID, on); - CheckNumHitsRemaining(NUMHIT_OffensiveSpellProcs,0, SkillProcs[i].base_spellID); + if (!spellbonuses.LimitToSkill[skill] && !itembonuses.LimitToSkill[skill] && !aabonuses.LimitToSkill[skill]) + return; + + /*Allow one proc from each (Spell/Item/AA) + Kayen: Due to limited avialability of effects on live it is too difficult + to confirm how they stack at this time, will adjust formula when more data is avialablle to test.*/ + bool CanProc = true; + + uint16 base_spell_id = 0; + uint16 proc_spell_id = 0; + float ProcMod = 0; + float chance = 0; + + if (IsDefensive) + chance = on->GetSkillProcChances(ReuseTime, hand); + else + chance = GetSkillProcChances(ReuseTime, hand); + + if (spellbonuses.LimitToSkill[skill]){ + + for(int e = 0; e < MAX_SKILL_PROCS; e++){ + + if (CanProc && + (!Success && spellbonuses.SkillProc[e] && IsValidSpell(spellbonuses.SkillProc[e])) + || (Success && spellbonuses.SkillProcSuccess[e] && IsValidSpell(spellbonuses.SkillProcSuccess[e]))) { + base_spell_id = spellbonuses.SkillProc[e]; + base_spell_id = 0; + ProcMod = 0; + + for (int i = 0; i < EFFECT_COUNT; i++) { + + if (spells[base_spell_id].effectid[i] == SE_SkillProc) { + proc_spell_id = spells[base_spell_id].base[i]; + ProcMod = static_cast(spells[base_spell_id].base2[i]); + } + + else if (spells[base_spell_id].effectid[i] == SE_LimitToSkill && spells[base_spell_id].effectid[i] <= HIGHEST_SKILL) { + + if (CanProc && spells[base_spell_id].base[i] == skill && IsValidSpell(proc_spell_id)) { + float final_chance = chance * (ProcMod / 100.0f); + if (MakeRandomFloat(0, 1) <= final_chance) { + ExecWeaponProc(nullptr, proc_spell_id, on); + CheckNumHitsRemaining(NUMHIT_OffensiveSpellProcs,0, base_spell_id); + CanProc = false; + break; + } + } + } + else { + proc_spell_id = 0; + ProcMod = 0; + } } } } } + + if (itembonuses.LimitToSkill[skill]){ + CanProc = true; + for(int e = 0; e < MAX_SKILL_PROCS; e++){ + + if (CanProc && + (!Success && itembonuses.SkillProc[e] && IsValidSpell(itembonuses.SkillProc[e])) + || (Success && itembonuses.SkillProcSuccess[e] && IsValidSpell(itembonuses.SkillProcSuccess[e]))) { + base_spell_id = itembonuses.SkillProc[e]; + base_spell_id = 0; + ProcMod = 0; + + for (int i = 0; i < EFFECT_COUNT; i++) { + + if (spells[base_spell_id].effectid[i] == SE_SkillProc) { + proc_spell_id = spells[base_spell_id].base[i]; + ProcMod = static_cast(spells[base_spell_id].base2[i]); + } + + else if (spells[base_spell_id].effectid[i] == SE_LimitToSkill && spells[base_spell_id].effectid[i] <= HIGHEST_SKILL) { + + if (CanProc && spells[base_spell_id].base[i] == skill && IsValidSpell(proc_spell_id)) { + float final_chance = chance * (ProcMod / 100.0f); + if (MakeRandomFloat(0, 1) <= final_chance) { + ExecWeaponProc(nullptr, proc_spell_id, on); + CanProc = false; + break; + } + } + } + else { + proc_spell_id = 0; + ProcMod = 0; + } + } + } + } + } + + if (IsClient() && aabonuses.LimitToSkill[skill]){ + + CanProc = true; + uint32 effect = 0; + int32 base1 = 0; + int32 base2 = 0; + uint32 slot = 0; + + for(int e = 0; e < MAX_SKILL_PROCS; e++){ + + if (CanProc && + (!Success && aabonuses.SkillProc[e]) + || (Success && aabonuses.SkillProcSuccess[e])){ + int aaid = aabonuses.SkillProc[e]; + base_spell_id = 0; + ProcMod = 0; + + std::map >::const_iterator find_iter = aa_effects.find(aaid); + if(find_iter == aa_effects.end()) + break; + + for (std::map::const_iterator iter = aa_effects[aaid].begin(); iter != aa_effects[aaid].end(); ++iter) { + effect = iter->second.skill_id; + base1 = iter->second.base1; + base2 = iter->second.base2; + slot = iter->second.slot; + + if (effect == SE_SkillProc) { + proc_spell_id = base1; + ProcMod = static_cast(base2); + } + + else if (effect == SE_LimitToSkill && effect <= HIGHEST_SKILL) { + + if (CanProc && base1 == skill && IsValidSpell(proc_spell_id)) { + float final_chance = chance * (ProcMod / 100.0f); + + if (MakeRandomFloat(0, 1) <= final_chance) { + ExecWeaponProc(nullptr, proc_spell_id, on); + CanProc = false; + break; + } + } + } + else { + proc_spell_id = 0; + ProcMod = 0; + } + } + } + } + } +} + +float Mob::GetSkillProcChances(uint16 ReuseTime, uint16 hand) { + + uint16 weapon_speed; + float ProcChance = 0; + + if (!ReuseTime && hand) { + + weapon_speed = GetWeaponSpeedbyHand(hand); + + ProcChance = static_cast(weapon_speed) * (RuleR(Combat, AvgProcsPerMinute) / 60000.0f); + + if (hand != 13) + ProcChance /= 2; + } + + else + ProcChance = static_cast(ReuseTime) * (RuleR(Combat, AvgProcsPerMinute) / 60000.0f); + + return ProcChance; } bool Mob::TryRootFadeByDamage(int buffslot, Mob* attacker) { diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 48a3d95e1..e9501c4dd 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -637,7 +637,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) continue; _log(AA__BONUSES, "Applying Effect %d from AA %u in slot %d (base1: %d, base2: %d) on %s", effect, aaid, slot, base1, base2, this->GetCleanName()); - + uint8 focus = IsFocusEffect(0, 0, true,effect); if (focus) { @@ -1329,6 +1329,45 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) break; } + case SE_IllusionPersistence: + newbon->IllusionPersistence = true; + break; + + case SE_LimitToSkill:{ + if (base1 <= HIGHEST_SKILL) + newbon->LimitToSkill[base1] = true; + break; + } + + case SE_SkillProc:{ + for(int e = 0; e < MAX_SKILL_PROCS; e++) + { + if(newbon->SkillProc[e] && newbon->SkillProc[e] == aaid) + break; //Do not use the same aa id more than once. + + else if(!newbon->SkillProc[e]){ + newbon->SkillProc[e] = aaid; + break; + } + } + break; + } + + case SE_SkillProcSuccess:{ + + for(int e = 0; e < MAX_SKILL_PROCS; e++) + { + if(newbon->SkillProcSuccess[e] && newbon->SkillProcSuccess[e] == aaid) + break; //Do not use the same spell id more than once. + + else if(!newbon->SkillProcSuccess[e]){ + newbon->SkillProcSuccess[e] = aaid; + break; + } + } + break; + } + } } } @@ -2876,6 +2915,47 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne break; } + case SE_IllusionPersistence: + newbon->IllusionPersistence = true; + break; + + case SE_LimitToSkill:{ + if (effect_value <= HIGHEST_SKILL){ + newbon->LimitToSkill[effect_value] = true; + } + break; + } + + case SE_SkillProc:{ + + for(int e = 0; e < MAX_SKILL_PROCS; e++) + { + if(newbon->SkillProc[e] && newbon->SkillProc[e] == spell_id) + break; //Do not use the same spell id more than once. + + else if(!newbon->SkillProc[e]){ + newbon->SkillProc[e] = spell_id; + break; + } + } + break; + } + + case SE_SkillProcSuccess:{ + + for(int e = 0; e < MAX_SKILL_PROCS; e++) + { + if(newbon->SkillProcSuccess[e] && newbon->SkillProcSuccess[e] == spell_id) + break; //Do not use the same spell id more than once. + + else if(!newbon->SkillProcSuccess[e]){ + newbon->SkillProcSuccess[e] = spell_id; + break; + } + } + break; + } + //Special custom cases for loading effects on to NPC from 'npc_spels_effects' table if (IsAISpellEffect) { @@ -4356,6 +4436,17 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) aabonuses.FactionModPct = effect_value; break; + case SE_MeleeVulnerability: + spellbonuses.MeleeVulnerability = effect_value; + itembonuses.MeleeVulnerability = effect_value; + aabonuses.MeleeVulnerability = effect_value; + break; + + case SE_IllusionPersistence: + spellbonuses.IllusionPersistence = effect_value; + itembonuses.IllusionPersistence = effect_value; + aabonuses.IllusionPersistence = effect_value; + break; } } } diff --git a/zone/bot.cpp b/zone/bot.cpp index e961bd7ec..86a2a9b64 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3292,7 +3292,7 @@ bool Bot::CheckBotDoubleAttack(bool tripleAttack) { return false; } -void Bot::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes skillinuse, int16 chance_mod, int16 focus, bool CanRiposte) +void Bot::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes skillinuse, int16 chance_mod, int16 focus, bool CanRiposte, int ReuseTime) { if (!CanDoSpecialAttack(other)) return; @@ -3396,10 +3396,11 @@ void Bot::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes other->Stun(100); } - if (CanSkillProc && HasSkillProcs()){ - float chance = 10.0f*RuleR(Combat, AvgProcsPerMinute)/60000.0f; - TrySkillProc(other, skillinuse, chance); - } + if (CanSkillProc && HasSkillProcs()) + TrySkillProc(other, skillinuse, ReuseTime); + + if (CanSkillProc && (damage > 0) && HasSkillProcSuccess()) + TrySkillProc(other, skillinuse, ReuseTime, true); } void Bot::ApplySpecialAttackMod(SkillUseTypes skill, int32 &dmg, int32 &mindmg) { @@ -7641,10 +7642,8 @@ int16 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel { if(bottype == BotfocusSympatheticProc) { - float ProcChance, ProcBonus; - int16 ProcRateMod = focus_spell.base[i]; //Baseline is 100 for most Sympathetic foci - int32 cast_time = GetActSpellCasttime(spell_id, spells[spell_id].cast_time); - GetSympatheticProcChances(ProcBonus, ProcChance, cast_time, ProcRateMod); + + float ProcChance = GetSympatheticProcChances(spell_id, focus_spell.base[i]); if(MakeRandomFloat(0, 1) <= ProcChance) value = focus_id; @@ -8104,10 +8103,11 @@ void Bot::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, //who->Stun(100); Kayen: This effect does not stun on live, it only moves the NPC. } - if (HasSkillProcs()){ - float chance = (float)ReuseTime*RuleR(Combat, AvgProcsPerMinute)/60000.0f; - TrySkillProc(who, skill, chance); - } + if (HasSkillProcs()) + TrySkillProc(who, skill, ReuseTime*1000); + + if (max_damage > 0 && HasSkillProcSuccess()) + TrySkillProc(who, skill, ReuseTime*1000, true); if(max_damage == -3 && !(who->GetHP() <= 0)) DoRiposte(who); diff --git a/zone/bot.h b/zone/bot.h index 485d37377..84f04e85b 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -185,7 +185,7 @@ public: virtual void RogueAssassinate(Mob* other); virtual void DoClassAttacks(Mob *target, bool IsRiposte=false); virtual bool TryHeadShot(Mob* defender, SkillUseTypes skillInUse); - virtual void DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes skillinuse, int16 chance_mod=0, int16 focus=0, bool CanRiposte=false); + virtual void DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes skillinuse, int16 chance_mod=0, int16 focus=0, bool CanRiposte=false, int ReuseTime =0); virtual void ApplySpecialAttackMod(SkillUseTypes skill, int32 &dmg, int32 &mindmg); bool CanDoSpecialAttack(Mob *other); virtual int32 CheckAggroAmount(uint16 spellid); diff --git a/zone/client.h b/zone/client.h index 9e2503df2..e26465628 100644 --- a/zone/client.h +++ b/zone/client.h @@ -776,7 +776,10 @@ public: int GetSpentAA() { return m_pp.aapoints_spent; } void RefundAA(); void IncrementAA(int aa_id); - + int32 GetAAEffectDataBySlot(uint32 aa_ID, uint32 slot_id, bool GetEffect, bool GetBase1, bool GetBase2); + int32 GetAAEffectid(uint32 aa_ID, uint32 slot_id) { return GetAAEffectDataBySlot(aa_ID, slot_id, true, false,false); } + int32 GetAABase1(uint32 aa_ID, uint32 slot_id) { return GetAAEffectDataBySlot(aa_ID, slot_id, false, true,false); } + int32 GetAABase2(uint32 aa_ID, uint32 slot_id) { return GetAAEffectDataBySlot(aa_ID, slot_id, false, false,true); } int16 acmod(); // Item methods diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index fa708acaf..61b432db5 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -9490,13 +9490,6 @@ void Client::CompleteConnect() AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100+spells[buffs[j1].spellid].base2[x1],buffs[j1].spellid); break; } - case SE_SkillProc2: - case SE_SkillProc: - { - AddSkillProc(GetProcID(buffs[j1].spellid, x1), 100+spells[buffs[j1].spellid].base2[x1],buffs[j1].spellid); - break; - } - } } } diff --git a/zone/common.h b/zone/common.h index e3a9dfb09..2aa0101be 100644 --- a/zone/common.h +++ b/zone/common.h @@ -375,6 +375,9 @@ struct StatBonuses { bool Sanctuary; // Sanctuary effect, lowers place on hate list until cast on others. int16 FactionModPct; // Modifies amount of faction gained. int16 MeleeVulnerability; // Weakness/mitigation to melee damage + bool LimitToSkill[HIGHEST_SKILL+2]; // Determines if we need to search for a skill proc. + uint16 SkillProc[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs. + uint16 SkillProcSuccess[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs_success. // AAs int8 Packrat; //weight reduction for items, 1 point = 10% @@ -431,7 +434,7 @@ struct StatBonuses { uint32 Assassinate[2]; // Assassinate AA (Massive dmg vs humaniod w/ assassinate) 0= ? 1= Dmg uint8 AssassinateLevel; // Max Level Assassinate will be effective at. int32 PetMeleeMitigation; // Add AC to owner's pet. - + bool IllusionPersistence; // Causes illusions not to fade. }; typedef struct diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index 68dd2a6cb..abe32b5a7 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -308,6 +308,16 @@ Mob *HateList::GetTop(Mob *center) } } + if (cur->ent->Sanctuary()) { + if(hate == -1) + { + top = cur->ent; + hate = 1; + } + ++iterator; + continue; + } + if(cur->ent->DivineAura() || cur->ent->IsMezzed() || cur->ent->IsFeared()){ if(hate == -1) { diff --git a/zone/mob.cpp b/zone/mob.cpp index 59e02568b..f9b275631 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2761,6 +2761,14 @@ bool Mob::DivineAura() const return false; } +bool Mob::Sanctuary() const +{ + if (spellbonuses.Sanctuary) + return true; + + return false; +} + int16 Mob::GetResist(uint8 type) const { if (IsNPC()) @@ -3042,6 +3050,34 @@ void Mob::TriggerDefensiveProcs(const ItemInst* weapon, Mob *on, uint16 hand, in return; on->TryDefensiveProc(weapon, this, hand, damage); + + //Defensive Skill Procs + if (damage < 0 && damage >= -4) { + uint16 skillinuse = 0; + switch (damage) { + case (-1): + skillinuse = SkillBlock; + break; + + case (-2): + skillinuse = SkillParry; + break; + + case (-3): + skillinuse = SkillRiposte; + break; + + case (-4): + skillinuse = SkillDodge; + break; + } + + if (on->HasSkillProcs()) + on->TrySkillProc(this, skillinuse, 0, false, hand, true); + + if (on->HasSkillProcSuccess()) + on->TrySkillProc(this, skillinuse, 0, true, hand, true); + } } void Mob::SetDeltas(float dx, float dy, float dz, float dh) { @@ -4706,6 +4742,28 @@ bool Mob::PassLimitToSkill(uint16 spell_id, uint16 skill) { return false; } +uint16 Mob::GetWeaponSpeedbyHand(uint16 hand) { + + uint16 weapon_speed = 0; + switch (hand) { + + case 13: + weapon_speed = attack_timer.GetDuration(); + break; + case 14: + weapon_speed = attack_dw_timer.GetDuration(); + break; + case 11: + weapon_speed = ranged_timer.GetDuration(); + break; + } + + if (weapon_speed < RuleI(Combat, MinHastedDelay)) + weapon_speed = RuleI(Combat, MinHastedDelay); + + return weapon_speed; +} + int8 Mob::GetDecayEffectValue(uint16 spell_id, uint16 spelleffect) { if (!IsValidSpell(spell_id)) diff --git a/zone/mob.h b/zone/mob.h index 0476a20dc..fcb0ece3d 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -506,9 +506,8 @@ public: bool AddDefensiveProc(uint16 spell_id, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN); bool RemoveDefensiveProc(uint16 spell_id, bool bAll = false); bool HasDefensiveProcs() const; - bool AddSkillProc(uint16 spell_id, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN); - bool RemoveSkillProc(uint16 spell_id, bool bAll = false); bool HasSkillProcs() const; + bool HasSkillProcSuccess() const; bool AddProcToWeapon(uint16 spell_id, bool bPerma = false, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN); bool RemoveProcFromWeapon(uint16 spell_id, bool bAll = false); bool HasProcs() const; @@ -700,9 +699,9 @@ public: int32 ReduceAllDamage(int32 damage); virtual void DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, int32 min_damage = 1, int32 hate_override = -1, int ReuseTime = 10, bool HitChance=false, bool CanAvoid=true); - virtual void DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const Item_Struct* item=nullptr, uint16 weapon_damage=0, int16 chance_mod=0,int16 focus=0); - virtual void DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes skillinuse, int16 chance_mod=0, int16 focus=0, bool CanRiposte=false); - virtual void DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const ItemInst* Ammo=nullptr, uint16 weapon_damage=0, int16 chance_mod=0, int16 focus=0); + virtual void DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const Item_Struct* item=nullptr, uint16 weapon_damage=0, int16 chance_mod=0,int16 focus=0, int ReuseTime=0); + virtual void DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes skillinuse, int16 chance_mod=0, int16 focus=0, bool CanRiposte=false, int ReuseTime=0); + virtual void DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const ItemInst* Ammo=nullptr, uint16 weapon_damage=0, int16 chance_mod=0, int16 focus=0, int ReuseTime=0); bool CanDoSpecialAttack(Mob *other); bool Flurry(ExtraAttackOptions *opts); bool Rampage(ExtraAttackOptions *opts); @@ -820,6 +819,7 @@ public: void SetNextIncHPEvent( int inchpevent ); bool DivineAura() const; + bool Sanctuary() const; bool HasNPCSpecialAtk(const char* parse); int GetSpecialAbility(int ability); @@ -986,7 +986,7 @@ protected: bool focused; void CalcSpellBonuses(StatBonuses* newbon); virtual void CalcBonuses(); - void TrySkillProc(Mob *on, uint16 skill, float chance); + void TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success = false, uint16 hand = 0, bool IsDefensive = false); bool PassLimitToSkill(uint16 spell_id, uint16 skill); bool PassLimitClass(uint32 Classes_, uint16 Class_); void TryDefensiveProc(const ItemInst* weapon, Mob *on, uint16 hand = 13, int damage=0); @@ -998,6 +998,8 @@ protected: virtual float GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed = 30, uint16 hand = 13); virtual float GetSpecialProcChances(uint16 hand); virtual float GetAssassinateProcChances(uint16 ReuseTime); + virtual float GetSkillProcChances(uint16 ReuseTime, uint16 hand = 0); + uint16 GetWeaponSpeedbyHand(uint16 hand); int GetWeaponDamage(Mob *against, const Item_Struct *weapon_item); int GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate = nullptr); int GetKickDamage(); @@ -1009,7 +1011,7 @@ protected: Map::Vertex UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &WaypointChange, bool &NodeReached); void PrintRoute(); - virtual float GetSympatheticProcChances(float &ProcBonus, float &ProcChance, int32 cast_time, int16 ProcRateMod); + virtual float GetSympatheticProcChances(uint16 spell_id, int16 ProcRateMod, int32 ItemProcRate = 0); enum {MAX_PROCS = 4}; tProc PermaProcs[MAX_PROCS]; diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 4ffa21ab4..46bd810ad 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -171,11 +171,12 @@ void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, //who->Stun(100); Kayen: This effect does not stun on live, it only moves the NPC. } - if (HasSkillProcs()){ - float chance = (float)ReuseTime*RuleR(Combat, AvgProcsPerMinute)/60000.0f; - TrySkillProc(who, skill, chance); - } - + if (HasSkillProcs()) + TrySkillProc(who, skill, ReuseTime*1000); + + if (max_damage > 0 && HasSkillProcSuccess()) + TrySkillProc(who, skill, ReuseTime*1000, true); + if(max_damage == -3 && !who->HasDied()) DoRiposte(who); } @@ -847,7 +848,7 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) { } } -void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const ItemInst* Ammo, uint16 weapon_damage, int16 chance_mod, int16 focus) +void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const ItemInst* Ammo, uint16 weapon_damage, int16 chance_mod, int16 focus, int ReuseTime) { if (!CanDoSpecialAttack(other)) return; @@ -874,6 +875,9 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item else WDmg = weapon_damage; + if (focus) //From FcBaseEffects + WDmg += WDmg*focus/100; + if((WDmg > 0) || (ADmg > 0)) { if(WDmg < 0) @@ -948,7 +952,6 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item other->MeleeMitigation(this, TotalDmg, minDmg); if(TotalDmg > 0) { - TotalDmg += TotalDmg*focus/100; ApplyMeleeDamageBonus(SkillArchery, TotalDmg); TotalDmg += other->GetFcDamageAmtIncoming(this, 0, true, SkillArchery); TotalDmg += (itembonuses.HeroicDEX / 10) + (TotalDmg * other->GetSkillDmgTaken(SkillArchery) / 100) + GetSkillDmgAmt(SkillArchery); @@ -967,20 +970,33 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, FATAL_BOW_SHOT, GetName()); other->Damage(this, TotalDmg, SPELL_UNKNOWN, SkillArchery); - + + if (TotalDmg > 0 && HasSkillProcSuccess() && GetTarget() && other && !other->HasDied()){ + if (ReuseTime) + TrySkillProc(other, SkillArchery, ReuseTime); + else + TrySkillProc(other, SkillArchery, 0, true, 11); + } } //try proc on hits and misses - if((RangeWeapon != nullptr) && GetTarget() && other && (other->GetHP() > -10)) + if((RangeWeapon != nullptr) && GetTarget() && other && !other->HasDied()) { TryWeaponProc(RangeWeapon, other, 11); } //Arrow procs because why not? - if((Ammo != NULL) && GetTarget() && other && (other->GetHP() > -10)) + if((Ammo != NULL) && GetTarget() && other && !other->HasDied()) { TryWeaponProc(Ammo, other, 11); } + + if (HasSkillProcs() && GetTarget() && other && !other->HasDied()){ + if (ReuseTime) + TrySkillProc(other, SkillArchery, ReuseTime); + else + TrySkillProc(other, SkillArchery, 0, false, 11); + } } void NPC::RangedAttack(Mob* other) @@ -1253,7 +1269,7 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 } } -void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item_Struct* item, uint16 weapon_damage, int16 chance_mod,int16 focus) +void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item_Struct* item, uint16 weapon_damage, int16 chance_mod,int16 focus, int ReuseTime) { if (!CanDoSpecialAttack(other)) return; @@ -1268,9 +1284,12 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite if (!weapon_damage && item != nullptr) WDmg = GetWeaponDamage(other, item); - else + else WDmg = weapon_damage; + if (focus) //From FcBaseEffects + WDmg += WDmg*focus/100; + int32 TotalDmg = 0; uint32 Assassinate_Dmg = 0; @@ -1294,7 +1313,6 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite other->MeleeMitigation(this, TotalDmg, minDmg); if(TotalDmg > 0) { - TotalDmg += TotalDmg*focus/100; ApplyMeleeDamageBonus(SkillThrowing, TotalDmg); TotalDmg += other->GetFcDamageAmtIncoming(this, 0, true, SkillThrowing); TotalDmg += (itembonuses.HeroicDEX / 10) + (TotalDmg * other->GetSkillDmgTaken(SkillThrowing) / 100) + GetSkillDmgAmt(SkillThrowing); @@ -1309,10 +1327,25 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite TotalDmg = -5; other->Damage(this, TotalDmg, SPELL_UNKNOWN, SkillThrowing); + + if (TotalDmg > 0 && HasSkillProcSuccess() && GetTarget() && other && !other->HasDied()){ + if (ReuseTime) + TrySkillProc(other, SkillThrowing, ReuseTime); + else + TrySkillProc(other, SkillThrowing, 0, true, 11); + } } if((RangeWeapon != nullptr) && GetTarget() && other && (other->GetHP() > -10)) TryWeaponProc(RangeWeapon, other, 11); + + if (HasSkillProcs() && GetTarget() && other && !other->HasDied()){ + if (ReuseTime) + TrySkillProc(other, SkillThrowing, ReuseTime); + else + TrySkillProc(other, SkillThrowing, 0, false, 11); + } + } void Mob::SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skillInUse) { @@ -1891,6 +1924,7 @@ void Mob::Taunt(NPC* who, bool always_succeed, float chance_bonus) { Mob *hate_top = who->GetHateMost(); float level_difference = GetLevel() - who->GetLevel(); + bool Success = false; //Support for how taunt worked pre 2000 on LIVE - Can not taunt NPC over your level. if ((RuleB(Combat,TauntOverLevel) == false) && (level_difference < 0) || who->GetSpecialAbility(IMMUNE_TAUNT)){ @@ -1903,7 +1937,7 @@ void Mob::Taunt(NPC* who, bool always_succeed, float chance_bonus) { int32 newhate = 0; float tauntchance = 50.0f; - + if(always_succeed) tauntchance = 101.0f; @@ -1940,6 +1974,7 @@ void Mob::Taunt(NPC* who, bool always_succeed, float chance_bonus) { if (hate_top && hate_top != this){ newhate = (who->GetNPCHate(hate_top) - who->GetNPCHate(this)) + 1; who->CastToNPC()->AddToHateList(this, newhate); + Success = true; } else who->CastToNPC()->AddToHateList(this,12); @@ -1955,10 +1990,12 @@ void Mob::Taunt(NPC* who, bool always_succeed, float chance_bonus) { else Message_StringID(MT_SpellFailure,FAILED_TAUNT); - if (HasSkillProcs()){ - float chance = (float)TauntReuseTime*RuleR(Combat, AvgProcsPerMinute)/60000.0f; - TrySkillProc(who, SkillTaunt, chance); - } + if (HasSkillProcs()) + TrySkillProc(who, SkillTaunt, TauntReuseTime*1000); + + + if (Success && HasSkillProcSuccess()) + TrySkillProc(who, SkillTaunt, TauntReuseTime*1000, true); } @@ -2045,20 +2082,7 @@ float Mob::GetSpecialProcChances(uint16 hand) float ProcChance = 0.0f; float ProcBonus = 0.0f; - switch (hand) { - case 13: - weapon_speed = attack_timer.GetDuration(); - break; - case 14: - weapon_speed = attack_dw_timer.GetDuration(); - break; - case 11: - weapon_speed = ranged_timer.GetDuration(); - break; - } - - if (weapon_speed < RuleI(Combat, MinHastedDelay)) - weapon_speed = RuleI(Combat, MinHastedDelay); + weapon_speed = GetWeaponSpeedbyHand(hand); if (RuleB(Combat, AdjustSpecialProcPerMinute)) { ProcChance = (static_cast(weapon_speed) * @@ -2138,7 +2162,7 @@ float Mob::GetAssassinateProcChances(uint16 ReuseTime) return ProcChance; } -void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes skillinuse, int16 chance_mod, int16 focus, bool CanRiposte) +void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes skillinuse, int16 chance_mod, int16 focus, bool CanRiposte, int ReuseTime) { if (!CanDoSpecialAttack(other)) return; @@ -2155,6 +2179,9 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes if(weapon_damage > 0){ + if (focus) //From FcBaseEffects + weapon_damage += weapon_damage*focus/100; + if(GetClass() == BERSERKER){ int bonus = 3 + GetLevel()/10; weapon_damage = weapon_damage * (100+bonus) / 100; @@ -2190,7 +2217,6 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes other->AvoidDamage(this, damage, CanRiposte); other->MeleeMitigation(this, damage, min_hit); if(damage > 0) { - damage += damage*focus/100; ApplyMeleeDamageBonus(skillinuse, damage); damage += other->GetFcDamageAmtIncoming(this, 0, true, skillinuse); damage += (itembonuses.HeroicSTR / 10) + (damage * other->GetSkillDmgTaken(skillinuse) / 100) + GetSkillDmgAmt(skillinuse); @@ -2244,10 +2270,11 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes SpellFinished(904, other, 10, 0, -1, spells[904].ResistDiff); } - if (CanSkillProc && HasSkillProcs()){ - float chance = 10.0f*RuleR(Combat, AvgProcsPerMinute)/60000.0f; - TrySkillProc(other, skillinuse, chance); - } + if (CanSkillProc && HasSkillProcs()) + TrySkillProc(other, skillinuse, ReuseTime); + + if (CanSkillProc && (damage > 0) && HasSkillProcSuccess()) + TrySkillProc(other, skillinuse, ReuseTime, true); } bool Mob::CanDoSpecialAttack(Mob *other) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 61333edfd..84c1ec889 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1430,7 +1430,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) for(int x = 0; x < 7; x++){ SendWearChange(x); } - if(caster && caster->GetAA(aaPermanentIllusion)) + if(caster && (caster->spellbonuses.IllusionPersistence || caster->aabonuses.IllusionPersistence + || caster->itembonuses.IllusionPersistence)) buffs[buffslot].persistant_buff = 1; else buffs[buffslot].persistant_buff = 0; @@ -1772,20 +1773,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) break; } - case SE_SkillProc2: - case SE_SkillProc: - { - uint16 procid = GetProcID(spell_id, i); -#ifdef SPELL_EFFECT_SPAM - snprintf(effect_desc, _EDLEN, "Weapon Proc: %s (id %d)", spells[effect_value].name, procid); -#endif - if(spells[spell_id].base2[i] == 0) - AddSkillProc(procid, 100, spell_id); - else - AddSkillProc(procid, spells[spell_id].base2[i]+100, spell_id); - break; - } - case SE_NegateAttacks: { #ifdef SPELL_EFFECT_SPAM @@ -2256,6 +2243,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) *Max is lower value then Weapon base, possibly min hit vs Weapon Damage range ie. MakeRandInt(max,base) */ int16 focus = 0; + int ReuseTime = spells[spell_id].recast_time + spells[spell_id].recovery_time; if(caster->IsClient()) focus = caster->CastToClient()->GetFocusEffect(focusFcBaseEffects, spell_id); @@ -2263,15 +2251,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) switch(spells[spell_id].skill) { case SkillThrowing: - caster->DoThrowingAttackDmg(this, nullptr, nullptr, spells[spell_id].base[i],spells[spell_id].base2[i], focus); + caster->DoThrowingAttackDmg(this, nullptr, nullptr, spells[spell_id].base[i],spells[spell_id].base2[i], focus, ReuseTime); break; case SkillArchery: - caster->DoArcheryAttackDmg(this, nullptr, nullptr, spells[spell_id].base[i],spells[spell_id].base2[i],focus); + caster->DoArcheryAttackDmg(this, nullptr, nullptr, spells[spell_id].base[i],spells[spell_id].base2[i],focus,ReuseTime); break; default: - caster->DoMeleeSkillAttackDmg(this, spells[spell_id].base[i], spells[spell_id].skill, spells[spell_id].base2[i], focus); + caster->DoMeleeSkillAttackDmg(this, spells[spell_id].base[i], spells[spell_id].skill, spells[spell_id].base2[i], focus, ReuseTime); break; } break; @@ -2329,7 +2317,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if (buffslot >= 0) break; //This effect does no damage if target is moving. - if (IsMoving()) + if (!RuleB(Spells, PreNerfBardAEDoT) && IsMoving()) break; // for offensive spells check if we have a spell rune on @@ -2735,22 +2723,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) break; } - case SE_Sanctuary: - { - std::list npc_list; - entity_list.GetNPCList(npc_list); - - for(std::list::iterator itr = npc_list.begin(); itr != npc_list.end(); ++itr) { - - NPC* npc = *itr; - - if (npc && npc->CheckAggro(this)) - npc->SetHate(caster, 1); - - } - break; - } - // Handled Elsewhere case SE_ImmuneFleeing: case SE_NegateSpellEffect: @@ -2984,6 +2956,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_AssassinateLevel: case SE_FactionModPct: case SE_LimitSpellClass: + case SE_Sanctuary: + case SE_PetMeleeMitigation: + case SE_SkillProc: + case SE_SkillProcSuccess: { break; } @@ -3403,7 +3379,7 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste { effect_value = CalcSpellEffectValue(spell_id, i, caster_level, caster); - if (IsMoving() || invulnerable || /*effect_value > 0 ||*/ DivineAura()) + if ((!RuleB(Spells, PreNerfBardAEDoT) && IsMoving()) || invulnerable || /*effect_value > 0 ||*/ DivineAura()) break; if(effect_value < 0) { @@ -3630,21 +3606,6 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste break; } - case SE_Sanctuary: - { - std::list npc_list; - entity_list.GetNPCList(npc_list); - - for(std::list::iterator itr = npc_list.begin(); itr != npc_list.end(); ++itr) { - - NPC* npc = *itr; - - if (npc && npc->CheckAggro(this)) - npc->SetHate(caster, 1); - - } - break; - } default: { @@ -3712,14 +3673,6 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) break; } - case SE_SkillProc2: - case SE_SkillProc: - { - uint16 procid = GetProcID(buffs[slot].spellid, i); - RemoveSkillProc(procid); - break; - } - case SE_DefensiveProc: { uint16 procid = GetProcID(buffs[slot].spellid, i); @@ -4084,39 +4037,41 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) CalcBonuses(); } -/* No longer used. -int16 Client::CalcAAFocusEffect(focusType type, uint16 focus_spell, uint16 spell_id) +int32 Client::GetAAEffectDataBySlot(uint32 aa_ID, uint32 slot_id, bool GetEffect, bool GetBase1, bool GetBase2) { - uint32 slots = 0; - uint32 aa_AA = 0; - uint32 aa_value = 0; + int32 aa_effects_data[3] = { 0 }; + uint32 effect = 0; + int32 base1 = 0; + int32 base2 = 0; + uint32 slot = 0; - int32 value = 0; - // Iterate through all of the client's AAs - for (int i = 0; i < MAX_PP_AA_ARRAY; i++) + + std::map >::const_iterator find_iter = aa_effects.find(aa_ID); + if(find_iter == aa_effects.end()) + return 0; + + for (std::map::const_iterator iter = aa_effects[aa_ID].begin(); iter != aa_effects[aa_ID].end(); ++iter) { - aa_AA = this->aa[i]->AA; - aa_value = this->aa[i]->value; - if (aa_AA > 0 || aa_value > 0) - { - slots = zone->GetTotalAALevels(aa_AA); - if (slots > 0) - for(int j = 1;j <= slots; j++) - { - switch (aa_effects[aa_AA][j].skill_id) - { - case SE_TriggerOnCast: - // If focus_spell matches the spell listed in the DB, load these restrictions - if(type == focusTriggerOnCast && focus_spell == aa_effects[aa_AA][j].base1) - value = CalcAAFocus(type, aa_AA, spell_id); - break; - } - } + effect = iter->second.skill_id; + base1 = iter->second.base1; + base2 = iter->second.base2; + slot = iter->second.slot; + + if (slot && slot == slot_id) { + + if (GetEffect) + return effect; + + if (GetBase1) + return base1; + + if (GetBase2) + return base2; } } - return value; + + return 0; } -*/ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) @@ -4496,9 +4451,11 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) value = base1; break; - case SE_SympatheticProc: - //No AA support at this time. - break; + //Note if using these as AA, make sure this is first focus used. + case SE_SympatheticProc: + if(type == focusSympatheticProc) + value = base2; + break; case SE_FcDamageAmt: if(type == focusFcDamageAmt) @@ -4962,16 +4919,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_SympatheticProc: if(type == focusSympatheticProc) { - float ProcChance, ProcBonus; - int16 ProcRateMod = focus_spell.base[i]; //Baseline is 100 for most Sympathetic foci - int32 cast_time = GetActSpellCasttime(spell_id, spells[spell_id].cast_time); - GetSympatheticProcChances(ProcBonus, ProcChance, cast_time, ProcRateMod); - - if(MakeRandomFloat(0, 1) <= ProcChance) - value = focus_id; - - else - value = 0; + value = focus_id; } break; @@ -5089,8 +5037,8 @@ int16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { return 0; uint16 proc_spellid = 0; - uint8 SizeProcList = 0; uint8 MAX_SYMPATHETIC = 10; + float ProcChance = 0.0f; std::vector SympatheticProcList; @@ -5101,7 +5049,7 @@ int16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { for(int x=0; x<=21; x++) { - if (SizeProcList > MAX_SYMPATHETIC) + if (SympatheticProcList.size() > MAX_SYMPATHETIC) continue; TempItem = nullptr; @@ -5109,20 +5057,22 @@ int16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { if (!ins) continue; TempItem = ins->GetItem(); - if (TempItem && TempItem->Focus.Effect > 0 && TempItem->Focus.Effect != SPELL_UNKNOWN) { + if (TempItem && TempItem->Focus.Effect > 0 && IsValidSpell(TempItem->Focus.Effect)) { - proc_spellid = CalcFocusEffect(type, TempItem->Focus.Effect, spell_id); + proc_spellid = CalcFocusEffect(type, TempItem->Focus.Effect, spell_id); + + if (IsValidSpell(proc_spellid)){ - if (proc_spellid > 0) - { - SympatheticProcList.push_back(proc_spellid); - SizeProcList = SympatheticProcList.size(); - } + ProcChance = GetSympatheticProcChances(spell_id, spells[TempItem->Focus.Effect].base[0], TempItem->ProcRate); + + if(MakeRandomFloat(0, 1) <= ProcChance) + SympatheticProcList.push_back(proc_spellid); + } } for(int y = 0; y < MAX_AUGMENT_SLOTS; ++y) { - if (SizeProcList > MAX_SYMPATHETIC) + if (SympatheticProcList.size() > MAX_SYMPATHETIC) continue; ItemInst *aug = nullptr; @@ -5130,14 +5080,16 @@ int16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { if(aug) { const Item_Struct* TempItemAug = aug->GetItem(); - if (TempItemAug && TempItemAug->Focus.Effect > 0 && TempItemAug->Focus.Effect != SPELL_UNKNOWN) { + if (TempItemAug && TempItemAug->Focus.Effect > 0 && IsValidSpell(TempItemAug->Focus.Effect)) { proc_spellid = CalcFocusEffect(type, TempItemAug->Focus.Effect, spell_id); - if (proc_spellid > 0) - { - SympatheticProcList.push_back(proc_spellid); - SizeProcList = SympatheticProcList.size(); + if (IsValidSpell(proc_spellid)){ + + ProcChance = GetSympatheticProcChances(spell_id, spells[TempItem->Focus.Effect].base[0], TempItemAug->ProcRate); + + if(MakeRandomFloat(0, 1) <= ProcChance) + SympatheticProcList.push_back(proc_spellid); } } } @@ -5152,26 +5104,57 @@ int16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { uint32 buff_max = GetMaxTotalSlots(); for (buff_slot = 0; buff_slot < buff_max; buff_slot++) { - if (SizeProcList > MAX_SYMPATHETIC) + if (SympatheticProcList.size() > MAX_SYMPATHETIC) continue; focusspellid = buffs[buff_slot].spellid; - if (focusspellid == 0 || focusspellid >= SPDAT_RECORDS) + if (IsValidSpell(focusspellid)) continue; proc_spellid = CalcFocusEffect(type, focusspellid, spell_id); - if (proc_spellid > 0) - { - SympatheticProcList.push_back(proc_spellid); - SizeProcList = SympatheticProcList.size(); - } + if (IsValidSpell(proc_spellid)){ + + ProcChance = GetSympatheticProcChances(spell_id, spells[focusspellid].base[0]); + + if(MakeRandomFloat(0, 1) <= ProcChance) + SympatheticProcList.push_back(proc_spellid); + } } } - if (SizeProcList > 0) + /*Note: At present, ff designing custom AA to have a sympathetic proc effect, only use one focus + effect within the aa_effects data for each AA*[No live AA's use this effect to my knowledge]*/ + if (aabonuses.FocusEffects[type]){ + + uint32 aa_AA = 0; + uint32 aa_value = 0; + + for (int i = 0; i < MAX_PP_AA_ARRAY; i++) + { + aa_AA = this->aa[i]->AA; + aa_value = this->aa[i]->value; + if (aa_AA < 1 || aa_value < 1) + continue; + + if (SympatheticProcList.size() > MAX_SYMPATHETIC) + continue; + + proc_spellid = CalcAAFocus(type, aa_AA, spell_id); + + if (IsValidSpell(proc_spellid)){ + + ProcChance = GetSympatheticProcChances(spell_id, GetAABase1(aa_AA, 1)); + + if(MakeRandomFloat(0, 1) <= ProcChance) + SympatheticProcList.push_back(proc_spellid); + } + } + } + + if (SympatheticProcList.size() > 0) { - uint8 random = MakeRandomInt(0, SizeProcList-1); + uint8 random = MakeRandomInt(0, SympatheticProcList.size()-1); int FinalSympatheticProc = SympatheticProcList[random]; SympatheticProcList.clear(); return FinalSympatheticProc; @@ -5674,17 +5657,31 @@ bool Mob::AffectedBySpellExcludingSlot(int slot, int effect) return false; } -float Mob::GetSympatheticProcChances(float &ProcBonus, float &ProcChance, int32 cast_time, int16 ProcRateMod) { +float Mob::GetSympatheticProcChances(uint16 spell_id, int16 ProcRateMod, int32 ItemProcRate) { - ProcChance = 0; + float ProcChance = 0.0f; + int32 total_cast_time = 0; + float cast_time_mod = 0.0f; + ProcRateMod -= 100; + - if(cast_time > 0) - { - ProcChance = ((float)cast_time * RuleR(Casting, AvgSpellProcsPerMinute) / 60000.0f); - ProcChance = ProcChance * (float)(ProcRateMod/100); - } - return ProcChance; -} + if (spells[spell_id].recast_time >= spells[spell_id].recovery_time) + total_cast_time = spells[spell_id].recast_time + spells[spell_id].cast_time; + else + total_cast_time = spells[spell_id].recovery_time + spells[spell_id].cast_time; + + if (total_cast_time > 0 && total_cast_time <= 2500) + cast_time_mod = 0.25f; + else if (total_cast_time > 2500 && total_cast_time < 7000) + cast_time_mod = 0.167*((static_cast(total_cast_time) - 1000.0f)/1000.0f); + else + cast_time_mod = static_cast(total_cast_time) / 7000.0f; + + ProcChance = (RuleR(Casting, AvgSpellProcsPerMinute)/100.0f) * (static_cast(100.0f + ProcRateMod) / 10.0f) + * cast_time_mod * (static_cast(100.0f + ItemProcRate)/100.0f); + + return ProcChance; + } bool Mob::DoHPToManaCovert(uint16 mana_cost) { diff --git a/zone/spells.cpp b/zone/spells.cpp index 6a87f2409..f8ce8baa0 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -5103,37 +5103,6 @@ bool Mob::RemoveDefensiveProc(uint16 spell_id, bool bAll) return true; } -bool Mob::AddSkillProc(uint16 spell_id, uint16 iChance, uint16 base_spell_id) -{ - if(spell_id == SPELL_UNKNOWN) - return(false); - - int i; - for (i = 0; i < MAX_PROCS; i++) { - if (SkillProcs[i].spellID == SPELL_UNKNOWN) { - SkillProcs[i].spellID = spell_id; - SkillProcs[i].chance = iChance; - SkillProcs[i].base_spellID = base_spell_id; - mlog(SPELLS__PROCS, "Added spell-granted skill proc spell %d with chance %d to slot %d", spell_id, iChance, i); - return true; - } - } - return false; -} - -bool Mob::RemoveSkillProc(uint16 spell_id, bool bAll) -{ - for (int i = 0; i < MAX_PROCS; i++) { - if (bAll || SkillProcs[i].spellID == spell_id) { - SkillProcs[i].spellID = SPELL_UNKNOWN; - SkillProcs[i].chance = 0; - SkillProcs[i].base_spellID = SPELL_UNKNOWN; - mlog(SPELLS__PROCS, "Removed Skill proc %d from slot %d", spell_id, i); - } - } - return true; -} - bool Mob::AddRangedProc(uint16 spell_id, uint16 iChance, uint16 base_spell_id) { if(spell_id == SPELL_UNKNOWN)