/* 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 "data_bucket.h" #include "command.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" extern QueryServ* QServ; extern WorldServer worldserver; extern TaskManager *taskmanager; 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", "[spellid] - 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("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("crashtest", "- Crash the zoneserver", 255, command_crashtest) || 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("doanim", "[animnum] [type] - Send an EmoteAnim for you or your target", 50, command_doanim) || 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("enablerecipe", "[recipe_id] - Enables a recipe using the recipe id.", 80, command_enablerecipe) || 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("findaliases", "[search term]- Searches for available command aliases, by alias or command", 0, command_findaliases) || command_add("findnpctype", "[search criteria] - Search database NPC types", 100, command_findnpctype) || command_add("findspell", "[searchstring] - Search for a spell", 50, command_findspell) || 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("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("listnpcs", "[name/range] - Search NPCs", 20, command_listnpcs) || 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("logtest", "Performs log performance testing.", 250, command_logtest) || 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("mysqltest", "Akkadius MySQL Bench Test", 250, command_mysqltest) || 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("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", "LEADER|GROUPLEADER|SELECTED|ALL - Sets your raid loot settings 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("repopclose", "[distance in units] Repops only NPC's nearby for fast development purposes", 100, command_repopclose) || 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("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", "[value] - Set your or your player target's available AA points", 100, command_setaapts) || command_add("setaaxp", "[value] - Set your or your player target's AA experience", 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", "[value] - 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("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("spoff", "- Sends OP_ManaChange", 80, command_spoff) || command_add("spon", "- Sends OP_MemorizeSpell", 80, command_spon) || 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("test", "Test command", 200, command_test) || 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 ideal statical 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", "[npctype id] - Show info about an npctype", 100, command_viewnpctype) || command_add("viewpetition", "[petition number] - View a petition", 20, command_viewpetition) || 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_num] [pause] [wp_num] [-h] - Add/delete a waypoint to/from a wandering grid", 170, command_wp) || command_add("wpadd", "[pause] [-h] - Add your current location as a waypoint to your NPC target's AI path", 170, command_wpadd) || command_add("wpinfo", "- Show waypoint info about your NPC target", 170, command_wpinfo) || 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] - Set/query lock flag for zoneservers", 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_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); } 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); database.QueryDatabase(query); } void command_serversidename(Client *c, const Seperator *sep) { if(c->GetTarget()) c->Message(Chat::White, c->GetTarget()->GetName()); else c->Message(Chat::White, "Error: no target"); } 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 = database.GetZoneID(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 = database.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 = database.GetZoneID(sep->arg[1]); destzone = database.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], (char*) 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_listnpcs(Client *c, const Seperator *sep) { c->Message(Chat::White, "Deprecated, use the #list command (#list npcs )"); } 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()); c->Message( 0, "| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f", EQEmu::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", EQEmu::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", EQEmu::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", EQEmu::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", EQEmu::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 npc (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); database.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()) if(c->BehindMob(c->GetTarget(), c->GetX(), c->GetY())) c->Message(Chat::White, "You are behind mob %s, it is looking to %d", c->GetTarget()->GetName(), c->GetTarget()->GetHeading()); else c->Message(Chat::White, "You are NOT behind mob %s, it is looking to %d", c->GetTarget()->GetName(), c->GetTarget()->GetHeading()); else c->Message(Chat::White, "I Need a target!"); } void command_npcstats(Client *c, const Seperator *sep) { if (c->GetTarget() == 0) c->Message(Chat::White, "ERROR: No target!"); else if (!c->GetTarget()->IsNPC()) c->Message(Chat::White, "ERROR: Target is not a NPC!"); else { auto target_npc = c->GetTarget()->CastToNPC(); c->Message(Chat::White, "# NPC Stats"); c->Message(Chat::White, "- Name: %s NpcID: %u", target_npc->GetName(), target_npc->GetNPCTypeID()); c->Message(Chat::White, "- Race: %i Level: %i Class: %i Material: %i", target_npc->GetRace(), target_npc->GetLevel(), target_npc->GetClass(), target_npc->GetTexture()); c->Message(Chat::White, "- Current HP: %i Max HP: %i", target_npc->GetHP(), target_npc->GetMaxHP()); //c->Message(Chat::White, "Weapon Item Number: %s", target_npc->GetWeapNo()); c->Message(Chat::White, "- Gender: %i Size: %f Bodytype: %d", target_npc->GetGender(), target_npc->GetSize(), target_npc->GetBodyType()); c->Message(Chat::White, "- Runspeed: %.3f Walkspeed: %.3f", static_cast(0.025f * target_npc->GetRunspeed()), static_cast(0.025f * target_npc->GetWalkspeed())); c->Message(Chat::White, "- Spawn Group: %i Grid: %i", target_npc->GetSpawnGroupId(), target_npc->GetGrid()); if (target_npc->proximity) { c->Message(Chat::White, "- Proximity: Enabled"); c->Message(Chat::White, "-- Cur_X: %1.3f, Cur_Y: %1.3f, Cur_Z: %1.3f", target_npc->GetX(), target_npc->GetY(), target_npc->GetZ()); c->Message(Chat::White, "-- Min_X: %1.3f(%1.3f), Max_X: %1.3f(%1.3f), X_Range: %1.3f", target_npc->proximity->min_x, (target_npc->proximity->min_x - target_npc->GetX()), target_npc->proximity->max_x, (target_npc->proximity->max_x - target_npc->GetX()), (target_npc->proximity->max_x - target_npc->proximity->min_x)); c->Message(Chat::White, "-- Min_Y: %1.3f(%1.3f), Max_Y: %1.3f(%1.3f), Y_Range: %1.3f", target_npc->proximity->min_y, (target_npc->proximity->min_y - target_npc->GetY()), target_npc->proximity->max_y, (target_npc->proximity->max_y - target_npc->GetY()), (target_npc->proximity->max_y - target_npc->proximity->min_y)); c->Message(Chat::White, "-- Min_Z: %1.3f(%1.3f), Max_Z: %1.3f(%1.3f), Z_Range: %1.3f", target_npc->proximity->min_z, (target_npc->proximity->min_z - target_npc->GetZ()), target_npc->proximity->max_z, (target_npc->proximity->max_z - target_npc->GetZ()), (target_npc->proximity->max_z - target_npc->proximity->min_z)); c->Message(Chat::White, "-- Say: %s", (target_npc->proximity->say ? "Enabled" : "Disabled")); } else { c->Message(Chat::White, "-Proximity: Disabled"); } c->Message(Chat::White, ""); c->Message(Chat::White, "EmoteID: %i", target_npc->GetEmoteID()); target_npc->QueryLoot(c); } } 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() && !sep->IsNumber(1) && sep->arg[1] != 0 && sep->IsNumber(2)) { Mob* spelltar = entity_list.GetMob(sep->arg[1]); if (spelltar) c->GetTarget()->CastSpell(atoi(sep->arg[2]), spelltar->GetID()); else c->Message(Chat::White, "Error: %s not found", sep->arg[1]); } else if (c->GetTarget() && c->GetTarget()->IsNPC() && sep->IsNumber(1) && sep->IsNumber(2) ) { Mob* spelltar = entity_list.GetMob(atoi(sep->arg[1])); if (spelltar) c->GetTarget()->CastSpell(atoi(sep->arg[2]), spelltar->GetID()); else c->Message(Chat::White, "Error: target ID %i not found", atoi(sep->arg[1])); } else c->Message(Chat::White, "Usage: (needs NPC targeted) #npccast targetname/entityid spellid"); } void command_zstats(Client *c, const Seperator *sep) { c->Message(Chat::White, "Zone Header Data:"); c->Message(Chat::White, "Sky Type: %i", zone->newzone_data.sky); c->Message(Chat::White, "Fog Colour: Red: %i; Blue: %i; Green %i", zone->newzone_data.fog_red[0], zone->newzone_data.fog_green[0], zone->newzone_data.fog_blue[0]); c->Message(Chat::White, "Safe Coords: %f, %f, %f", zone->newzone_data.safe_x, zone->newzone_data.safe_y, zone->newzone_data.safe_z); c->Message(Chat::White, "Underworld Coords: %f", zone->newzone_data.underworld); c->Message(Chat::White, "Clip Plane: %f - %f", zone->newzone_data.minclip, zone->newzone_data.maxclip); } 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(database.GetZoneID(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_spon(Client *c, const Seperator *sep) { c->MemorizeSpell(0, SPELLBAR_UNLOCK, memSpellSpellbar); } void command_spoff(Client *c, const Seperator *sep) { auto outapp = new EQApplicationPacket(OP_ManaChange, 0); outapp->priority = 5; c->QueuePacket(outapp); safe_delete(outapp); } void command_itemtest(Client *c, const Seperator *sep) { char chBuffer[8192] = {0}; //Using this to determine new item layout FILE* f = nullptr; if (!(f = fopen("c:\\EQEMUcvs\\ItemDump.txt", "rb"))) { c->Message(Chat::Red, "Error: Could not open c:\\EQEMUcvs\\ItemDump.txt"); return; } fread(chBuffer, sizeof(chBuffer), sizeof(char), f); fclose(f); auto outapp = new EQApplicationPacket(OP_ItemLinkResponse, strlen(chBuffer) + 5); memcpy(&outapp->pBuffer[4], chBuffer, strlen(chBuffer)); c->QueuePacket(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=atoi(sep->arg[1]))>0) && ((interval=atoi(sep->arg[2]))>0)) { worldserver.SendEmoteMessage(0,0,15,":SYSTEM MSG:World coming down in %i minutes, everyone log out before this time.", (time / 60 )); c->Message(Chat::White, "Sending shutdown packet now, World will shutdown in: %i minutes with an interval of: %i seconds", (time / 60), interval); 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") == 0){ worldserver.SendEmoteMessage(0,0,15,":SYSTEM MSG:World coming down, everyone log out now."); c->Message(Chat::White, "Sending shutdown packet"); auto pack = new ServerPacket; pack->opcode = ServerOP_ShutdownAll; pack->size=0; worldserver.SendPacket(pack); safe_delete(pack); } else if(strcasecmp(sep->arg[1], "disable") == 0){ c->Message(Chat::White, "Shutdown prevented, next time I may not be so forgiving..."); 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 sends warning every [interval] seconds."); } } else c->Message(Chat::White, "Error: World server 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) { if (strcasecmp("max", sep->arg[1]) == 0) { c->Message(Chat::White, "Highest grid ID in this zone: %d", database.GetHighestGrid(zone->GetZoneID())); } else if (strcasecmp("add", sep->arg[1]) == 0) { database.ModifyGrid(c, false, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), zone->GetZoneID()); } else if (strcasecmp("show", sep->arg[1]) == 0) { Mob *target = c->GetTarget(); if (!target || !target->IsNPC()) { c->Message(Chat::White, "You need a NPC target!"); return; } std::string query = StringFormat( "SELECT `x`, `y`, `z`, `heading`, `number`, `pause` " "FROM `grid_entries` " "WHERE `zoneid` = %u and `gridid` = %i " "ORDER BY `number` ", zone->GetZoneID(), target->CastToNPC()->GetGrid() ); auto results = database.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 */ auto &mob_list = entity_list.GetMobList(); for (auto itr = mob_list.begin(); itr != mob_list.end(); ++itr) { Mob *mob = itr->second; if (mob->IsNPC() && mob->GetRace() == 2254) { mob->Depop(); } } /** * Spawn grid nodes */ for (auto row = results.begin(); row != results.end(); ++row) { auto node_position = glm::vec4(atof(row[0]), atof(row[1]), atof(row[2]), atof(row[3])); NPC *npc = NPC::SpawnGridNodeNPC( target->GetCleanName(), node_position, static_cast(target->CastToNPC()->GetGrid()), static_cast(atoi(row[4])), static_cast(atoi(row[5])) ); npc->SetFlyMode(GravityBehavior::Flying); npc->GMMove(node_position.x, node_position.y, node_position.z, node_position.w); } } else if (strcasecmp("delete", sep->arg[1]) == 0) { database.ModifyGrid(c, true, atoi(sep->arg[2]), 0, 0, zone->GetZoneID()); } else { c->Message(Chat::White, "Usage: #grid add/delete grid_num wandertype pausetype"); c->Message(Chat::White, "Usage: #grid max - displays the highest grid ID used in this zone (for add)"); } } void command_wp(Client *c, const Seperator *sep) { int wp = atoi(sep->arg[4]); if (strcasecmp("add", sep->arg[1]) == 0) { if (wp == 0) //default to highest if it's left blank, or we enter 0 wp = database.GetHighestWaypoint(zone->GetZoneID(), atoi(sep->arg[2])) + 1; if (strcasecmp("-h", sep->arg[5]) == 0) { database.AddWP(c, atoi(sep->arg[2]),wp, c->GetPosition(), atoi(sep->arg[3]),zone->GetZoneID()); } else { auto position = c->GetPosition(); position.w = -1; database.AddWP(c, atoi(sep->arg[2]),wp, position, atoi(sep->arg[3]),zone->GetZoneID()); } } else if (strcasecmp("delete", sep->arg[1]) == 0) database.DeleteWaypoint(c, atoi(sep->arg[2]),wp,zone->GetZoneID()); else c->Message(Chat::White,"Usage: #wp add/delete grid_num pause wp_num [-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->GetRace(); 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) { Mob *t; t = c->GetTarget() ? c->GetTarget() : c; if(t->IsClient()) t->CastToClient()->SetMana(t->CastToClient()->CalcMaxMana()); else t->SetMana(t->CalcMaxMana()); } void command_flymode(Client *c, const Seperator *sep) { Mob *t = c; if (strlen(sep->arg[1]) == 1 && !(sep->arg[1][0] == '0' || sep->arg[1][0] == '1' || sep->arg[1][0] == '2' || sep->arg[1][0] == '3' || sep->arg[1][0] == '4' || sep->arg[1][0] == '5')) c->Message(Chat::White, "#flymode [0/1/2/3/4/5]"); else { 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()); } } } void command_showskills(Client *c, const Seperator *sep) { Client *t=c; if(c->GetTarget() && c->GetTarget()->IsClient()) t=c->GetTarget()->CastToClient(); c->Message(Chat::White, "Skills for %s", t->GetName()); for (EQEmu::skills::SkillType i = EQEmu::skills::Skill1HBlunt; i <= EQEmu::skills::HIGHEST_SKILL; i = (EQEmu::skills::SkillType)(i + 1)) c->Message(Chat::White, "Skill [%d] is at [%d] - %u", i, t->GetSkill(i), t->GetRawSkill(i)); } void command_findspell(Client *c, const Seperator *sep) { if (sep->arg[1][0] == 0) c->Message(Chat::White, "Usage: #FindSpell [spellname]"); else if (SPDAT_RECORDS <= 0) c->Message(Chat::White, "Spells not loaded"); else if (Seperator::IsNumber(sep->argplus[1])) { int spellid = atoi(sep->argplus[1]); if (spellid <= 0 || spellid >= SPDAT_RECORDS) { c->Message(Chat::White, "Error: Number out of range"); } else { c->Message(Chat::White, " %i: %s", spellid, spells[spellid].name); } } else { int count=0; //int iSearchLen = strlen(sep->argplus[1])+1; char sName[64]; char sCriteria[65]; strn0cpy(sCriteria, sep->argplus[1], 64); strupr(sCriteria); for (int i=0; iMessage(Chat::White, " %i: %s", i, spells[i].name); count++; } else if (count > 20) break; } } if (count > 20) c->Message(Chat::White, "20 spells found... max reached."); else c->Message(Chat::White, "%i spells found.", count); } } void command_castspell(Client *c, const Seperator *sep) { if (!sep->IsNumber(1)) c->Message(Chat::White, "Usage: #CastSpell spellid"); else { uint16 spellid = atoi(sep->arg[1]); /* Spell restrictions. */ if (((spellid == 2859) || (spellid == 841) || (spellid == 300) || (spellid == 2314) || (spellid == 3716) || (spellid == 911) || (spellid == 3014) || (spellid == 982) || (spellid == 905) || (spellid == 2079) || (spellid == 1218) || (spellid == 819) || ((spellid >= 780) && (spellid <= 785)) || ((spellid >= 1200) && (spellid <= 1205)) || ((spellid >= 1342) && (spellid <= 1348)) || (spellid == 1923) || (spellid == 1924) || (spellid == 3355)) && c->Admin() < commandCastSpecials) c->Message(Chat::Red, "Unable to cast spell."); else if (spellid >= SPDAT_RECORDS) c->Message(Chat::White, "Error: #CastSpell: Argument out of range"); else if (c->GetTarget() == 0) if(c->Admin() >= commandInstacast) c->SpellFinished(spellid, 0, EQEmu::spells::CastingSlot::Item, 0, -1, spells[spellid].ResistDiff); else c->CastSpell(spellid, 0, EQEmu::spells::CastingSlot::Item, 0); else if(c->Admin() >= commandInstacast) c->SpellFinished(spellid, c->GetTarget(), EQEmu::spells::CastingSlot::Item, 0, -1, spells[spellid].ResistDiff); else c->CastSpell(spellid, c->GetTarget()->GetID(), EQEmu::spells::CastingSlot::Item, 0); } } void command_setlanguage(Client *c, const Seperator *sep) { if (strcasecmp(sep->arg[1], "list" ) == 0 ) { c->Message(Chat::White, "Languages:"); c->Message(Chat::White, "(0) Common Tongue"); c->Message(Chat::White, "(1) Barbarian"); c->Message(Chat::White, "(2) Erudian"); c->Message(Chat::White, "(3) Elvish"); c->Message(Chat::White, "(4) Dark Elvish"); c->Message(Chat::White, "(5) Dwarvish"); c->Message(Chat::White, "(6) Troll"); c->Message(Chat::White, "(7) Ogre"); c->Message(Chat::White, "(8) Gnomish"); c->Message(Chat::White, "(9) Halfling"); c->Message(Chat::White, "(10) Thieves Cant"); c->Message(Chat::White, "(11) Old Erudian"); c->Message(Chat::White, "(12) Elder Elvish"); c->Message(Chat::White, "(13) Froglok"); c->Message(Chat::White, "(14) Goblin"); c->Message(Chat::White, "(15) Gnoll"); c->Message(Chat::White, "(16) Combine Tongue"); c->Message(Chat::White, "(17) Elder Teir`Dal"); c->Message(Chat::White, "(18) Lizardman"); c->Message(Chat::White, "(19) Orcish"); c->Message(Chat::White, "(20) Faerie"); c->Message(Chat::White, "(21) Dragon"); c->Message(Chat::White, "(22) Elder Dragon"); c->Message(Chat::White, "(23) Dark Speech"); c->Message(Chat::White, "(24) Vah Shir"); c->Message(Chat::White, "(25) Alaran"); c->Message(Chat::White, "(26) Hadal"); c->Message(Chat::White, "(27) Unknown1"); } else if( c->GetTarget() == 0 ) { c->Message(Chat::White, "Error: #setlanguage: No target."); } else if( !c->GetTarget()->IsClient() ) { c->Message(Chat::White, "Error: Target must be a player."); } else if ( !sep->IsNumber(1) || atoi(sep->arg[1]) < 0 || atoi(sep->arg[1]) > 27 || !sep->IsNumber(2) || atoi(sep->arg[2]) < 0 || atoi(sep->arg[2]) > 100 ) { c->Message(Chat::White, "Usage: #setlanguage [language ID] [value] (0-27, 0-100)"); c->Message(Chat::White, "Try #setlanguage list for a list of language IDs"); } else { LogInfo("Set language request from [{}], target:[{}] lang_id:[{}] value:[{}]", c->GetName(), c->GetTarget()->GetName(), atoi(sep->arg[1]), atoi(sep->arg[2]) ); uint8 langid = (uint8)atoi(sep->arg[1]); uint8 value = (uint8)atoi(sep->arg[2]); c->GetTarget()->CastToClient()->SetLanguageSkill( langid, value ); } } void command_setskill(Client *c, const Seperator *sep) { if (c->GetTarget() == nullptr) { c->Message(Chat::White, "Error: #setskill: 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]) > EQEmu::skills::HIGHEST_SKILL || !sep->IsNumber(2) || atoi(sep->arg[2]) < 0 || atoi(sep->arg[2]) > HIGHEST_CAN_SET_SKILL ) { c->Message(Chat::White, "Usage: #setskill skill x "); c->Message(Chat::White, " skill = 0 to %d", EQEmu::skills::HIGHEST_SKILL); c->Message(Chat::White, " x = 0 to %d", HIGHEST_CAN_SET_SKILL); } else { LogInfo("Set skill request from [{}], target:[{}] skill_id:[{}] value:[{}]", c->GetName(), c->GetTarget()->GetName(), atoi(sep->arg[1]), atoi(sep->arg[2]) ); int skill_num = atoi(sep->arg[1]); uint16 skill_value = atoi(sep->arg[2]); if (skill_num <= EQEmu::skills::HIGHEST_SKILL) c->GetTarget()->CastToClient()->SetSkill((EQEmu::skills::SkillType)skill_num, skill_value); } } 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 (EQEmu::skills::SkillType skill_num = EQEmu::skills::Skill1HBlunt; skill_num <= EQEmu::skills::HIGHEST_SKILL; skill_num = (EQEmu::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 <= 732) || (race >= 2253 && race <= 2259)) { if ((c->GetTarget()) && c->Admin() >= commandRaceOthers) { target = c->GetTarget(); } target->SendIllusionPacket(race); } else { c->Message(Chat::White, "Usage: #race [0-732, 2253-2259] (0 for back to normal)"); } } else { c->Message(Chat::White, "Usage: #race [0-732, 2253-2259] (0 for back to normal)"); } } 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