diff --git a/changelog.txt b/changelog.txt index 43c045ce8..53585d982 100644 --- a/changelog.txt +++ b/changelog.txt @@ -12,8 +12,11 @@ Kayen: Update SE_AETaunt - Base value will now determine AE taunt range (This wi Kayen: Udpated SE_ReclaimPet - Correct forumla for mana returned to properly return 75% of actual pet spell mana cost. Kayen: Implemented SE_ImprovedReclaimEnergy - Modifies % mana returned from SE_ReclaimPet. Kayen: Implemented SE_HeadShot, SE_HeadShotLevel - Defines headshot damage and level requirements. -Revised HeadShot mechanic so damage now recieves all archery bonuses, proc chance can be set to either (lives new Proc Per minute +Revised HeadShot mechanic so damage now receives all archery bonuses, proc chance can be set to either (lives new Proc Per minute system, or flat chance based on dex (formula updated). +Kayen: Implemented SE_Assassinate, SE_AssassinateLevel - Defines assassinate damage and level requirements. +Revised Assassinate mechanic so damage now receives all backstab bonuses, proc chance can be set to either (lives new Proc Per minute +system, or flat chance based on dex (formula updated). Assassinate can now proc from THROW if behind target, various other adjustments. Required SQL: utils/sql/git/required/2014_06_25_AA_Update.sql Optional SQL: utils/sql/git/optiional/2014_06_29_HeadShotRules.sql diff --git a/utils/sql/git/required/2014_06_25_AA_Updates..sql b/utils/sql/git/required/2014_06_25_AA_Updates..sql index c9ae42237..d51948af9 100644 --- a/utils/sql/git/required/2014_06_25_AA_Updates..sql +++ b/utils/sql/git/required/2014_06_25_AA_Updates..sql @@ -13,5 +13,11 @@ INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('644', '1', '217', '0', '32000'); INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('644', '2', '346', '46', '0'); --- spells_new update -ALTER TABLE `spells_new` CHANGE `field175` `numhits_type` INT(11) NOT NULL DEFAULT '0'; \ No newline at end of file +-- AA Anatomy (Rogue Assassinate) +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 ('1604', 'Anatomy', '5', '3', '4294967295', '4294967295', '1604', '1604', '1', '4294967295', '0', '0', '0', '0', '512', '0', '60', '1', '10', '4294967295', '3', '0', '3', '1604', '1', '0', '0', '0', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1604', '1', '439', '0', '32000'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1604', '2', '345', '48', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1605', '1', '439', '0', '32000'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1605', '2', '345', '51', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1606', '1', '439', '0', '32000'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1606', '2', '345', '53', '0'); \ No newline at end of file diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 345f602f8..0871e3679 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1296,6 +1296,22 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) break; } + case SE_Assassinate: + { + if(newbon->Assassinate[1] < base2){ + newbon->Assassinate[0] = base1; + newbon->Assassinate[1] = base2; + } + break; + } + + case SE_AssassinateLevel: + { + if(newbon->AssassinateLevel < base1) + newbon->AssassinateLevel = base1; + break; + } + } } } diff --git a/zone/common.h b/zone/common.h index 95f1df783..6d6ae581f 100644 --- a/zone/common.h +++ b/zone/common.h @@ -423,9 +423,9 @@ struct StatBonuses { int8 IncreaseChanceMemwipe; // increases chance to memory wipe int8 CriticalMend; // chance critical monk mend int16 ImprovedReclaimEnergy; // Modifies amount of mana returned from reclaim energy - int32 HeadShot[2]; // Headshot AA (Massive dmg vs humaniod w/ archery) 0= ? 1= Dmg + uint32 HeadShot[2]; // Headshot AA (Massive dmg vs humaniod w/ archery) 0= ? 1= Dmg uint8 HSLevel; // Max Level Headshot will be effective at. - int32 Assassinate[2]; // Assassinate AA (Massive dmg vs humaniod w/ assassinate) 0= ? 1= Dmg + uint32 Assassinate[2]; // Assassinate AA (Massive dmg vs humaniod w/ assassinate) 0= ? 1= Dmg uint8 AssassinateLevel; // Max Level Assassinate will be effective at. }; diff --git a/zone/mob.h b/zone/mob.h index b5e21c932..5c1240e83 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -142,6 +142,7 @@ public: void TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage); virtual bool TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse); uint32 TryHeadShot(Mob* defender, SkillUseTypes skillInUse); + uint32 TryAssassinate(Mob* defender, SkillUseTypes skillInUse, uint16 ReuseTime); virtual void DoRiposte(Mob* defender); void ApplyMeleeDamageBonus(uint16 skill, int32 &damage); virtual void MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit, ExtraAttackOptions *opts = nullptr); @@ -694,7 +695,7 @@ public: int32 AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTic, Mob* attacker); 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); + 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); @@ -991,7 +992,8 @@ protected: void ExecWeaponProc(const ItemInst* weapon, uint16 spell_id, Mob *on); virtual float GetProcChances(float ProcBonus, uint16 weapon_speed = 30, uint16 hand = 13); virtual float GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed = 30, uint16 hand = 13); - virtual float GetSpecialProcChances(uint16 hand = 13); + virtual float GetSpecialProcChances(uint16 hand); + virtual float GetAssassinateProcChances(uint16 ReuseTime); int GetWeaponDamage(Mob *against, const Item_Struct *weapon_item); int GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate = nullptr); int GetKickDamage(); diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 55e919bc3..4ffa21ab4 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -100,7 +100,8 @@ void Mob::ApplySpecialAttackMod(SkillUseTypes skill, int32 &dmg, int32 &mindmg) } } -void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, int32 min_damage, int32 hate_override,int ReuseTime, bool HitChance) { +void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, int32 min_damage, int32 hate_override,int ReuseTime, + bool HitChance, bool CanAvoid) { //this really should go through the same code as normal melee damage to //pick up all the special behavior there @@ -135,7 +136,9 @@ void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, if(skill == SkillThrowing || skill == SkillArchery) // changed from '&&' CanRiposte = false; - who->AvoidDamage(this, max_damage, CanRiposte); + if (CanAvoid) + who->AvoidDamage(this, max_damage, CanRiposte); + who->MeleeMitigation(this, max_damage, min_damage); if(max_damage > 0) { @@ -373,8 +376,8 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) { if (ca_atk->m_atk != 100 || ca_atk->m_skill != SkillBackstab) { break; } - TryBackstab(GetTarget(), ReuseTime); ReuseTime = BackstabReuseTime-1 - skill_reduction; + TryBackstab(GetTarget(), ReuseTime); break; } default: @@ -527,64 +530,47 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) { if (FrontalBSChance && (FrontalBSChance > MakeRandomInt(0, 100))) bCanFrontalBS = true; } - + if (bIsBehind || bCanFrontalBS){ // Player is behind other OR can do Frontal Backstab - if (bCanFrontalBS) { + if (bCanFrontalBS) CastToClient()->Message(0,"Your fierce attack is executed with such grace, your target did not see it coming!"); - } + + RogueBackstab(other,false,ReuseTime); + if (level > 54) { - // solar - chance to assassinate - int chance = 10 + (GetDEX()/10) + (itembonuses.HeroicDEX/10); //18.5% chance at 85 dex 40% chance at 300 dex - if( - level >= 60 && // player is 60 or higher - other->GetLevel() <= 45 && // mob 45 or under - !other->CastToNPC()->IsEngaged() && // not aggro - other->GetHP()<=32000 - && other->IsNPC() - && MakeRandomFloat(0, 99) < chance // chance - ) { - entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, ASSASSINATES, GetName()); - if(IsClient()) - CastToClient()->CheckIncreaseSkill(SkillBackstab, other, 10); - RogueAssassinate(other); - } - else { - RogueBackstab(other); - if (level > 54) { - float DoubleAttackProbability = (GetSkill(SkillDoubleAttack) + GetLevel()) / 500.0f; // 62.4 max - // Check for double attack with main hand assuming maxed DA Skill (MS) + if(IsClient() && CastToClient()->CheckDoubleAttack(false)) + { + if(other->GetHP() > 0) + RogueBackstab(other,false,ReuseTime); - if(MakeRandomFloat(0, 1) < DoubleAttackProbability) // Max 62.4 % chance of DA - { - if(other->GetHP() > 0) - RogueBackstab(other,false,ReuseTime); - - if (tripleChance && other->GetHP() > 0 && tripleChance > MakeRandomInt(0, 100)) - RogueBackstab(other,false,ReuseTime); - } + if (tripleChance && other->GetHP() > 0 && tripleChance > MakeRandomInt(0, 100)) + RogueBackstab(other,false,ReuseTime); } - if(IsClient()) - CastToClient()->CheckIncreaseSkill(SkillBackstab, other, 10); } + + if(IsClient()) + CastToClient()->CheckIncreaseSkill(SkillBackstab, other, 10); + } //Live AA - Chaotic Backstab else if(aabonuses.FrontalBackstabMinDmg || itembonuses.FrontalBackstabMinDmg || spellbonuses.FrontalBackstabMinDmg) { //we can stab from any angle, we do min damage though. - RogueBackstab(other, true); + RogueBackstab(other, true, ReuseTime); if (level > 54) { - float DoubleAttackProbability = (GetSkill(SkillDoubleAttack) + GetLevel()) / 500.0f; // 62.4 max - if(IsClient()) - CastToClient()->CheckIncreaseSkill(SkillBackstab, other, 10); + // Check for double attack with main hand assuming maxed DA Skill (MS) - if(MakeRandomFloat(0, 1) < DoubleAttackProbability) // Max 62.4 % chance of DA + if(IsClient() && CastToClient()->CheckDoubleAttack(false)) if(other->GetHP() > 0) RogueBackstab(other,true, ReuseTime); if (tripleChance && other->GetHP() > 0 && tripleChance > MakeRandomInt(0, 100)) RogueBackstab(other,false,ReuseTime); } + + if(IsClient()) + CastToClient()->CheckIncreaseSkill(SkillBackstab, other, 10); } else { //We do a single regular attack if we attack from the front without chaotic stab Attack(other, 13); @@ -594,6 +580,9 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) { //heko: backstab void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime) { + if (!other) + return; + int32 ndamage = 0; int32 max_hit = 0; int32 min_hit = 0; @@ -668,12 +657,20 @@ void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime) } ndamage = mod_backstab_damage(ndamage); + + uint32 Assassinate_Dmg = 0; + Assassinate_Dmg = TryAssassinate(other, SkillBackstab, ReuseTime); + + if (Assassinate_Dmg) { + ndamage = Assassinate_Dmg; + entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, ASSASSINATES, GetName()); + } - DoSpecialAttackDamage(other, SkillBackstab, ndamage, min_hit, hate, ReuseTime); + DoSpecialAttackDamage(other, SkillBackstab, ndamage, min_hit, hate, ReuseTime, false, false); DoAnim(animPiercing); } -// solar - assassinate +// solar - assassinate [Kayen: No longer used for regular assassinate 6-29-14] void Mob::RogueAssassinate(Mob* other) { //can you dodge, parry, etc.. an assassinate?? @@ -1276,13 +1273,24 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite int32 TotalDmg = 0; + uint32 Assassinate_Dmg = 0; + if (GetClass() == ROGUE && (BehindMob(other, GetX(), GetY()))) + Assassinate_Dmg = TryAssassinate(other, SkillThrowing, ranged_timer.GetDuration()); + if(WDmg > 0) { int minDmg = 1; uint16 MaxDmg = GetThrownDamage(WDmg, TotalDmg, minDmg); + if (Assassinate_Dmg) { + TotalDmg = Assassinate_Dmg; + entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, ASSASSINATES, GetName()); + } + mlog(COMBAT__RANGED, "Item DMG %d. Max Damage %d. Hit for damage %d", WDmg, MaxDmg, TotalDmg); - other->AvoidDamage(this, TotalDmg, false); //CanRiposte=false - Can not riposte throw attacks. + if (!Assassinate_Dmg) + other->AvoidDamage(this, TotalDmg, false); //CanRiposte=false - Can not riposte throw attacks. + other->MeleeMitigation(this, TotalDmg, minDmg); if(TotalDmg > 0) { @@ -1866,92 +1874,6 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) } } -/* -void Mob::Taunt(NPC* who, bool always_succeed, float chance_bonus) { - - if (who == nullptr) - return; - - if(DivineAura()) - return; - - if(!CombatRange(who)) - return; - - if(!always_succeed && IsClient()) - CastToClient()->CheckIncreaseSkill(TAUNT, who, 10); - - int level = GetLevel(); - Mob *hate_top = who->GetHateMost(); - - // Check to see if we're already at the top of the target's hate list - // a mob will not be taunted if its target's health is below 20% - if ((hate_top != this) - && (who->GetLevel() < level) - && (hate_top == nullptr || hate_top->GetHPRatio() >= 20) ) { - int32 newhate, tauntvalue; - - float tauntchance; - if(always_succeed) { - tauntchance = 101; - } else { - - // no idea how taunt success is actually calculated - // TODO: chance for level 50+ mobs should be lower - int level_difference = level - who->GetLevel(); - if (level_difference <= 5) { - tauntchance = 25.0; // minimum - tauntchance += tauntchance * (float)GetSkill(TAUNT) / 200.0; // skill modifier - if (tauntchance > 65.0) - tauntchance = 65.0; - } - else if (level_difference <= 10) { - tauntchance = 30.0; // minimum - tauntchance += tauntchance * (float)GetSkill(TAUNT) / 200.0; // skill modifier - if (tauntchance > 85.0) - tauntchance = 85.0; - } - else if (level_difference <= 15) { - tauntchance = 40.0; // minimum - tauntchance += tauntchance * (float)GetSkill(TAUNT) / 200.0; // skill modifier - if (tauntchance > 90.0) - tauntchance = 90.0; - } - else { - tauntchance = 50.0; // minimum - tauntchance += tauntchance * (float)GetSkill(TAUNT) / 200.0; // skill modifier - if (tauntchance > 95.0) - tauntchance = 95.0; - } - } - - if (chance_bonus) - tauntchance = tauntchance + (tauntchance*chance_bonus/100.0f); - - if (tauntchance > MakeRandomFloat(0, 100)) { - // this is the max additional hate added per succesfull taunt - tauntvalue = (MakeRandomInt(2, 4) * level); - //tauntvalue = (int32) ((float)level * 10.0 * (float)rand()/(float)RAND_MAX + 1); - // new hate: find diff of player's hate and whoever's at top of list, add that plus tauntvalue to players hate - newhate = who->GetNPCHate(hate_top) - who->GetNPCHate(this) + tauntvalue; - // add the hate - who->CastToNPC()->AddToHateList(this, newhate); - } - else{ - //generate at least some hate reguardless of the outcome. - who->CastToNPC()->AddToHateList(this, (MakeRandomInt(2, 4)*level)); - } - } - - //generate at least some hate reguardless of the outcome. - who->CastToNPC()->AddToHateList(this, (MakeRandomInt(2, 4)*level)); - if (HasSkillProcs()){ - float chance = (float)TauntReuseTime*RuleR(Combat, AvgProcsPerMinute)/60000.0f; - TrySkillProc(who, TAUNT, chance); - } -} -*/ - void Mob::Taunt(NPC* who, bool always_succeed, float chance_bonus) { if (who == nullptr) @@ -2089,9 +2011,10 @@ void Mob::InstillDoubt(Mob *who) { uint32 Mob::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) { //Only works on YOUR target. - if(defender && (skillInUse == SkillArchery) && (GetTarget() == defender)) { + if(defender && (defender->GetBodyType() == BT_Humanoid) && !defender->IsClient() + && (skillInUse == SkillArchery) && (GetTarget() == defender)) { - int32 HeadShot_Dmg = aabonuses.HeadShot[1] + spellbonuses.HeadShot[1] + itembonuses.HeadShot[1]; + uint32 HeadShot_Dmg = aabonuses.HeadShot[1] + spellbonuses.HeadShot[1] + itembonuses.HeadShot[1]; uint8 HeadShot_Level = 0; //Get Highest Headshot Level HeadShot_Level = aabonuses.HSLevel; @@ -2100,14 +2023,11 @@ uint32 Mob::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) { else if (HeadShot_Level < itembonuses.HSLevel) HeadShot_Level = itembonuses.HSLevel; - if(HeadShot_Dmg && defender->GetBodyType() == BT_Humanoid) { - if(HeadShot_Level && (defender->GetLevel() <= HeadShot_Level) && !defender->IsClient()){ + if(HeadShot_Dmg && HeadShot_Level && (defender->GetLevel() <= HeadShot_Level)){ - float ProcChance = GetSpecialProcChances(11); - if(ProcChance > MakeRandomFloat(0,1)) - return HeadShot_Dmg; - - } + float ProcChance = GetSpecialProcChances(11); + if(ProcChance > MakeRandomFloat(0,1)) + return HeadShot_Dmg; } } @@ -2119,7 +2039,7 @@ float Mob::GetSpecialProcChances(uint16 hand) int mydex = GetDEX(); if (mydex > 255) - mydex = 255; + mydex = 255; uint16 weapon_speed; float ProcChance = 0.0f; @@ -2155,6 +2075,69 @@ float Mob::GetSpecialProcChances(uint16 hand) return ProcChance; } +uint32 Mob::TryAssassinate(Mob* defender, SkillUseTypes skillInUse, uint16 ReuseTime) { + + if(defender && (defender->GetBodyType() == BT_Humanoid) && !defender->IsClient() && + (skillInUse == SkillBackstab || skillInUse == SkillThrowing)) { + + uint32 Assassinate_Dmg = aabonuses.Assassinate[1] + spellbonuses.Assassinate[1] + itembonuses.Assassinate[1]; + + uint8 Assassinate_Level = 0; //Get Highest Headshot Level + Assassinate_Level = aabonuses.AssassinateLevel; + if (Assassinate_Level < spellbonuses.AssassinateLevel) + Assassinate_Level = spellbonuses.AssassinateLevel; + else if (Assassinate_Level < itembonuses.AssassinateLevel) + Assassinate_Level = itembonuses.AssassinateLevel; + + if (GetLevel() >= 60){ //Innate Assassinate Ability if client as no bonuses. + if (!Assassinate_Level) + Assassinate_Level = 45; + + if (!Assassinate_Dmg) + Assassinate_Dmg = 32000; + } + + if(Assassinate_Dmg && Assassinate_Level && (defender->GetLevel() <= Assassinate_Level)){ + float ProcChance = 0.0f; + + if (skillInUse == SkillThrowing) + ProcChance = GetSpecialProcChances(11); + else + ProcChance = GetAssassinateProcChances(ReuseTime); + + if(ProcChance > MakeRandomFloat(0,1)) + return Assassinate_Dmg; + } + } + + return 0; +} + +float Mob::GetAssassinateProcChances(uint16 ReuseTime) +{ + int mydex = GetDEX(); + + if (mydex > 255) + mydex = 255; + + float ProcChance = 0.0f; + float ProcBonus = 0.0f; + + if (RuleB(Combat, AdjustSpecialProcPerMinute)) { + ProcChance = (static_cast(ReuseTime*1000) * + RuleR(Combat, AvgSpecialProcsPerMinute) / 60000.0f); + ProcBonus += (10 + (static_cast(mydex/10) + static_cast(itembonuses.HeroicDEX /10)))/100.0f; + ProcChance += ProcChance * ProcBonus / 100.0f; + + } else { + /*Kayen: Unable to find data on old proc rate of assassinate, no idea if our formula is real or made up.*/ + ProcChance = (10 + (static_cast(mydex/10) + static_cast(itembonuses.HeroicDEX /10)))/100.0f; + + } + + return ProcChance; +} + void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes skillinuse, int16 chance_mod, int16 focus, bool CanRiposte) { if (!CanDoSpecialAttack(other))