/* EQEMu: Everquest Server Emulator Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.org) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY except by those people which sell it, which are required to give you total support for your newly bought product; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* To add a new command 3 things must be done: 1. At the bottom of command.h you must add a prototype for it. 2. Add the function in this file. 3. In the command_init function you must add a call to command_add for your function. Notes: If you want an alias for your command, add an entry to the `command_settings` table in your database. The access level you set with command_add is the default setting if the command isn't listed in the `command_settings` db table. */ #include #include #include #include #include #include #include #ifdef _WINDOWS #define strcasecmp _stricmp #endif #include "../common/global_define.h" #include "../common/eq_packet.h" #include "../common/features.h" #include "../common/guilds.h" #include "../common/patches/patches.h" #include "../common/ptimer.h" #include "../common/rulesys.h" #include "../common/serverinfo.h" #include "../common/string_util.h" #include "../common/say_link.h" #include "../common/eqemu_logsys.h" #include "../common/profanity_manager.h" #include "../common/net/eqstream.h" #include "../common/repositories/dynamic_zones_repository.h" #include "data_bucket.h" #include "command.h" #include "dynamic_zone.h" #include "expedition.h" #include "guild_mgr.h" #include "map.h" #include "qglobals.h" #include "queryserv.h" #include "quest_parser_collection.h" #include "string_ids.h" #include "titles.h" #include "water_map.h" #include "worldserver.h" #include "fastmath.h" #include "mob_movement_manager.h" #include "npc_scale_manager.h" #include "../common/content/world_content_service.h" #include "../common/http/httplib.h" #include "../common/shared_tasks.h" #include "gm_commands/door_manipulation.h" #include "../common/languages.h" extern QueryServ* QServ; extern WorldServer worldserver; extern TaskManager *task_manager; extern FastMath g_Math; void CatchSignal(int sig_num); int commandcount; // how many commands we have // this is the pointer to the dispatch function, updated once // init has been performed to point at the real function int (*command_dispatch)(Client *,char const *)=command_notavail; void command_bestz(Client *c, const Seperator *message); void command_pf(Client *c, const Seperator *message); std::map commandlist; std::map commandaliases; // All allocated CommandRecords get put in here so they get deleted on shutdown LinkedList cleanup_commandlist; /* * command_notavail * This is the default dispatch function when commands aren't loaded. * * Parameters: * not used * */ int command_notavail(Client *c, const char *message) { c->Message(Chat::Red, "Commands not available."); return -1; } /************************************************************************** /* the rest below here could be in a dynamically loaded module eventually * /*************************************************************************/ /* Access Levels: 0 Normal 10 * Steward * 20 * Apprentice Guide * 50 * Guide * 80 * QuestTroupe * 81 * Senior Guide * 85 * GM-Tester * 90 * EQ Support * 95 * GM-Staff * 100 * GM-Admin * 150 * GM-Lead Admin * 160 * QuestMaster * 170 * GM-Areas * 180 * GM-Coder * 200 * GM-Mgmt * 250 * GM-Impossible * */ /* * command_init * initializes the command list, call at startup * * Parameters: * none * * When adding a new command, only hard-code 'real' commands - * all command aliases are added later through a database call * */ int command_init(void) { commandaliases.clear(); if ( command_add("acceptrules", "[acceptrules] - Accept the EQEmu Agreement", 0, command_acceptrules) || command_add("advnpcspawn", "[maketype|makegroup|addgroupentry|addgroupspawn][removegroupspawn|movespawn|editgroupbox|cleargroupbox]", 150, command_advnpcspawn) || command_add("aggro", "(range) [-v] - Display aggro information for all mobs 'range' distance from your target. -v is verbose faction info.", 80, command_aggro) || command_add("aggrozone", "[aggro] - Aggro every mob in the zone with X aggro. Default is 0. Not recommend if you're not invulnerable.", 100, command_aggrozone) || command_add("ai", "[factionid/spellslist/con/guard/roambox/stop/start] - Modify AI on NPC target", 100, command_ai) || command_add("appearance", "[type] [value] - Send an appearance packet for you or your target", 150, command_appearance) || command_add("apply_shared_memory", "[shared_memory_name] - Tells every zone and world to apply a specific shared memory segment by name.", 250, command_apply_shared_memory) || command_add("attack", "[targetname] - Make your NPC target attack targetname", 150, command_attack) || command_add("augmentitem", "Force augments an item. Must have the augment item window open.", 250, command_augmentitem) || command_add("ban", "[name] [reason]- Ban by character name", 150, command_ban) || command_add("beard", "- Change the beard of your target", 80, command_beard) || command_add("beardcolor", "- Change the beard color of your target", 80, command_beardcolor) || command_add("bestz", "- Ask map for a good Z coord for your x,y coords.", 0, command_bestz) || command_add("bind", "- Sets your targets bind spot to their current location", 200, command_bind) || #ifdef BOTS command_add("bot", "- Type \"#bot help\" or \"^help\" to the see the list of available commands for bots.", 0, command_bot) || #endif command_add("camerashake", "Shakes the camera on everyone's screen globally.", 80, command_camerashake) || command_add("castspell", "[Spell ID] [Instant (0 = False, 1 = True, Default is 1 if Unused)] - Cast a spell", 50, command_castspell) || command_add("chat", "[channel num] [message] - Send a channel message to all zones", 200, command_chat) || command_add("checklos", "- Check for line of sight to your target", 50, command_checklos) || command_add("copycharacter", "[source_char_name] [dest_char_name] [dest_account_name] Copies character to destination account", 250, command_copycharacter) || command_add("corpse", "- Manipulate corpses, use with no arguments for help", 50, command_corpse) || command_add("corpsefix", "Attempts to bring corpses from underneath the ground within close proximity of the player", 0, command_corpsefix) || command_add("cvs", "- Summary of client versions currently online.", 200, command_cvs) || command_add("damage", "[amount] - Damage your target", 100, command_damage) || command_add("databuckets", "View|Delete [key] [limit]- View data buckets, limit 50 default or Delete databucket by key", 80, command_databuckets) || command_add("date", "[yyyy] [mm] [dd] [HH] [MM] - Set EQ time", 90, command_date) || command_add("dbspawn2", "[spawngroup] [respawn] [variance] - Spawn an NPC from a predefined row in the spawn2 table", 100, command_dbspawn2) || command_add("delacct", "[accountname] - Delete an account", 150, command_delacct) || command_add("deletegraveyard", "[zone name] - Deletes the graveyard for the specified zone.", 200, command_deletegraveyard) || command_add("delpetition", "[petition number] - Delete a petition", 20, command_delpetition) || command_add("depop", "- Depop your NPC target", 50, command_depop) || command_add("depopzone", "- Depop the zone", 100, command_depopzone) || command_add("devtools", "- Manages devtools", 200, command_devtools) || command_add("details", "- Change the details of your target (Drakkin Only)", 80, command_details) || command_add("disablerecipe", "[recipe_id] - Disables a recipe using the recipe id.", 80, command_disablerecipe) || command_add("disarmtrap", "Analog for ldon disarm trap for the newer clients since we still don't have it working.", 80, command_disarmtrap) || command_add("distance", "- Reports the distance between you and your target.", 80, command_distance) || command_add("door", "Door editing command", 80, command_door) || command_add("doanim", "[animnum] [type] - Send an EmoteAnim for you or your target", 50, command_doanim) || command_add("dye", "[slot|'help'] [red] [green] [blue] [use_tint] - Dyes the specified armor slot to Red, Green, and Blue provided, allows you to bypass darkness limits.", 20, command_dye) || command_add("dz", "Manage expeditions and dynamic zone instances", 80, command_dz) || command_add("dzkickplayers", "Removes all players from current expedition. (/kickplayers alternative for pre-RoF clients)", 0, command_dzkickplayers) || command_add("editmassrespawn", "[name-search] [second-value] - Mass (Zone wide) NPC respawn timer editing command", 100, command_editmassrespawn) || command_add("emote", "['name'/'world'/'zone'] [type] [message] - Send an emote message", 80, command_emote) || command_add("emotesearch", "Searches NPC Emotes", 80, command_emotesearch) || command_add("emoteview", "Lists all NPC Emotes", 80, command_emoteview) || command_add("emptyinventory", "- Clears you or your target's entire inventory (Equipment, General, Bank, and Shared Bank)", 250, command_emptyinventory) || command_add("enablerecipe", "[recipe_id] - Enables a recipe using the recipe id.", 80, command_enablerecipe) || command_add("endurance", "Restores you or your target's endurance.", 50, command_endurance) || command_add("equipitem", "[slotid(0-21)] - Equip the item on your cursor into the specified slot", 50, command_equipitem) || command_add("face", "- Change the face of your target", 80, command_face) || command_add("faction", "[Find (criteria | all ) | Review (criteria | all) | Reset (id)] - Resets Player's Faction", 80, command_faction) || command_add("findaliases", "[search criteria]- Searches for available command aliases, by alias or command", 0, command_findaliases) || command_add("findclass", "[search criteria] - Search for a class", 50, command_findclass) || command_add("findfaction", "[search criteria] - Search for a faction", 50, command_findfaction) || command_add("findnpctype", "[search criteria] - Search database NPC types", 100, command_findnpctype) || command_add("findrace", "[search criteria] - Search for a race", 50, command_findrace) || command_add("findskill", "[search criteria] - Search for a skill", 50, command_findskill) || command_add("findspell", "[search criteria] - Search for a spell", 50, command_findspell) || command_add("findtask", "[search criteria] - Search for a task", 50, command_findtask) || command_add("findzone", "[search criteria] - Search database zones", 100, command_findzone) || command_add("fixmob", "[race|gender|texture|helm|face|hair|haircolor|beard|beardcolor|heritage|tattoo|detail] [next|prev] - Manipulate appearance of your target", 80, command_fixmob) || command_add("flag", "[status] [acctname] - Refresh your admin status, or set an account's admin status if arguments provided", 0, command_flag) || command_add("flagedit", "- Edit zone flags on your target", 100, command_flagedit) || command_add("flags", "- displays the flags of you or your target", 0, command_flags) || command_add("flymode", "[0/1/2/3/4/5] - Set your or your player target's flymode to ground/flying/levitate/water/floating/levitate_running", 50, command_flymode) || command_add("fov", "- Check wether you're behind or in your target's field of view", 80, command_fov) || command_add("freeze", "- Freeze your target", 80, command_freeze) || command_add("gassign", "[id] - Assign targetted NPC to predefined wandering grid id", 100, command_gassign) || command_add("gearup", "Developer tool to quickly equip a character", 200, command_gearup) || command_add("gender", "[0/1/2] - Change your or your target's gender to male/female/neuter", 50, command_gender) || command_add("getplayerburiedcorpsecount", "- Get the target's total number of buried player corpses.", 100, command_getplayerburiedcorpsecount) || command_add("getvariable", "[varname] - Get the value of a variable from the database", 200, command_getvariable) || command_add("ginfo", "- get group info on target.", 20, command_ginfo) || command_add("giveitem", "[itemid] [charges] - Summon an item onto your target's cursor. Charges are optional.", 200, command_giveitem) || command_add("givemoney", "[pp] [gp] [sp] [cp] - Gives specified amount of money to the target player.", 200, command_givemoney) || command_add("globalview", "Lists all qglobals in cache if you were to do a quest with this target.", 80, command_globalview) || command_add("gm", "- Turn player target's or your GM flag on or off", 80, command_gm) || command_add("gmspeed", "[on/off] - Turn GM speed hack on/off for you or your player target", 100, command_gmspeed) || command_add("gmzone", "[zone_short_name] [zone_version=0] [identifier=gmzone] - Zones to a private GM instance", 100, command_gmzone) || command_add("goto", "[playername] or [x y z] [h] - Teleport to the provided coordinates or to your target", 10, command_goto) || command_add("grid", "[add/delete] [grid_num] [wandertype] [pausetype] - Create/delete a wandering grid", 170, command_grid) || command_add("guild", "- Guild manipulation commands. Use argument help for more info.", 10, command_guild) || command_add("guildapprove", "[guildapproveid] - Approve a guild with specified ID (guild creator receives the id)", 0, command_guildapprove) || command_add("guildcreate", "[guildname] - Creates an approval setup for guild name specified", 0, command_guildcreate) || command_add("guildlist", "[guildapproveid] - Lists character names who have approved the guild specified by the approve id", 0, command_guildlist) || command_add("hair", "- Change the hair style of your target", 80, command_hair) || command_add("haircolor", "- Change the hair color of your target", 80, command_haircolor) || command_add("haste", "[percentage] - Set your haste percentage", 100, command_haste) || command_add("hatelist", " - Display hate list for target.", 80, command_hatelist) || command_add("heal", "- Completely heal your target", 10, command_heal) || command_add("helm", "- Change the helm of your target", 80, command_helm) || command_add("help", "[search term] - List available commands and their description, specify partial command as argument to search", 0, command_help) || command_add("heritage", "- Change the heritage of your target (Drakkin Only)", 80, command_heritage) || command_add("heromodel", "[hero model] [slot] - Full set of Hero's Forge Armor appearance. If slot is set, sends exact model just to slot.", 200, command_heromodel) || command_add("hideme", "[on/off] - Hide yourself from spawn lists.", 80, command_hideme) || command_add("hotfix", "[hotfix_name] - Reloads shared memory into a hotfix, equiv to load_shared_memory followed by apply_shared_memory", 250, command_hotfix) || command_add("hp", "- Refresh your HP bar from the server.", 0, command_hp) || command_add("incstat", "- Increases or Decreases a client's stats permanently.", 200, command_incstat) || command_add("instance", "- Modify Instances", 200, command_instance) || command_add("interrogateinv", "- use [help] argument for available options", 0, command_interrogateinv) || command_add("interrupt", "[message id] [color] - Interrupt your casting. Arguments are optional.", 50, command_interrupt) || command_add("invsnapshot", "- Manipulates inventory snapshots for your current target", 80, command_invsnapshot) || command_add("invul", "[on/off] - Turn player target's or your invulnerable flag on or off", 80, command_invul) || command_add("ipban", "[IP address] - Ban IP by character name", 200, command_ipban) || command_add("iplookup", "[charname] - Look up IP address of charname", 200, command_iplookup) || command_add("iteminfo", "- Get information about the item on your cursor", 10, command_iteminfo) || command_add("itemsearch", "[search criteria] - Search for an item", 10, command_itemsearch) || command_add("kick", "[charname] - Disconnect charname", 150, command_kick) || command_add("kill", "- Kill your target", 100, command_kill) || command_add("killallnpcs", " [npc_name] Kills all npcs by search name, leave blank for all attackable NPC's", 200, command_killallnpcs) || command_add("lastname", "[new lastname] - Set your or your player target's lastname", 50, command_lastname) || command_add("level", "[level] - Set your or your target's level", 10, command_level) || command_add("list", "[npcs|players|corpses|doors|objects] [search] - Search entities", 20, command_list) || command_add("listpetition", "- List petitions", 50, command_listpetition) || command_add("load_shared_memory", "[shared_memory_name] - Reloads shared memory and uses the input as output", 250, command_load_shared_memory) || command_add("loc", "- Print out your or your target's current location and heading", 0, command_loc) || command_add("lock", "- Lock the worldserver", 150, command_lock) || command_add("logs", "Manage anything to do with logs", 250, command_logs) || command_add("makepet", "[level] [class] [race] [texture] - Make a pet", 50, command_makepet) || command_add("mana", "- Fill your or your target's mana", 50, command_mana) || command_add("maxskills", "Maxes skills for you.", 200, command_max_all_skills) || command_add("memspell", "[slotid] [spellid] - Memorize spellid in the specified slot", 50, command_memspell) || command_add("merchant_close_shop", "Closes a merchant shop", 100, command_merchantcloseshop) || command_add("merchant_open_shop", "Opens a merchants shop", 100, command_merchantopenshop) || command_add("modifynpcstat", "- Modifys a NPC's stats", 150, command_modifynpcstat) || command_add("motd", "[new motd] - Set message of the day", 150, command_motd) || command_add("movechar", "[charname] [zonename] - Move charname to zonename", 50, command_movechar) || command_add("movement", "Various movement commands", 200, command_movement) || command_add("myskills", "- Show details about your current skill levels", 0, command_myskills) || command_add("mysql", "Mysql CLI, see 'help' for options.", 250, command_mysql) || command_add("mystats", "- Show details about you or your pet", 50, command_mystats) || command_add("name", "[newname] - Rename your player target", 150, command_name) || command_add("netstats", "- Gets the network stats for a stream.", 200, command_netstats) || command_add("network", "- Admin commands for the udp network interface.", 250, command_network) || command_add("npccast", "[targetname/entityid] [spellid] - Causes NPC target to cast spellid on targetname/entityid", 80, command_npccast) || command_add("npcedit", "[column] [value] - Mega NPC editing command", 100, command_npcedit) || command_add("npceditmass", "[name-search] [column] [value] - Mass (Zone wide) NPC data editing command", 100, command_npceditmass) || command_add("npcemote", "[message] - Make your NPC target emote a message.", 150, command_npcemote) || command_add("npcloot", "[show/money/add/remove] [itemid/all/money: pp gp sp cp] - Manipulate the loot an NPC is carrying", 80, command_npcloot) || command_add("npcsay", "[message] - Make your NPC target say a message.", 150, command_npcsay) || command_add("npcshout", "[message] - Make your NPC target shout a message.", 150, command_npcshout) || command_add("npcspawn", "[create/add/update/remove/delete] - Manipulate spawn DB", 170, command_npcspawn) || command_add("npcspecialattk", "[flagchar] [perm] - Set NPC special attack flags. Flags are E(nrage) F(lurry) R(ampage) S(ummon).", 80, command_npcspecialattk) || command_add("npcstats", "- Show stats about target NPC", 80, command_npcstats) || command_add("npctype_cache", "[id] or all - Clears the npc type cache for either the id or all npcs.", 250, command_npctype_cache) || command_add("npctypespawn", "[npctypeid] [factionid] - Spawn an NPC from the db", 10, command_npctypespawn) || command_add("nudge", "- Nudge your target's current position by specific values", 80, command_nudge) || command_add("nukebuffs", "- Strip all buffs on you or your target", 50, command_nukebuffs) || command_add("nukeitem", "[itemid] - Remove itemid from your player target's inventory", 150, command_nukeitem) || command_add("object", "List|Add|Edit|Move|Rotate|Copy|Save|Undo|Delete - Manipulate static and tradeskill objects within the zone", 100, command_object) || command_add("oocmute", "[1/0] - Mutes OOC chat", 200, command_oocmute) || command_add("opcode", "- opcode management", 250, command_opcode) || #ifdef PACKET_PROFILER command_add("packetprofile", "- Dump packet profile for target or self.", 250, command_packetprofile) || #endif command_add("path", "- view and edit pathing", 200, command_path) || command_add("peekinv", "[equip/gen/cursor/poss/limbo/curlim/trib/bank/shbank/allbank/trade/world/all] - Print out contents of your player target's inventory", 100, command_peekinv) || command_add("peqzone", "[zonename] - Go to specified zone, if you have > 75% health", 0, command_peqzone) || command_add("permaclass", "[classnum] - Change your or your player target's class (target is disconnected)", 80, command_permaclass) || command_add("permagender", "[gendernum] - Change your or your player target's gender (zone to take effect)", 80, command_permagender) || command_add("permarace", "[racenum] - Change your or your player target's race (zone to take effect)", 80, command_permarace) || command_add("petitioninfo", "[petition number] - Get info about a petition", 20, command_petitioninfo) || command_add("pf", "- Display additional mob coordinate and wandering data", 0, command_pf) || command_add("picklock", "Analog for ldon pick lock for the newer clients since we still don't have it working.", 0, command_picklock) || command_add("profanity", "Manage censored language.", 150, command_profanity) || #ifdef EQPROFILE command_add("profiledump", "- Dump profiling info to logs", 250, command_profiledump) || command_add("profilereset", "- Reset profiling info", 250, command_profilereset) || #endif command_add("push", "Lets you do spell push", 150, command_push) || command_add("proximity", "Shows NPC proximity", 150, command_proximity) || command_add("pvp", "[on/off] - Set your or your player target's PVP status", 100, command_pvp) || command_add("qglobal", "[on/off/view] - Toggles qglobal functionality on an NPC", 100, command_qglobal) || command_add("questerrors", "Shows quest errors.", 100, command_questerrors) || command_add("race", "[racenum] - Change your or your target's race. Use racenum 0 to return to normal", 50, command_race) || command_add("raidloot", "[All|GroupLeader|RaidLeader|Selected] - Sets your Raid Loot Type if you have permission to do so.", 0, command_raidloot) || command_add("randomfeatures", "- Temporarily randomizes the Facial Features of your target", 80, command_randomfeatures) || command_add("refreshgroup", "- Refreshes Group.", 0, command_refreshgroup) || command_add("reloadaa", "Reloads AA data", 200, command_reloadaa) || command_add("reloadallrules", "Executes a reload of all rules.", 80, command_reloadallrules) || command_add("reloademote", "Reloads NPC Emotes", 80, command_reloademote) || command_add("reloadlevelmods", nullptr, 255, command_reloadlevelmods) || command_add("reloadmerchants", nullptr, 255, command_reloadmerchants) || command_add("reloadperlexportsettings", nullptr, 255, command_reloadperlexportsettings) || command_add("reloadqst", " - Clear quest cache (any argument causes it to also stop all timers)", 150, command_reloadqst) || command_add("reloadrulesworld", "Executes a reload of all rules in world specifically.", 80, command_reloadworldrules) || command_add("reloadstatic", "- Reload Static Zone Data", 150, command_reloadstatic) || command_add("reloadtraps", "- Repops all traps in the current zone.", 80, command_reloadtraps) || command_add("reloadtitles", "- Reload player titles from the database", 150, command_reloadtitles) || command_add("reloadworld", "[0|1] - Clear quest cache (0 - no repop, 1 - repop)", 255, command_reloadworld) || command_add("reloadzps", "- Reload zone points from database", 150, command_reloadzps) || command_add("repop", "[delay] - Repop the zone with optional delay", 100, command_repop) || command_add("resetaa", "- Resets a Player's AA in their profile and refunds spent AA's to unspent, may disconnect player.", 200, command_resetaa) || command_add("resetaa_timer", "Command to reset AA cooldown timers.", 200, command_resetaa_timer) || command_add("resetdisc_timer", "Command to reset all discipline cooldown timers.", 200, command_resetdisc_timer) || command_add("revoke", "[charname] [1/0] - Makes charname unable to talk on OOC", 200, command_revoke) || command_add("roambox", "Manages roambox settings for an NPC", 200, command_roambox) || command_add("rules", "(subcommand) - Manage server rules", 250, command_rules) || command_add("save", "- Force your player or player corpse target to be saved to the database", 50, command_save) || command_add("scale", "- Handles npc scaling", 150, command_scale) || command_add("scribespell", "[spellid] - Scribe specified spell in your target's spell book.", 180, command_scribespell) || command_add("scribespells", "[max level] [min level] - Scribe all spells for you or your player target that are usable by them, up to level specified. (may freeze client for a few seconds)", 150, command_scribespells) || command_add("sendzonespawns", "- Refresh spawn list for all clients in zone", 150, command_sendzonespawns) || command_add("sensetrap", "Analog for ldon sense trap for the newer clients since we still don't have it working.", 0, command_sensetrap) || command_add("serverinfo", "- Get OS info about server host", 200, command_serverinfo) || command_add("serverrules", "- Read this server's rules", 0, command_serverrules) || command_add("setaapts", "[AA|Group|Raid] [AA Amount] - Set your or your player target's Available AA Points by Type", 100, command_setaapts) || command_add("setaaxp", "[AA|Group|Raid] [AA Experience] - Set your or your player target's AA Experience by Type", 100, command_setaaxp) || command_add("setadventurepoints", "- Set your or your player target's available adventure points", 150, command_set_adventure_points) || command_add("setanim", "[animnum] - Set target's appearance to animnum", 200, command_setanim) || command_add("setcrystals", "[value] - Set your or your player target's available radiant or ebon crystals", 100, command_setcrystals) || command_add("setfaction", "[faction number] - Sets targeted NPC's faction in the database", 170, command_setfaction) || command_add("setgraveyard", "[zone name] - Creates a graveyard for the specified zone based on your target's LOC.", 200, command_setgraveyard) || command_add("setlanguage", "[language ID] [value] - Set your target's language skillnum to value", 50, command_setlanguage) || command_add("setlsinfo", "[email] [password] - Set login server email address and password (if supported by login server)", 10, command_setlsinfo) || command_add("setpass", "[accountname] [password] - Set local password for accountname", 150, command_setpass) || command_add("setpvppoints", "[Amount] - Set your or your player target's PVP points", 100, command_setpvppoints) || command_add("setskill", "[skillnum] [value] - Set your target's skill skillnum to value", 50, command_setskill) || command_add("setskillall", "[value] - Set all of your target's skills to value", 50, command_setskillall) || command_add("setstartzone", "[zoneid] - Set target's starting zone. Set to zero to allow the player to use /setstartcity", 80, command_setstartzone) || command_add("setstat", "- Sets the stats to a specific value.", 255, command_setstat) || command_add("setxp", "[value] - Set your or your player target's experience", 100, command_setxp) || command_add("showbonusstats", "[item|spell|all] Shows bonus stats for target from items or spells. Shows both by default.", 50, command_showbonusstats) || command_add("showbuffs", "- List buffs active on your target or you if no target", 50, command_showbuffs) || command_add("shownumhits", "Shows buffs numhits for yourself.", 0, command_shownumhits) || command_add("shownpcgloballoot", "Show GlobalLoot entires on this npc", 50, command_shownpcgloballoot) || command_add("showskills", "- Show the values of your or your player target's skills", 50, command_showskills) || command_add("showspellslist", "Shows spell list of targeted NPC", 100, command_showspellslist) || command_add("showstats", "- Show details about you or your target", 50, command_showstats) || command_add("showzonegloballoot", "Show GlobalLoot entires on this zone", 50, command_showzonegloballoot) || command_add("showzonepoints", "Show zone points for current zone", 50, command_showzonepoints) || command_add("shutdown", "- Shut this zone process down", 150, command_shutdown) || command_add("size", "[size] - Change size of you or your target", 50, command_size) || command_add("spawn", "[name] [race] [level] [material] [hp] [gender] [class] [priweapon] [secweapon] [merchantid] - Spawn an NPC", 10, command_spawn) || command_add("spawneditmass", "Mass editing spawn command", 150, command_spawneditmass) || command_add("spawnfix", "- Find targeted NPC in database based on its X/Y/heading and update the database to make it spawn at your current location/heading.", 170, command_spawnfix) || command_add("spawnstatus", "- Show respawn timer status", 100, command_spawnstatus) || command_add("spellinfo", "[spellid] - Get detailed info about a spell", 10, command_spellinfo) || command_add("stun", "[duration] - Stuns you or your target for duration", 100, command_stun) || command_add("summon", "[charname] - Summons your player/npc/corpse target, or charname if specified", 80, command_summon) || command_add("summonburiedplayercorpse", "- Summons the target's oldest buried corpse, if any exist.", 100, command_summonburiedplayercorpse) || command_add("summonitem", "[itemid] [charges] - Summon an item onto your cursor. Charges are optional.", 200, command_summonitem) || command_add("suspend", "[name] [days] [reason] - Suspend by character name and for specificed number of days", 150, command_suspend) || command_add("task", "(subcommand) - Task system commands", 150, command_task) || command_add("tattoo", "- Change the tattoo of your target (Drakkin Only)", 80, command_tattoo) || command_add("tempname", "[newname] - Temporarily renames your target. Leave name blank to restore the original name.", 100, command_tempname) || command_add("petname", "[newname] - Temporarily renames your pet. Leave name blank to restore the original name.", 100, command_petname) || command_add("texture", "[texture] [helmtexture] - Change your or your target's appearance, use 255 to show equipment", 10, command_texture) || command_add("time", "[HH] [MM] - Set EQ time", 90, command_time) || command_add("timers", "- Display persistent timers for target", 200, command_timers) || command_add("timezone", "[HH] [MM] - Set timezone. Minutes are optional", 90, command_timezone) || command_add("title", "[text] [1 = create title table row] - Set your or your player target's title", 50, command_title) || command_add("titlesuffix", "[text] [1 = create title table row] - Set your or your player target's title suffix", 50, command_titlesuffix) || command_add("traindisc", "[level] - Trains all the disciplines usable by the target, up to level specified. (may freeze client for a few seconds)", 150, command_traindisc) || command_add("trapinfo", "- Gets infomation about the traps currently spawned in the zone.", 81, command_trapinfo) || command_add("tune", "Calculate statistical values related to combat.", 100, command_tune) || command_add("ucs", "- Attempts to reconnect to the UCS server", 0, command_ucs) || command_add("undyeme", "- Remove dye from all of your armor slots", 0, command_undyeme) || command_add("unfreeze", "- Unfreeze your target", 80, command_unfreeze) || command_add("unlock", "- Unlock the worldserver", 150, command_unlock) || command_add("unscribespell", "[spellid] - Unscribe specified spell from your target's spell book.", 180, command_unscribespell) || command_add("unscribespells", "- Clear out your or your player target's spell book.", 180, command_unscribespells) || command_add("untraindisc", "[spellid] - Untrain specified discipline from your target.", 180, command_untraindisc) || command_add("untraindiscs", "- Untrains all disciplines from your target.", 180, command_untraindiscs) || command_add("uptime", "[zone server id] - Get uptime of worldserver, or zone server if argument provided", 10, command_uptime) || command_add("version", "- Display current version of EQEmu server", 0, command_version) || command_add("viewnpctype", "[NPC ID] - Show stats for an NPC by NPC ID", 100, command_viewnpctype) || command_add("viewpetition", "[petition number] - View a petition", 20, command_viewpetition) || command_add("viewzoneloot", "[item id] - Allows you to search a zone's loot for a specific item ID. (0 shows all loot in the zone)", 80, command_viewzoneloot) || command_add("wc", "[wear slot] [material] - Sends an OP_WearChange for your target", 200, command_wc) || command_add("weather", "[0/1/2/3] (Off/Rain/Snow/Manual) - Change the weather", 80, command_weather) || command_add("who", "[search]", 20, command_who) || command_add("worldshutdown", "- Shut down world and all zones", 200, command_worldshutdown) || command_add("wp", "[add|delete] [grid_id] [pause] [waypoint_id] [-h] - Add or delete a waypoint by grid ID. (-h to use current heading)", 170, command_wp) || command_add("wpadd", "[pause] [-h] - Add your current location as a waypoint to your NPC target's AI path. (-h to use current heading)", 170, command_wpadd) || command_add("wpinfo", "- Show waypoint info about your NPC target", 170, command_wpinfo) || command_add("worldwide", "Performs world-wide GM functions such as cast (can be extended for other commands). Use caution", 250, command_worldwide) || command_add("xtargets", "Show your targets Extended Targets and optionally set how many xtargets they can have.", 250, command_xtargets) || command_add("zclip", "[min] [max] - modifies and resends zhdr packet", 80, command_zclip) || command_add("zcolor", "[red] [green] [blue] - Change sky color", 80, command_zcolor) || command_add("zheader", "[zonename] - Load zheader for zonename from the database", 80, command_zheader) || command_add("zone", "[zonename] [x] [y] [z] - Go to specified zone (coords optional)", 50, command_zone) || command_add("zonebootup", "[ZoneServerID] [shortname] - Make a zone server boot a specific zone", 150, command_zonebootup) || command_add("zoneinstance", "[instanceid] [x] [y] [z] - Go to specified instance zone (coords optional)", 50, command_zone_instance) || command_add("zonelock", "[List|Lock|Unlock] [Zone ID|Zone Short Name] - Set or get lock status of a Zone by ID or Short Name", 100, command_zonelock) || command_add("zoneshutdown", "[shortname] - Shut down a zone server", 150, command_zoneshutdown) || command_add("zonespawn", "- Not implemented", 250, command_zonespawn) || command_add("zonestatus", "- Show connected zoneservers, synonymous with /servers", 150, command_zonestatus) || command_add("zopp", "Troubleshooting command - Sends a fake item packet to you. No server reference is created.", 250, command_zopp) || command_add("zsafecoords", "[x] [y] [z] - Set safe coords", 80, command_zsafecoords) || 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("zuwcoords", "[z coord] - Set underworld coord", 80, command_zuwcoords) ) { command_deinit(); return -1; } std::map>> command_settings; database.GetCommandSettings(command_settings); std::vector> injected_command_settings; std::vector orphaned_command_settings; for (auto cs_iter : command_settings) { auto cl_iter = commandlist.find(cs_iter.first); if (cl_iter == commandlist.end()) { orphaned_command_settings.push_back(cs_iter.first); LogInfo( "Command [{}] no longer exists... Deleting orphaned entry from `command_settings` table...", cs_iter.first.c_str() ); } } if (orphaned_command_settings.size()) { if (!database.UpdateOrphanedCommandSettings(orphaned_command_settings)) { LogInfo("Failed to process 'Orphaned Commands' update operation."); } } auto working_cl = commandlist; for (auto working_cl_iter : working_cl) { auto cs_iter = command_settings.find(working_cl_iter.first); if (cs_iter == command_settings.end()) { injected_command_settings.push_back(std::pair(working_cl_iter.first, working_cl_iter.second->access)); LogInfo( "New Command [{}] found... Adding to `command_settings` table with access [{}]...", working_cl_iter.first.c_str(), working_cl_iter.second->access ); if (working_cl_iter.second->access == 0) { LogCommands( "command_init(): Warning: Command [{}] defaulting to access level 0!", working_cl_iter.first.c_str() ); } continue; } working_cl_iter.second->access = cs_iter->second.first; LogCommands( "command_init(): - Command [{}] set to access level [{}]", working_cl_iter.first.c_str(), cs_iter->second.first ); if (cs_iter->second.second.empty()) { continue; } for (auto alias_iter : cs_iter->second.second) { if (alias_iter.empty()) { continue; } if (commandlist.find(alias_iter) != commandlist.end()) { LogCommands( "command_init(): Warning: Alias [{}] already exists as a command - skipping!", alias_iter.c_str() ); continue; } commandlist[alias_iter] = working_cl_iter.second; commandaliases[alias_iter] = working_cl_iter.first; LogCommands( "command_init(): - Alias [{}] added to command [{}]", alias_iter.c_str(), commandaliases[alias_iter].c_str() ); } } if (injected_command_settings.size()) { if (!database.UpdateInjectedCommandSettings(injected_command_settings)) { LogInfo("Failed to process 'Injected Commands' update operation."); } } command_dispatch = command_realdispatch; return commandcount; } /* * command_deinit * clears the command list, freeing resources * * Parameters: * none * */ void command_deinit(void) { commandlist.clear(); commandaliases.clear(); command_dispatch = command_notavail; commandcount = 0; } /* * command_add * adds a command to the command list; used by command_init * * Parameters: * command_name - the command ex: "spawn" * desc - text description of command for #help * access - default access level required to use command * function - pointer to function that handles command * */ int command_add(std::string command_name, const char *desc, int access, CmdFuncPtr function) { if (command_name.empty()) { LogError("command_add() - Command added with empty name string - check command.cpp"); return -1; } if (function == nullptr) { LogError("command_add() - Command [{}] added without a valid function pointer - check command.cpp", command_name.c_str()); return -1; } if (commandlist.count(command_name) != 0) { LogError("command_add() - Command [{}] is a duplicate command name - check command.cpp", command_name.c_str()); return -1; } for (auto iter = commandlist.begin(); iter != commandlist.end(); ++iter) { if (iter->second->function != function) continue; LogError("command_add() - Command [{}] equates to an alias of [{}] - check command.cpp", command_name.c_str(), iter->first.c_str()); return -1; } auto c = new CommandRecord; c->access = access; c->desc = desc; c->function = function; commandlist[command_name] = c; commandaliases[command_name] = command_name; cleanup_commandlist.Append(c); commandcount++; return 0; } /* * * command_realdispatch * Calls the correct function to process the client's command string. * Called from Client::ChannelMessageReceived if message starts with * command character (#). * * Parameters: * c - pointer to the calling client object * message - what the client typed * */ int command_realdispatch(Client *c, const char *message) { Seperator sep(message, ' ', 10, 100, true); // "three word argument" should be considered 1 arg command_logcommand(c, message); std::string cstr(sep.arg[0]+1); if(commandlist.count(cstr) != 1) { return(-2); } CommandRecord *cur = commandlist[cstr]; if(c->Admin() < cur->access){ c->Message(Chat::Red,"Your access level is not high enough to use this command."); return(-1); } /* QS: Player_Log_Issued_Commands */ if (RuleB(QueryServ, PlayerLogIssuedCommandes)){ std::string event_desc = StringFormat("Issued command :: '%s' in zoneid:%i instid:%i", message, c->GetZoneID(), c->GetInstanceID()); QServ->PlayerLogEvent(Player_Log_Issued_Commands, c->CharacterID(), event_desc); } if(cur->access >= COMMANDS_LOGGING_MIN_STATUS) { LogCommands("[{}] ([{}]) used command: [{}] (target=[{}])", c->GetName(), c->AccountName(), message, c->GetTarget()?c->GetTarget()->GetName():"NONE"); } if(cur->function == nullptr) { LogError("Command [{}] has a null function\n", cstr.c_str()); return(-1); } else { //dispatch C++ command cur->function(c, &sep); // dispatch command } return 0; } void command_logcommand(Client *c, const char *message) { int admin=c->Admin(); bool continueevents=false; switch (zone->loglevelvar){ //catch failsafe case 9: { // log only LeadGM if ((admin>= 150) && (admin <200)) continueevents=true; break; } case 8: { // log only GM if ((admin>= 100) && (admin <150)) continueevents=true; break; } case 1: { if ((admin>= 200)) continueevents=true; break; } case 2: { if ((admin>= 150)) continueevents=true; break; } case 3: { if ((admin>= 100)) continueevents=true; break; } case 4: { if ((admin>= 80)) continueevents=true; break; } case 5: { if ((admin>= 20)) continueevents=true; break; } case 6: { if ((admin>= 10)) continueevents=true; break; } case 7: { continueevents=true; break; } } if (continueevents) database.logevents( c->AccountName(), c->AccountID(), admin,c->GetName(), c->GetTarget()?c->GetTarget()->GetName():"None", "Command", message, 1 ); } /* * commands go below here */ void command_worldwide(Client *c, const Seperator *sep) { std::string sub_command; if (sep->arg[1]) { sub_command = sep->arg[1]; } if (sub_command == "cast") { if (sep->arg[2] && Seperator::IsNumber(sep->arg[2])) { uint8 update_type = WWSpellUpdateType_Cast; auto spell_id = std::stoul(sep->arg[2]); bool disable_message = false; if (sep->arg[3] && Seperator::IsNumber(sep->arg[3])) { disable_message = std::stoi(sep->arg[3]) ? true : false; } c->Message( Chat::White, fmt::format( "World Wide Cast Spell | Spell: {} ({})", GetSpellName(spell_id), spell_id ).c_str() ); quest_manager.WorldWideSpell(update_type, spell_id); if (!disable_message) { quest_manager.WorldWideMessage( Chat::Yellow, fmt::format( "[SYSTEM] A GM has cast [{}] world-wide!", GetSpellName(spell_id) ).c_str() ); } } else { c->Message(Chat::White, "Usage: #worldwide cast [Spell ID] [Disable Message]"); } } else if (sub_command == "remove") { if (sep->arg[2] && Seperator::IsNumber(sep->arg[2])) { uint8 update_type = WWSpellUpdateType_Remove; auto spell_id = std::stoul(sep->arg[2]); c->Message( Chat::White, fmt::format( "World Wide Remove Spell | Spell: {} ({})", GetSpellName(spell_id), spell_id ).c_str() ); quest_manager.WorldWideSpell(update_type, spell_id); } else { c->Message(Chat::White, "Usage: #worldwide remove [Spell ID]"); } } else if (sub_command == "message") { if (sep->arg[2]) { std::string message = sep->arg[2]; quest_manager.WorldWideMessage( Chat::White, fmt::format( "{}", message ).c_str() ); } else { c->Message(Chat::White, "Usage: #worldwide message [Message]"); } } else if (sub_command == "move") { if (sep->arg[2]) { uint8 update_type = WWMoveUpdateType_MoveZone; uint32 zone_id = 0; std::string zone_short_name; if (Seperator::IsNumber(sep->arg[2])) { zone_id = std::stoul(sep->arg[2]); } if (zone_id) { zone_short_name = ZoneName(zone_id); } else { zone_short_name = sep->arg[2]; } c->Message( Chat::White, fmt::format( "World Wide Zone | Zone: {} ({}) ID: {}", ZoneLongName( ZoneID(zone_short_name) ), zone_short_name, ZoneID(zone_short_name) ).c_str() ); quest_manager.WorldWideMove(update_type, zone_short_name.c_str()); } else { c->Message( Chat::White, "Usage: #worldwide move [Zone ID] or #worldwide move [Zone Short Name]" ); } } else if (sub_command == "moveinstance") { if (Seperator::IsNumber(sep->arg[2])) { uint8 update_type = WWMoveUpdateType_MoveZoneInstance; const char* zone_short_name = ""; uint16 instance_id = std::stoi(sep->arg[2]); c->Message( Chat::White, fmt::format( "World Wide Zone Instance | Instance ID: {}", instance_id ).c_str() ); quest_manager.WorldWideMove(update_type, zone_short_name, instance_id); } else { c->Message(Chat::White, "Usage: #worldwide moveinstance [Instance ID]"); } } if (!sep->arg[1]) { c->Message(Chat::White, "This command is used to perform world-wide tasks."); c->Message(Chat::White, "Usage: #worldwide cast [Spell ID] [Disable Message]"); c->Message(Chat::White, "Usage: #worldwide remove [Spell ID]"); c->Message(Chat::White, "Usage: #worldwide message [Message]"); c->Message( Chat::White, "Usage: #worldwide move [Zone ID] or #worldwide move [Zone Short Name]" ); c->Message(Chat::White, "Usage: #worldwide moveinstance [Instance ID]"); } } void command_endurance(Client *c, const Seperator *sep) { auto target = c->GetTarget() ? c->GetTarget() : c; if (target->IsClient()) { target->CastToClient()->SetEndurance(target->CastToClient()->GetMaxEndurance()); } else { target->SetEndurance(target->GetMaxEndurance()); } if (c != target) { c->Message( Chat::White, fmt::format( "Set {} ({}) to full Endurance.", target->GetCleanName(), target->GetID() ).c_str() ); } else { c->Message(Chat::White, "Restored your Endurance to full."); } } void command_setstat(Client* c, const Seperator* sep){ if(sep->arg[1][0] && sep->arg[2][0] && c->GetTarget()!=0 && c->GetTarget()->IsClient()){ c->GetTarget()->CastToClient()->SetStats(atoi(sep->arg[1]),atoi(sep->arg[2])); } else{ c->Message(Chat::White,"This command is used to permanently increase or decrease a players stats."); c->Message(Chat::White,"Usage: #setstat {type} {value the stat should be}"); c->Message(Chat::White,"Types: Str: 0, Sta: 1, Agi: 2, Dex: 3, Int: 4, Wis: 5, Cha: 6"); } } void command_incstat(Client* c, const Seperator* sep){ if(sep->arg[1][0] && sep->arg[2][0] && c->GetTarget()!=0 && c->GetTarget()->IsClient()){ c->GetTarget()->CastToClient()->IncStats(atoi(sep->arg[1]),atoi(sep->arg[2])); } else{ c->Message(Chat::White,"This command is used to permanently increase or decrease a players stats."); c->Message(Chat::White,"Usage: #setstat {type} {value by which to increase or decrease}"); c->Message(Chat::White,"Note: The value is in increments of 2, so a value of 3 will actually increase the stat by 6"); c->Message(Chat::White,"Types: Str: 0, Sta: 1, Agi: 2, Dex: 3, Int: 4, Wis: 5, Cha: 6"); } } void command_resetaa(Client* c,const Seperator *sep) { if(c->GetTarget() && c->GetTarget()->IsClient()){ c->GetTarget()->CastToClient()->ResetAA(); c->Message(Chat::Red,"Successfully reset %s's AAs", c->GetTarget()->GetName()); } else c->Message(Chat::White,"Usage: Target a client and use #resetaa to reset the AA data in their Profile."); } void command_help(Client *c, const Seperator *sep) { int commands_shown=0; c->Message(Chat::White, "Available EQEMu commands:"); std::map::iterator cur,end; cur = commandlist.begin(); end = commandlist.end(); for(; cur != end; ++cur) { if(sep->arg[1][0]) { if(cur->first.find(sep->arg[1]) == std::string::npos) { continue; } } if(c->Admin() < cur->second->access) continue; commands_shown++; c->Message(Chat::White, " %c%s %s", COMMAND_CHAR, cur->first.c_str(), cur->second->desc == nullptr?"":cur->second->desc); } if (parse->PlayerHasQuestSub(EVENT_COMMAND)) { int i = parse->EventPlayer(EVENT_COMMAND, c, sep->msg, 0); if (i >= 1) { commands_shown += i; } } c->Message(Chat::White, "%d command%s listed.", commands_shown, commands_shown!=1?"s":""); } void command_version(Client *c, const Seperator *sep) { c->Message(Chat::White, "Current version information."); c->Message(Chat::White, " %s", CURRENT_VERSION); c->Message(Chat::White, " Compiled on: %s at %s", COMPILE_DATE, COMPILE_TIME); c->Message(Chat::White, " Last modified on: %s", LAST_MODIFIED); } void command_setfaction(Client *c, const Seperator *sep) { if((sep->arg[1][0] == 0 || strcasecmp(sep->arg[1],"*")==0) || ((c->GetTarget()==0) || (c->GetTarget()->IsClient()))) { c->Message(Chat::White, "Usage: #setfaction [faction number]"); return; } auto npcTypeID = c->GetTarget()->CastToNPC()->GetNPCTypeID(); c->Message(Chat::Yellow,"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", atoi(sep->argplus[1]), npcTypeID); content_db.QueryDatabase(query); } void command_wc(Client *c, const Seperator *sep) { if (sep->argnum < 2) { c->Message( 0, "Usage: #wc [wear slot] [material] [ [hero_forge_model] [elite_material] [unknown06] [unknown18] ]" ); } else if (c->GetTarget() == nullptr) { c->Message(Chat::Red, "You must have a target to do a wear change."); } else { uint32 hero_forge_model = 0; uint32 wearslot = atoi(sep->arg[1]); // Hero Forge if (sep->argnum > 2) { hero_forge_model = atoi(sep->arg[3]); if (hero_forge_model != 0 && hero_forge_model < 1000) { // Shorthand Hero Forge ID. Otherwise use the value the user entered. hero_forge_model = (hero_forge_model * 100) + wearslot; } } /* // Leaving here to add color option to the #wc command eventually uint32 Color; if (c->GetTarget()->IsClient()) Color = c->GetTarget()->GetEquipmentColor(atoi(sep->arg[1])); else Color = c->GetTarget()->GetArmorTint(atoi(sep->arg[1])); */ c->GetTarget()->SendTextureWC( wearslot, atoi(sep->arg[2]), hero_forge_model, atoi(sep->arg[4]), atoi(sep->arg[5]), atoi(sep->arg[6])); } } void command_heromodel(Client *c, const Seperator *sep) { if (sep->argnum < 1) { c->Message(Chat::White, "Usage: #heromodel [hero forge model] [ [slot] ] (example: #heromodel 63)"); } else if (c->GetTarget() == nullptr) { c->Message(Chat::Red, "You must have a target to do a wear change for Hero's Forge Models."); } else { uint32 hero_forge_model = atoi(sep->arg[1]); if (sep->argnum > 1) { uint8 wearslot = (uint8) atoi(sep->arg[2]); c->GetTarget()->SendTextureWC(wearslot, 0, hero_forge_model, 0, 0, 0); } else { if (hero_forge_model > 0) { // Conversion to simplify the command arguments // Hero's Forge model is actually model * 1000 + texture * 100 + wearslot // Hero's Forge Model slot 7 is actually for Robes, but it still needs to use wearslot 1 in the packet hero_forge_model *= 100; for (uint8 wearslot = 0; wearslot < 7; wearslot++) { c->GetTarget()->SendTextureWC(wearslot, 0, (hero_forge_model + wearslot), 0, 0, 0); } } else { c->Message(Chat::Red, "Hero's Forge Model must be greater than 0."); } } } } void command_setanim(Client *c, const Seperator *sep) { if (c->GetTarget() && sep->IsNumber(1)) { int num = atoi(sep->arg[1]); if (num < 0 || num >= _eaMaxAppearance) { c->Message(Chat::White, "Invalid animation number, between 0 and %d", _eaMaxAppearance - 1); } c->GetTarget()->SetAppearance(EmuAppearance(num)); } else { c->Message(Chat::White, "Usage: #setanim [animnum]"); } } void command_serverinfo(Client *c, const Seperator *sep) { auto os = EQ::GetOS(); auto cpus = EQ::GetCPUs(); auto pid = EQ::GetPID(); auto rss = EQ::GetRSS(); auto uptime = EQ::GetUptime(); c->Message(Chat::White, "Operating System Information"); c->Message(Chat::White, "=================================================="); c->Message(Chat::White, "System: %s", os.sysname.c_str()); c->Message(Chat::White, "Release: %s", os.release.c_str()); c->Message(Chat::White, "Version: %s", os.version.c_str()); c->Message(Chat::White, "Machine: %s", os.machine.c_str()); c->Message(Chat::White, "Uptime: %.2f seconds", uptime); c->Message(Chat::White, "=================================================="); c->Message(Chat::White, "CPU Information"); c->Message(Chat::White, "=================================================="); for (size_t i = 0; i < cpus.size(); ++i) { auto &cp = cpus[i]; c->Message(Chat::White, "CPU #%i: %s, Speed: %.2fGhz", i, cp.model.c_str(), cp.speed); } c->Message(Chat::White, "=================================================="); c->Message(Chat::White, "Process Information"); c->Message(Chat::White, "=================================================="); c->Message(Chat::White, "PID: %u", pid); c->Message(Chat::White, "RSS: %.2f MB", rss / 1048576.0); c->Message(Chat::White, "=================================================="); } void command_getvariable(Client *c, const Seperator *sep) { std::string tmp; if (database.GetVariable(sep->argplus[1], tmp)) c->Message(Chat::White, "%s = %s", sep->argplus[1], tmp.c_str()); else c->Message(Chat::White, "GetVariable(%s) returned false", sep->argplus[1]); } void command_chat(Client *c, const Seperator *sep) { if (sep->arg[2][0] == 0) c->Message(Chat::White, "Usage: #chat [channum] [message]"); else if (!worldserver.SendChannelMessage(0, 0, (uint8) atoi(sep->arg[1]), 0, 0, 100, sep->argplus[2])) c->Message(Chat::White, "Error: World server disconnected"); } void command_npcloot(Client *c, const Seperator *sep) { if (c->GetTarget() == 0) c->Message(Chat::White, "Error: No target"); // #npcloot show else if (strcasecmp(sep->arg[1], "show") == 0) { if (c->GetTarget()->IsNPC()) c->GetTarget()->CastToNPC()->QueryLoot(c); else if (c->GetTarget()->IsCorpse()) c->GetTarget()->CastToCorpse()->QueryLoot(c); else c->Message(Chat::White, "Error: Target's type doesnt have loot"); } // These 2 types are *BAD* for the next few commands else if (c->GetTarget()->IsClient() || c->GetTarget()->IsCorpse()) c->Message(Chat::White, "Error: Invalid target type, try a NPC =)."); // #npcloot add else if (strcasecmp(sep->arg[1], "add") == 0) { // #npcloot add item if (c->GetTarget()->IsNPC() && sep->IsNumber(2)) { uint32 item = atoi(sep->arg[2]); if (database.GetItem(item)) { if (sep->arg[3][0] != 0 && sep->IsNumber(3)) c->GetTarget()->CastToNPC()->AddItem(item, atoi(sep->arg[3]), 0); else c->GetTarget()->CastToNPC()->AddItem(item, 1, 0); c->Message(Chat::White, "Added item(%i) to the %s's loot.", item, c->GetTarget()->GetName()); } else c->Message(Chat::White, "Error: #npcloot add: Item(%i) does not exist!", item); } else if (!sep->IsNumber(2)) c->Message(Chat::White, "Error: #npcloot add: Itemid must be a number."); else c->Message(Chat::White, "Error: #npcloot add: This is not a valid target."); } // #npcloot remove else if (strcasecmp(sep->arg[1], "remove") == 0) { //#npcloot remove all if (strcasecmp(sep->arg[2], "all") == 0) c->Message(Chat::White, "Error: #npcloot remove all: Not yet implemented."); //#npcloot remove itemid else { if(c->GetTarget()->IsNPC() && sep->IsNumber(2)) { uint32 item = atoi(sep->arg[2]); c->GetTarget()->CastToNPC()->RemoveItem(item); c->Message(Chat::White, "Removed item(%i) from the %s's loot.", item, c->GetTarget()->GetName()); } else if (!sep->IsNumber(2)) c->Message(Chat::White, "Error: #npcloot remove: Item must be a number."); else c->Message(Chat::White, "Error: #npcloot remove: This is not a valid target."); } } // #npcloot money else if (strcasecmp(sep->arg[1], "money") == 0) { if (c->GetTarget()->IsNPC() && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4) && sep->IsNumber(5)) { if ((atoi(sep->arg[2]) < 34465 && atoi(sep->arg[2]) >= 0) && (atoi(sep->arg[3]) < 34465 && atoi(sep->arg[3]) >= 0) && (atoi(sep->arg[4]) < 34465 && atoi(sep->arg[4]) >= 0) && (atoi(sep->arg[5]) < 34465 && atoi(sep->arg[5]) >= 0)) { c->GetTarget()->CastToNPC()->AddCash(atoi(sep->arg[5]), atoi(sep->arg[4]), atoi(sep->arg[3]), atoi(sep->arg[2])); c->Message(Chat::White, "Set %i Platinum, %i Gold, %i Silver, and %i Copper as %s's money.", atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), atoi(sep->arg[5]), c->GetTarget()->GetName()); } else c->Message(Chat::White, "Error: #npcloot money: Values must be between 0-34465."); } else c->Message(Chat::White, "Usage: #npcloot money platinum gold silver copper"); } else c->Message(Chat::White, "Usage: #npcloot [show/money/add/remove] [itemid/all/money: pp gp sp cp]"); } void command_gm(Client *c, const Seperator *sep) { bool state=atobool(sep->arg[1]); Client *t=c; if(c->GetTarget() && c->GetTarget()->IsClient()) t=c->GetTarget()->CastToClient(); if(sep->arg[1][0] != 0) { t->SetGM(state); c->Message(Chat::White, "%s is %s a GM.", t->GetName(), state?"now":"no longer"); } else c->Message(Chat::White, "Usage: #gm [on/off]"); } // there's no need for this, as /summon already takes care of it // this command is here for reference but it is not added to the // list above //To whoever wrote the above: And what about /kill, /zone, /zoneserver, etc? //There is a reason for the # commands: so that admins can specifically enable certain //commands for their users. Some might want users to #summon but not to /kill. Cant do that if they are a GM void command_summon(Client *c, const Seperator *sep) { Mob *t; if(sep->arg[1][0] != 0) // arg specified { Client* client = entity_list.GetClientByName(sep->arg[1]); if (client != 0) // found player in zone t=client->CastToMob(); else { if (!worldserver.Connected()) c->Message(Chat::White, "Error: World server disconnected."); else { // player is in another zone //Taking this command out until we test the factor of 8 in ServerOP_ZonePlayer //c->Message(Chat::White, "Summoning player from another zone not yet implemented."); //return; auto pack = new ServerPacket(ServerOP_ZonePlayer, sizeof(ServerZonePlayer_Struct)); ServerZonePlayer_Struct* szp = (ServerZonePlayer_Struct*) pack->pBuffer; strcpy(szp->adminname, c->GetName()); szp->adminrank = c->Admin(); szp->ignorerestrictions = 2; strcpy(szp->name, sep->arg[1]); strcpy(szp->zone, zone->GetShortName()); szp->x_pos = c->GetX(); // May need to add a factor of 8 in here.. szp->y_pos = c->GetY(); szp->z_pos = c->GetZ(); szp->instance_id = zone->GetInstanceID(); worldserver.SendPacket(pack); safe_delete(pack); } return; } } else if(c->GetTarget()) // have target t=c->GetTarget(); else { /*if(c->Admin() < 150) c->Message(Chat::White, "You need a NPC/corpse target for this command"); else*/ c->Message(Chat::White, "Usage: #summon [charname] Either target or charname is required"); return; } if(!t) return; if (t->IsNPC()) { // npc target c->Message(Chat::White, "Summoning NPC %s to %1.1f, %1.1f, %1.1f", t->GetName(), c->GetX(), c->GetY(), c->GetZ()); t->CastToNPC()->GMMove(c->GetX(), c->GetY(), c->GetZ(), c->GetHeading()); t->CastToNPC()->SaveGuardSpot(glm::vec4(0.0f)); } else if (t->IsCorpse()) { // corpse target c->Message(Chat::White, "Summoning corpse %s to %1.1f, %1.1f, %1.1f", t->GetName(), c->GetX(), c->GetY(), c->GetZ()); t->CastToCorpse()->GMMove(c->GetX(), c->GetY(), c->GetZ(), c->GetHeading()); } else if (t->IsClient()) { /*if(c->Admin() < 150) { c->Message(Chat::White, "You may not summon a player."); return; }*/ c->Message(Chat::White, "Summoning player %s to %1.1f, %1.1f, %1.1f", t->GetName(), c->GetX(), c->GetY(), c->GetZ()); t->CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), c->GetX(), c->GetY(), c->GetZ(), c->GetHeading(), 2, GMSummon); } } void command_zone(Client *c, const Seperator *sep) { if(c->Admin() < commandZoneToCoords && (sep->IsNumber(2) || sep->IsNumber(3) || sep->IsNumber(4))) { c->Message(Chat::White, "Your status is not high enough to zone to specific coordinates."); return; } uint16 zoneid = 0; if (sep->IsNumber(1)) { if(atoi(sep->arg[1])==26 && (c->Admin() < commandZoneToSpecials)){ //cshome c->Message(Chat::White, "Only Guides and above can goto that zone."); return; } zoneid = atoi(sep->arg[1]); } else if (sep->arg[1][0] == 0) { c->Message(Chat::White, "Usage: #zone [zonename]"); c->Message(Chat::White, "Optional Usage: #zone [zonename] y x z"); return; } else if (zone->GetZoneID() == 184 && c->Admin() < commandZoneToSpecials) { // Zone: 'Load' c->Message(Chat::White, "The Gods brought you here, only they can send you away."); return; } else { if((strcasecmp(sep->arg[1], "cshome")==0) && (c->Admin() < commandZoneToSpecials)){ c->Message(Chat::White, "Only Guides and above can goto that zone."); return; } zoneid = ZoneID(sep->arg[1]); if(zoneid == 0) { c->Message(Chat::White, "Unable to locate zone '%s'", sep->arg[1]); return; } } #ifdef BOTS // This block is necessary to clean up any bot objects owned by a Client if(zoneid != c->GetZoneID()) Bot::ProcessClientZoneChange(c); #endif if (sep->IsNumber(2) || sep->IsNumber(3) || sep->IsNumber(4)){ //zone to specific coords c->MovePC(zoneid, (float)atof(sep->arg[2]), atof(sep->arg[3]), atof(sep->arg[4]), 0.0f, 0); } else //zone to safe coords c->MovePC(zoneid, 0.0f, 0.0f, 0.0f, 0.0f, 0, ZoneToSafeCoords); } //todo: fix this so it checks if you're in the instance set void command_zone_instance(Client *c, const Seperator *sep) { if(c->Admin() < commandZoneToCoords && (sep->IsNumber(2) || sep->IsNumber(3) || sep->IsNumber(4))) { c->Message(Chat::White, "Your status is not high enough to zone to specific coordinates."); return; } if (sep->arg[1][0] == 0) { c->Message(Chat::White, "Usage: #zoneinstance [instance id]"); c->Message(Chat::White, "Optional Usage: #zoneinstance [instance id] y x z"); return; } uint16 zoneid = 0; uint16 instanceid = 0; if(sep->IsNumber(1)) { instanceid = atoi(sep->arg[1]); if(!instanceid) { c->Message(Chat::White, "Must enter a valid instance id."); return; } zoneid = database.ZoneIDFromInstanceID(instanceid); if(!zoneid) { c->Message(Chat::White, "Instance not found or zone is set to null."); return; } } else { c->Message(Chat::White, "Must enter a valid instance id."); return; } if(!database.VerifyInstanceAlive(instanceid, c->CharacterID())) { c->Message(Chat::White, "Instance ID expiried or you are not apart of this instance."); return; } if (sep->IsNumber(2) || sep->IsNumber(3) || sep->IsNumber(4)){ //zone to specific coords c->MovePC(zoneid, instanceid, atof(sep->arg[2]), atof(sep->arg[3]), atof(sep->arg[4]), 0.0f, 0); } else{ c->MovePC(zoneid, instanceid, 0.0f, 0.0f, 0.0f, 0.0f, 0, ZoneToSafeCoords); } } void command_showbuffs(Client *c, const Seperator *sep) { if (c->GetTarget() == 0) c->CastToMob()->ShowBuffs(c); else c->GetTarget()->CastToMob()->ShowBuffs(c); } void command_peqzone(Client *c, const Seperator *sep) { uint32 timeleft = c->GetPTimers().GetRemainingTime(pTimerPeqzoneReuse)/60; if(!c->GetPTimers().Expired(&database, pTimerPeqzoneReuse, false)) { c->Message(Chat::Red,"You must wait %i minute(s) before using this ability again.", timeleft); return; } if(c->GetHPRatio() < 75) { c->Message(Chat::White, "You cannot use this command with less than 75 percent health."); return; } //this isnt perfect, but its better... if( c->IsInvisible(c) || c->IsRooted() || c->IsStunned() || c->IsMezzed() || c->AutoAttackEnabled() || c->GetInvul() ) { c->Message(Chat::White, "You cannot use this command in your current state. Settle down and wait."); return; } uint16 zoneid = 0; uint8 destzone = 0; if (sep->IsNumber(1)) { zoneid = atoi(sep->arg[1]); destzone = content_db.GetPEQZone(zoneid, 0); if(destzone == 0){ c->Message(Chat::Red, "You cannot use this command to enter that zone!"); return; } if(zoneid == zone->GetZoneID()) { c->Message(Chat::Red, "You cannot use this command on the zone you are in!"); return; } } else if (sep->arg[1][0] == 0 || sep->IsNumber(2) || sep->IsNumber(3) || sep->IsNumber(4) || sep->IsNumber(5)) { c->Message(Chat::White, "Usage: #peqzone [zonename]"); c->Message(Chat::White, "Optional Usage: #peqzone [zoneid]"); return; } else { zoneid = ZoneID(sep->arg[1]); destzone = content_db.GetPEQZone(zoneid, 0); if(zoneid == 0) { c->Message(Chat::White, "Unable to locate zone '%s'", sep->arg[1]); return; } if(destzone == 0){ c->Message(Chat::Red, "You cannot use this command to enter that zone!"); return; } if(zoneid == zone->GetZoneID()) { c->Message(Chat::Red, "You cannot use this command on the zone you are in!"); return; } } if(RuleB (Zone, UsePEQZoneDebuffs)){ c->SpellOnTarget(RuleI(Zone, PEQZoneDebuff1), c); c->SpellOnTarget(RuleI(Zone, PEQZoneDebuff2), c); } //zone to safe coords c->GetPTimers().Start(pTimerPeqzoneReuse, RuleI(Zone, PEQZoneReuseTime)); c->MovePC(zoneid, 0.0f, 0.0f, 0.0f, 0.0f, 0, ZoneToSafeCoords); } void command_movechar(Client *c, const Seperator *sep) { if(sep->arg[1][0]==0 || sep->arg[2][0] == 0) c->Message(Chat::White, "Usage: #movechar [charactername] [zonename]"); else if (c->Admin() < commandMovecharToSpecials && strcasecmp(sep->arg[2], "cshome") == 0 || strcasecmp(sep->arg[2], "load") == 0 || strcasecmp(sep->arg[2], "load2") == 0) c->Message(Chat::White, "Invalid zone name"); else { uint32 tmp = database.GetAccountIDByChar(sep->arg[1]); if (tmp) { if (c->Admin() >= commandMovecharSelfOnly || tmp == c->AccountID()) if (!database.MoveCharacterToZone((char*) sep->arg[1], ZoneID(sep->arg[2]))) c->Message(Chat::White, "Character Move Failed!"); else c->Message(Chat::White, "Character has been moved."); else c->Message(Chat::Red,"You cannot move characters that are not on your account."); } else c->Message(Chat::White, "Character Does Not Exist"); } } void command_movement(Client *c, const Seperator *sep) { auto &mgr = MobMovementManager::Get(); if (sep->arg[1][0] == 0) { c->Message(Chat::White, "Usage: #movement stats/clearstats/walkto/runto/rotateto/stop/packet"); return; } if (strcasecmp(sep->arg[1], "stats") == 0) { mgr.DumpStats(c); } else if (strcasecmp(sep->arg[1], "clearstats") == 0) { mgr.ClearStats(); } else if (strcasecmp(sep->arg[1], "walkto") == 0) { auto target = c->GetTarget(); if (target == nullptr) { c->Message(Chat::White, "No target found."); return; } target->WalkTo(c->GetX(), c->GetY(), c->GetZ()); } else if (strcasecmp(sep->arg[1], "runto") == 0) { auto target = c->GetTarget(); if (target == nullptr) { c->Message(Chat::White, "No target found."); return; } target->RunTo(c->GetX(), c->GetY(), c->GetZ()); } else if (strcasecmp(sep->arg[1], "rotateto") == 0) { auto target = c->GetTarget(); if (target == nullptr) { c->Message(Chat::White, "No target found."); return; } target->RotateToWalking(target->CalculateHeadingToTarget(c->GetX(), c->GetY())); } else if (strcasecmp(sep->arg[1], "stop") == 0) { auto target = c->GetTarget(); if (target == nullptr) { c->Message(Chat::White, "No target found."); return; } target->StopNavigation(); } else if (strcasecmp(sep->arg[1], "packet") == 0) { auto target = c->GetTarget(); if (target == nullptr) { c->Message(Chat::White, "No target found."); return; } mgr.SendCommandToClients(target, atof(sep->arg[2]), atof(sep->arg[3]), atof(sep->arg[4]), atof(sep->arg[5]), atoi(sep->arg[6]), ClientRangeAny); } else { c->Message(Chat::White, "Usage: #movement stats/clearstats/walkto/runto/rotateto/stop/packet"); } } void command_viewpetition(Client *c, const Seperator *sep) { if (sep->arg[1][0] == 0) { c->Message(Chat::White, "Usage: #viewpetition (petition number) Type #listpetition for a list"); return; } c->Message(Chat::Red," ID : Character Name , Petition Text"); std::string query = "SELECT petid, charname, petitiontext FROM petitions ORDER BY petid"; auto results = database.QueryDatabase(query); if (!results.Success()) return; LogInfo("View petition request from [{}], petition number: [{}]", c->GetName(), atoi(sep->argplus[1]) ); if (results.RowCount() == 0) { c->Message(Chat::Red,"There was an error in your request: ID not found! Please check the Id and try again."); return; } for (auto row = results.begin(); row != results.end(); ++row) if (strcasecmp(row[0], sep->argplus[1]) == 0) c->Message(Chat::Yellow, " %s: %s , %s ", row[0], row[1], row[2]); } void command_petitioninfo(Client *c, const Seperator *sep) { if (sep->arg[1][0] == 0) { c->Message(Chat::White, "Usage: #petitioninfo (petition number) Type #listpetition for a list"); return; } std::string query = "SELECT petid, charname, accountname, zone, charclass, charrace, charlevel FROM petitions ORDER BY petid"; auto results = database.QueryDatabase(query); if (!results.Success()) return; LogInfo("Petition information request from [{}], petition number:", c->GetName(), atoi(sep->argplus[1]) ); if (results.RowCount() == 0) { c->Message(Chat::Red,"There was an error in your request: ID not found! Please check the Id and try again."); return; } for (auto row = results.begin(); row != results.end(); ++row) if (strcasecmp(row[0],sep->argplus[1])== 0) c->Message(Chat::Red," ID : %s Character Name: %s Account Name: %s Zone: %s Character Class: %s Character Race: %s Character Level: %s", row[0],row[1],row[2],row[3],row[4],row[5],row[6]); } void command_delpetition(Client *c, const Seperator *sep) { if (sep->arg[1][0] == 0 || strcasecmp(sep->arg[1],"*") == 0) { c->Message(Chat::White, "Usage: #delpetition (petition number) Type #listpetition for a list"); return; } c->Message(Chat::Red,"Attempting to delete petition number: %i", atoi(sep->argplus[1])); std::string query = StringFormat("DELETE FROM petitions WHERE petid = %i", atoi(sep->argplus[1])); auto results = database.QueryDatabase(query); if (!results.Success()) return; LogInfo("Delete petition request from [{}], petition number:", c->GetName(), atoi(sep->argplus[1]) ); } void command_list(Client *c, const Seperator *sep) { std::string search_type; if (strcasecmp(sep->arg[1], "npcs") == 0) { search_type = "npcs"; } if (strcasecmp(sep->arg[1], "players") == 0) { search_type = "players"; } if (strcasecmp(sep->arg[1], "corpses") == 0) { search_type = "corpses"; } if (strcasecmp(sep->arg[1], "doors") == 0) { search_type = "doors"; } if (strcasecmp(sep->arg[1], "objects") == 0) { search_type = "objects"; } if (search_type.length() > 0) { int entity_count = 0; int found_count = 0; std::string search_string; if (sep->arg[2]) { search_string = sep->arg[2]; } /** * NPC */ if (search_type.find("npcs") != std::string::npos) { auto &entity_list_search = entity_list.GetMobList(); for (auto &itr : entity_list_search) { Mob *entity = itr.second; if (!entity->IsNPC()) { continue; } entity_count++; std::string entity_name = entity->GetName(); /** * Filter by name */ if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { continue; } std::string saylink = StringFormat( "#goto %.0f %0.f %.0f", entity->GetX(), entity->GetY(), entity->GetZ() + (entity->IsBoat() ? 50 : 0)); c->Message( 0, "| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f", EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), entity->GetID(), entity->GetName(), entity->GetX(), entity->GetY(), entity->GetZ() ); found_count++; } } /** * Client */ if (search_type.find("players") != std::string::npos) { auto &entity_list_search = entity_list.GetClientList(); for (auto &itr : entity_list_search) { Client *entity = itr.second; entity_count++; std::string entity_name = entity->GetName(); /** * Filter by name */ if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { continue; } std::string saylink = StringFormat( "#goto %.0f %0.f %.0f", entity->GetX(), entity->GetY(), entity->GetZ()); c->Message( 0, "| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f", EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), entity->GetID(), entity->GetName(), entity->GetX(), entity->GetY(), entity->GetZ() ); found_count++; } } /** * Corpse */ if (search_type.find("corpses") != std::string::npos) { auto &entity_list_search = entity_list.GetCorpseList(); for (auto &itr : entity_list_search) { Corpse *entity = itr.second; entity_count++; std::string entity_name = entity->GetName(); /** * Filter by name */ if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { continue; } std::string saylink = StringFormat( "#goto %.0f %0.f %.0f", entity->GetX(), entity->GetY(), entity->GetZ()); c->Message( 0, "| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f", EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), entity->GetID(), entity->GetName(), entity->GetX(), entity->GetY(), entity->GetZ() ); found_count++; } } /** * Doors */ if (search_type.find("doors") != std::string::npos) { auto &entity_list_search = entity_list.GetDoorsList(); for (auto &itr : entity_list_search) { Doors * entity = itr.second; entity_count++; std::string entity_name = entity->GetDoorName(); /** * Filter by name */ if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { continue; } std::string saylink = StringFormat( "#goto %.0f %0.f %.0f", entity->GetX(), entity->GetY(), entity->GetZ()); c->Message( 0, "| %s | Entity ID %5d | Door ID %i | %s | x %.0f | y %0.f | z %.0f", EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), entity->GetID(), entity->GetDoorID(), entity->GetDoorName(), entity->GetX(), entity->GetY(), entity->GetZ() ); found_count++; } } /** * Objects */ if (search_type.find("objects") != std::string::npos) { auto &entity_list_search = entity_list.GetObjectList(); for (auto &itr : entity_list_search) { Object * entity = itr.second; entity_count++; std::string entity_name = entity->GetModelName(); /** * Filter by name */ if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { continue; } std::string saylink = StringFormat( "#goto %.0f %0.f %.0f", entity->GetX(), entity->GetY(), entity->GetZ()); c->Message( 0, "| %s | Entity ID %5d | Object DBID %i | %s | x %.0f | y %0.f | z %.0f", EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), entity->GetID(), entity->GetDBID(), entity->GetModelName(), entity->GetX(), entity->GetY(), entity->GetZ() ); found_count++; } } if (found_count) { c->Message( 0, "Found (%i) of type (%s) in zone (%i) total", found_count, search_type.c_str(), entity_count ); } } else { c->Message(Chat::White, "Usage of #list"); c->Message(Chat::White, "- #list [npcs|players|corpses|doors|objects] [search]"); c->Message(Chat::White, "- Example: #list npcs (Blank for all)"); } } void command_date(Client *c, const Seperator *sep) { //yyyy mm dd hh mm local if(sep->arg[3][0]==0 || !sep->IsNumber(1) || !sep->IsNumber(2) || !sep->IsNumber(3)) { c->Message(Chat::Red, "Usage: #date yyyy mm dd [HH MM]"); } else { int h=0, m=0; TimeOfDay_Struct eqTime; zone->zone_time.GetCurrentEQTimeOfDay( time(0), &eqTime); if(!sep->IsNumber(4)) h=eqTime.hour; else h=atoi(sep->arg[4]); if(!sep->IsNumber(5)) m=eqTime.minute; else m=atoi(sep->arg[5]); c->Message(Chat::Red, "Setting world time to %s-%s-%s %i:%i...", sep->arg[1], sep->arg[2], sep->arg[3], h, m); zone->SetDate(atoi(sep->arg[1]), atoi(sep->arg[2]), atoi(sep->arg[3]), h, m); } } void command_timezone(Client *c, const Seperator *sep) { if(sep->arg[1][0]==0 && !sep->IsNumber(1)) { c->Message(Chat::Red, "Usage: #timezone HH [MM]"); c->Message(Chat::Red, "Current timezone is: %ih %im", zone->zone_time.getEQTimeZoneHr(), zone->zone_time.getEQTimeZoneMin()); } else { uint8 hours = atoi(sep->arg[1]); uint8 minutes = atoi(sep->arg[2]); if(!sep->IsNumber(2)) minutes = 0; c->Message(Chat::Red, "Setting timezone to %i h %i m", hours, minutes); uint32 ntz=(hours*60)+minutes; zone->zone_time.setEQTimeZone(ntz); content_db.SetZoneTZ(zone->GetZoneID(), zone->GetInstanceVersion(), ntz); // Update all clients with new TZ. auto outapp = new EQApplicationPacket(OP_TimeOfDay, sizeof(TimeOfDay_Struct)); TimeOfDay_Struct* tod = (TimeOfDay_Struct*)outapp->pBuffer; zone->zone_time.GetCurrentEQTimeOfDay(time(0), tod); entity_list.QueueClients(c, outapp); safe_delete(outapp); } } void command_invul(Client *c, const Seperator *sep) { bool state=atobool(sep->arg[1]); Client *t=c; if(c->GetTarget() && c->GetTarget()->IsClient()) t=c->GetTarget()->CastToClient(); if(sep->arg[1][0] != 0) { t->SetInvul(state); c->Message(Chat::White, "%s is %s invulnerable from attack.", t->GetName(), state?"now":"no longer"); } else c->Message(Chat::White, "Usage: #invulnerable [on/off]"); } void command_hideme(Client *c, const Seperator *sep) { bool state=atobool(sep->arg[1]); if(sep->arg[1][0]==0) c->Message(Chat::White, "Usage: #hideme [on/off]"); else { c->SetHideMe(state); c->MessageString(Chat::Broadcasts, c->GetHideMe() ? NOW_INVISIBLE : NOW_VISIBLE, c->GetName()); } } void command_emote(Client *c, const Seperator *sep) { if (sep->arg[3][0] == 0) c->Message(Chat::White, "Usage: #emote [name | world | zone] type# message"); else { if (strcasecmp(sep->arg[1], "zone") == 0){ char* newmessage=0; if(strstr(sep->arg[3],"^")==0) entity_list.Message(0, atoi(sep->arg[2]), sep->argplus[3]); else{ for(newmessage = strtok((char*)sep->arg[3],"^");newmessage!=nullptr;newmessage=strtok(nullptr, "^")) entity_list.Message(0, atoi(sep->arg[2]), newmessage); } } else if (!worldserver.Connected()) c->Message(Chat::White, "Error: World server disconnected"); else if (strcasecmp(sep->arg[1], "world") == 0) worldserver.SendEmoteMessage(0, 0, atoi(sep->arg[2]), sep->argplus[3]); else worldserver.SendEmoteMessage(sep->arg[1], 0, atoi(sep->arg[2]), sep->argplus[3]); } } void command_fov(Client *c, const Seperator *sep) { if (c->GetTarget()) { auto target = c->GetTarget(); std::string behind_message = ( c->BehindMob( target, c->GetX(), c->GetY() ) ? "behind" : "not behind" ); std::string gender_message = ( target->GetGender() == MALE ? "he" : ( target->GetGender() == FEMALE ? "she" : "it" ) ); c->Message( Chat::White, fmt::format( "You are {} {} ({}), {} has a heading of {}.", behind_message, target->GetCleanName(), target->GetID(), gender_message, target->GetHeading() ).c_str() ); } else { c->Message(Chat::White, "You must have a target to use this command."); } } void command_npcstats(Client *c, const Seperator *sep) { if (c->GetTarget() && c->GetTarget()->IsNPC()) { NPC* target = c->GetTarget()->CastToNPC(); // Stats target->ShowStats(c); // Loot Data if (target->GetLoottableID()) { target->QueryLoot(c); } } else { c->Message(Chat::White, "You must target an NPC to use this command."); } } void command_zclip(Client *c, const Seperator *sep) { // modifys and resends zhdr packet if(sep->arg[2][0]==0) c->Message(Chat::White, "Usage: #zclip "); else if(atoi(sep->arg[1])<=0) c->Message(Chat::White, "ERROR: Min clip can not be zero or less!"); else if(atoi(sep->arg[2])<=0) c->Message(Chat::White, "ERROR: Max clip can not be zero or less!"); else if(atoi(sep->arg[1])>atoi(sep->arg[2])) c->Message(Chat::White, "ERROR: Min clip is greater than max clip!"); else { zone->newzone_data.minclip = atof(sep->arg[1]); zone->newzone_data.maxclip = atof(sep->arg[2]); if(sep->arg[3][0]!=0) zone->newzone_data.fog_minclip[0]=atof(sep->arg[3]); if(sep->arg[4][0]!=0) zone->newzone_data.fog_minclip[1]=atof(sep->arg[4]); if(sep->arg[5][0]!=0) zone->newzone_data.fog_maxclip[0]=atof(sep->arg[5]); if(sep->arg[6][0]!=0) zone->newzone_data.fog_maxclip[1]=atof(sep->arg[6]); auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); entity_list.QueueClients(c, outapp); safe_delete(outapp); } } void command_npccast(Client *c, const Seperator *sep) { if (c->GetTarget() && c->GetTarget()->IsNPC()) { NPC* target = c->GetTarget()->CastToNPC(); if (!sep->IsNumber(1) && sep->arg[1] && sep->IsNumber(2)) { const char* entity_name = sep->arg[1] ? sep->arg[1] : 0; auto spell_id = sep->arg[2] ? std::stoul(sep->arg[2]) : 0; Mob* spell_target = entity_list.GetMob(entity_name); if (spell_target && IsValidSpell(spell_id) && spell_id < SPDAT_RECORDS) { c->Message( Chat::White, fmt::format( "{} ({}) casting {} ({}) on {} ({}).", target->GetCleanName(), target->GetID(), GetSpellName(static_cast(spell_id)), spell_id, spell_target->GetCleanName(), spell_target->GetID() ).c_str() ); target->CastSpell(spell_id, spell_target->GetID()); } else { if (!spell_target) { c->Message( Chat::White, fmt::format( "Entity {} was not found", entity_name ).c_str() ); } else if (!spell_id || !IsValidSpell(spell_id)) { c->Message( Chat::White, fmt::format( "Spell ID {} was not found", spell_id ).c_str() ); } } } else if (sep->IsNumber(1) && sep->IsNumber(2) ) { uint16 entity_id = sep->arg[1] ? std::stoul(sep->arg[1]) : 0; auto spell_id = sep->arg[2] ? std::stoul(sep->arg[2]) : 0; Mob* spell_target = entity_list.GetMob(entity_id); if (spell_target && IsValidSpell(spell_id) && spell_id < SPDAT_RECORDS) { c->Message( Chat::White, fmt::format( "{} ({}) casting {} ({}) on {} ({}).", target->GetCleanName(), target->GetID(), GetSpellName(static_cast(spell_id)), spell_id, spell_target->GetCleanName(), spell_target->GetID() ).c_str() ); target->CastSpell(spell_id, spell_target->GetID()); } else { if (!spell_target) { c->Message( Chat::White, fmt::format( "Entity ID {} was not found", entity_id ).c_str() ); } else if (!spell_id || !IsValidSpell(spell_id)) { c->Message( Chat::White, fmt::format( "Spell ID {} was not found", spell_id ).c_str() ); } } } } else { c->Message(Chat::White, "You must target an NPC to use this command."); } } void command_zstats(Client *c, const Seperator *sep) { // Zone c->Message( Chat::White, fmt::format( "Zone | ID: {} Instance ID: {} Name: {} ({})", zone->GetZoneID(), zone->GetInstanceID(), zone->GetLongName(), zone->GetShortName() ).c_str() ); // Type c->Message( Chat::White, fmt::format( "Type: {}", zone->newzone_data.ztype ).c_str() ); // Fog Data for (int fog_index = 0; fog_index < 4; fog_index++) { int fog_number = (fog_index + 1); c->Message( Chat::White, fmt::format( "Fog {} Colors | Red: {} Blue: {} Green: {} ", fog_number, zone->newzone_data.fog_red[fog_index], zone->newzone_data.fog_green[fog_index], zone->newzone_data.fog_blue[fog_index] ).c_str() ); c->Message( Chat::White, fmt::format( "Fog {} Clipping Distance | Min: {} Max: {}", fog_number, zone->newzone_data.fog_minclip[fog_index], zone->newzone_data.fog_maxclip[fog_index] ).c_str() ); } // Fog Density c->Message( Chat::White, fmt::format( "Fog Density: {}", zone->newzone_data.fog_density ).c_str() ); // Gravity c->Message( Chat::White, fmt::format( "Gravity: {}", zone->newzone_data.gravity ).c_str() ); // Time Type c->Message( Chat::White, fmt::format( "Time Type: {}", zone->newzone_data.time_type ).c_str() ); // Experience Multiplier c->Message( Chat::White, fmt::format( "Experience Multiplier: {}", zone->newzone_data.zone_exp_multiplier ).c_str() ); // Safe Coordinates c->Message( Chat::White, fmt::format( "Safe Coordinates: {}, {}, {}", zone->newzone_data.safe_x, zone->newzone_data.safe_y, zone->newzone_data.safe_z ).c_str() ); // Max Z c->Message( Chat::White, fmt::format( "Max Z: {}", zone->newzone_data.max_z ).c_str() ); // Underworld Z c->Message( Chat::White, fmt::format( "Underworld Z: {}", zone->newzone_data.underworld ).c_str() ); // Clipping Distance c->Message( Chat::White, fmt::format( "Clipping Distance | Min: {} Max: {}", zone->newzone_data.minclip, zone->newzone_data.maxclip ).c_str() ); // Weather Data for (int weather_index = 0; weather_index < 4; weather_index++) { int weather_number = (weather_index + 1); c->Message( Chat::White, fmt::format( "Rain {} | Chance: {} Duration: {} ", weather_number, zone->newzone_data.rain_chance[weather_index], zone->newzone_data.rain_duration[weather_index] ).c_str() ); c->Message( Chat::White, fmt::format( "Snow {} | Chance: {} Duration: {}", weather_number, zone->newzone_data.snow_chance[weather_index], zone->newzone_data.snow_duration[weather_index] ).c_str() ); } // Sky Type c->Message( Chat::White, fmt::format( "Sky Type: {}", zone->newzone_data.sky ).c_str() ); // Suspend Buffs c->Message( Chat::White, fmt::format( "Suspend Buffs: {}", zone->newzone_data.SuspendBuffs ).c_str() ); // Regeneration Data c->Message( Chat::White, fmt::format( "Regen | Health: {} Mana: {} Endurance: {}", zone->newzone_data.FastRegenHP, zone->newzone_data.FastRegenMana, zone->newzone_data.FastRegenEndurance ).c_str() ); // NPC Max Aggro Distance c->Message( Chat::White, fmt::format( "NPC Max Aggro Distance: {}", zone->newzone_data.NPCAggroMaxDist ).c_str() ); // Underworld Teleport Index c->Message( Chat::White, fmt::format( "Underworld Teleport Index: {}", zone->newzone_data.underworld_teleport_index ).c_str() ); // Lava Damage c->Message( Chat::White, fmt::format( "Lava Damage | Min: {} Max: {}", zone->newzone_data.MinLavaDamage, zone->newzone_data.LavaDamage ).c_str() ); } void command_permaclass(Client *c, const Seperator *sep) { Client *t=c; if(c->GetTarget() && c->GetTarget()->IsClient()) t=c->GetTarget()->CastToClient(); if(sep->arg[1][0]==0) { c->Message(Chat::White,"Usage: #permaclass "); } else if(!t->IsClient()) c->Message(Chat::White,"Target is not a client."); else { c->Message(Chat::White, "Setting %s's class...Sending to char select.", t->GetName()); LogInfo("Class change request from [{}] for [{}], requested class:[{}]", c->GetName(), t->GetName(), atoi(sep->arg[1]) ); t->SetBaseClass(atoi(sep->arg[1])); t->Save(); t->Kick("Class was changed."); } } void command_permarace(Client *c, const Seperator *sep) { Client *t=c; if(c->GetTarget() && c->GetTarget()->IsClient()) t=c->GetTarget()->CastToClient(); if(sep->arg[1][0]==0) { c->Message(Chat::White,"Usage: #permarace "); c->Message(Chat::White,"NOTE: Not all models are global. If a model is not global, it will appear as a human on character select and in zones without the model."); } else if(!t->IsClient()) c->Message(Chat::White,"Target is not a client."); else { c->Message(Chat::White, "Setting %s's race - zone to take effect", t->GetName()); LogInfo("Permanant race change request from [{}] for [{}], requested race:[{}]", c->GetName(), t->GetName(), atoi(sep->arg[1]) ); uint32 tmp = Mob::GetDefaultGender(atoi(sep->arg[1]), t->GetBaseGender()); t->SetBaseRace(atoi(sep->arg[1])); t->SetBaseGender(tmp); t->Save(); t->SendIllusionPacket(atoi(sep->arg[1])); } } void command_permagender(Client *c, const Seperator *sep) { Client *t=c; if(c->GetTarget() && c->GetTarget()->IsClient()) t=c->GetTarget()->CastToClient(); if(sep->arg[1][0]==0) { c->Message(Chat::White,"Usage: #permagender "); c->Message(Chat::White,"Gender Numbers: 0=Male, 1=Female, 2=Neuter"); } else if(!t->IsClient()) c->Message(Chat::White,"Target is not a client."); else { c->Message(Chat::White, "Setting %s's gender - zone to take effect", t->GetName()); LogInfo("Permanant gender change request from [{}] for [{}], requested gender:[{}]", c->GetName(), t->GetName(), atoi(sep->arg[1]) ); t->SetBaseGender(atoi(sep->arg[1])); t->Save(); t->SendIllusionPacket(atoi(sep->arg[1])); } } void command_weather(Client *c, const Seperator *sep) { if (!(sep->arg[1][0] == '0' || sep->arg[1][0] == '1' || sep->arg[1][0] == '2' || sep->arg[1][0] == '3')) { c->Message(Chat::White, "Usage: #weather <0/1/2/3> - Off/Rain/Snow/Manual."); } else if(zone->zone_weather == 0) { if(sep->arg[1][0] == '3') { // Put in modifications here because it had a very good chance at screwing up the client's weather system if rain was sent during snow -T7 if(sep->arg[2][0] != 0 && sep->arg[3][0] != 0) { c->Message(Chat::White, "Sending weather packet... TYPE=%s, INTENSITY=%s", sep->arg[2], sep->arg[3]); zone->zone_weather = atoi(sep->arg[2]); auto outapp = new EQApplicationPacket(OP_Weather, 8); outapp->pBuffer[0] = atoi(sep->arg[2]); outapp->pBuffer[4] = atoi(sep->arg[3]); // This number changes in the packets, intensity? entity_list.QueueClients(c, outapp); safe_delete(outapp); } else { c->Message(Chat::White, "Manual Usage: #weather 3 "); } } else if(sep->arg[1][0] == '2') { entity_list.Message(0, 0, "Snowflakes begin to fall from the sky."); zone->zone_weather = 2; auto outapp = new EQApplicationPacket(OP_Weather, 8); outapp->pBuffer[0] = 0x01; outapp->pBuffer[4] = 0x02; // This number changes in the packets, intensity? entity_list.QueueClients(c, outapp); safe_delete(outapp); } else if(sep->arg[1][0] == '1') { entity_list.Message(0, 0, "Raindrops begin to fall from the sky."); zone->zone_weather = 1; auto outapp = new EQApplicationPacket(OP_Weather, 8); outapp->pBuffer[4] = 0x01; // This is how it's done in Fear, and you can see a decent distance with it at this value entity_list.QueueClients(c, outapp); safe_delete(outapp); } } else { if(zone->zone_weather == 1) { // Doing this because if you have rain/snow on, you can only turn one off. entity_list.Message(0, 0, "The sky clears as the rain ceases to fall."); zone->zone_weather = 0; auto outapp = new EQApplicationPacket(OP_Weather, 8); // To shutoff weather you send an empty 8 byte packet (You get this everytime you zone even if the sky is clear) entity_list.QueueClients(c, outapp); safe_delete(outapp); } else if(zone->zone_weather == 2) { entity_list.Message(0, 0, "The sky clears as the snow stops falling."); zone->zone_weather = 0; auto outapp = new EQApplicationPacket(OP_Weather, 8); // To shutoff weather you send an empty 8 byte packet (You get this everytime you zone even if the sky is clear) outapp->pBuffer[0] = 0x01; // Snow has it's own shutoff packet entity_list.QueueClients(c, outapp); safe_delete(outapp); } else { entity_list.Message(0, 0, "The sky clears."); zone->zone_weather = 0; auto outapp = new EQApplicationPacket(OP_Weather, 8); // To shutoff weather you send an empty 8 byte packet (You get this everytime you zone even if the sky is clear) entity_list.QueueClients(c, outapp); safe_delete(outapp); } } } void command_zheader(Client *c, const Seperator *sep) { // sends zhdr packet if(sep->arg[1][0]==0) { c->Message(Chat::White, "Usage: #zheader "); } else if(ZoneID(sep->argplus[1])==0) c->Message(Chat::White, "Invalid Zone Name: %s", sep->argplus[1]); else { if (zone->LoadZoneCFG(sep->argplus[1], 0)) c->Message(Chat::White, "Successfully loaded zone header for %s from database.", sep->argplus[1]); else c->Message(Chat::White, "Failed to load zone header %s from database", sep->argplus[1]); auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); entity_list.QueueClients(c, outapp); safe_delete(outapp); } } void command_zsky(Client *c, const Seperator *sep) { // modifys and resends zhdr packet if(sep->arg[1][0]==0) c->Message(Chat::White, "Usage: #zsky "); else if(atoi(sep->arg[1])<0||atoi(sep->arg[1])>255) c->Message(Chat::White, "ERROR: Sky type can not be less than 0 or greater than 255!"); else { zone->newzone_data.sky = atoi(sep->arg[1]); auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); entity_list.QueueClients(c, outapp); safe_delete(outapp); } } void command_zcolor(Client *c, const Seperator *sep) { // modifys and resends zhdr packet if (sep->arg[3][0]==0) c->Message(Chat::White, "Usage: #zcolor "); else if (atoi(sep->arg[1])<0||atoi(sep->arg[1])>255) c->Message(Chat::White, "ERROR: Red can not be less than 0 or greater than 255!"); else if (atoi(sep->arg[2])<0||atoi(sep->arg[2])>255) c->Message(Chat::White, "ERROR: Green can not be less than 0 or greater than 255!"); else if (atoi(sep->arg[3])<0||atoi(sep->arg[3])>255) c->Message(Chat::White, "ERROR: Blue can not be less than 0 or greater than 255!"); else { for (int z=0; z<4; z++) { zone->newzone_data.fog_red[z] = atoi(sep->arg[1]); zone->newzone_data.fog_green[z] = atoi(sep->arg[2]); zone->newzone_data.fog_blue[z] = atoi(sep->arg[3]); } auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); entity_list.QueueClients(c, outapp); safe_delete(outapp); } } void command_gassign(Client *c, const Seperator *sep) { if (sep->IsNumber(1) && c->GetTarget() && c->GetTarget()->IsNPC() && c->GetTarget()->CastToNPC()->GetSpawnPointID() > 0) { int spawn2id = c->GetTarget()->CastToNPC()->GetSpawnPointID(); database.AssignGrid(c, atoi(sep->arg[1]), spawn2id); } else c->Message(Chat::White, "Usage: #gassign [num] - must have an npc target!"); } void command_ai(Client *c, const Seperator *sep) { Mob *target=c->GetTarget(); 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]"); } 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]"); } 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) 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); } else if ((sep->argnum == 3 || sep->argnum == 4) && sep->IsNumber(2) && sep->IsNumber(3)) { uint32 tmp = 2500; uint32 tmp2 = 2500; if (sep->IsNumber(4)) tmp = atoi(sep->arg[4]); if (sep->IsNumber(5)) tmp2 = atoi(sep->arg[5]); target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), tmp, tmp2); } 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 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"); } } void command_worldshutdown(Client *c, const Seperator *sep) { // GM command to shutdown world server and all zone servers uint32 time = 0; uint32 interval = 0; if (worldserver.Connected()) { if ( sep->IsNumber(1) && sep->IsNumber(2) && (time = std::stoi(sep->arg[1]) > 0) && (interval = std::stoi(sep->arg[2]) > 0) ) { int time_minutes = (time / 60); quest_manager.WorldWideMessage( Chat::Yellow, fmt::format( "[SYSTEM] World will be shutting down in {} minutes.", time_minutes ).c_str() ); c->Message( Chat::White, fmt::format( "World will be shutting down in {} minutes, notifying every {} seconds", time_minutes, interval ).c_str() ); auto pack = new ServerPacket(ServerOP_ShutdownAll, sizeof(WorldShutDown_Struct)); WorldShutDown_Struct* wsd = (WorldShutDown_Struct*)pack->pBuffer; wsd->time = (time * 1000); wsd->interval = (interval * 1000); worldserver.SendPacket(pack); safe_delete(pack); } else if (!strcasecmp(sep->arg[1], "now")){ quest_manager.WorldWideMessage( Chat::Yellow, "[SYSTEM] World is shutting down now." ); c->Message(Chat::White, "World is shutting down now."); auto pack = new ServerPacket; pack->opcode = ServerOP_ShutdownAll; pack->size = 0; worldserver.SendPacket(pack); safe_delete(pack); } else if (!strcasecmp(sep->arg[1], "disable")) { c->Message(Chat::White, "World shutdown has been aborted."); auto pack = new ServerPacket(ServerOP_ShutdownAll, sizeof(WorldShutDown_Struct)); WorldShutDown_Struct* wsd = (WorldShutDown_Struct*)pack->pBuffer; wsd->time = 0; wsd->interval = 0; worldserver.SendPacket(pack); safe_delete(pack); } else { c->Message(Chat::White,"#worldshutdown - Shuts down the server and all zones."); c->Message(Chat::White,"Usage: #worldshutdown now - Shuts down the server and all zones immediately."); c->Message(Chat::White,"Usage: #worldshutdown disable - Stops the server from a previously scheduled shut down."); c->Message(Chat::White,"Usage: #worldshutdown [timer] [interval] - Shuts down the server and all zones after [timer] seconds and notifies players every [interval] seconds."); } } else { c->Message(Chat::White, "Error: World server is disconnected."); } } void command_sendzonespawns(Client *c, const Seperator *sep) { entity_list.SendZoneSpawns(c); } void command_zsave(Client *c, const Seperator *sep) { if (zone->SaveZoneCFG()) { c->Message(Chat::Red, "Zone header saved successfully."); } else { c->Message(Chat::Red, "ERROR: Zone header data was NOT saved."); } } void command_dbspawn2(Client *c, const Seperator *sep) { if (sep->IsNumber(1) && sep->IsNumber(2) && sep->IsNumber(3)) { LogInfo("Spawning database spawn"); uint16 cond = 0; int16 cond_min = 0; if(sep->IsNumber(4)) { cond = atoi(sep->arg[4]); if(sep->IsNumber(5)) cond_min = atoi(sep->arg[5]); } database.CreateSpawn2(c, atoi(sep->arg[1]), zone->GetShortName(), c->GetPosition(), atoi(sep->arg[2]), atoi(sep->arg[3]), cond, cond_min); } else { c->Message(Chat::White, "Usage: #dbspawn2 spawngroup respawn variance [condition_id] [condition_min]"); } } void command_shutdown(Client *c, const Seperator *sep) { CatchSignal(2); } void command_delacct(Client *c, const Seperator *sep) { if (sep->arg[1][0] == 0) c->Message(Chat::White, "Format: #delacct accountname"); else { std::string user; std::string loginserver; ParseAccountString(sep->arg[1], user, loginserver); if (database.DeleteAccount(user.c_str(), loginserver.c_str())) c->Message(Chat::White, "The account was deleted."); else c->Message(Chat::White, "Unable to delete account."); } } void command_setpass(Client *c, const Seperator *sep) { if(sep->argnum != 2) c->Message(Chat::White, "Format: #setpass accountname password"); else { std::string user; std::string loginserver; ParseAccountString(sep->arg[1], user, loginserver); int16 tmpstatus = 0; uint32 tmpid = database.GetAccountIDByName(user.c_str(), loginserver.c_str(), &tmpstatus); if (!tmpid) c->Message(Chat::White, "Error: Account not found"); else if (tmpstatus > c->Admin()) c->Message(Chat::White, "Cannot change password: Account's status is higher than yours"); else if (database.SetLocalPassword(tmpid, sep->arg[2])) c->Message(Chat::White, "Password changed."); else c->Message(Chat::White, "Error changing password."); } } void command_setlsinfo(Client *c, const Seperator *sep) { if(sep->argnum != 2) c->Message(Chat::White, "Format: #setlsinfo email password"); else { auto pack = new ServerPacket(ServerOP_LSAccountUpdate, sizeof(ServerLSAccountUpdate_Struct)); ServerLSAccountUpdate_Struct* s = (ServerLSAccountUpdate_Struct *) pack->pBuffer; s->useraccountid = c->LSAccountID(); strn0cpy(s->useraccount, c->AccountName(), 30); strn0cpy(s->user_email, sep->arg[1], 100); strn0cpy(s->userpassword, sep->arg[2], 50); worldserver.SendPacket(pack); c->Message(Chat::White, "Login Server update packet sent."); } } void command_grid(Client *c, const Seperator *sep) { auto command_type = sep->arg[1]; auto zone_id = zone->GetZoneID(); if (strcasecmp("max", command_type) == 0) { c->Message( Chat::White, fmt::format( "Highest grid ID in this zone is {}.", content_db.GetHighestGrid(zone_id) ).c_str() ); } else if (strcasecmp("add", command_type) == 0) { auto grid_id = atoi(sep->arg[2]); auto wander_type = atoi(sep->arg[3]); auto pause_type = atoi(sep->arg[4]); if (!content_db.GridExistsInZone(zone_id, grid_id)) { content_db.ModifyGrid(c, false, grid_id, wander_type, pause_type, zone_id); c->Message( Chat::White, fmt::format( "Grid {} added to zone ID {} with wander type {} and pause type {}.", grid_id, zone_id, wander_type, pause_type ).c_str() ); } else { c->Message( Chat::White, fmt::format( "Grid {} already exists in zone ID {}.", grid_id, zone_id ).c_str() ); return; } } else if (strcasecmp("show", command_type) == 0) { Mob *target = c->GetTarget(); if (!target || !target->IsNPC()) { c->Message(Chat::White, "You need to target an NPC!"); return; } auto grid_id = target->CastToNPC()->GetGrid(); std::string query = fmt::format( "SELECT `x`, `y`, `z`, `heading`, `number` " "FROM `grid_entries` " "WHERE `zoneid` = {} AND `gridid` = {} " "ORDER BY `number`", zone_id, grid_id ); auto results = content_db.QueryDatabase(query); if (!results.Success()) { c->Message(Chat::White, "Error querying database."); c->Message(Chat::White, query.c_str()); } if (results.RowCount() == 0) { c->Message(Chat::White, "No grid found."); return; } // Depop any node npc's already spawned entity_list.DespawnGridNodes(grid_id); // Spawn grid nodes std::map, int32> zoffset; for (auto row : results) { glm::vec4 node_position = glm::vec4(atof(row[0]), atof(row[1]), atof(row[2]), atof(row[3])); std::vector node_loc { node_position.x, node_position.y, node_position.z }; // If we already have a node at this location, set the z offset // higher from the existing one so we can see it. Adjust so if // there is another at the same spot we adjust again. auto search = zoffset.find(node_loc); if (search != zoffset.end()) { search->second = search->second + 3; } else { zoffset[node_loc] = 0.0; } node_position.z += zoffset[node_loc]; NPC::SpawnGridNodeNPC(node_position, grid_id, atoi(row[4]), zoffset[node_loc]); } c->Message( Chat::White, fmt::format( "Spawning nodes for grid {}.", grid_id ).c_str() ); } else if (strcasecmp("hide", command_type) == 0) { Mob* target = c->GetTarget(); if (!target || !target->IsNPC()) { c->Message(Chat::White, "You need to target an NPC!"); return; } auto grid_id = target->CastToNPC()->GetGrid(); entity_list.DespawnGridNodes(grid_id); c->Message( Chat::White, fmt::format( "Depawning nodes for grid {}.", grid_id ).c_str() ); } else if (strcasecmp("delete", command_type) == 0) { auto grid_id = atoi(sep->arg[2]); content_db.ModifyGrid(c, true, grid_id, 0, 0, zone_id); c->Message( Chat::White, fmt::format( "Grid {} deleted from zone ID {}.", grid_id, zone_id ).c_str() ); } else { c->Message(Chat::White, "Usage: #grid [add|delete] [grid_id] [wander_type] [pause_type]"); c->Message(Chat::White, "Usage: #grid [max] - displays the highest grid ID used in this zone (for add)"); c->Message(Chat::White, "Usage: #grid [show] - displays wp nodes as boxes"); } } void command_wp(Client *c, const Seperator *sep) { auto command_type = sep->arg[1]; auto grid_id = atoi(sep->arg[2]); if (grid_id != 0) { auto pause = atoi(sep->arg[3]); auto waypoint = atoi(sep->arg[4]); auto zone_id = zone->GetZoneID(); if (strcasecmp("add", command_type) == 0) { if (waypoint == 0) { // Default to highest if it's left blank, or we enter 0 waypoint = (content_db.GetHighestWaypoint(zone_id, grid_id) + 1); } if (strcasecmp("-h", sep->arg[5]) == 0) { content_db.AddWP(c, grid_id, waypoint, c->GetPosition(), pause, zone_id); } else { auto position = c->GetPosition(); position.w = -1; content_db.AddWP(c, grid_id, waypoint, position, pause, zone_id); } c->Message( Chat::White, fmt::format( "Waypoint {} added to grid {} with a pause of {} {}.", waypoint, grid_id, pause, (pause == 1 ? "second" : "seconds") ).c_str() ); } else if (strcasecmp("delete", command_type) == 0) { content_db.DeleteWaypoint(c, grid_id, waypoint, zone_id); c->Message( Chat::White, fmt::format( "Waypoint {} deleted from grid {}.", waypoint, grid_id ).c_str() ); } } else { c->Message(Chat::White,"Usage: #wp [add|delete] [grid_id] [pause] [waypoint_id] [-h]"); } } void command_iplookup(Client *c, const Seperator *sep) { auto pack = new ServerPacket(ServerOP_IPLookup, sizeof(ServerGenericWorldQuery_Struct) + strlen(sep->argplus[1]) + 1); ServerGenericWorldQuery_Struct* s = (ServerGenericWorldQuery_Struct *) pack->pBuffer; strcpy(s->from, c->GetName()); s->admin = c->Admin(); if (sep->argplus[1][0] != 0) strcpy(s->query, sep->argplus[1]); worldserver.SendPacket(pack); safe_delete(pack); } void command_size(Client *c, const Seperator *sep) { Mob *target=c->GetTarget(); if (!sep->IsNumber(1)) c->Message(Chat::White, "Usage: #size [0 - 255] (Decimal increments are allowed)"); else { float newsize = atof(sep->arg[1]); if (newsize > 255) c->Message(Chat::White, "Error: #size: Size can not be greater than 255."); else if (newsize < 0) c->Message(Chat::White, "Error: #size: Size can not be less than 0."); else if (!target) c->Message(Chat::White,"Error: this command requires a target"); else { uint16 Race = target->GetModel(); uint8 Gender = target->GetGender(); uint8 Texture = 0xFF; uint8 HelmTexture = 0xFF; uint8 HairColor = target->GetHairColor(); uint8 BeardColor = target->GetBeardColor(); uint8 EyeColor1 = target->GetEyeColor1(); uint8 EyeColor2 = target->GetEyeColor2(); uint8 HairStyle = target->GetHairStyle(); uint8 LuclinFace = target->GetLuclinFace(); uint8 Beard = target->GetBeard(); uint32 DrakkinHeritage = target->GetDrakkinHeritage(); uint32 DrakkinTattoo = target->GetDrakkinTattoo(); uint32 DrakkinDetails = target->GetDrakkinDetails(); target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, DrakkinHeritage, DrakkinTattoo, DrakkinDetails, newsize); c->Message(Chat::White,"Size = %f", atof(sep->arg[1])); } } } void command_mana(Client *c, const Seperator *sep) { auto target = c->GetTarget() ? c->GetTarget() : c; if(target->IsClient()) { target->CastToClient()->SetMana(target->CastToClient()->CalcMaxMana()); } else { target->SetMana(target->CalcMaxMana()); } if (c != target) { c->Message( Chat::White, fmt::format( "Set {} ({}) to full Mana.", target->GetCleanName(), target->GetID() ).c_str() ); } else { c->Message(Chat::White, "Restored your Mana to full."); } } void command_flymode(Client *c, const Seperator *sep) { Mob *t = c; if (strlen(sep->arg[1]) == 1 && sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 5) { if (c->GetTarget()) { t = c->GetTarget(); } int fm = atoi(sep->arg[1]); t->SetFlyMode(static_cast(fm)); t->SendAppearancePacket(AT_Levitate, fm); if (sep->arg[1][0] == '0') { c->Message(Chat::White, "Setting %s to Grounded", t->GetName()); } else if (sep->arg[1][0] == '1') { c->Message(Chat::White, "Setting %s to Flying", t->GetName()); } else if (sep->arg[1][0] == '2') { c->Message(Chat::White, "Setting %s to Levitating", t->GetName()); } else if (sep->arg[1][0] == '3') { c->Message(Chat::White, "Setting %s to In Water", t->GetName()); } else if (sep->arg[1][0] == '4') { c->Message(Chat::White, "Setting %s to Floating(Boat)", t->GetName()); } else if (sep->arg[1][0] == '5') { c->Message(Chat::White, "Setting %s to Levitating While Running", t->GetName()); } } else { c->Message(Chat::White, "#flymode [0/1/2/3/4/5]"); } } void command_showskills(Client *c, const Seperator *sep) { Client *target = c; if (c->GetTarget() && c->GetTarget()->IsClient()) { target = c->GetTarget()->CastToClient(); } bool show_all = false; if (!strcasecmp("all", sep->arg[1])) { show_all = true; } c->Message( Chat::White, fmt::format( "Skills | Name: {}", target->GetCleanName() ).c_str() ); for ( EQ::skills::SkillType skill_type = EQ::skills::Skill1HBlunt; skill_type <= EQ::skills::HIGHEST_SKILL; skill_type = (EQ::skills::SkillType)(skill_type + 1) ) { if (show_all || (target->CanHaveSkill(skill_type) && target->MaxSkill(skill_type))) { c->Message( Chat::White, fmt::format( "{} ({}) | Current: {} Max: {} Raw: {}", EQ::skills::GetSkillName(skill_type), skill_type, target->GetSkill(skill_type), target->MaxSkill(skill_type), target->GetRawSkill(skill_type) ).c_str() ); } } } void command_findclass(Client *c, const Seperator *sep) { int arguments = sep->argnum; if (arguments == 0) { c->Message(Chat::White, "Command Syntax: #findclass [search criteria]"); return; } if (sep->IsNumber(1)) { int class_id = std::stoi(sep->arg[1]); if (class_id >= WARRIOR && class_id <= MERCERNARY_MASTER) { std::string class_name = GetClassIDName(class_id); c->Message( Chat::White, fmt::format( "Class {}: {}", class_id, class_name ).c_str() ); } else { c->Message( Chat::White, fmt::format( "Class ID {} was not found.", class_id ).c_str() ); } } else { std::string search_criteria = str_tolower(sep->argplus[1]); int found_count = 0; for (int class_id = WARRIOR; class_id <= MERCERNARY_MASTER; class_id++) { std::string class_name = GetClassIDName(class_id); std::string class_name_lower = str_tolower(class_name); if (search_criteria.length() > 0 && class_name_lower.find(search_criteria) == std::string::npos) { continue; } c->Message( Chat::White, fmt::format( "Class {}: {}", class_id, class_name ).c_str() ); found_count++; if (found_count == 20) { break; } } if (found_count == 20) { c->Message(Chat::White, "20 Classes found... max reached."); } else { auto class_message = ( found_count > 0 ? ( found_count == 1 ? "A Class was" : fmt::format("{} Classes were", found_count) ) : "No Classes were" ); c->Message( Chat::White, fmt::format( "{} found.", class_message ).c_str() ); } } } void command_findfaction(Client *c, const Seperator *sep) { int arguments = sep->argnum; if (arguments == 0) { c->Message(Chat::White, "Command Syntax: #findfaction [search criteria]"); return; } if (sep->IsNumber(1)) { int faction_id = std::stoi(sep->arg[1]); auto faction_name = content_db.GetFactionName(faction_id); if (!faction_name.empty()) { c->Message( Chat::White, fmt::format( "Faction {}: {}", faction_id, faction_name ).c_str() ); } else { c->Message( Chat::White, fmt::format( "Faction ID {} was not found.", faction_id ).c_str() ); } } else { std::string search_criteria = str_tolower(sep->argplus[1]); int found_count = 0; int max_faction_id = content_db.GetMaxFaction(); for (int faction_id = 0; faction_id < max_faction_id; faction_id++) { std::string faction_name = content_db.GetFactionName(faction_id); std::string faction_name_lower = str_tolower(faction_name); if (faction_name.empty()) { continue; } if (faction_name.find(search_criteria) == std::string::npos) { continue; } c->Message( Chat::White, fmt::format( "Faction {}: {}", faction_id, faction_name ).c_str() ); found_count++; if (found_count == 20) { break; } } if (found_count == 20) { c->Message(Chat::White, "20 Factions found... max reached."); } else { auto faction_message = ( found_count > 0 ? ( found_count == 1 ? "A Faction was" : fmt::format("{} Factions were", found_count) ) : "No Factions were" ); c->Message( Chat::White, fmt::format( "{} found.", faction_message ).c_str() ); } } } void command_findrace(Client *c, const Seperator *sep) { int arguments = sep->argnum; if (arguments == 0) { c->Message(Chat::White, "Command Syntax: #findrace [search criteria]"); return; } if (sep->IsNumber(1)) { int race_id = std::stoi(sep->arg[1]); std::string race_name = GetRaceIDName(race_id); if (race_id >= RACE_HUMAN_1 && race_id <= RACE_PEGASUS_732) { c->Message( Chat::White, fmt::format( "Race {}: {}", race_id, race_name ).c_str() ); } else { c->Message( Chat::White, fmt::format( "Race ID {} was not found.", race_id ).c_str() ); } } else { std::string search_criteria = str_tolower(sep->argplus[1]); int found_count = 0; for (int race_id = RACE_HUMAN_1; race_id <= RACE_PEGASUS_732; race_id++) { std::string race_name = GetRaceIDName(race_id); std::string race_name_lower = str_tolower(race_name); if (search_criteria.length() > 0 && race_name_lower.find(search_criteria) == std::string::npos) { continue; } c->Message( Chat::White, fmt::format( "Race {}: {}", race_id, race_name ).c_str() ); found_count++; if (found_count == 20) { break; } } if (found_count == 20) { c->Message(Chat::White, "20 Races found... max reached."); } else { auto race_message = ( found_count > 0 ? ( found_count == 1 ? "A Race was" : fmt::format("{} Races were", found_count) ) : "No Races were" ); c->Message( Chat::White, fmt::format( "{} found.", race_message ).c_str() ); } } } void command_findskill(Client *c, const Seperator *sep) { int arguments = sep->argnum; if (arguments == 0) { c->Message(Chat::White, "Command Syntax: #findskill [search criteria]"); return; } std::map skills = EQ::skills::GetSkillTypeMap(); if (sep->IsNumber(1)) { int skill_id = std::stoi(sep->arg[1]); if (skill_id >= EQ::skills::Skill1HBlunt && skill_id < EQ::skills::SkillCount) { for (auto skill : skills) { if (skill_id == skill.first) { c->Message( Chat::White, fmt::format( "Skill {}: {}", skill.first, skill.second ).c_str() ); break; } } } else { c->Message( Chat::White, fmt::format( "Skill ID {} was not found.", skill_id ).c_str() ); } } else { std::string search_criteria = str_tolower(sep->argplus[1]); if (!search_criteria.empty()) { int found_count = 0; for (auto skill : skills) { std::string skill_name_lower = str_tolower(skill.second); if (skill_name_lower.find(search_criteria) == std::string::npos) { continue; } c->Message( Chat::White, fmt::format( "Skill {}: {}", skill.first, skill.second ).c_str() ); found_count++; if (found_count == 20) { break; } } if (found_count == 20) { c->Message(Chat::White, "20 Skills were found, max reached."); } else { auto skill_message = ( found_count > 0 ? ( found_count == 1 ? "A Skill was" : fmt::format("{} Skills were", found_count) ) : "No Skills were" ); c->Message( Chat::White, fmt::format( "{} found.", skill_message ).c_str() ); } } } } void command_findspell(Client *c, const Seperator *sep) { if (SPDAT_RECORDS <= 0) { c->Message(Chat::White, "Spells not loaded"); return; } int arguments = sep->argnum; if (arguments == 0) { c->Message(Chat::White, "Command Syntax: #findspell [search criteria]"); return; } if (sep->IsNumber(1)) { int spell_id = std::stoi(sep->arg[1]); if (!IsValidSpell(spell_id)) { c->Message( Chat::White, fmt::format( "Spell ID {} was not found.", spell_id ).c_str() ); } else { c->Message( Chat::White, fmt::format( "Spell {}: {}", spell_id, spells[spell_id].name ).c_str() ); } } else { std::string search_criteria = str_tolower(sep->argplus[1]); int found_count = 0; for (int spell_id = 0; spell_id < SPDAT_RECORDS; spell_id++) { auto current_spell = spells[spell_id]; if (current_spell.name[0] != 0) { std::string spell_name = current_spell.name; std::string spell_name_lower = str_tolower(spell_name); if (search_criteria.length() > 0 && spell_name_lower.find(search_criteria) == std::string::npos) { continue; } c->Message( Chat::White, fmt::format( "Spell {}: {}", spell_id, spell_name ).c_str() ); found_count++; if (found_count == 20) { break; } } } if (found_count == 20) { c->Message(Chat::White, "20 Spells found... max reached."); } else { auto spell_message = ( found_count > 0 ? ( found_count == 1 ? "A Spell was" : fmt::format("{} Spells were", found_count) ) : "No Spells were" ); c->Message( Chat::White, fmt::format( "{} found.", spell_message ).c_str() ); } } } inline bool CastRestrictedSpell(int spellid) { switch (spellid) { case SPELL_TOUCH_OF_VINITRAS: case SPELL_DESPERATE_HOPE: case SPELL_CHARM: case SPELL_METAMORPHOSIS65: case SPELL_JT_BUFF: case SPELL_CAN_O_WHOOP_ASS: case SPELL_PHOENIX_CHARM: case SPELL_CAZIC_TOUCH: case SPELL_AVATAR_KNOCKBACK: case SPELL_SHAPECHANGE65: case SPELL_SUNSET_HOME1218: case SPELL_SUNSET_HOME819: case SPELL_SHAPECHANGE75: case SPELL_SHAPECHANGE80: case SPELL_SHAPECHANGE85: case SPELL_SHAPECHANGE90: case SPELL_SHAPECHANGE95: case SPELL_SHAPECHANGE100: case SPELL_SHAPECHANGE25: case SPELL_SHAPECHANGE30: case SPELL_SHAPECHANGE35: case SPELL_SHAPECHANGE40: case SPELL_SHAPECHANGE45: case SPELL_SHAPECHANGE50: case SPELL_NPC_AEGOLISM: case SPELL_SHAPECHANGE55: case SPELL_SHAPECHANGE60: case SPELL_COMMAND_OF_DRUZZIL: case SPELL_SHAPECHANGE70: return true; default: return false; } } void command_castspell(Client *c, const Seperator *sep) { if (SPDAT_RECORDS <= 0) { c->Message(Chat::White, "Spells not loaded."); return; } Mob *target = c; if(c->GetTarget()) { target = c->GetTarget(); } if (!sep->IsNumber(1)) { c->Message(Chat::White, "Usage: #castspell [Spell ID] [Instant (0 = False, 1 = True, Default is 1 if Unused)]"); } else { uint16 spell_id = std::stoul(sep->arg[1]); if (CastRestrictedSpell(spell_id) && c->Admin() < commandCastSpecials) { c->Message(Chat::Red, "Unable to cast spell."); } else if (spell_id >= SPDAT_RECORDS) { c->Message(Chat::White, "Invalid Spell ID."); } else { bool instant_cast = (c->Admin() >= commandInstacast ? true : false); if (instant_cast && sep->IsNumber(2)) { instant_cast = std::stoi(sep->arg[2]) ? true : false; c->Message(Chat::White, fmt::format("{}", std::stoi(sep->arg[2])).c_str()); } if (c->Admin() >= commandInstacast && instant_cast) { c->SpellFinished(spell_id, target, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty); } else { c->CastSpell(spell_id, target->GetID(), EQ::spells::CastingSlot::Item, spells[spell_id].cast_time); } if (c != target) { c->Message( Chat::White, fmt::format( "Cast {} ({}) on {}{}.", GetSpellName(spell_id), spell_id, target->GetCleanName(), instant_cast ? " instantly" : "" ).c_str() ); } else { c->Message( Chat::White, fmt::format( "Cast {} ({}) on yourself{}.", GetSpellName(spell_id), spell_id, instant_cast ? " instantly" : "" ).c_str() ); } } } } void command_setlanguage(Client *c, const Seperator *sep) { Client* target = c; if (c->GetTarget() && c->GetTarget()->IsClient()) { target = c->GetTarget()->CastToClient(); } auto language_id = sep->IsNumber(1) ? std::stoi(sep->arg[1]) : -1; auto language_value = sep->IsNumber(2) ? std::stoi(sep->arg[2]) : -1; if (!strcasecmp(sep->arg[1], "list" )) { for (int language = LANG_COMMON_TONGUE; language <= LANG_UNKNOWN; language++) { c->Message( Chat::White, fmt::format( "Language {}: {}", language, EQ::constants::GetLanguageName(language) ).c_str() ); } } else if ( language_id < LANG_COMMON_TONGUE || language_id > LANG_UNKNOWN || language_value < 0 || language_value > 100 ) { c->Message(Chat::White, "Usage: #setlanguage [Language ID] [Language Value]"); c->Message(Chat::White, "Usage: #setlanguage [List]"); c->Message(Chat::White, "Language ID = 0 to 27", LANG_UNKNOWN); c->Message(Chat::White, "Language Value = 0 to 100", HIGHEST_CAN_SET_SKILL); } else { LogInfo( "Set language request from [{}], Target: [{}] Language ID: [{}] Language Value: [{}]", c->GetCleanName(), target->GetCleanName(), language_id, language_value ); target->SetLanguageSkill(language_id, language_value); if (c != target) { c->Message( Chat::White, fmt::format( "Set {} ({}) to {} for {}.", EQ::constants::GetLanguageName(language_id), language_id, language_value, target->GetCleanName() ).c_str() ); } } } void command_setskill(Client* c, const Seperator* sep) { Client* target = c; if (c->GetTarget() && c->GetTarget()->IsClient()) { target = c->GetTarget()->CastToClient(); } auto skill_id = sep->IsNumber(1) ? std::stoi(sep->arg[1]) : -1; auto skill_value = sep->IsNumber(2) ? std::stoi(sep->arg[2]) : -1; if ( skill_id < 0 || skill_id > EQ::skills::HIGHEST_SKILL || skill_value < 0 || skill_value > HIGHEST_CAN_SET_SKILL ) { c->Message(Chat::White, "Usage: #setskill [Skill ID] [Skill Value]"); c->Message(Chat::White, fmt::format("Skill ID = 0 to {}", EQ::skills::HIGHEST_SKILL).c_str()); c->Message(Chat::White, fmt::format("Skill Value = 0 to {}", HIGHEST_CAN_SET_SKILL).c_str()); } else { LogInfo( "Set skill request from [{}], Target: [{}] Skill ID: [{}] Skill Value: [{}]", c->GetCleanName(), target->GetCleanName(), skill_id, skill_value ); if ( skill_id >= EQ::skills::Skill1HBlunt && skill_id <= EQ::skills::HIGHEST_SKILL ) { target->SetSkill( (EQ::skills::SkillType)skill_id, skill_value ); if (c != target) { c->Message( Chat::White, fmt::format( "Set {} ({}) to {} for {}.", EQ::skills::GetSkillName((EQ::skills::SkillType)skill_id), skill_id, skill_value, target->GetCleanName() ).c_str() ); } } } } void command_setskillall(Client *c, const Seperator *sep) { if (c->GetTarget() == 0) c->Message(Chat::White, "Error: #setallskill: No target."); else if (!c->GetTarget()->IsClient()) c->Message(Chat::White, "Error: #setskill: Target must be a client."); else if (!sep->IsNumber(1) || atoi(sep->arg[1]) < 0 || atoi(sep->arg[1]) > HIGHEST_CAN_SET_SKILL) { c->Message(Chat::White, "Usage: #setskillall value "); c->Message(Chat::White, " value = 0 to %d", HIGHEST_CAN_SET_SKILL); } else { if (c->Admin() >= commandSetSkillsOther || c->GetTarget()==c || c->GetTarget()==0) { LogInfo("Set ALL skill request from [{}], target:[{}]", c->GetName(), c->GetTarget()->GetName()); uint16 level = atoi(sep->arg[1]); for (EQ::skills::SkillType skill_num = EQ::skills::Skill1HBlunt; skill_num <= EQ::skills::HIGHEST_SKILL; skill_num = (EQ::skills::SkillType)(skill_num + 1)) { c->GetTarget()->CastToClient()->SetSkill(skill_num, level); } } else c->Message(Chat::White, "Error: Your status is not high enough to set anothers skills"); } } void command_race(Client *c, const Seperator *sep) { Mob *target = c->CastToMob(); if (sep->IsNumber(1)) { auto race = atoi(sep->arg[1]); if ((race >= 0 && race <= RuleI(NPC, MaxRaceID)) || (race >= 2253 && race <= 2259)) { if ((c->GetTarget()) && c->Admin() >= commandRaceOthers) { target = c->GetTarget(); } target->SendIllusionPacket(race); } else { c->Message(Chat::White, fmt::format("Usage: #race [0-{}, 2253-2259] (0 for back to normal)", RuleI(NPC, MaxRaceID)).c_str()); } } else { c->Message(Chat::White, fmt::format("Usage: #race [0-{}, 2253-2259] (0 for back to normal)", RuleI(NPC, MaxRaceID)).c_str()); } } void command_gearup(Client *c, const Seperator *sep) { std::string tool_table_name = "tool_gearup_armor_sets"; if (!database.DoesTableExist(tool_table_name)) { c->Message( Chat::Yellow, fmt::format( "Table [{}] does not exist. Downloading from Github and installing...", tool_table_name ).c_str() ); // http get request httplib::Client cli("https://raw.githubusercontent.com"); cli.set_connection_timeout(0, 15000000); // 15 sec cli.set_read_timeout(15, 0); // 15 seconds cli.set_write_timeout(15, 0); // 15 seconds int sourced_queries = 0; std::string url = "/EQEmu/Server/master/utils/sql/git/optional/2020_07_20_tool_gearup_armor_sets.sql"; if (auto res = cli.Get(url.c_str())) { if (res->status == 200) { for (auto &s: SplitString(res->body, ';')) { if (!trim(s).empty()) { auto results = database.QueryDatabase(s); if (!results.ErrorMessage().empty()) { c->Message( Chat::Yellow, fmt::format( "Error sourcing SQL [{}]", results.ErrorMessage() ).c_str() ); return; } sourced_queries++; } } } } else { c->Message( Chat::Yellow, fmt::format( "Error retrieving URL [{}]", url ).c_str() ); } c->Message( Chat::Yellow, fmt::format( "Table [{}] installed. Sourced [{}] queries", tool_table_name, sourced_queries ).c_str() ); } std::string expansion_arg = sep->arg[1]; std::string expansion_filter; if (expansion_arg.length() > 0) { expansion_filter = fmt::format("and `expansion` = {}", expansion_arg); } auto results = database.QueryDatabase( fmt::format( SQL ( select item_id, slot from {} where `class` = {} and `level` = {} {} order by score desc, expansion desc ), tool_table_name, c->GetClass(), c->GetLevel(), expansion_filter ) ); int items_equipped = 0; int items_already_have = 0; std::set equipped; for (auto row = results.begin(); row != results.end(); ++row) { int item_id = atoi(row[0]); int slot_id = atoi(row[1]); if (equipped.find(slot_id) != equipped.end()) { if (slot_id == EQ::invslot::slotEar1) { slot_id = EQ::invslot::slotEar2; } if (slot_id == EQ::invslot::slotFinger1) { slot_id = EQ::invslot::slotFinger2; } if (slot_id == EQ::invslot::slotWrist1) { slot_id = EQ::invslot::slotWrist2; } } if (equipped.find(slot_id) == equipped.end()) { const EQ::ItemData *item = database.GetItem(item_id); bool has_item = (c->GetInv().HasItem(item_id, 1, invWhereWorn) != INVALID_INDEX); bool can_wear_item = !c->CheckLoreConflict(item) && !has_item; if (!can_wear_item) { items_already_have++; } if (c->CastToMob()->CanClassEquipItem(item_id) && can_wear_item) { equipped.insert(slot_id); c->SummonItem( item_id, 0, 0, 0, 0, 0, 0, 0, 0, slot_id ); items_equipped++; } } } c->Message( Chat::White, fmt::format( "Equipped items [{}] already had [{}] items equipped", items_equipped, items_already_have ).c_str() ); if (expansion_arg.empty()) { results = database.QueryDatabase( fmt::format( SQL ( select expansion from {} where class = {} and level = {} group by expansion; ), tool_table_name, c->GetClass(), c->GetLevel() ) ); c->Message(Chat::White, "Choose armor from a specific era"); std::string message; for (auto row = results.begin(); row != results.end(); ++row) { int expansion = atoi(row[0]); message += "[" + EQ::SayLinkEngine::GenerateQuestSaylink( fmt::format("#gearup {}", expansion), false, Expansion::ExpansionName[expansion] ) + "] "; if (message.length() > 2000) { c->Message(Chat::White, message.c_str()); message = ""; } } if (message.length() > 0) { c->Message(Chat::White, message.c_str()); } } } void command_gender(Client *c, const Seperator *sep) { Mob *t=c->CastToMob(); if (sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 500) { if ((c->GetTarget()) && c->Admin() >= commandGenderOthers) t=c->GetTarget(); t->SendIllusionPacket(t->GetRace(), atoi(sep->arg[1])); } else c->Message(Chat::White, "Usage: #gender [0/1/2]"); } void command_makepet(Client *c, const Seperator *sep) { if (sep->arg[1][0] == '\0') c->Message(Chat::White, "Usage: #makepet pet_type_name (will not survive across zones)"); else c->MakePet(0, sep->arg[1]); } void command_level(Client *c, const Seperator *sep) { uint16 level = atoi(sep->arg[1]); if ((level <= 0) || ((level > RuleI(Character, MaxLevel)) && (c->Admin() < commandLevelAboveCap))) { c->Message(Chat::White, "Error: #Level: Invalid Level"); } else if (c->Admin() < RuleI(GM, MinStatusToLevelTarget)) { c->SetLevel(level, true); #ifdef BOTS if(RuleB(Bots, BotLevelsWithOwner)) Bot::LevelBotWithClient(c, level, true); #endif } else if (!c->GetTarget()) { c->Message(Chat::White, "Error: #Level: No target"); } else { if (!c->GetTarget()->IsNPC() && ((c->Admin() < commandLevelNPCAboveCap) && (level > RuleI(Character, MaxLevel)))) { c->Message(Chat::White, "Error: #Level: Invalid Level"); } else { c->GetTarget()->SetLevel(level, true); if(c->GetTarget()->IsClient()) { c->GetTarget()->CastToClient()->SendLevelAppearance(); #ifdef BOTS if(RuleB(Bots, BotLevelsWithOwner)) Bot::LevelBotWithClient(c->GetTarget()->CastToClient(), level, true); #endif } } } } void command_spawneditmass(Client *c, const Seperator *sep) { std::string query = fmt::format( SQL( SELECT npc_types.id, npc_types.name, spawn2.respawntime, spawn2.id FROM npc_types JOIN spawnentry ON spawnentry.npcID = npc_types.id JOIN spawn2 ON spawn2.spawngroupID = spawnentry.spawngroupID WHERE spawn2.zone = '{0}' and spawn2.version = {1} GROUP BY npc_types.id ), zone->GetShortName(), zone->GetInstanceVersion() ); std::string status = "(Searching)"; if (strcasecmp(sep->arg[4], "apply") == 0) { status = "(Applying)"; } std::string search_value; std::string edit_option; std::string edit_value; std::string apply_set; if (sep->arg[1]) { search_value = sep->arg[1]; } if (sep->arg[2]) { edit_option = sep->arg[2]; } if (sep->arg[3]) { edit_value = sep->arg[3]; } if (sep->arg[4]) { apply_set = sep->arg[4]; } if (!edit_option.empty() && edit_value.empty()) { c->Message(Chat::Yellow, "Please specify an edit option value | #npceditmass