mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-16 18:52:22 +00:00
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 for AA updates
This commit is contained in:
+123
-140
@@ -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<float>(ReuseTime*1000) *
|
||||
RuleR(Combat, AvgSpecialProcsPerMinute) / 60000.0f);
|
||||
ProcBonus += (10 + (static_cast<float>(mydex/10) + static_cast<float>(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<float>(mydex/10) + static_cast<float>(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))
|
||||
|
||||
Reference in New Issue
Block a user