From d2d7b8108d41586a1d7a3e3530a48f22ada9b9f0 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 11 Feb 2022 16:26:08 -0500 Subject: [PATCH] [Commands] Cleanup #ai Command. (#1980) - Cleanup messages and logic. - Remove #ai start/#ai stop as they can crash zones and are mostly useless. - Add EQ::constants::GetConsiderLevelMap() and EQ::constants::GetConsiderLevelName(). - Add quest::getconsiderlevelname(consider_level) to Perl. - Add eq.get_consider_level_name(consider_level) to Lua. --- common/emu_constants.cpp | 25 +++ common/emu_constants.h | 15 ++ zone/embparser_api.cpp | 18 ++ zone/gm_commands/ai.cpp | 360 ++++++++++++++++++++++++++------------- zone/lua_general.cpp | 5 + zone/questmgr.cpp | 4 + zone/questmgr.h | 1 + 7 files changed, 310 insertions(+), 118 deletions(-) diff --git a/common/emu_constants.cpp b/common/emu_constants.cpp index 99710712e..4d5c40b64 100644 --- a/common/emu_constants.cpp +++ b/common/emu_constants.cpp @@ -338,6 +338,31 @@ std::string EQ::constants::GetAccountStatusName(uint8 account_status) return status_name; } +const std::map& EQ::constants::GetConsiderLevelMap() +{ + static const std::map consider_level_map = { + { ConsiderLevel::Ally, "Ally" }, + { ConsiderLevel::Warmly, "Warmly" }, + { ConsiderLevel::Kindly, "Kindly" }, + { ConsiderLevel::Amiably, "Amiably" }, + { ConsiderLevel::Indifferently, "Indifferently" }, + { ConsiderLevel::Apprehensively, "Apprehensively" }, + { ConsiderLevel::Dubiously, "Dubiously" }, + { ConsiderLevel::Threateningly, "Threateningly" }, + { ConsiderLevel::Scowls, "Scowls" } + }; + return consider_level_map; +} + +std::string EQ::constants::GetConsiderLevelName(uint8 faction_consider_level) +{ + auto consider_levels = EQ::constants::GetConsiderLevelMap(); + if (!consider_levels[faction_consider_level].empty()) { + return consider_levels[faction_consider_level]; + } + return std::string(); +} + const std::map& EQ::constants::GetEnvironmentalDamageMap() { static const std::map damage_type_map = { diff --git a/common/emu_constants.h b/common/emu_constants.h index 527df11a1..f6272274e 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -255,6 +255,9 @@ namespace EQ extern const std::map& GetAccountStatusMap(); std::string GetAccountStatusName(uint8 account_status); + extern const std::map& GetConsiderLevelMap(); + std::string GetConsiderLevelName(uint8 consider_level); + extern const std::map& GetEnvironmentalDamageMap(); std::string GetEnvironmentalDamageName(uint8 damage_type); @@ -401,4 +404,16 @@ enum AugmentActions : int { Destroy }; +enum ConsiderLevel : uint8 { + Ally = 1, + Warmly, + Kindly, + Amiably, + Indifferently, + Apprehensively, + Dubiously, + Threateningly, + Scowls +}; + #endif /*COMMON_EMU_CONSTANTS_H*/ diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 47c85161b..04a8654f1 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -8092,6 +8092,23 @@ XS(XS__getbodytypename) { } } +XS(XS__getconsiderlevelname); +XS(XS__getconsiderlevelname) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getconsiderlevelname(uint8 consider_level)"); + { + dXSTARG; + uint8 consider_level = (uint8) SvUV(ST(0)); + std::string consider_level_name = quest_manager.getconsiderlevelname(consider_level); + + sv_setpv(TARG, consider_level_name.c_str()); + XSprePUSH; + PUSHTARG; + XSRETURN(1); + } +} + XS(XS__getenvironmentaldamagename); XS(XS__getenvironmentaldamagename) { dXSARGS; @@ -8392,6 +8409,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "getcharidbyname"), XS__getcharidbyname, file); newXS(strcpy(buf, "getclassname"), XS__getclassname, file); newXS(strcpy(buf, "getcleannpcnamebyid"), XS__getcleannpcnamebyid, file); + newXS(strcpy(buf, "getconsiderlevelname"), XS__getconsiderlevelname, file); newXS(strcpy(buf, "gethexcolorcode"), XS__gethexcolorcode, file); newXS(strcpy(buf, "getcurrencyid"), XS__getcurrencyid, file); newXS(strcpy(buf, "getexpmodifierbycharid"), XS__getexpmodifierbycharid, file); diff --git a/zone/gm_commands/ai.cpp b/zone/gm_commands/ai.cpp index 93f9a057f..e7f686307 100755 --- a/zone/gm_commands/ai.cpp +++ b/zone/gm_commands/ai.cpp @@ -2,138 +2,262 @@ void command_ai(Client *c, const Seperator *sep) { - Mob *target = c->GetTarget(); + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #ai consider [Mob Name] - Show how an NPC considers to a mob"); + c->Message(Chat::White, "Usage: #ai faction [Faction ID] - Set an NPC's Faction ID"); + c->Message(Chat::White, "Usage: #ai guard - Save an NPC's guard spot to their current location"); + c->Message(Chat::White, "Usage: #ai roambox [Distance] [Min X] [Max X] [Min Y] [Max Y] [Delay] [Minimum Delay] - Set an NPC's roambox using X and Y coordinates"); + c->Message(Chat::White, "Usage: #ai roambox [Distance] [Roam Distance] [Delay] [Minimum Delay] - Set an NPC's roambox using roam distance"); + c->Message(Chat::White, "Usage: #ai spells [Spell List ID] - Set an NPC's Spell List ID"); + return; + } - if (strcasecmp(sep->arg[1], "factionid") == 0) { - if (target && sep->IsNumber(2)) { - if (target->IsNPC()) { - target->CastToNPC()->SetNPCFactionID(atoi(sep->arg[2])); - } - else { - c->Message(Chat::White, "%s is not an NPC.", target->GetName()); - } - } - else { - c->Message(Chat::White, "Usage: (targeted) #ai factionid [factionid]"); - } + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; } - else if (strcasecmp(sep->arg[1], "spellslist") == 0) { - if (target && sep->IsNumber(2) && atoi(sep->arg[2]) >= 0) { - if (target->IsNPC()) { - target->CastToNPC()->AI_AddNPCSpells(atoi(sep->arg[2])); - } - else { - c->Message(Chat::White, "%s is not an NPC.", target->GetName()); - } - } - else { - c->Message(Chat::White, "Usage: (targeted) #ai spellslist [npc_spells_id]"); - } + + auto target = c->GetTarget()->CastToNPC(); + + bool is_consider = !strcasecmp(sep->arg[1], "consider"); + bool is_faction = !strcasecmp(sep->arg[1], "faction"); + bool is_guard = !strcasecmp(sep->arg[1], "guard"); + bool is_roambox = !strcasecmp(sep->arg[1], "roambox"); + bool is_spells = !strcasecmp(sep->arg[1], "spells"); + + if ( + !is_consider && + !is_faction && + !is_guard && + !is_roambox && + !is_spells + ) { + c->Message(Chat::White, "Usage: #ai consider [Mob Name] - Show how an NPC considers to a mob"); + c->Message(Chat::White, "Usage: #ai faction [Faction ID] - Set an NPC's Faction ID"); + c->Message(Chat::White, "Usage: #ai guard - Save an NPC's guard spot to their current location"); + c->Message(Chat::White, "Usage: #ai roambox [Distance] [Min X] [Max X] [Min Y] [Max Y] [Delay] [Minimum Delay] - Set an NPC's roambox using X and Y coordinates"); + c->Message(Chat::White, "Usage: #ai roambox [Distance] [Roam Distance] [Delay] [Minimum Delay] - Set an NPC's roambox using roam distance"); + c->Message(Chat::White, "Usage: #ai spells [Spell List ID] - Set an NPC's Spell List ID"); + return; } - else if (strcasecmp(sep->arg[1], "con") == 0) { - if (target && sep->arg[2][0] != 0) { - Mob *tar2 = entity_list.GetMob(sep->arg[2]); - if (tar2) { + + if (is_consider) { + if (arguments == 2) { + auto mob_name = sep->arg[2]; + auto mob_to_consider = entity_list.GetMob(mob_name); + if (mob_to_consider) { + auto consider_level = static_cast(mob_to_consider->GetReverseFactionCon(target)); c->Message( Chat::White, - "%s considering %s: %i", - target->GetName(), - tar2->GetName(), - tar2->GetReverseFactionCon(target)); - } - else { - c->Message(Chat::White, "Error: %s not found.", sep->arg[2]); - } - } - else { - c->Message(Chat::White, "Usage: (targeted) #ai con [mob name]"); - } - } - else if (strcasecmp(sep->arg[1], "guard") == 0) { - if (target && target->IsNPC()) { - target->CastToNPC()->SaveGuardSpot(target->GetPosition()); - } - else { - c->Message( - Chat::White, - "Usage: (targeted) #ai guard - sets npc to guard the current location (use #summon to move)" - ); - } - } - else if (strcasecmp(sep->arg[1], "roambox") == 0) { - if (target && target->IsAIControlled() && target->IsNPC()) { - if ((sep->argnum == 6 || sep->argnum == 7 || sep->argnum == 8) && sep->IsNumber(2) && sep->IsNumber(3) && - sep->IsNumber(4) && sep->IsNumber(5) && sep->IsNumber(6)) { - uint32 tmp = 2500; - uint32 tmp2 = 2500; - if (sep->IsNumber(7)) { - tmp = atoi(sep->arg[7]); - } - if (sep->IsNumber(8)) { - tmp2 = atoi(sep->arg[8]); - } - target->CastToNPC()->AI_SetRoambox( - atof(sep->arg[2]), - atof(sep->arg[3]), - atof(sep->arg[4]), - atof(sep->arg[5]), - atof(sep->arg[6]), - tmp, - tmp2 + fmt::format( + "{} ({}) considers {} ({}) as {} ({}).", + target->GetCleanName(), + target->GetID(), + mob_to_consider->GetCleanName(), + mob_to_consider->GetID(), + EQ::constants::GetConsiderLevelName(consider_level), + consider_level + ).c_str() ); } - else if ((sep->argnum == 3 || sep->argnum == 4) && sep->IsNumber(2) && sep->IsNumber(3)) { - uint32 tmp = 2500; - uint32 tmp2 = 2500; + } else { + c->Message(Chat::White, "Usage: #ai consider [Mob Name] - Show how an NPC considers a mob"); + } + } else if (is_faction) { + if (sep->IsNumber(2)) { + auto faction_id = std::stoi(sep->arg[2]); + auto faction_name = content_db.GetFactionName(faction_id); + target->SetNPCFactionID(faction_id); + c->Message( + Chat::White, + fmt::format( + "{} ({}) is now on Faction {}.", + target->GetCleanName(), + target->GetID(), + ( + faction_name.empty() ? + std::to_string(faction_id) : + fmt::format( + "{} ({})", + faction_name, + faction_id + ) + ) + ).c_str() + ); + } else { + c->Message(Chat::White, "Usage: #ai faction [Faction ID] - Set an NPC's Faction ID"); + } + } else if (is_guard) { + auto target_position = target->GetPosition(); + + target->SaveGuardSpot(target_position); + + c->Message( + Chat::White, + fmt::format( + "{} ({}) now has a guard spot of {:.2f}, {:.2f}, {:.2f} with a heading of {:.2f}.", + target->GetCleanName(), + target->GetID(), + target_position.x, + target_position.y, + target_position.z, + target_position.w + ).c_str() + ); + } else if (is_roambox) { + if (target->IsAIControlled()) { + if ( + arguments >= 6 && + arguments <= 8 && + sep->IsNumber(2) && + sep->IsNumber(3) && + sep->IsNumber(4) && + sep->IsNumber(5) && + sep->IsNumber(6) + ) { + auto distance = std::stof(sep->arg[2]); + auto min_x = std::stof(sep->arg[3]); + auto max_x = std::stof(sep->arg[4]); + auto min_y = std::stof(sep->arg[5]); + auto max_y = std::stof(sep->arg[6]); + + uint32 delay = 2500; + uint32 minimum_delay = 2500; + + if (sep->IsNumber(7)) { + delay = std::stoul(sep->arg[7]); + } + + if (sep->IsNumber(8)) { + minimum_delay = std::stoul(sep->arg[8]); + } + + target->CastToNPC()->AI_SetRoambox( + distance, + max_x, + min_x, + max_y, + min_y, + delay, + minimum_delay + ); + + c->Message( + Chat::White, + fmt::format( + "{} ({}) now has a roambox from {}, {} to {}, {} with {} and {} and a distance of {}.", + target->GetCleanName(), + target->GetID(), + min_x, + min_y, + max_x, + max_y, + ( + delay ? + fmt::format( + "a delay of {} ({})", + ConvertMillisecondsToTime(delay), + delay + ): + "no delay" + ), + ( + minimum_delay ? + fmt::format( + "a minimum delay of {} ({})", + ConvertMillisecondsToTime(minimum_delay), + minimum_delay + ): + "no minimum delay" + ), + distance + ).c_str() + ); + } else if ( + arguments >= 3 && + arguments <= 4 && + sep->IsNumber(2) && + sep->IsNumber(3) + ) { + auto max_distance = std::stof(sep->arg[2]); + auto roam_distance_variance = std::stof(sep->arg[3]); + + uint32 delay = 2500; + uint32 minimum_delay = 2500; + if (sep->IsNumber(4)) { - tmp = atoi(sep->arg[4]); + delay = std::stoul(sep->arg[4]); } + if (sep->IsNumber(5)) { - tmp2 = atoi(sep->arg[5]); + minimum_delay = std::stoul(sep->arg[5]); } - target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), tmp, tmp2); + + target->CastToNPC()->AI_SetRoambox( + max_distance, + roam_distance_variance, + delay, + minimum_delay + ); + + c->Message( + Chat::White, + fmt::format( + "{} ({}) now has a roambox with a max distance of {} and a roam distance variance of {} with {} and {}.", + target->GetCleanName(), + target->GetID(), + max_distance, + roam_distance_variance, + ( + delay ? + fmt::format( + "a delay of {} ({})", + delay, + ConvertMillisecondsToTime(delay) + ): + "no delay" + ), + ( + minimum_delay ? + fmt::format( + "a minimum delay of {} ({})", + minimum_delay, + ConvertMillisecondsToTime(delay) + ): + "no minimum delay" + ) + ).c_str() + ); + } else { + c->Message(Chat::White, "Usage: #ai roambox [Distance] [Min X] [Max X] [Min Y] [Max Y] [Delay] [Minimum Delay] - Set an NPC's roambox using X and Y coordinates"); + c->Message(Chat::White, "Usage: #ai roambox [Distance] [Roam Distance] [Delay] [Minimum Delay] - Set an NPC's roambox using roam distance"); } - else { - c->Message(Chat::White, "Usage: #ai roambox dist max_x min_x max_y min_y [delay] [mindelay]"); - c->Message(Chat::White, "Usage: #ai roambox dist roamdist [delay] [mindelay]"); + } else { + c->Message(Chat::White, "You must target an NPC with AI."); + } + } else if (is_spells) { + if (sep->IsNumber(2)) { + auto spell_list_id = std::stoul(sep->arg[2]); + if (spell_list_id >= 0) { + target->CastToNPC()->AI_AddNPCSpells(spell_list_id); + + c->Message( + Chat::White, + fmt::format( + "{} ({}) is now using Spell List {}.", + target->GetCleanName(), + target->GetID(), + spell_list_id + ).c_str() + ); + } else { + c->Message(Chat::White, "Spell List ID must be greater than or equal to 0."); } + } else { + c->Message(Chat::White, "Usage: #ai spells [Spell List ID] - Set an NPC's Spell List ID"); } - else { - c->Message(Chat::White, "You need a AI NPC targeted"); - } - } - else if (strcasecmp(sep->arg[1], "stop") == 0 && c->Admin() >= commandToggleAI) { - if (target) { - if (target->IsAIControlled()) { - target->AI_Stop(); - } - else { - c->Message(Chat::White, "Error: Target is not AI controlled"); - } - } - else { - c->Message(Chat::White, "Usage: Target a Mob with AI enabled and use this to turn off their AI."); - } - } - else if (strcasecmp(sep->arg[1], "start") == 0 && c->Admin() >= commandToggleAI) { - if (target) { - if (!target->IsAIControlled()) { - target->AI_Start(); - } - else { - c->Message(Chat::White, "Error: Target is already AI controlled"); - } - } - else { - c->Message(Chat::White, "Usage: Target a Mob with AI disabled and use this to turn on their AI."); - } - } - else { - c->Message(Chat::White, "#AI Sub-commands"); - c->Message(Chat::White, " factionid"); - c->Message(Chat::White, " spellslist"); - c->Message(Chat::White, " con"); - c->Message(Chat::White, " guard"); } } diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 2d6ae07cd..db4394b0f 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -3367,6 +3367,10 @@ std::string lua_get_body_type_name(uint32 bodytype_id) { return quest_manager.getbodytypename(bodytype_id); } +std::string lua_get_consider_level_name(uint8 consider_level) { + return quest_manager.getconsiderlevelname(consider_level); +} + std::string lua_get_environmental_damage_name(uint8 damage_type) { return quest_manager.getenvironmentaldamagename(damage_type); } @@ -3818,6 +3822,7 @@ luabind::scope lua_register_general() { luabind::def("get_faction_name", &lua_get_faction_name), luabind::def("get_language_name", &lua_get_language_name), luabind::def("get_body_type_name", &lua_get_body_type_name), + luabind::def("get_consider_level_name", &lua_get_consider_level_name), luabind::def("get_environmental_damage_name", &lua_get_environmental_damage_name), /* diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 189a399eb..02ffb60df 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1052,6 +1052,10 @@ std::string QuestManager::getbodytypename(uint32 bodytype_id) { return EQ::constants::GetBodyTypeName(static_cast(bodytype_id)); } +std::string QuestManager::getconsiderlevelname(uint8 consider_level) { + return EQ::constants::GetConsiderLevelName(consider_level); +} + void QuestManager::safemove() { QuestManagerCurrentQuestVars(); if (initiator && initiator->IsClient()) diff --git a/zone/questmgr.h b/zone/questmgr.h index b8a296ed7..f859a012d 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -119,6 +119,7 @@ public: std::string getfactionname(int faction_id); std::string getlanguagename(int language_id); std::string getbodytypename(uint32 bodytype_id); + std::string getconsiderlevelname(uint8 consider_level); void safemove(); void rain(int weather); void snow(int weather);