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
system, or flat chance based on dex (formula updated).

Required SQL for AA data
Optional SQL for rules
This commit is contained in:
KayenEQ
2014-06-29 01:10:19 -04:00
parent ce2a79b63e
commit 8a92fada5a
11 changed files with 176 additions and 32 deletions
+71 -2
View File
@@ -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)
{
@@ -1279,6 +1279,23 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
newbon->ImprovedReclaimEnergy = base1;
break;
}
case SE_HeadShot:
{
if(newbon->HeadShot[1] < base2){
newbon->HeadShot[0] = base1;
newbon->HeadShot[1] = base2;
}
break;
}
case SE_HeadShotLevel:
{
if(newbon->HSLevel < base1)
newbon->HSLevel = base1;
break;
}
}
}
}
@@ -2760,6 +2777,38 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
break;
}
case SE_HeadShot:
{
if(newbon->HeadShot[1] < base2){
newbon->HeadShot[0] = effect_value;
newbon->HeadShot[1] = base2;
}
break;
}
case SE_HeadShotLevel:
{
if(newbon->HSLevel < effect_value)
newbon->HSLevel = effect_value;
break;
}
case SE_Assassinate:
{
if(newbon->Assassinate[1] < base2){
newbon->Assassinate[0] = effect_value;
newbon->Assassinate[1] = base2;
}
break;
}
case SE_AssassinateLevel:
{
if(newbon->AssassinateLevel < effect_value)
newbon->AssassinateLevel = effect_value;
break;
}
//Special custom cases for loading effects on to NPC from 'npc_spels_effects' table
if (IsAISpellEffect) {
@@ -4173,7 +4222,27 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
aabonuses.Metabolism = effect_value;
itembonuses.Metabolism = effect_value;
break;
case SE_ImprovedReclaimEnergy:
spellbonuses.ImprovedReclaimEnergy = effect_value;
aabonuses.ImprovedReclaimEnergy = effect_value;
itembonuses.ImprovedReclaimEnergy = effect_value;
break;
case SE_HeadShot:
spellbonuses.HeadShot[0] = effect_value;
aabonuses.HeadShot[0] = effect_value;
itembonuses.HeadShot[0] = effect_value;
spellbonuses.HeadShot[1] = effect_value;
aabonuses.HeadShot[1] = effect_value;
itembonuses.HeadShot[1] = effect_value;
break;
case SE_HeadShotLevel:
spellbonuses.HSLevel = effect_value;
aabonuses.HSLevel = effect_value;
itembonuses.HSLevel = effect_value;
}
}
}
+1 -1
View File
@@ -1864,7 +1864,7 @@ uint16 Mob::GetInstrumentMod(uint16 spell_id) const
break;
}
effectmodcap += aabonuses.songModCap + spellbonuses.songModCap;
effectmodcap += aabonuses.songModCap + spellbonuses.songModCap + itembonuses.songModCap;
if (effectmod < 10)
effectmod = 10;
+6 -1
View File
@@ -422,7 +422,12 @@ struct StatBonuses {
int8 StunBashChance; // chance to stun with bash.
int8 IncreaseChanceMemwipe; // increases chance to memory wipe
int8 CriticalMend; // chance critical monk mend
int16 ImprovedReclaimEnergy; // Modifies amount of mana returned from reclaim energy
int16 ImprovedReclaimEnergy; // Modifies amount of mana returned from reclaim energy
int32 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
uint8 AssassinateLevel; // Max Level Assassinate will be effective at.
};
typedef struct
+2 -1
View File
@@ -141,7 +141,7 @@ public:
virtual void TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttackOptions *opts = nullptr);
void TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage);
virtual bool TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse);
virtual bool TryHeadShot(Mob* defender, SkillUseTypes skillInUse);
uint32 TryHeadShot(Mob* defender, SkillUseTypes skillInUse);
virtual void DoRiposte(Mob* defender);
void ApplyMeleeDamageBonus(uint16 skill, int32 &damage);
virtual void MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit, ExtraAttackOptions *opts = nullptr);
@@ -991,6 +991,7 @@ 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);
int GetWeaponDamage(Mob *against, const Item_Struct *weapon_item);
int GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate = nullptr);
int GetKickDamage();
+78 -23
View File
@@ -861,8 +861,12 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item
} else {
mlog(COMBAT__RANGED, "Ranged attack hit %s.", other->GetName());
if(!TryHeadShot(other, SkillArchery))
{
bool HeadShot = false;
uint32 HeadShot_Dmg = TryHeadShot(other, SkillArchery);
if (HeadShot_Dmg)
HeadShot = true;
int32 TotalDmg = 0;
int16 WDmg = 0;
int16 ADmg = 0;
@@ -882,6 +886,9 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item
uint32 MaxDmg = (RuleR(Combat, ArcheryBaseDamageBonus)*(WDmg+ADmg)*GetDamageTable(SkillArchery)) / 100;
int32 hate = ((WDmg+ADmg));
if (HeadShot)
MaxDmg = HeadShot_Dmg;
uint16 bonusArcheryDamageModifier = aabonuses.ArcheryDamageModifier + itembonuses.ArcheryDamageModifier + spellbonuses.ArcheryDamageModifier;
MaxDmg += MaxDmg*bonusArcheryDamageModifier / 100;
@@ -938,7 +945,9 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item
hate += (2*((GetLevel()-25)/3));
}
other->AvoidDamage(this, TotalDmg, false);
if (!HeadShot)
other->AvoidDamage(this, TotalDmg, false);
other->MeleeMitigation(this, TotalDmg, minDmg);
if(TotalDmg > 0)
{
@@ -957,8 +966,11 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item
else
TotalDmg = -5;
if (HeadShot)
entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, FATAL_BOW_SHOT, GetName());
other->Damage(this, TotalDmg, SPELL_UNKNOWN, SkillArchery);
}
}
//try proc on hits and misses
@@ -2074,30 +2086,73 @@ void Mob::InstillDoubt(Mob *who) {
}
}
bool Mob::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) {
bool Result = false;
uint32 Mob::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) {
if(defender && skillInUse == SkillArchery) {
if(GetAA(aaHeadshot) && defender->GetBodyType() == BT_Humanoid) {
if((GetLevelCon(GetLevel(), defender->GetLevel()) == CON_LIGHTBLUE || GetLevelCon(GetLevel(), defender->GetLevel()) == CON_GREEN) && defender->GetLevel() <= 60 && !defender->IsClient()) {
// WildcardX: These chance formula's below are arbitrary. If someone has a better formula that is more
// consistent with live, feel free to update these.
int AttackerChance = 20 + ((GetLevel() - 51) / 2) + (itembonuses.HeroicDEX / 10);
int DefenderChance = MakeRandomInt(0, 100);
if(AttackerChance > DefenderChance) {
mlog(COMBAT__ATTACKS, "Landed a headshot: Attacker chance was %f and Defender chance was %f.", AttackerChance, DefenderChance);
entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, FATAL_BOW_SHOT, GetName());
defender->Damage(this, 32000, SPELL_UNKNOWN, skillInUse);
Result = true;
}
else {
mlog(COMBAT__ATTACKS, "FAILED a headshot: Attacker chance was %f and Defender chance was %f.", AttackerChance, DefenderChance);
}
//Only works on YOUR target.
if(defender && (skillInUse == SkillArchery) && (GetTarget() == defender)) {
int32 HeadShot_Dmg = aabonuses.HeadShot[1] + spellbonuses.HeadShot[1] + itembonuses.HeadShot[1];
uint8 HeadShot_Level = 0; //Get Highest Headshot Level
HeadShot_Level = aabonuses.HSLevel;
if (HeadShot_Level < spellbonuses.HSLevel)
HeadShot_Level = spellbonuses.HSLevel;
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()){
float ProcChance = GetSpecialProcChances(11);
if(ProcChance > MakeRandomFloat(0,1))
return HeadShot_Dmg;
}
}
}
return Result;
return 0;
}
float Mob::GetSpecialProcChances(uint16 hand)
{
int mydex = GetDEX();
if (mydex > 255)
mydex = 255;
uint16 weapon_speed;
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);
if (RuleB(Combat, AdjustSpecialProcPerMinute)) {
ProcChance = (static_cast<float>(weapon_speed) *
RuleR(Combat, AvgSpecialProcsPerMinute) / 60000.0f);
ProcBonus += static_cast<float>(mydex/35) + static_cast<float>(itembonuses.HeroicDEX / 25);
ProcChance += ProcChance * ProcBonus / 100.0f;
} else {
/*PRE 2014 CHANGE Dev Quote - "Elidroth SOE:Proc chance is a function of your base hardcapped Dexterity / 35 + Heroic Dexterity / 25.”
Kayen: Most reports suggest a ~ 6% chance to Headshot which consistent with above.*/
ProcChance = (static_cast<float>(mydex/35) + static_cast<float>(itembonuses.HeroicDEX / 25))/100.0f;
}
return ProcChance;
}
void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes skillinuse, int16 chance_mod, int16 focus, bool CanRiposte)
+2
View File
@@ -2922,6 +2922,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_CombatStability:
case SE_AddSingingMod:
case SE_SongModCap:
case SE_HeadShot:
case SE_HeadShotLevel:
case SE_PetAvoidance:
case SE_GiveDoubleRiposte:
case SE_Ambidexterity: