From 7f6414d685805e8c6ce8332d5a3229f4a2df8871 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 2 Mar 2020 12:09:55 -0500 Subject: [PATCH] Some bot-related changes (Master Wu's Technique, Tiger Claw timer) --- zone/bot.cpp | 183 ++++++++++++++++++++++++++++++++------- zone/bot_command.cpp | 44 ++++++++-- zone/bot_database.cpp | 1 + zone/client.cpp | 1 + zone/client.h | 1 + zone/npc.cpp | 13 ++- zone/npc.h | 1 + zone/special_attacks.cpp | 30 ++++--- 8 files changed, 221 insertions(+), 53 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 5579b4bc5..6d14ce740 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -6421,32 +6421,52 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { bool taunt_time = taunt_timer.Check(); bool ca_time = classattack_timer.Check(false); + bool ma_time = monkattack_timer.Check(false); bool ka_time = knightattack_timer.Check(false); - if((taunt_time || ca_time || ka_time) && !IsAttackAllowed(target)) + + if (taunt_time) { + + // Bots without this skill shouldn't be 'checking' on this timer..let's just disable it and avoid the extra IsAttackAllowed() checks + // Note: this is done here instead of NPC::ctor() because taunt skill can be acquired during level ups (the timer is re-enabled in CalcBotStats()) + if (!GetSkill(EQEmu::skills::SkillTaunt)) { + + taunt_timer.Disable(); + return; + } + + if (!IsAttackAllowed(target)) { + return; + } + } + + if ((ca_time || ma_time || ka_time) && !IsAttackAllowed(target)) { return; + } if(ka_time){ - int knightreuse = 1000; + switch(GetClass()){ - case SHADOWKNIGHT: - case SHADOWKNIGHTGM: { + case SHADOWKNIGHT: { CastSpell(SPELL_NPC_HARM_TOUCH, target->GetID()); - knightreuse = (HarmTouchReuseTime * 1000); + knightattack_timer.Start(HarmTouchReuseTime * 1000); + break; } - case PALADIN: - case PALADINGM: { + case PALADIN: { if(GetHPRatio() < 20) { CastSpell(SPELL_LAY_ON_HANDS, GetID()); - knightreuse = (LayOnHandsReuseTime * 1000); + knightattack_timer.Start(LayOnHandsReuseTime * 1000); + } + else { + knightattack_timer.Start(2000); } - else - knightreuse = 2000; break; } + default: { + break; + } } - knightattack_timer.Start(knightreuse); } if(taunting && target && target->IsNPC() && taunt_time) { @@ -6457,8 +6477,66 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { } } - if(!ca_time) + if (ma_time) { + switch (GetClass()) { + case MONK: { + int reuse = (MonkSpecialAttack(target, EQEmu::skills::SkillTigerClaw) - 1); + + // Live AA - Technique of Master Wu + int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; + + if (wuchance) { + const int MonkSPA[5] = { + EQEmu::skills::SkillFlyingKick, + EQEmu::skills::SkillDragonPunch, + EQEmu::skills::SkillEagleStrike, + EQEmu::skills::SkillTigerClaw, + EQEmu::skills::SkillRoundKick + }; + int extra = 0; + // always 1/4 of the double attack chance, 25% at rank 5 (100/4) + while (wuchance > 0) { + if (zone->random.Roll(wuchance)) { + ++extra; + } + else { + break; + } + wuchance /= 4; + } + + Mob* bo = GetBotOwner(); + if (bo && bo->IsClient() && bo->CastToClient()->GetBotOption(Client::booMonkWuMessage)) { + + bo->Message( + GENERIC_EMOTE, + "The spirit of Master Wu fills %s! %s gains %d additional attack(s).", + GetCleanName(), + GetCleanName(), + extra + ); + } + + auto classic = RuleB(Combat, ClassicMasterWu); + while (extra) { + MonkSpecialAttack(GetTarget(), (classic ? MonkSPA[zone->random.Int(0, 4)] : EQEmu::skills::SkillTigerClaw)); + --extra; + } + } + + float HasteModifier = (GetHaste() * 0.01f); + monkattack_timer.Start((reuse * 1000) / HasteModifier); + + break; + } + default: + break;; + } + } + + if (!ca_time) { return; + } float HasteModifier = (GetHaste() * 0.01f); uint16 skill_to_use = -1; @@ -6493,18 +6571,22 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { } break; case MONK: - if(GetLevel() >= 30) + if (GetLevel() >= 30) { skill_to_use = EQEmu::skills::SkillFlyingKick; - else if(GetLevel() >= 25) + } + else if (GetLevel() >= 25) { skill_to_use = EQEmu::skills::SkillDragonPunch; - else if(GetLevel() >= 20) + } + else if (GetLevel() >= 20) { skill_to_use = EQEmu::skills::SkillEagleStrike; - else if(GetLevel() >= 10) - skill_to_use = EQEmu::skills::SkillTigerClaw; - else if(GetLevel() >= 5) + } + else if (GetLevel() >= 5) { skill_to_use = EQEmu::skills::SkillRoundKick; - else + } + else { skill_to_use = EQEmu::skills::SkillKick; + } + break; case ROGUE: skill_to_use = EQEmu::skills::SkillBackstab; @@ -6555,19 +6637,54 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { } } - if (skill_to_use == EQEmu::skills::SkillFlyingKick || skill_to_use == EQEmu::skills::SkillDragonPunch || skill_to_use == EQEmu::skills::SkillEagleStrike || skill_to_use == EQEmu::skills::SkillTigerClaw || skill_to_use == EQEmu::skills::SkillRoundKick) { + if ( + skill_to_use == EQEmu::skills::SkillFlyingKick || + skill_to_use == EQEmu::skills::SkillDragonPunch || + skill_to_use == EQEmu::skills::SkillEagleStrike || + skill_to_use == EQEmu::skills::SkillRoundKick + ) { reuse = (MonkSpecialAttack(target, skill_to_use) - 1); - MonkSpecialAttack(target, skill_to_use); - uint32 bDoubleSpecialAttack = (itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack); - if(bDoubleSpecialAttack && (bDoubleSpecialAttack >= 100 || bDoubleSpecialAttack > zone->random.Int(0, 100))) { - int MonkSPA[5] = { EQEmu::skills::SkillFlyingKick, EQEmu::skills::SkillDragonPunch, EQEmu::skills::SkillEagleStrike, EQEmu::skills::SkillTigerClaw, EQEmu::skills::SkillRoundKick }; - MonkSpecialAttack(target, MonkSPA[zone->random.Int(0, 4)]); - int TripleChance = 25; - if (bDoubleSpecialAttack > 100) - TripleChance += (TripleChance * (100 - bDoubleSpecialAttack) / 100); + + // Live AA - Technique of Master Wu + int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; - if(TripleChance > zone->random.Int(0,100)) - MonkSpecialAttack(target, MonkSPA[zone->random.Int(0, 4)]); + if (wuchance) { + const int MonkSPA[5] = { + EQEmu::skills::SkillFlyingKick, + EQEmu::skills::SkillDragonPunch, + EQEmu::skills::SkillEagleStrike, + EQEmu::skills::SkillTigerClaw, + EQEmu::skills::SkillRoundKick + }; + int extra = 0; + // always 1/4 of the double attack chance, 25% at rank 5 (100/4) + while (wuchance > 0) { + if (zone->random.Roll(wuchance)) { + ++extra; + } + else { + break; + } + wuchance /= 4; + } + + Mob* bo = GetBotOwner(); + if (bo && bo->IsClient() && bo->CastToClient()->GetBotOption(Client::booMonkWuMessage)) { + + bo->Message( + GENERIC_EMOTE, + "The spirit of Master Wu fills %s! %s gains %d additional attack(s).", + GetCleanName(), + GetCleanName(), + extra + ); + } + + auto classic = RuleB(Combat, ClassicMasterWu); + while (extra) { + MonkSpecialAttack(GetTarget(), (classic ? MonkSPA[zone->random.Int(0, 4)] : skill_to_use)); + --extra; + } } reuse *= 1000; @@ -8966,6 +9083,12 @@ void Bot::CalcBotStats(bool showtext) { skills[sindex] = database.GetSkillCap(GetClass(), (EQEmu::skills::SkillType)sindex, GetLevel()); } + taunt_timer.Start(1000); + + if (GetClass() == MONK && GetLevel() >= 10) { + monkattack_timer.Start(1000); + } + LoadAAs(); GenerateSpecialAttacks(); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index dc9d40aab..9c202fed8 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -3928,6 +3928,16 @@ void bot_command_owner_option(Client *c, const Seperator *sep) "null" "(toggles)" "" + "" + "monkwumessage" + "enable | disable" + "displays monk wu trigger messages" + "" + "" + "" + "null" + "(toggles)" + "" "" "current" "" @@ -4103,6 +4113,22 @@ void bot_command_owner_option(Client *c, const Seperator *sep) c->Message(m_action, "Bot 'buff counter' is now %s.", (c->GetBotOption(Client::booBuffCounter) == true ? "enabled" : "disabled")); } + else if (!owner_option.compare("monkwumessage")) { + + if (!argument.compare("enable")) { + c->SetBotOption(Client::booMonkWuMessage, true); + } + else if (!argument.compare("disable")) { + c->SetBotOption(Client::booMonkWuMessage, false); + } + else { + c->SetBotOption(Client::booMonkWuMessage, !c->GetBotOption(Client::booMonkWuMessage)); + } + + database.botdb.SaveOwnerOption(c->CharacterID(), Client::booMonkWuMessage, c->GetBotOption(Client::booMonkWuMessage)); + + c->Message(m_action, "Bot 'monk wu message' is now %s.", (c->GetBotOption(Client::booMonkWuMessage) == true ? "enabled" : "disabled")); + } else if (!owner_option.compare("current")) { std::string window_title = "Current Bot Owner Options Settings"; @@ -4112,13 +4138,14 @@ void bot_command_owner_option(Client *c, const Seperator *sep) "Option
------" "Argument
-------" "" - "" "deathmarquee" "{}" "" - "" "statsupdate" "{}" "" - "" "spawnmessage" "{}" "" - "" "spawnmessage" "{}" "" - "" "altcombat" "{}" "" - "" "autodefend" "{}" "" - "" "buffcounter" "{}" "" + "" "deathmarquee" "{}" "" + "" "statsupdate" "{}" "" + "" "spawnmessage" "{}" "" + "" "spawnmessage" "{}" "" + "" "altcombat" "{}" "" + "" "autodefend" "{}" "" + "" "buffcounter" "{}" "" + "" "monkwumessage" "{}" "" "", (c->GetBotOption(Client::booDeathMarquee) ? "enabled" : "disabled"), (c->GetBotOption(Client::booStatsUpdate) ? "enabled" : "disabled"), @@ -4126,7 +4153,8 @@ void bot_command_owner_option(Client *c, const Seperator *sep) (c->GetBotOption(Client::booSpawnMessageClassSpecific) ? "class" : "default"), (RuleB(Bots, AllowOwnerOptionAltCombat) ? (c->GetBotOption(Client::booAltCombat) ? "enabled" : "disabled") : "restricted"), (RuleB(Bots, AllowOwnerOptionAutoDefend) ? (c->GetBotOption(Client::booAutoDefend) ? "enabled" : "disabled") : "restricted"), - (c->GetBotOption(Client::booBuffCounter) ? "enabled" : "disabled") + (c->GetBotOption(Client::booBuffCounter) ? "enabled" : "disabled"), + (c->GetBotOption(Client::booMonkWuMessage) ? "enabled" : "disabled") ); c->SendPopupToClient(window_title.c_str(), window_text.c_str()); diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 2f6f9c1c6..28b5288e0 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -2257,6 +2257,7 @@ bool BotDatabase::SaveOwnerOption(const uint32 owner_id, size_t type, const bool case Client::booAltCombat: case Client::booAutoDefend: case Client::booBuffCounter: + case Client::booMonkWuMessage: { query = fmt::format( "REPLACE INTO `bot_owner_options`(`owner_id`, `option_type`, `option_value`) VALUES ('{}', '{}', '{}')", diff --git a/zone/client.cpp b/zone/client.cpp index 2398db500..6bd9941f8 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -358,6 +358,7 @@ Client::Client(EQStreamInterface* ieqs) bot_owner_options[booAltCombat] = RuleB(Bots, AllowOwnerOptionAltCombat); bot_owner_options[booAutoDefend] = RuleB(Bots, AllowOwnerOptionAutoDefend); bot_owner_options[booBuffCounter] = false; + bot_owner_options[booMonkWuMessage] = false; SetBotPulling(false); SetBotPrecombat(false); diff --git a/zone/client.h b/zone/client.h index 52af842be..58b0d9af8 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1646,6 +1646,7 @@ public: booAltCombat, booAutoDefend, booBuffCounter, + booMonkWuMessage, _booCount }; diff --git a/zone/npc.cpp b/zone/npc.cpp index 17e3c5957..0b22bab3a 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -118,6 +118,7 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi attacked_timer(CombatEventTimer_expire), swarm_timer(100), classattack_timer(1000), + monkattack_timer(1000), knightattack_timer(1000), assist_timer(AIassistcheck_delay), qglobal_purge_timer(30000), @@ -307,7 +308,15 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi // some overrides -- really we need to be able to set skills for mobs in the DB // There are some known low level SHM/BST pets that do not follow this, which supports // the theory of needing to be able to set skills for each mob separately - if (!IsBot()) { + if (IsBot()) { + if (GetClass() != PALADIN && GetClass() != SHADOWKNIGHT) { + knightattack_timer.Disable(); + } + else if (GetClass() != MONK || GetLevel() < 10) { + monkattack_timer.Disable(); + } + } + else { if (moblevel > 50) { skills[EQEmu::skills::SkillDoubleAttack] = 250; skills[EQEmu::skills::SkillDualWield] = 250; @@ -3233,4 +3242,4 @@ void NPC::RecalculateSkills() skills[EQEmu::skills::SkillDoubleAttack] = level * 5; } } -} \ No newline at end of file +} diff --git a/zone/npc.h b/zone/npc.h index e545ffb38..2b8edc9b9 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -499,6 +499,7 @@ protected: Timer attacked_timer; //running while we are being attacked (damaged) Timer swarm_timer; + Timer monkattack_timer; //additional timer for tiger claw usage Timer classattack_timer; Timer knightattack_timer; Timer assist_timer; //ask for help from nearby mobs diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 42ec94616..9334e4f6c 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -379,31 +379,35 @@ void Client::OPCombatAbility(const CombatAbility_Struct *ca_atk) ReuseTime = MonkSpecialAttack(GetTarget(), ca_atk->m_skill) - 1 - skill_reduction; // Live AA - Technique of Master Wu - int wuchance = - itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; + int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; + if (wuchance) { - const int MonkSPA[5] = {EQEmu::skills::SkillFlyingKick, EQEmu::skills::SkillDragonPunch, - EQEmu::skills::SkillEagleStrike, EQEmu::skills::SkillTigerClaw, - EQEmu::skills::SkillRoundKick}; + const int MonkSPA[5] = { + EQEmu::skills::SkillFlyingKick, + EQEmu::skills::SkillDragonPunch, + EQEmu::skills::SkillEagleStrike, + EQEmu::skills::SkillTigerClaw, + EQEmu::skills::SkillRoundKick + }; int extra = 0; // always 1/4 of the double attack chance, 25% at rank 5 (100/4) while (wuchance > 0) { - if (zone->random.Roll(wuchance)) - extra++; - else + if (zone->random.Roll(wuchance)) { + ++extra; + } + else { break; + } wuchance /= 4; } // They didn't add a string ID for this. - std::string msg = StringFormat( - "The spirit of Master Wu fills you! You gain %d additional attack(s).", extra); + std::string msg = StringFormat("The spirit of Master Wu fills you! You gain %d additional attack(s).", extra); // live uses 400 here -- not sure if it's the best for all clients though SendColoredText(400, msg); auto classic = RuleB(Combat, ClassicMasterWu); while (extra) { - MonkSpecialAttack(GetTarget(), - classic ? MonkSPA[zone->random.Int(0, 4)] : ca_atk->m_skill); - extra--; + MonkSpecialAttack(GetTarget(), (classic ? MonkSPA[zone->random.Int(0, 4)] : ca_atk->m_skill)); + --extra; } }