Implement Triple Attack as a skill

See change log for more details
Optional SQL will max toons triple attack skills
This commit is contained in:
Michael Cook (mackal) 2015-07-06 16:11:00 -04:00
parent 5a73d26d12
commit 1e75b4ba77
9 changed files with 157 additions and 107 deletions

View File

@ -1,5 +1,12 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50) EQEMu Changelog (Started on Sept 24, 2003 15:50)
------------------------------------------------------- -------------------------------------------------------
== 07/06/2015 ==
mackal: Implement Triple Attack Skill
Parses showed about rand(1000) for the chance, may need more investigating
Corrected Double Attack chances as well
Running optional 2015_07_06_TripleAttack.sql will set current toons to their max skill
This is optional because the admins might want to go a different route.
== 07/05/2015 == == 07/05/2015 ==
mackal: Rewrite NPC combat attack round logic mackal: Rewrite NPC combat attack round logic
An NPC "quading" is really just an NPC with innate dual wield that doubles on both hands An NPC "quading" is really just an NPC with innate dual wield that doubles on both hands

View File

@ -108,16 +108,17 @@ enum SkillUseTypes
/*13869*/ SkillBerserking, /*13869*/ SkillBerserking,
/*13902*/ SkillTaunt, /*13902*/ SkillTaunt,
/*05837*/ SkillFrenzy, // This appears to be the only listed one not grouped with the others /*05837*/ SkillFrenzy, // This appears to be the only listed one not grouped with the others
/*00000*/ _EmuSkillCount // move to last position of active enumeration labels
// SoF+ specific skills // SoF+ specific skills
// /*03670*/ SkillRemoveTraps, /*03670*/ SkillRemoveTraps,
// /*13049*/ SkillTripleAttack, /*13049*/ SkillTripleAttack,
// RoF2+ specific skills // RoF2+ specific skills
// /*00789*/ Skill2HPiercing, // /*00789*/ Skill2HPiercing,
// /*01216*/ SkillNone, // This needs to move down as new skills are added // /*01216*/ SkillNone, // This needs to move down as new skills are added
/*00000*/ _EmuSkillCount // move to last position of active enumeration labels
// Skill Counts // Skill Counts
// /*-----*/ _SkillCount_62 = 75, // use for Ti and earlier max skill checks // /*-----*/ _SkillCount_62 = 75, // use for Ti and earlier max skill checks
// /*-----*/ _SkillCount_SoF = 77, // use for SoF thru RoF1 max skill checks // /*-----*/ _SkillCount_SoF = 77, // use for SoF thru RoF1 max skill checks
@ -170,7 +171,7 @@ enum SkillUseTypes
}; };
// temporary until it can be sorted out... // temporary until it can be sorted out...
#define HIGHEST_SKILL SkillFrenzy #define HIGHEST_SKILL SkillTripleAttack
// Spell Effects use this value to determine if an effect applies to all skills. // Spell Effects use this value to determine if an effect applies to all skills.
#define ALL_SKILLS -1 #define ALL_SKILLS -1

View File

@ -0,0 +1,27 @@
DELIMITER $$
DROP PROCEDURE IF EXISTS GrantTripleAttack$$
CREATE PROCEDURE GrantTripleAttack()
BEGIN
DECLARE finished INT;
DECLARE char_id INT;
DECLARE skill_max INT;
DECLARE cur CURSOR FOR SELECT character_data.id, skill_caps.cap FROM `character_data` LEFT JOIN `skill_caps` ON character_data.`level` = skill_caps.`level` AND character_data.class = skill_caps.class AND skill_caps.skillID = 76;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
OPEN cur;
SET finished = 0;
REPEAT
FETCH cur INTO char_id, skill_max;
IF skill_max IS NOT NULL AND skill_max > 0 THEN
REPLACE INTO `character_skills` (`id`, `skill_id`, `value`) VALUES(char_id, 76, skill_max);
END IF;
UNTIL finished END REPEAT;
CLOSE cur;
END$$
DELIMITER ;
CALL GrantTripleAttack();
DROP PROCEDURE GrantTripleAttack;

View File

@ -3406,41 +3406,39 @@ bool Mob::HasRangedProcs() const
return false; return false;
} }
bool Client::CheckDoubleAttack(bool tripleAttack) { bool Client::CheckDoubleAttack()
{
int chance = 0;
int skill = GetSkill(SkillDoubleAttack);
//Check for bonuses that give you a double attack chance regardless of skill (ie Bestial Frenzy/Harmonious Attack AA) //Check for bonuses that give you a double attack chance regardless of skill (ie Bestial Frenzy/Harmonious Attack AA)
uint32 bonusGiveDA = aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack; int bonusGiveDA = aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack;
if (skill > 0)
if(!HasSkill(SkillDoubleAttack) && !bonusGiveDA) chance = skill + GetLevel();
else if (!bonusGiveDA)
return false; return false;
float chance = 0.0f; if (bonusGiveDA)
chance += bonusGiveDA / 100.0f * 500; // convert to skill value
int per_inc = aabonuses.DoubleAttackChance + spellbonuses.DoubleAttackChance + itembonuses.DoubleAttackChance;
if (per_inc)
chance += chance * per_inc / 100;
uint16 skill = GetSkill(SkillDoubleAttack); return zone->random.Int(1, 500) <= chance;
}
int32 bonusDA = aabonuses.DoubleAttackChance + spellbonuses.DoubleAttackChance + itembonuses.DoubleAttackChance;
//Use skill calculations otherwise, if you only have AA applied GiveDoubleAttack chance then use that value as the base.
if (skill)
chance = (float(skill+GetLevel()) * (float(100.0f+bonusDA+bonusGiveDA) /100.0f)) /500.0f;
else
chance = (float(bonusGiveDA) * (float(100.0f+bonusDA)/100.0f) ) /100.0f;
//Live now uses a static Triple Attack skill (lv 46 = 2% lv 60 = 20%) - We do not have this skill on EMU ATM.
//A reasonable forumla would then be TA = 20% * chance
//AA's can also give triple attack skill over cap. (ie Burst of Power) NOTE: Skill ID in spell data is 76 (Triple Attack)
//Kayen: Need to decide if we can implement triple attack skill before working in over the cap effect.
if(tripleAttack) {
// Only some Double Attack classes get Triple Attack [This is already checked in client_processes.cpp]
int32 triple_bonus = spellbonuses.TripleAttackChance + itembonuses.TripleAttackChance;
chance *= 0.2f; //Baseline chance is 20% of your double attack chance.
chance *= float(100.0f+triple_bonus)/100.0f; //Apply modifiers.
}
if(zone->random.Roll(chance))
return true;
// Admittedly these parses were short, but this check worked for 3 toons across multiple levels
// with varying triple attack skill (1-3% error at least)
bool Client::CheckTripleAttack()
{
int chance = GetSkill(SkillTripleAttack);
if (chance < 1)
return false; return false;
int per_inc = aabonuses.TripleAttackChance + spellbonuses.TripleAttackChance + itembonuses.TripleAttackChance;
if (per_inc)
chance += chance * per_inc / 100;
return zone->random.Int(1, 1000) <= chance;
} }
bool Client::CheckDoubleRangedAttack() { bool Client::CheckDoubleRangedAttack() {
@ -3452,7 +3450,7 @@ bool Client::CheckDoubleRangedAttack() {
return false; return false;
} }
bool Mob::CheckDoubleAttack(bool tripleAttack) bool Mob::CheckDoubleAttack()
{ {
// Not 100% certain pets follow this or if it's just from pets not always // Not 100% certain pets follow this or if it's just from pets not always
// having the same skills as most mobs // having the same skills as most mobs
@ -5054,21 +5052,28 @@ void Client::DoAttackRounds(Mob *target, int hand, bool IsFromSpell)
if (candouble) { if (candouble) {
CheckIncreaseSkill(SkillDoubleAttack, target, -10); CheckIncreaseSkill(SkillDoubleAttack, target, -10);
if (CheckDoubleAttack()) if (CheckDoubleAttack()) {
Attack(target, hand, false, false, IsFromSpell); Attack(target, hand, false, false, IsFromSpell);
if (hand == MainPrimary && GetLevel() >= 60 && // you can only triple from the main hand
(GetClass() == MONK || GetClass() == WARRIOR || GetClass() == RANGER || GetClass() == BERSERKER) && if (hand == MainPrimary && CanThisClassTripleAttack()) {
CheckDoubleAttack(true)) CheckIncreaseSkill(SkillTripleAttack, target, -10);
if (CheckTripleAttack())
Attack(target, hand, false, false, IsFromSpell); Attack(target, hand, false, false, IsFromSpell);
} }
}
}
if (hand == MainPrimary) { if (hand == MainPrimary) {
// According to http://www.monkly-business.net/forums/showpost.php?p=312095&postcount=168 a dev told them flurry isn't dependant on triple attack
// the parses kind of back that up and all of my parses seemed to be 4 or 5 attacks in the round which would work out to be
// doubles or triples with 2 from flurries or triple with 1 or 2 flurries ... Going with the "dev quote" I guess like we've always had it
auto flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance; auto flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance;
if (flurrychance && zone->random.Roll(flurrychance)) { if (flurrychance && zone->random.Roll(flurrychance)) {
Attack(target, hand, false, false, IsFromSpell); Attack(target, hand, false, false, IsFromSpell);
Attack(target, hand, false, false, IsFromSpell); Attack(target, hand, false, false, IsFromSpell);
Message_StringID(MT_NPCFlurry, YOU_FLURRY); Message_StringID(MT_NPCFlurry, YOU_FLURRY);
} }
// I haven't parsed where this guy happens, but it's not part of the normal chain above so this is fine
auto extraattackchance = aabonuses.ExtraAttackChance + spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance; auto extraattackchance = aabonuses.ExtraAttackChance + spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance;
if (extraattackchance && HasTwoHanderEquipped() && zone->random.Roll(extraattackchance)) if (extraattackchance && HasTwoHanderEquipped() && zone->random.Roll(extraattackchance))
Attack(target, hand, false, false, IsFromSpell); Attack(target, hand, false, false, IsFromSpell);

View File

@ -888,7 +888,8 @@ public:
bool CheckTradeLoreConflict(Client* other); bool CheckTradeLoreConflict(Client* other);
void LinkDead(); void LinkDead();
void Insight(uint32 t_id); void Insight(uint32 t_id);
bool CheckDoubleAttack(bool tripleAttack = false); bool CheckDoubleAttack();
bool CheckTripleAttack();
bool CheckDoubleRangedAttack(); bool CheckDoubleRangedAttack();
bool CheckDualWield(); bool CheckDualWield();

View File

@ -2302,7 +2302,7 @@ void command_setskill(Client *c, const Seperator *sep)
Log.Out(Logs::General, Logs::Normal, "Set skill request from %s, target:%s skill_id:%i value:%i", c->GetName(), c->GetTarget()->GetName(), atoi(sep->arg[1]), atoi(sep->arg[2]) ); Log.Out(Logs::General, Logs::Normal, "Set skill request from %s, target:%s skill_id:%i value:%i", c->GetName(), c->GetTarget()->GetName(), atoi(sep->arg[1]), atoi(sep->arg[2]) );
int skill_num = atoi(sep->arg[1]); int skill_num = atoi(sep->arg[1]);
uint16 skill_value = atoi(sep->arg[2]); uint16 skill_value = atoi(sep->arg[2]);
if(skill_num < HIGHEST_SKILL) if(skill_num <= HIGHEST_SKILL)
c->GetTarget()->CastToClient()->SetSkill((SkillUseTypes)skill_num, skill_value); c->GetTarget()->CastToClient()->SetSkill((SkillUseTypes)skill_num, skill_value);
} }
} }

View File

@ -2409,6 +2409,14 @@ bool Mob::CanThisClassDoubleAttack(void) const
} }
} }
bool Mob::CanThisClassTripleAttack() const
{
if (!IsClient())
return false; // When they added the real triple attack skill, mobs lost the ability to triple
else
return CastToClient()->HasSkill(SkillTripleAttack);
}
bool Mob::IsWarriorClass(void) const bool Mob::IsWarriorClass(void) const
{ {
switch(GetClass()) switch(GetClass())

View File

@ -169,7 +169,7 @@ public:
virtual bool CheckDualWield(); virtual bool CheckDualWield();
void DoMainHandAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr); void DoMainHandAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr);
void DoOffHandAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr); void DoOffHandAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr);
virtual bool CheckDoubleAttack(bool tripleAttack = false); // mob version doesn't use this flag virtual bool CheckDoubleAttack();
// inline process for places where we need to do them outside of the AI_Process // inline process for places where we need to do them outside of the AI_Process
void ProcessAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr) void ProcessAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr)
{ {
@ -750,6 +750,7 @@ public:
virtual int GetMonkHandToHandDamage(void); virtual int GetMonkHandToHandDamage(void);
bool CanThisClassDoubleAttack(void) const; bool CanThisClassDoubleAttack(void) const;
bool CanThisClassTripleAttack() const;
bool CanThisClassDualWield(void) const; bool CanThisClassDualWield(void) const;
bool CanThisClassRiposte(void) const; bool CanThisClassRiposte(void) const;
bool CanThisClassDodge(void) const; bool CanThisClassDodge(void) const;

View File

@ -536,7 +536,7 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) {
RogueBackstab(other,false,ReuseTime); RogueBackstab(other,false,ReuseTime);
if (level > 54) { if (level > 54) {
if(IsClient() && CastToClient()->CheckDoubleAttack(false)) if(IsClient() && CastToClient()->CheckDoubleAttack())
{ {
if(other->GetHP() > 0) if(other->GetHP() > 0)
RogueBackstab(other,false,ReuseTime); RogueBackstab(other,false,ReuseTime);
@ -558,7 +558,7 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) {
if (level > 54) { if (level > 54) {
// Check for double attack with main hand assuming maxed DA Skill (MS) // Check for double attack with main hand assuming maxed DA Skill (MS)
if(IsClient() && CastToClient()->CheckDoubleAttack(false)) if(IsClient() && CastToClient()->CheckDoubleAttack())
if(other->GetHP() > 0) if(other->GetHP() > 0)
RogueBackstab(other,true, ReuseTime); RogueBackstab(other,true, ReuseTime);