From 1e75b4ba779635edb4a5162e61461e29eec89ade Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 6 Jul 2015 16:11:00 -0400 Subject: [PATCH] Implement Triple Attack as a skill See change log for more details Optional SQL will max toons triple attack skills --- changelog.txt | 7 + common/skills.h | 9 +- .../git/optional/2015_07_06_TripleAttack.sql | 27 ++++ zone/attack.cpp | 75 +++++----- zone/client.h | 3 +- zone/command.cpp | 128 +++++++++--------- zone/mob.cpp | 8 ++ zone/mob.h | 3 +- zone/special_attacks.cpp | 4 +- 9 files changed, 157 insertions(+), 107 deletions(-) create mode 100644 utils/sql/git/optional/2015_07_06_TripleAttack.sql diff --git a/changelog.txt b/changelog.txt index 227058eba..f5d1803d0 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,12 @@ 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 == mackal: Rewrite NPC combat attack round logic An NPC "quading" is really just an NPC with innate dual wield that doubles on both hands diff --git a/common/skills.h b/common/skills.h index 1fad4de3e..c79a522ee 100644 --- a/common/skills.h +++ b/common/skills.h @@ -108,16 +108,17 @@ enum SkillUseTypes /*13869*/ SkillBerserking, /*13902*/ SkillTaunt, /*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 -// /*03670*/ SkillRemoveTraps, -// /*13049*/ SkillTripleAttack, +/*03670*/ SkillRemoveTraps, +/*13049*/ SkillTripleAttack, // RoF2+ specific skills // /*00789*/ Skill2HPiercing, // /*01216*/ SkillNone, // This needs to move down as new skills are added +/*00000*/ _EmuSkillCount // move to last position of active enumeration labels + // Skill Counts // /*-----*/ _SkillCount_62 = 75, // use for Ti and earlier 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... -#define HIGHEST_SKILL SkillFrenzy +#define HIGHEST_SKILL SkillTripleAttack // Spell Effects use this value to determine if an effect applies to all skills. #define ALL_SKILLS -1 diff --git a/utils/sql/git/optional/2015_07_06_TripleAttack.sql b/utils/sql/git/optional/2015_07_06_TripleAttack.sql new file mode 100644 index 000000000..e5061e108 --- /dev/null +++ b/utils/sql/git/optional/2015_07_06_TripleAttack.sql @@ -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; diff --git a/zone/attack.cpp b/zone/attack.cpp index 02b16c64e..440e8c59a 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3406,41 +3406,39 @@ bool Mob::HasRangedProcs() const 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) - uint32 bonusGiveDA = aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack; - - if(!HasSkill(SkillDoubleAttack) && !bonusGiveDA) + int bonusGiveDA = aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack; + if (skill > 0) + chance = skill + GetLevel(); + else if (!bonusGiveDA) 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; +// 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; - //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; + int per_inc = aabonuses.TripleAttackChance + spellbonuses.TripleAttackChance + itembonuses.TripleAttackChance; + if (per_inc) + chance += chance * per_inc / 100; - //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; - - return false; + return zone->random.Int(1, 1000) <= chance; } bool Client::CheckDoubleRangedAttack() { @@ -3452,7 +3450,7 @@ bool Client::CheckDoubleRangedAttack() { 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 // having the same skills as most mobs @@ -5054,21 +5052,28 @@ void Client::DoAttackRounds(Mob *target, int hand, bool IsFromSpell) if (candouble) { CheckIncreaseSkill(SkillDoubleAttack, target, -10); - if (CheckDoubleAttack()) - Attack(target, hand, false, false, IsFromSpell); - if (hand == MainPrimary && GetLevel() >= 60 && - (GetClass() == MONK || GetClass() == WARRIOR || GetClass() == RANGER || GetClass() == BERSERKER) && - CheckDoubleAttack(true)) + if (CheckDoubleAttack()) { Attack(target, hand, false, false, IsFromSpell); + // you can only triple from the main hand + if (hand == MainPrimary && CanThisClassTripleAttack()) { + CheckIncreaseSkill(SkillTripleAttack, target, -10); + if (CheckTripleAttack()) + Attack(target, hand, false, false, IsFromSpell); + } + } } + 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; if (flurrychance && zone->random.Roll(flurrychance)) { Attack(target, hand, false, false, IsFromSpell); Attack(target, hand, false, false, IsFromSpell); 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; if (extraattackchance && HasTwoHanderEquipped() && zone->random.Roll(extraattackchance)) Attack(target, hand, false, false, IsFromSpell); diff --git a/zone/client.h b/zone/client.h index 7558d9bb6..03d09d50c 100644 --- a/zone/client.h +++ b/zone/client.h @@ -888,7 +888,8 @@ public: bool CheckTradeLoreConflict(Client* other); void LinkDead(); void Insight(uint32 t_id); - bool CheckDoubleAttack(bool tripleAttack = false); + bool CheckDoubleAttack(); + bool CheckTripleAttack(); bool CheckDoubleRangedAttack(); bool CheckDualWield(); diff --git a/zone/command.cpp b/zone/command.cpp index 7a846898b..3b8fba3f6 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -146,7 +146,7 @@ Access Levels: */ int command_init(void) { - + if ( @@ -434,7 +434,7 @@ int command_init(void) { command_add("zsave", " - Saves zheader to the database", 80, command_zsave) || command_add("zsky", "[skytype] - Change zone sky type", 80, command_zsky) || command_add("zstats", "- Show info about zone header", 80, command_zstats) || - command_add("zunderworld", "[zcoord] - Sets the underworld using zcoord", 80, command_zunderworld) || + command_add("zunderworld", "[zcoord] - Sets the underworld using zcoord", 80, command_zunderworld) || command_add("zuwcoords", "[z coord] - Set underworld coord", 80, command_zuwcoords) ) { @@ -644,8 +644,8 @@ void command_logcommand(Client *c, const char *message) c->AccountName(), c->AccountID(), admin,c->GetName(), - c->GetTarget()?c->GetTarget()->GetName():"None", - "Command", + c->GetTarget()?c->GetTarget()->GetName():"None", + "Command", message, 1 ); @@ -731,7 +731,7 @@ void command_setfaction(Client *c, const Seperator *sep) auto npcTypeID = c->GetTarget()->CastToNPC()->GetNPCTypeID(); c->Message(15,"Setting NPC %u to faction %i", npcTypeID, atoi(sep->argplus[1])); - std::string query = StringFormat("UPDATE npc_types SET npc_faction_id = %i WHERE id = %i", + std::string query = StringFormat("UPDATE npc_types SET npc_faction_id = %i WHERE id = %i", atoi(sep->argplus[1]), npcTypeID); database.QueryDatabase(query); } @@ -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]) ); int skill_num = atoi(sep->arg[1]); 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); } } @@ -2573,7 +2573,7 @@ void command_peekinv(Client *c, const Seperator *sep) item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "WornSlot: %i, Item: %i (%s), Charges: %i", + c->Message((item_data == nullptr), "WornSlot: %i, Item: %i (%s), Charges: %i", indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); } @@ -2584,7 +2584,7 @@ void command_peekinv(Client *c, const Seperator *sep) item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "WornSlot: %i, Item: %i (%s), Charges: %i", + c->Message((item_data == nullptr), "WornSlot: %i, Item: %i (%s), Charges: %i", MainPowerSource, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); } @@ -2596,7 +2596,7 @@ void command_peekinv(Client *c, const Seperator *sep) item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "InvSlot: %i, Item: %i (%s), Charges: %i", + c->Message((item_data == nullptr), "InvSlot: %i, Item: %i (%s), Charges: %i", indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); for (uint8 indexSub = SUB_BEGIN; inst_main && inst_main->IsType(ItemClassContainer) && (indexSub < EmuConstants::ITEM_CONTAINER_SIZE); ++indexSub) { @@ -2606,7 +2606,7 @@ void command_peekinv(Client *c, const Seperator *sep) item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + c->Message((item_data == nullptr), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", Inventory::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); } } @@ -2618,7 +2618,7 @@ void command_peekinv(Client *c, const Seperator *sep) item_link = linker.GenerateLink(); - c->Message(1, "CursorSlot: %i, Item: %i (%s), Charges: %i", + c->Message(1, "CursorSlot: %i, Item: %i (%s), Charges: %i", MainCursor, 0, item_link.c_str(), 0); } else { @@ -2630,7 +2630,7 @@ void command_peekinv(Client *c, const Seperator *sep) item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "CursorSlot: %i, Depth: %i, Item: %i (%s), Charges: %i", + c->Message((item_data == nullptr), "CursorSlot: %i, Depth: %i, Item: %i (%s), Charges: %i", MainCursor, cursorDepth, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); for (uint8 indexSub = SUB_BEGIN; (cursorDepth == 0) && inst_main && inst_main->IsType(ItemClassContainer) && (indexSub < EmuConstants::ITEM_CONTAINER_SIZE); ++indexSub) { @@ -2640,7 +2640,7 @@ void command_peekinv(Client *c, const Seperator *sep) item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + c->Message((item_data == nullptr), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", Inventory::CalcSlotId(MainCursor, indexSub), MainCursor, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); } } @@ -2655,7 +2655,7 @@ void command_peekinv(Client *c, const Seperator *sep) item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "TributeSlot: %i, Item: %i (%s), Charges: %i", + c->Message((item_data == nullptr), "TributeSlot: %i, Item: %i (%s), Charges: %i", indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); } @@ -2667,7 +2667,7 @@ void command_peekinv(Client *c, const Seperator *sep) item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "BankSlot: %i, Item: %i (%s), Charges: %i", + c->Message((item_data == nullptr), "BankSlot: %i, Item: %i (%s), Charges: %i", indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); for (uint8 indexSub = SUB_BEGIN; inst_main && inst_main->IsType(ItemClassContainer) && (indexSub < EmuConstants::ITEM_CONTAINER_SIZE); ++indexSub) { @@ -2677,7 +2677,7 @@ void command_peekinv(Client *c, const Seperator *sep) item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + c->Message((item_data == nullptr), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", Inventory::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); } } @@ -2688,8 +2688,8 @@ void command_peekinv(Client *c, const Seperator *sep) linker.SetItemInst(inst_main); item_link = linker.GenerateLink(); - - c->Message((item_data == nullptr), "SharedBankSlot: %i, Item: %i (%s), Charges: %i", + + c->Message((item_data == nullptr), "SharedBankSlot: %i, Item: %i (%s), Charges: %i", indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); for (uint8 indexSub = SUB_BEGIN; inst_main && inst_main->IsType(ItemClassContainer) && (indexSub < EmuConstants::ITEM_CONTAINER_SIZE); ++indexSub) { @@ -2699,7 +2699,7 @@ void command_peekinv(Client *c, const Seperator *sep) item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), " SharedBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + c->Message((item_data == nullptr), " SharedBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", Inventory::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); } } @@ -2712,7 +2712,7 @@ void command_peekinv(Client *c, const Seperator *sep) item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "TradeSlot: %i, Item: %i (%s), Charges: %i", + c->Message((item_data == nullptr), "TradeSlot: %i, Item: %i (%s), Charges: %i", indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); for (uint8 indexSub = SUB_BEGIN; inst_main && inst_main->IsType(ItemClassContainer) && (indexSub < EmuConstants::ITEM_CONTAINER_SIZE); ++indexSub) { @@ -2722,7 +2722,7 @@ void command_peekinv(Client *c, const Seperator *sep) item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + c->Message((item_data == nullptr), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", Inventory::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); } } @@ -2744,7 +2744,7 @@ void command_peekinv(Client *c, const Seperator *sep) item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "WorldSlot: %i, Item: %i (%s), Charges: %i", + c->Message((item_data == nullptr), "WorldSlot: %i, Item: %i (%s), Charges: %i", (EmuConstants::WORLD_BEGIN + indexMain), ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); for (uint8 indexSub = SUB_BEGIN; inst_main && inst_main->IsType(ItemClassContainer) && (indexSub < EmuConstants::ITEM_CONTAINER_SIZE); ++indexSub) { @@ -2754,7 +2754,7 @@ void command_peekinv(Client *c, const Seperator *sep) item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), " WorldBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + c->Message((item_data == nullptr), " WorldBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", INVALID_INDEX, indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); } } @@ -3752,7 +3752,7 @@ void command_spellinfo(Client *c, const Seperator *sep) c->Message(0, " zonetype: %d", s->zonetype); c->Message(0, " EnvironmentType: %d", s->EnvironmentType); c->Message(0, " TimeOfDay: %d", s->TimeOfDay); - c->Message(0, " classes[15]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", + c->Message(0, " classes[15]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", s->classes[0], s->classes[1], s->classes[2], s->classes[3], s->classes[4], s->classes[5], s->classes[6], s->classes[7], s->classes[8], s->classes[9], s->classes[10], s->classes[11], s->classes[12], s->classes[13], s->classes[14]); @@ -3882,7 +3882,7 @@ void command_repop(Client *c, const Seperator *sep) LinkedListIterator iterator(zone->spawn2_list); iterator.Reset(); while (iterator.MoreElements()) { - std::string query = StringFormat("DELETE FROM respawn_times WHERE id = %lu AND instance_id = %lu", + std::string query = StringFormat("DELETE FROM respawn_times WHERE id = %lu AND instance_id = %lu", (unsigned long)iterator.GetData()->GetID(), (unsigned long)zone->GetInstanceID()); auto results = database.QueryDatabase(query); @@ -4256,7 +4256,7 @@ void command_spawnfix(Client *c, const Seperator *sep) { return; } - std::string query = StringFormat("UPDATE spawn2 SET x = '%f', y = '%f', z = '%f', heading = '%f' WHERE id = '%i'", + std::string query = StringFormat("UPDATE spawn2 SET x = '%f', y = '%f', z = '%f', heading = '%f' WHERE id = '%i'", c->GetX(), c->GetY(), c->GetZ(), c->GetHeading(),s2->GetID()); auto results = database.QueryDatabase(query); if (!results.Success()) { @@ -4399,11 +4399,11 @@ void command_time(Client *c, const Seperator *sep) c->Message(13, "To set the Time: #time HH [MM]"); TimeOfDay_Struct eqTime; zone->zone_time.GetCurrentEQTimeOfDay( time(0), &eqTime); - sprintf(timeMessage,"%02d:%s%d %s (Timezone: %ih %im)", + sprintf(timeMessage,"%02d:%s%d %s (Timezone: %ih %im)", ((eqTime.hour - 1) % 12) == 0 ? 12 : ((eqTime.hour - 1) % 12), - (eqTime.minute < 10) ? "0" : "", + (eqTime.minute < 10) ? "0" : "", eqTime.minute, - (eqTime.hour >= 13) ? "pm" : "am", + (eqTime.hour >= 13) ? "pm" : "am", zone->zone_time.getEQTimeZoneHr(), zone->zone_time.getEQTimeZoneMin() ); @@ -5734,7 +5734,7 @@ void command_suspend(Client *c, const Seperator *sep) } std::string query = StringFormat("UPDATE `account` SET `suspendeduntil` = DATE_ADD(NOW(), INTERVAL %i DAY), " - "suspend_reason = '%s' WHERE `id` = %i", + "suspend_reason = '%s' WHERE `id` = %i", duration, EscapeString(message).c_str(), accountID); auto results = database.QueryDatabase(query); @@ -6206,21 +6206,21 @@ void command_npcedit(Client *c, const Seperator *sep) database.QueryDatabase(query); return; } - + if (strcasecmp(sep->arg[1], "meleetype") == 0) { c->Message(15,"NPCID %u now has a primary melee type of %i and a secondary melee type of %i.", npcTypeID, atoi(sep->arg[2]), atoi(sep->arg[3])); std::string query = StringFormat("UPDATE npc_types SET prim_melee_type = %i, sec_melee_type = %i WHERE id = %i", atoi(sep->arg[2]), atoi(sep->arg[3]), npcTypeID); database.QueryDatabase(query); return; } - + if (strcasecmp(sep->arg[1], "rangedtype") == 0) { c->Message(15,"NPCID %u now has a ranged type of %i.", npcTypeID, atoi(sep->argplus[2])); std::string query = StringFormat("UPDATE npc_types SET rangedtype = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); database.QueryDatabase(query); return; } - + if (strcasecmp(sep->arg[1], "ammoidfile") == 0) { c->Message(15,"NPCID %u's ammo id file is now %i", npcTypeID, atoi(sep->argplus[2])); std::string query = StringFormat("UPDATE npc_types SET ammoidfile = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); @@ -6255,7 +6255,7 @@ void command_npcedit(Client *c, const Seperator *sep) database.QueryDatabase(query); return; } - + if (strcasecmp(sep->arg[1], "walkspeed") == 0) { c->Message(15,"NPCID %u now walks at %f", npcTypeID, atof(sep->argplus[2])); std::string query = StringFormat("UPDATE npc_types SET walkspeed = %f WHERE id = %i", atof(sep->argplus[2]), npcTypeID); @@ -6409,7 +6409,7 @@ void command_npcedit(Client *c, const Seperator *sep) database.QueryDatabase(query); return; } - + if (strcasecmp(sep->arg[1], "Avoidance") == 0) { c->Message(15,"NPCID %u now has %i Avoidance.", npcTypeID, atoi(sep->argplus[2])); std::string query = StringFormat("UPDATE npc_types SET avoidance = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); @@ -6465,7 +6465,7 @@ void command_npcedit(Client *c, const Seperator *sep) database.QueryDatabase(query); return; } - + if (strcasecmp(sep->arg[1], "Attackcount") == 0) { c->Message(15,"NPCID %u now has attack_count set to %i", npcTypeID,atoi(sep->arg[2])); std::string query = StringFormat("UPDATE npc_types SET attack_count = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); @@ -6502,7 +6502,7 @@ void command_npcedit(Client *c, const Seperator *sep) "luclin_hairstyle = %i, luclin_beard = %i, " "face = %i, drakkin_heritage = %i, " "drakkin_tattoo = %i, drakkin_details = %i " - "WHERE id = %i", + "WHERE id = %i", target->GetHairColor(), target->GetBeardColor(), target->GetHairStyle(), target->GetBeard(), target->GetLuclinFace(), target->GetDrakkinHeritage(), @@ -6588,7 +6588,7 @@ void command_npcedit(Client *c, const Seperator *sep) database.QueryDatabase(query); return; } - + if (strcasecmp(sep->arg[1], "slow_mitigation") == 0) { c->Message(15, "NPCID %u's slow mitigation limit is now %i.", npcTypeID, atoi(sep->arg[2])); std::string query = StringFormat("UPDATE npc_types SET slow_mitigation = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); @@ -6643,7 +6643,7 @@ void command_qglobal(Client *c, const Seperator *sep) { } if(!strcasecmp(sep->arg[1], "on")) { - std::string query = StringFormat("UPDATE npc_types SET qglobal = 1 WHERE id = '%i'", + std::string query = StringFormat("UPDATE npc_types SET qglobal = 1 WHERE id = '%i'", target->GetNPCTypeID()); auto results = database.QueryDatabase(query); if(!results.Success()) { @@ -6656,7 +6656,7 @@ void command_qglobal(Client *c, const Seperator *sep) { } if(!strcasecmp(sep->arg[1], "off")) { - std::string query = StringFormat("UPDATE npc_types SET qglobal = 0 WHERE id = '%i'", + std::string query = StringFormat("UPDATE npc_types SET qglobal = 0 WHERE id = '%i'", target->GetNPCTypeID()); auto results = database.QueryDatabase(query); if(!results.Success()) { @@ -7052,13 +7052,13 @@ void command_ginfo(Client *c, const Seperator *sep) if(g->membername[r][0] == '\0') continue; c->Message(0, "...Zoned Member: %s, Roles: %s %s %s", g->membername[r], - (g->MemberRoles[r] & RoleAssist) ? "Assist" : "", - (g->MemberRoles[r] & RoleTank) ? "Tank" : "", + (g->MemberRoles[r] & RoleAssist) ? "Assist" : "", + (g->MemberRoles[r] & RoleTank) ? "Tank" : "", (g->MemberRoles[r] & RolePuller) ? "Puller" : ""); } else { c->Message(0, "...In-Zone Member: %s (0x%x) Roles: %s %s %s", g->membername[r], g->members[r], - (g->MemberRoles[r] & RoleAssist) ? "Assist" : "", - (g->MemberRoles[r] & RoleTank) ? "Tank" : "", + (g->MemberRoles[r] & RoleAssist) ? "Assist" : "", + (g->MemberRoles[r] & RoleTank) ? "Tank" : "", (g->MemberRoles[r] & RolePuller) ? "Puller" : ""); } @@ -7221,7 +7221,7 @@ void command_flagedit(Client *c, const Seperator *sep) { flag_name[127] = '\0'; std::string query = StringFormat("UPDATE zone SET flag_needed = '%s' " - "WHERE zoneidnumber = %d AND version = %d", + "WHERE zoneidnumber = %d AND version = %d", flag_name, zoneid, zone->GetInstanceVersion()); auto results = database.QueryDatabase(query); if(!results.Success()) { @@ -7248,7 +7248,7 @@ void command_flagedit(Client *c, const Seperator *sep) { } std::string query = StringFormat("UPDATE zone SET flag_needed = '' " - "WHERE zoneidnumber = %d AND version = %d", + "WHERE zoneidnumber = %d AND version = %d", zoneid, zone->GetInstanceVersion()); auto results = database.QueryDatabase(query); if(!results.Success()) { @@ -7880,7 +7880,7 @@ void command_advnpcspawn(Client *c, const Seperator *sep) std::string query = StringFormat("INSERT INTO spawngroup " "(name, spawn_limit, dist, max_x, min_x, max_y, min_y, delay) " - "VALUES (\"%s\", %i, %f, %f, %f, %f, %f, %i)", + "VALUES (\"%s\", %i, %f, %f, %f, %f, %f, %i)", sep->arg[2], (sep->arg[3]? atoi(sep->arg[3]): 0), (sep->arg[4]? atof(sep->arg[4]): 0), @@ -7907,7 +7907,7 @@ void command_advnpcspawn(Client *c, const Seperator *sep) } std::string query = StringFormat("INSERT INTO spawnentry (spawngroupID, npcID, chance) " - "VALUES (%i, %i, %i)", + "VALUES (%i, %i, %i)", atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4])); auto results = database.QueryDatabase(query); if (!results.Success()) { @@ -7928,7 +7928,7 @@ void command_advnpcspawn(Client *c, const Seperator *sep) } std::string query = StringFormat("UPDATE spawngroup SET dist = '%f', max_x = '%f', min_x = '%f', " - "max_y = '%f', min_y = '%f', delay = '%i' WHERE id = '%i'", + "max_y = '%f', min_y = '%f', delay = '%i' WHERE id = '%i'", atof(sep->arg[3]), atof(sep->arg[4]), atof(sep->arg[5]), atof(sep->arg[6]), atof(sep->arg[7]), atoi(sep->arg[8]), atoi(sep->arg[2])); @@ -8013,7 +8013,7 @@ void command_advnpcspawn(Client *c, const Seperator *sep) } std::string query = StringFormat("UPDATE spawn2 SET x = '%f', y = '%f', z = '%f', heading = '%f' " - "WHERE id = '%i'", + "WHERE id = '%i'", c->GetX(), c->GetY(), c->GetZ(), c->GetHeading(),s2->GetID()); auto results = database.QueryDatabase(query); if (!results.Success()) { @@ -8084,7 +8084,7 @@ void command_advnpcspawn(Client *c, const Seperator *sep) int16 version = atoi(sep->arg[2]); std::string query = StringFormat("UPDATE spawn2 SET version = %i " - "WHERE spawngroupID = '%i'", + "WHERE spawngroupID = '%i'", version, c->GetTarget()->CastToNPC()->GetSp2()); auto results = database.QueryDatabase(query); if (!results.Success()) { @@ -10135,7 +10135,7 @@ void command_zopp(Client *c, const Seperator *sep) ItemInst* FakeItemInst = database.CreateItem(FakeItem, charges); c->SendItemPacket(slotid, FakeItemInst, packettype); - c->Message(0, "Sending zephyr op packet to client - [%s] %s (%u) with %i %s to slot %i.", + c->Message(0, "Sending zephyr op packet to client - [%s] %s (%u) with %i %s to slot %i.", packettype == ItemPacketTrade ? "Trade" : "Summon", FakeItem->Name, itemid, charges, std::abs(charges == 1) ? "charge" : "charges", slotid); safe_delete(FakeItemInst); @@ -10354,7 +10354,7 @@ void command_tune(Client *c, const Seperator *sep) ac_override = 0; if (!info_level) info_level = 1; - + if(!strcasecmp(sep->arg[2], "A")) c->Tune_FindATKByPctMitigation(defender, attacker, pct_mitigation, interval, max_loop,ac_override,info_level); else if(!strcasecmp(sep->arg[2], "B")) @@ -10389,7 +10389,7 @@ void command_tune(Client *c, const Seperator *sep) atk_override = 0; if (!info_level) info_level = 1; - + if(!strcasecmp(sep->arg[2], "A")) c->Tune_FindACByPctMitigation(defender, attacker, pct_mitigation, interval, max_loop,atk_override,info_level); else if(!strcasecmp(sep->arg[2], "B")) @@ -10431,7 +10431,7 @@ void command_tune(Client *c, const Seperator *sep) c->Message(10, "#Tune - Error hit chance out of bounds. [Max %.2f Min .2f]", RuleR(Combat,MaxChancetoHit),RuleR(Combat,MinChancetoHit)); return; } - + if(!strcasecmp(sep->arg[2], "A")) c->Tune_FindAccuaryByHitChance(defender, attacker, hit_chance, interval, max_loop,avoid_override,info_level); else if(!strcasecmp(sep->arg[2], "B")) @@ -10473,7 +10473,7 @@ void command_tune(Client *c, const Seperator *sep) c->Message(10, "#Tune - Error hit chance out of bounds. [Max %.2f Min .2f]", RuleR(Combat,MaxChancetoHit),RuleR(Combat,MinChancetoHit)); return; } - + if(!strcasecmp(sep->arg[2], "A")) c->Tune_FindAvoidanceByHitChance(defender, attacker, hit_chance, interval, max_loop,acc_override, info_level); else if(!strcasecmp(sep->arg[2], "B")) @@ -10499,7 +10499,7 @@ void command_logtest(Client *c, const Seperator *sep){ for (i = 0; i < atoi(sep->arg[1]); i++){ Log.Out(Logs::General, Logs::Debug, "[%u] Test #2... Took %f seconds", i, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); } - + } } @@ -10555,7 +10555,7 @@ void command_logs(Client *c, const Seperator *sep){ c->Message(15, "Your Log Settings have been applied"); c->Message(15, "Output Method: %s :: Debug Level: %i - Category: %s", sep->arg[2], atoi(sep->arg[4]), Logs::LogCategoryName[atoi(sep->arg[3])]); } - /* We use a general 'is_category_enabled' now, let's update when we update any output settings + /* We use a general 'is_category_enabled' now, let's update when we update any output settings This is used in hot places of code to check if its enabled in any way before triggering logs */ if (sep->arg[4] > 0){ @@ -10583,9 +10583,9 @@ void command_mysqltest(Client *c, const Seperator *sep) for (i = 0; i < atoi(sep->arg[1]); i++){ std::string query = "SELECT * FROM `zone`"; auto results = database.QueryDatabase(query); - } + } } - Log.Out(Logs::General, Logs::Debug, "MySQL Test... Took %f seconds", ((float)(std::clock() - t)) / CLOCKS_PER_SEC); + Log.Out(Logs::General, Logs::Debug, "MySQL Test... Took %f seconds", ((float)(std::clock() - t)) / CLOCKS_PER_SEC); } void command_resetaa_timer(Client *c, const Seperator *sep) { @@ -10596,18 +10596,18 @@ void command_resetaa_timer(Client *c, const Seperator *sep) { target = c->GetTarget()->CastToClient(); } - if(sep->IsNumber(1)) + if(sep->IsNumber(1)) { int timer_id = atoi(sep->arg[1]); c->Message(0, "Reset of timer %i for %s", timer_id, c->GetName()); c->ResetAlternateAdvancementTimer(timer_id); } - else if(!strcasecmp(sep->arg[1], "all")) + else if(!strcasecmp(sep->arg[1], "all")) { c->Message(0, "Reset all timers for %s", c->GetName()); c->ResetAlternateAdvancementTimers(); - } - else + } + else { c->Message(0, "usage: #resetaa_timer [all | timer_id]"); } diff --git a/zone/mob.cpp b/zone/mob.cpp index ce0a97db9..938a9704b 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -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 { switch(GetClass()) diff --git a/zone/mob.h b/zone/mob.h index 0094b0fbc..fcabf93fd 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -169,7 +169,7 @@ public: virtual bool CheckDualWield(); void DoMainHandAttackRounds(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 void ProcessAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr) { @@ -750,6 +750,7 @@ public: virtual int GetMonkHandToHandDamage(void); bool CanThisClassDoubleAttack(void) const; + bool CanThisClassTripleAttack() const; bool CanThisClassDualWield(void) const; bool CanThisClassRiposte(void) const; bool CanThisClassDodge(void) const; diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 1bcccf272..1a134d167 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -536,7 +536,7 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) { RogueBackstab(other,false,ReuseTime); if (level > 54) { - if(IsClient() && CastToClient()->CheckDoubleAttack(false)) + if(IsClient() && CastToClient()->CheckDoubleAttack()) { if(other->GetHP() > 0) RogueBackstab(other,false,ReuseTime); @@ -558,7 +558,7 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) { if (level > 54) { // 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) RogueBackstab(other,true, ReuseTime);