mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-13 06:21:28 +00:00
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:
parent
786b8c80fd
commit
25c20f0629
@ -11,8 +11,12 @@ Kayen: Implemented SE_IllusionOther - Allows next Illusion buff (self only) cast
|
||||
Kayen: Update SE_AETaunt - Base value will now determine AE taunt range (This will not result in any change to currently used spells).
|
||||
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
|
||||
system, or flat chance based on dex (formula updated).
|
||||
|
||||
Required SQL: utils/sql/git/required/2014_06_25_AA_Update.sql
|
||||
Optional SQL: utils/sql/git/optiional/2014_06_29_HeadShotRules.sql
|
||||
|
||||
|
||||
== 06/17/2014 ==
|
||||
|
||||
@ -333,6 +333,8 @@ RULE_REAL ( Combat, AvgProcsPerMinute, 2.0)
|
||||
RULE_REAL ( Combat, ProcPerMinDexContrib, 0.075)
|
||||
RULE_REAL ( Combat, BaseProcChance, 0.035)
|
||||
RULE_REAL ( Combat, ProcDexDivideBy, 11000)
|
||||
RULE_BOOL ( Combat, AdjustSpecialProcPerMinute, true) //Set PPM for special abilities like HeadShot (Live does this as of 4-14)
|
||||
RULE_REAL ( Combat, AvgSpecialProcsPerMinute, 2.0) //Unclear what best value is atm.
|
||||
RULE_REAL ( Combat, BaseHitChance, 69.0)
|
||||
RULE_REAL ( Combat, NPCBonusHitChance, 26.0)
|
||||
RULE_REAL ( Combat, HitFalloffMinor, 5.0) //hit will fall off up to 5% over the initial level range
|
||||
|
||||
@ -364,7 +364,7 @@ typedef enum {
|
||||
#define SE_MaxHPChange 214 // implemented
|
||||
#define SE_PetAvoidance 215 // implemented[AA] - increases pet ability to avoid melee damage
|
||||
#define SE_Accuracy 216 // implemented
|
||||
//#define SE_HeadShot 217 // not implemented as bonus - ability to head shot (base2 = damage)
|
||||
#define SE_HeadShot 217 // implemented - ability to head shot (base2 = damage)
|
||||
#define SE_PetCriticalHit 218 // implemented[AA] - gives pets a baseline critical hit chance
|
||||
#define SE_SlayUndead 219 // implemented - Allow extra damage against undead (base1 = rate, base2 = damage mod).
|
||||
#define SE_SkillDamageAmount 220 // implemented
|
||||
@ -492,8 +492,8 @@ typedef enum {
|
||||
#define SE_ImmuneFleeing 342 // implemented - stop mob from fleeing
|
||||
#define SE_InterruptCasting 343 // implemented - % chance to interrupt spells being cast every tic. Cacophony (8272)
|
||||
#define SE_ChannelChanceItems 344 // implemented[AA] - chance to not have ITEM effects interrupted when you take damage.
|
||||
//#define SE_AssassinationLevel 345 // not implemented as bonus - AA Assisination max level to kill
|
||||
//#define SE_HeadShotLevel 346 // not implemented as bonus - AA HeadShot max level to kill
|
||||
#define SE_AssassinateLevel 345 // implemented as bonus - AA Assisination max level to kill
|
||||
#define SE_HeadShotLevel 346 // implemented[AA] - HeadShot max level to kill
|
||||
#define SE_DoubleRangedAttack 347 // implemented - chance at an additional archery attack (consumes arrow)
|
||||
#define SE_LimitManaMin 348 // implemented
|
||||
#define SE_ShieldEquipHateMod 349 // implemented[AA] Increase melee hate when wearing a shield.
|
||||
@ -586,7 +586,7 @@ typedef enum {
|
||||
//#define SE_BeneficialCountDownHold 436 // not used ( 23491 | ABTest Buff Hold)
|
||||
//#define SE_TeleporttoAnchor 437 // *not implemented - Teleport Guild Hall Anchor(33099)
|
||||
//#define SE_TranslocatetoAnchor 438 // *not implemented - Translocate Primary Anchor (27750)
|
||||
//#define SE_IncreaseAssassinationLvl 439 // *not implemented[AA] - increases the maximum level of humanoid that can be affected by assassination
|
||||
#define SE_Assassinate 439 // implemented[AA] - Assassinate damage
|
||||
#define SE_FinishingBlowLvl 440 // implemented[AA] - Sets the level Finishing blow can be triggered on an NPC
|
||||
#define SE_DistanceRemoval 441 // implemented - Buff is removed from target when target moves X amount of distance away from where initially hit.
|
||||
#define SE_TriggerOnReqTarget 442 // implemented - triggers a spell which a certain criteria are met (below X amount of hp,mana,end, number of pets on hatelist)
|
||||
|
||||
2
utils/sql/git/optional/2014_06_29_HeadShotRules.sql
Normal file
2
utils/sql/git/optional/2014_06_29_HeadShotRules.sql
Normal file
@ -0,0 +1,2 @@
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:AdjustSpecialProcPerMinute', 'false', 'Allow PPM for special abilities HeadShot, Assassinate, Decap ect.');
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:AvgSpecialProcsPerMinute', '2.0', 'Set PPM for special abilities HeadShot, Assassinate, Decap ect. (Unknown what value live uses) .');
|
||||
@ -9,5 +9,9 @@ UPDATE aa_actions SET spell_id = 5227, nonspell_action = 0 WHERE aaid = 643;
|
||||
-- AA Improved Reclaim Energy
|
||||
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('180', '1', '241', '95', '0');
|
||||
|
||||
-- AA Headshot
|
||||
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';
|
||||
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user