From 0550fcfd3f4757fd7f73d56b9743525f97e5c2da Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 14 Nov 2021 22:48:47 -0600 Subject: [PATCH] [GM Commands] Split GM Commands Into Separate Files (#1766) * Split GM commands into their own files * Code cleanup --- .gitignore | 2 + common/file_util.cpp | 7 +- common/file_util.h | 1 + common/spdat.cpp | 56 +- common/spdat.h | 533 +- utils/gm_commands/main.go | 183 + zone/CMakeLists.txt | 849 +- zone/command.cpp | 15194 +--------------- zone/command.h | 63 +- zone/gm_commands/acceptrules.cpp | 11 + zone/gm_commands/advnpcspawn.cpp | 534 + zone/gm_commands/aggro.cpp | 21 + zone/gm_commands/aggrozone.cpp | 19 + zone/gm_commands/ai.cpp | 139 + zone/gm_commands/appearance.cpp | 26 + zone/gm_commands/attack.cpp | 18 + zone/gm_commands/augmentitem.cpp | 18 + zone/gm_commands/ban.cpp | 72 + zone/gm_commands/beard.cpp | 37 + zone/gm_commands/beardcolor.cpp | 37 + zone/gm_commands/bestz.cpp | 89 + zone/gm_commands/bind.cpp | 17 + zone/gm_commands/camerashake.cpp | 24 + zone/gm_commands/castspell.cpp | 77 + zone/gm_commands/chat.cpp | 15 + zone/gm_commands/checklos.cpp | 20 + zone/gm_commands/copycharacter.cpp | 34 + zone/gm_commands/corpse.cpp | 168 + zone/gm_commands/corpsefix.cpp | 8 + zone/gm_commands/cvs.cpp | 17 + zone/gm_commands/damage.cpp | 21 + zone/gm_commands/databuckets.cpp | 92 + zone/gm_commands/date.cpp | 29 + zone/gm_commands/dbspawn2.cpp | 31 + zone/gm_commands/delacct.cpp | 21 + zone/gm_commands/deletegraveyard.cpp | 35 + zone/gm_commands/delpetition.cpp | 20 + zone/gm_commands/depop.cpp | 14 + zone/gm_commands/depopzone.cpp | 8 + zone/gm_commands/details.cpp | 37 + zone/gm_commands/devtools.cpp | 22 + zone/gm_commands/disablerecipe.cpp | 29 + zone/gm_commands/disarmtrap.cpp | 25 + zone/gm_commands/distance.cpp | 23 + zone/gm_commands/doanim.cpp | 20 + zone/gm_commands/door.cpp | 9 + zone/gm_commands/door_manipulation.cpp | 15 +- zone/gm_commands/dye.cpp | 87 + zone/gm_commands/dz.cpp | 244 + zone/gm_commands/dzkickplayers.cpp | 13 + zone/gm_commands/editmassrespawn.cpp | 140 + zone/gm_commands/emote.cpp | 45 + zone/gm_commands/emotesearch.cpp | 78 + zone/gm_commands/emoteview.cpp | 39 + zone/gm_commands/enablerecipe.cpp | 29 + zone/gm_commands/endurance.cpp | 27 + zone/gm_commands/equipitem.cpp | 82 + zone/gm_commands/face.cpp | 37 + zone/gm_commands/faction.cpp | 174 + zone/gm_commands/findclass.cpp | 84 + zone/gm_commands/findfaction.cpp | 89 + zone/gm_commands/findnpctype.cpp | 77 + zone/gm_commands/findrace.cpp | 84 + zone/gm_commands/findskill.cpp | 90 + zone/gm_commands/findspell.cpp | 129 + zone/gm_commands/findtask.cpp | 89 + zone/gm_commands/findzone.cpp | 95 + zone/gm_commands/fixmob.cpp | 250 + zone/gm_commands/flag.cpp | 53 + zone/gm_commands/flagedit.cpp | 157 + zone/gm_commands/flags.cpp | 16 + zone/gm_commands/flymode.cpp | 40 + zone/gm_commands/fov.cpp | 42 + zone/gm_commands/freeze.cpp | 12 + zone/gm_commands/gassign.cpp | 14 + zone/gm_commands/gearup.cpp | 181 + zone/gm_commands/gender.cpp | 17 + .../getplayerburiedcorpsecount.cpp | 27 + zone/gm_commands/getvariable.cpp | 13 + zone/gm_commands/ginfo.cpp | 52 + zone/gm_commands/giveitem.cpp | 111 + zone/gm_commands/givemoney.cpp | 33 + zone/gm_commands/globalview.cpp | 80 + zone/gm_commands/gm.cpp | 27 + zone/gm_commands/gmspeed.cpp | 20 + zone/gm_commands/gmzone.cpp | 88 + zone/gm_commands/goto.cpp | 63 + zone/gm_commands/grid.cpp | 143 + zone/gm_commands/guild.cpp | 444 + zone/gm_commands/guildapprove.cpp | 8 + zone/gm_commands/guildcreate.cpp | 13 + zone/gm_commands/guildlist.cpp | 14 + zone/gm_commands/hair.cpp | 37 + zone/gm_commands/haircolor.cpp | 37 + zone/gm_commands/haste.cpp | 20 + zone/gm_commands/hatelist.cpp | 15 + zone/gm_commands/heal.cpp | 21 + zone/gm_commands/helm.cpp | 37 + zone/gm_commands/heritage.cpp | 37 + zone/gm_commands/heromodel.cpp | 35 + zone/gm_commands/hideme.cpp | 16 + zone/gm_commands/hp.cpp | 8 + zone/gm_commands/incstat.cpp | 18 + zone/gm_commands/instance.cpp | 188 + zone/gm_commands/interrogateinv.cpp | 76 + zone/gm_commands/interrupt.cpp | 16 + zone/gm_commands/invsnapshot.cpp | 418 + zone/gm_commands/invul.cpp | 20 + zone/gm_commands/ipban.cpp | 21 + zone/gm_commands/iplookup.cpp | 23 + zone/gm_commands/iteminfo.cpp | 94 + zone/gm_commands/itemsearch.cpp | 125 + zone/gm_commands/kick.cpp | 36 + zone/gm_commands/kill.cpp | 12 + zone/gm_commands/killallnpcs.cpp | 45 + zone/gm_commands/lastname.cpp | 19 + zone/gm_commands/list.cpp | 265 + zone/gm_commands/listpetition.cpp | 22 + zone/gm_commands/loc.cpp | 32 + zone/gm_commands/lock.cpp | 15 + zone/gm_commands/logcommand.cpp | 85 + zone/gm_commands/logs.cpp | 101 + zone/gm_commands/makepet.cpp | 12 + zone/gm_commands/mana.cpp | 27 + zone/gm_commands/max_all_skills.cpp | 20 + zone/gm_commands/memspell.cpp | 22 + zone/gm_commands/merchantcloseshop.cpp | 13 + zone/gm_commands/merchantopenshop.cpp | 13 + zone/gm_commands/modifynpcstat.cpp | 30 + zone/gm_commands/motd.cpp | 15 + zone/gm_commands/movechar.cpp | 32 + zone/gm_commands/movement.cpp | 76 + zone/gm_commands/myskills.cpp | 7 + zone/gm_commands/mysql.cpp | 87 + zone/gm_commands/mystats.cpp | 17 + zone/gm_commands/name.cpp | 30 + zone/gm_commands/netstats.cpp | 141 + zone/gm_commands/network.cpp | 175 + zone/gm_commands/npccast.cpp | 94 + zone/gm_commands/npcedit.cpp | 1408 ++ zone/gm_commands/npceditmass.cpp | 194 + zone/gm_commands/npcemote.cpp | 12 + zone/gm_commands/npcloot.cpp | 104 + zone/gm_commands/npcsay.cpp | 12 + zone/gm_commands/npcshout.cpp | 12 + zone/gm_commands/npcspawn.cpp | 97 + zone/gm_commands/npcspecialattk.cpp | 16 + zone/gm_commands/npcstats.cpp | 20 + zone/gm_commands/npctype_cache.cpp | 27 + zone/gm_commands/npctypespawn.cpp | 29 + zone/gm_commands/nudge.cpp | 77 + zone/gm_commands/nukebuffs.cpp | 12 + zone/gm_commands/nukeitem.cpp | 16 + zone/gm_commands/object.cpp | 1263 ++ zone/gm_commands/oocmute.cpp | 18 + zone/gm_commands/opcode.cpp | 11 + zone/gm_commands/path.cpp | 27 + zone/gm_commands/peekinv.cpp | 332 + zone/gm_commands/peqzone.cpp | 75 + zone/gm_commands/permaclass.cpp | 28 + zone/gm_commands/permagender.cpp | 29 + zone/gm_commands/permarace.cpp | 34 + zone/gm_commands/petitioninfo.cpp | 39 + zone/gm_commands/petname.cpp | 22 + zone/gm_commands/pf.cpp | 21 + zone/gm_commands/picklock.cpp | 24 + zone/gm_commands/profanity.cpp | 74 + zone/gm_commands/profilereset.cpp | 8 + zone/gm_commands/proximity.cpp | 74 + zone/gm_commands/push.cpp | 35 + zone/gm_commands/pvp.cpp | 20 + zone/gm_commands/qglobal.cpp | 62 + zone/gm_commands/questerrors.cpp | 23 + zone/gm_commands/race.cpp | 29 + zone/gm_commands/raidloot.cpp | 70 + zone/gm_commands/randomfeatures.cpp | 18 + zone/gm_commands/refreshgroup.cpp | 19 + zone/gm_commands/reloadaa.cpp | 17 + zone/gm_commands/reloadallrules.cpp | 16 + zone/gm_commands/reloademote.cpp | 9 + zone/gm_commands/reloadlevelmods.cpp | 15 + zone/gm_commands/reloadmerchants.cpp | 8 + zone/gm_commands/reloadperlexportsettings.cpp | 16 + zone/gm_commands/reloadqst.cpp | 23 + zone/gm_commands/reloadstatic.cpp | 8 + zone/gm_commands/reloadtitles.cpp | 14 + zone/gm_commands/reloadtraps.cpp | 8 + zone/gm_commands/reloadworld.cpp | 22 + zone/gm_commands/reloadworldrules.cpp | 15 + zone/gm_commands/reloadzps.cpp | 8 + zone/gm_commands/repop.cpp | 40 + zone/gm_commands/resetaa.cpp | 13 + zone/gm_commands/resetaa_timer.cpp | 26 + zone/gm_commands/resetdisc_timer.cpp | 23 + zone/gm_commands/revoke.cpp | 48 + zone/gm_commands/roambox.cpp | 93 + zone/gm_commands/rules.cpp | 232 + zone/gm_commands/save.cpp | 33 + zone/gm_commands/scale.cpp | 136 + zone/gm_commands/scribespell.cpp | 66 + zone/gm_commands/scribespells.cpp | 65 + zone/gm_commands/sendzonespawns.cpp | 7 + zone/gm_commands/sensetrap.cpp | 24 + zone/gm_commands/serverinfo.cpp | 33 + zone/gm_commands/serverrules.cpp | 7 + zone/gm_commands/set_adventure_points.cpp | 24 + zone/gm_commands/setaapts.cpp | 63 + zone/gm_commands/setaaxp.cpp | 67 + zone/gm_commands/setanim.cpp | 16 + zone/gm_commands/setcrystals.cpp | 53 + zone/gm_commands/setfaction.cpp | 20 + zone/gm_commands/setgraveyard.cpp | 44 + zone/gm_commands/setlanguage.cpp | 61 + zone/gm_commands/setlsinfo.cpp | 24 + zone/gm_commands/setpass.cpp | 29 + zone/gm_commands/setpvppoints.cpp | 32 + zone/gm_commands/setskill.cpp | 55 + zone/gm_commands/setskillall.cpp | 30 + zone/gm_commands/setstartzone.cpp | 35 + zone/gm_commands/setstat.cpp | 14 + zone/gm_commands/setxp.cpp | 23 + zone/gm_commands/showbonusstats.cpp | 49 + zone/gm_commands/showbuffs.cpp | 12 + zone/gm_commands/shownpcgloballoot.cpp | 16 + zone/gm_commands/shownumhits.cpp | 8 + zone/gm_commands/showskills.cpp | 44 + zone/gm_commands/showspellslist.cpp | 13 + zone/gm_commands/showstats.cpp | 12 + zone/gm_commands/showzonegloballoot.cpp | 13 + zone/gm_commands/showzonepoints.cpp | 157 + zone/gm_commands/shutdown.cpp | 8 + zone/gm_commands/size.cpp | 46 + zone/gm_commands/spawn.cpp | 29 + zone/gm_commands/spawnfix.cpp | 39 + zone/gm_commands/spawnstatus.cpp | 28 + zone/gm_commands/spellinfo.cpp | 154 + zone/gm_commands/stun.cpp | 65 + zone/gm_commands/summon.cpp | 97 + zone/gm_commands/summonburiedplayercorpse.cpp | 28 + zone/gm_commands/summonitem.cpp | 92 + zone/gm_commands/suspend.cpp | 101 + zone/gm_commands/task.cpp | 198 + zone/gm_commands/tattoo.cpp | 37 + zone/gm_commands/tempname.cpp | 22 + zone/gm_commands/texture.cpp | 52 + zone/gm_commands/time.cpp | 32 + zone/gm_commands/timers.cpp | 22 + zone/gm_commands/timezone.cpp | 32 + zone/gm_commands/title.cpp | 65 + zone/gm_commands/titlesuffix.cpp | 65 + zone/gm_commands/traindisc.cpp | 65 + zone/gm_commands/trapinfo.cpp | 7 + zone/gm_commands/tune.cpp | 524 + zone/gm_commands/ucs.cpp | 89 + zone/gm_commands/undye.cpp | 12 + zone/gm_commands/undyeme.cpp | 10 + zone/gm_commands/unfreeze.cpp | 12 + zone/gm_commands/unlock.cpp | 15 + zone/gm_commands/unscribespell.cpp | 62 + zone/gm_commands/unscribespells.cpp | 13 + zone/gm_commands/untraindisc.cpp | 17 + zone/gm_commands/untraindiscs.cpp | 13 + zone/gm_commands/uptime.cpp | 22 + zone/gm_commands/version.cpp | 10 + zone/gm_commands/viewnpctype.cpp | 31 + zone/gm_commands/viewpetition.cpp | 31 + zone/gm_commands/viewzoneloot.cpp | 117 + zone/gm_commands/wc.cpp | 44 + zone/gm_commands/weather.cpp | 70 + zone/gm_commands/who.cpp | 202 + zone/gm_commands/worldshutdown.cpp | 79 + zone/gm_commands/worldwide.cpp | 148 + zone/gm_commands/wp.cpp | 51 + zone/gm_commands/wpadd.cpp | 52 + zone/gm_commands/wpinfo.cpp | 15 + zone/gm_commands/xtargets.cpp | 28 + zone/gm_commands/zclip.cpp | 39 + zone/gm_commands/zcolor.cpp | 30 + zone/gm_commands/zheader.cpp | 26 + zone/gm_commands/zonebootup.cpp | 25 + zone/gm_commands/zonelock.cpp | 76 + zone/gm_commands/zoneshutdown.cpp | 30 + zone/gm_commands/zonespawn.cpp | 40 + zone/gm_commands/zonestatus.cpp | 19 + zone/gm_commands/zopp.cpp | 64 + zone/gm_commands/zsafecoords.cpp | 26 + zone/gm_commands/zsave.cpp | 12 + zone/gm_commands/zsky.cpp | 20 + zone/gm_commands/zstats.cpp | 208 + zone/gm_commands/zunderworld.cpp | 12 + zone/gm_commands/zuwcoords.cpp | 19 + 291 files changed, 19224 insertions(+), 15798 deletions(-) create mode 100644 utils/gm_commands/main.go create mode 100755 zone/gm_commands/acceptrules.cpp create mode 100755 zone/gm_commands/advnpcspawn.cpp create mode 100755 zone/gm_commands/aggro.cpp create mode 100755 zone/gm_commands/aggrozone.cpp create mode 100755 zone/gm_commands/ai.cpp create mode 100755 zone/gm_commands/appearance.cpp create mode 100755 zone/gm_commands/attack.cpp create mode 100755 zone/gm_commands/augmentitem.cpp create mode 100755 zone/gm_commands/ban.cpp create mode 100755 zone/gm_commands/beard.cpp create mode 100755 zone/gm_commands/beardcolor.cpp create mode 100755 zone/gm_commands/bestz.cpp create mode 100755 zone/gm_commands/bind.cpp create mode 100755 zone/gm_commands/camerashake.cpp create mode 100755 zone/gm_commands/castspell.cpp create mode 100755 zone/gm_commands/chat.cpp create mode 100755 zone/gm_commands/checklos.cpp create mode 100755 zone/gm_commands/copycharacter.cpp create mode 100755 zone/gm_commands/corpse.cpp create mode 100755 zone/gm_commands/corpsefix.cpp create mode 100755 zone/gm_commands/cvs.cpp create mode 100755 zone/gm_commands/damage.cpp create mode 100755 zone/gm_commands/databuckets.cpp create mode 100755 zone/gm_commands/date.cpp create mode 100755 zone/gm_commands/dbspawn2.cpp create mode 100755 zone/gm_commands/delacct.cpp create mode 100755 zone/gm_commands/deletegraveyard.cpp create mode 100755 zone/gm_commands/delpetition.cpp create mode 100755 zone/gm_commands/depop.cpp create mode 100755 zone/gm_commands/depopzone.cpp create mode 100755 zone/gm_commands/details.cpp create mode 100755 zone/gm_commands/devtools.cpp create mode 100755 zone/gm_commands/disablerecipe.cpp create mode 100755 zone/gm_commands/disarmtrap.cpp create mode 100755 zone/gm_commands/distance.cpp create mode 100755 zone/gm_commands/doanim.cpp create mode 100755 zone/gm_commands/door.cpp create mode 100755 zone/gm_commands/dye.cpp create mode 100755 zone/gm_commands/dz.cpp create mode 100755 zone/gm_commands/dzkickplayers.cpp create mode 100755 zone/gm_commands/editmassrespawn.cpp create mode 100755 zone/gm_commands/emote.cpp create mode 100755 zone/gm_commands/emotesearch.cpp create mode 100755 zone/gm_commands/emoteview.cpp create mode 100755 zone/gm_commands/enablerecipe.cpp create mode 100755 zone/gm_commands/endurance.cpp create mode 100755 zone/gm_commands/equipitem.cpp create mode 100755 zone/gm_commands/face.cpp create mode 100755 zone/gm_commands/faction.cpp create mode 100755 zone/gm_commands/findclass.cpp create mode 100755 zone/gm_commands/findfaction.cpp create mode 100755 zone/gm_commands/findnpctype.cpp create mode 100755 zone/gm_commands/findrace.cpp create mode 100755 zone/gm_commands/findskill.cpp create mode 100755 zone/gm_commands/findspell.cpp create mode 100755 zone/gm_commands/findtask.cpp create mode 100755 zone/gm_commands/findzone.cpp create mode 100755 zone/gm_commands/fixmob.cpp create mode 100755 zone/gm_commands/flag.cpp create mode 100755 zone/gm_commands/flagedit.cpp create mode 100755 zone/gm_commands/flags.cpp create mode 100755 zone/gm_commands/flymode.cpp create mode 100755 zone/gm_commands/fov.cpp create mode 100755 zone/gm_commands/freeze.cpp create mode 100755 zone/gm_commands/gassign.cpp create mode 100755 zone/gm_commands/gearup.cpp create mode 100755 zone/gm_commands/gender.cpp create mode 100755 zone/gm_commands/getplayerburiedcorpsecount.cpp create mode 100755 zone/gm_commands/getvariable.cpp create mode 100755 zone/gm_commands/ginfo.cpp create mode 100755 zone/gm_commands/giveitem.cpp create mode 100755 zone/gm_commands/givemoney.cpp create mode 100755 zone/gm_commands/globalview.cpp create mode 100755 zone/gm_commands/gm.cpp create mode 100755 zone/gm_commands/gmspeed.cpp create mode 100755 zone/gm_commands/gmzone.cpp create mode 100755 zone/gm_commands/goto.cpp create mode 100755 zone/gm_commands/grid.cpp create mode 100755 zone/gm_commands/guild.cpp create mode 100755 zone/gm_commands/guildapprove.cpp create mode 100755 zone/gm_commands/guildcreate.cpp create mode 100755 zone/gm_commands/guildlist.cpp create mode 100755 zone/gm_commands/hair.cpp create mode 100755 zone/gm_commands/haircolor.cpp create mode 100755 zone/gm_commands/haste.cpp create mode 100755 zone/gm_commands/hatelist.cpp create mode 100755 zone/gm_commands/heal.cpp create mode 100755 zone/gm_commands/helm.cpp create mode 100755 zone/gm_commands/heritage.cpp create mode 100755 zone/gm_commands/heromodel.cpp create mode 100755 zone/gm_commands/hideme.cpp create mode 100755 zone/gm_commands/hp.cpp create mode 100755 zone/gm_commands/incstat.cpp create mode 100755 zone/gm_commands/instance.cpp create mode 100755 zone/gm_commands/interrogateinv.cpp create mode 100755 zone/gm_commands/interrupt.cpp create mode 100755 zone/gm_commands/invsnapshot.cpp create mode 100755 zone/gm_commands/invul.cpp create mode 100755 zone/gm_commands/ipban.cpp create mode 100755 zone/gm_commands/iplookup.cpp create mode 100755 zone/gm_commands/iteminfo.cpp create mode 100755 zone/gm_commands/itemsearch.cpp create mode 100755 zone/gm_commands/kick.cpp create mode 100755 zone/gm_commands/kill.cpp create mode 100755 zone/gm_commands/killallnpcs.cpp create mode 100755 zone/gm_commands/lastname.cpp create mode 100755 zone/gm_commands/list.cpp create mode 100755 zone/gm_commands/listpetition.cpp create mode 100755 zone/gm_commands/loc.cpp create mode 100755 zone/gm_commands/lock.cpp create mode 100755 zone/gm_commands/logcommand.cpp create mode 100755 zone/gm_commands/logs.cpp create mode 100755 zone/gm_commands/makepet.cpp create mode 100755 zone/gm_commands/mana.cpp create mode 100755 zone/gm_commands/max_all_skills.cpp create mode 100755 zone/gm_commands/memspell.cpp create mode 100755 zone/gm_commands/merchantcloseshop.cpp create mode 100755 zone/gm_commands/merchantopenshop.cpp create mode 100755 zone/gm_commands/modifynpcstat.cpp create mode 100755 zone/gm_commands/motd.cpp create mode 100755 zone/gm_commands/movechar.cpp create mode 100755 zone/gm_commands/movement.cpp create mode 100755 zone/gm_commands/myskills.cpp create mode 100755 zone/gm_commands/mysql.cpp create mode 100755 zone/gm_commands/mystats.cpp create mode 100755 zone/gm_commands/name.cpp create mode 100755 zone/gm_commands/netstats.cpp create mode 100755 zone/gm_commands/network.cpp create mode 100755 zone/gm_commands/npccast.cpp create mode 100755 zone/gm_commands/npcedit.cpp create mode 100755 zone/gm_commands/npceditmass.cpp create mode 100755 zone/gm_commands/npcemote.cpp create mode 100755 zone/gm_commands/npcloot.cpp create mode 100755 zone/gm_commands/npcsay.cpp create mode 100755 zone/gm_commands/npcshout.cpp create mode 100755 zone/gm_commands/npcspawn.cpp create mode 100755 zone/gm_commands/npcspecialattk.cpp create mode 100755 zone/gm_commands/npcstats.cpp create mode 100755 zone/gm_commands/npctype_cache.cpp create mode 100755 zone/gm_commands/npctypespawn.cpp create mode 100755 zone/gm_commands/nudge.cpp create mode 100755 zone/gm_commands/nukebuffs.cpp create mode 100755 zone/gm_commands/nukeitem.cpp create mode 100755 zone/gm_commands/object.cpp create mode 100755 zone/gm_commands/oocmute.cpp create mode 100755 zone/gm_commands/opcode.cpp create mode 100755 zone/gm_commands/path.cpp create mode 100755 zone/gm_commands/peekinv.cpp create mode 100755 zone/gm_commands/peqzone.cpp create mode 100755 zone/gm_commands/permaclass.cpp create mode 100755 zone/gm_commands/permagender.cpp create mode 100755 zone/gm_commands/permarace.cpp create mode 100755 zone/gm_commands/petitioninfo.cpp create mode 100755 zone/gm_commands/petname.cpp create mode 100755 zone/gm_commands/pf.cpp create mode 100755 zone/gm_commands/picklock.cpp create mode 100755 zone/gm_commands/profanity.cpp create mode 100755 zone/gm_commands/profilereset.cpp create mode 100755 zone/gm_commands/proximity.cpp create mode 100755 zone/gm_commands/push.cpp create mode 100755 zone/gm_commands/pvp.cpp create mode 100755 zone/gm_commands/qglobal.cpp create mode 100755 zone/gm_commands/questerrors.cpp create mode 100755 zone/gm_commands/race.cpp create mode 100755 zone/gm_commands/raidloot.cpp create mode 100755 zone/gm_commands/randomfeatures.cpp create mode 100755 zone/gm_commands/refreshgroup.cpp create mode 100755 zone/gm_commands/reloadaa.cpp create mode 100755 zone/gm_commands/reloadallrules.cpp create mode 100755 zone/gm_commands/reloademote.cpp create mode 100755 zone/gm_commands/reloadlevelmods.cpp create mode 100755 zone/gm_commands/reloadmerchants.cpp create mode 100755 zone/gm_commands/reloadperlexportsettings.cpp create mode 100755 zone/gm_commands/reloadqst.cpp create mode 100755 zone/gm_commands/reloadstatic.cpp create mode 100755 zone/gm_commands/reloadtitles.cpp create mode 100755 zone/gm_commands/reloadtraps.cpp create mode 100755 zone/gm_commands/reloadworld.cpp create mode 100755 zone/gm_commands/reloadworldrules.cpp create mode 100755 zone/gm_commands/reloadzps.cpp create mode 100755 zone/gm_commands/repop.cpp create mode 100755 zone/gm_commands/resetaa.cpp create mode 100755 zone/gm_commands/resetaa_timer.cpp create mode 100755 zone/gm_commands/resetdisc_timer.cpp create mode 100755 zone/gm_commands/revoke.cpp create mode 100755 zone/gm_commands/roambox.cpp create mode 100755 zone/gm_commands/rules.cpp create mode 100755 zone/gm_commands/save.cpp create mode 100755 zone/gm_commands/scale.cpp create mode 100755 zone/gm_commands/scribespell.cpp create mode 100755 zone/gm_commands/scribespells.cpp create mode 100755 zone/gm_commands/sendzonespawns.cpp create mode 100755 zone/gm_commands/sensetrap.cpp create mode 100755 zone/gm_commands/serverinfo.cpp create mode 100755 zone/gm_commands/serverrules.cpp create mode 100755 zone/gm_commands/set_adventure_points.cpp create mode 100755 zone/gm_commands/setaapts.cpp create mode 100755 zone/gm_commands/setaaxp.cpp create mode 100755 zone/gm_commands/setanim.cpp create mode 100755 zone/gm_commands/setcrystals.cpp create mode 100755 zone/gm_commands/setfaction.cpp create mode 100755 zone/gm_commands/setgraveyard.cpp create mode 100755 zone/gm_commands/setlanguage.cpp create mode 100755 zone/gm_commands/setlsinfo.cpp create mode 100755 zone/gm_commands/setpass.cpp create mode 100755 zone/gm_commands/setpvppoints.cpp create mode 100755 zone/gm_commands/setskill.cpp create mode 100755 zone/gm_commands/setskillall.cpp create mode 100755 zone/gm_commands/setstartzone.cpp create mode 100755 zone/gm_commands/setstat.cpp create mode 100755 zone/gm_commands/setxp.cpp create mode 100755 zone/gm_commands/showbonusstats.cpp create mode 100755 zone/gm_commands/showbuffs.cpp create mode 100755 zone/gm_commands/shownpcgloballoot.cpp create mode 100755 zone/gm_commands/shownumhits.cpp create mode 100755 zone/gm_commands/showskills.cpp create mode 100755 zone/gm_commands/showspellslist.cpp create mode 100755 zone/gm_commands/showstats.cpp create mode 100755 zone/gm_commands/showzonegloballoot.cpp create mode 100755 zone/gm_commands/showzonepoints.cpp create mode 100755 zone/gm_commands/shutdown.cpp create mode 100755 zone/gm_commands/size.cpp create mode 100755 zone/gm_commands/spawn.cpp create mode 100755 zone/gm_commands/spawnfix.cpp create mode 100755 zone/gm_commands/spawnstatus.cpp create mode 100755 zone/gm_commands/spellinfo.cpp create mode 100755 zone/gm_commands/stun.cpp create mode 100755 zone/gm_commands/summon.cpp create mode 100755 zone/gm_commands/summonburiedplayercorpse.cpp create mode 100755 zone/gm_commands/summonitem.cpp create mode 100755 zone/gm_commands/suspend.cpp create mode 100755 zone/gm_commands/task.cpp create mode 100755 zone/gm_commands/tattoo.cpp create mode 100755 zone/gm_commands/tempname.cpp create mode 100755 zone/gm_commands/texture.cpp create mode 100755 zone/gm_commands/time.cpp create mode 100755 zone/gm_commands/timers.cpp create mode 100755 zone/gm_commands/timezone.cpp create mode 100755 zone/gm_commands/title.cpp create mode 100755 zone/gm_commands/titlesuffix.cpp create mode 100755 zone/gm_commands/traindisc.cpp create mode 100755 zone/gm_commands/trapinfo.cpp create mode 100755 zone/gm_commands/tune.cpp create mode 100755 zone/gm_commands/ucs.cpp create mode 100755 zone/gm_commands/undye.cpp create mode 100755 zone/gm_commands/undyeme.cpp create mode 100755 zone/gm_commands/unfreeze.cpp create mode 100755 zone/gm_commands/unlock.cpp create mode 100755 zone/gm_commands/unscribespell.cpp create mode 100755 zone/gm_commands/unscribespells.cpp create mode 100755 zone/gm_commands/untraindisc.cpp create mode 100755 zone/gm_commands/untraindiscs.cpp create mode 100755 zone/gm_commands/uptime.cpp create mode 100755 zone/gm_commands/version.cpp create mode 100755 zone/gm_commands/viewnpctype.cpp create mode 100755 zone/gm_commands/viewpetition.cpp create mode 100755 zone/gm_commands/viewzoneloot.cpp create mode 100755 zone/gm_commands/wc.cpp create mode 100755 zone/gm_commands/weather.cpp create mode 100755 zone/gm_commands/who.cpp create mode 100755 zone/gm_commands/worldshutdown.cpp create mode 100755 zone/gm_commands/worldwide.cpp create mode 100755 zone/gm_commands/wp.cpp create mode 100755 zone/gm_commands/wpadd.cpp create mode 100755 zone/gm_commands/wpinfo.cpp create mode 100755 zone/gm_commands/xtargets.cpp create mode 100755 zone/gm_commands/zclip.cpp create mode 100755 zone/gm_commands/zcolor.cpp create mode 100755 zone/gm_commands/zheader.cpp create mode 100755 zone/gm_commands/zonebootup.cpp create mode 100755 zone/gm_commands/zonelock.cpp create mode 100755 zone/gm_commands/zoneshutdown.cpp create mode 100755 zone/gm_commands/zonespawn.cpp create mode 100755 zone/gm_commands/zonestatus.cpp create mode 100755 zone/gm_commands/zopp.cpp create mode 100755 zone/gm_commands/zsafecoords.cpp create mode 100755 zone/gm_commands/zsave.cpp create mode 100755 zone/gm_commands/zsky.cpp create mode 100755 zone/gm_commands/zstats.cpp create mode 100755 zone/gm_commands/zunderworld.cpp create mode 100755 zone/gm_commands/zuwcoords.cpp diff --git a/.gitignore b/.gitignore index 1db525804..d835a1ceb 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,5 @@ bin/ /Win32 /x64 /client_files/**/CMakeFiles/ + +.idea diff --git a/common/file_util.cpp b/common/file_util.cpp index 8c9aca9a4..7e2535512 100644 --- a/common/file_util.cpp +++ b/common/file_util.cpp @@ -64,4 +64,9 @@ void FileUtil::mkdir(const std::string& directory_name) } ::mkdir(directory_name.c_str(), 0755); #endif -} \ No newline at end of file +} + +bool file_exists(const std::string& name) { + std::ifstream f(name.c_str()); + return f.good(); +} diff --git a/common/file_util.h b/common/file_util.h index 05869d303..02e47d198 100644 --- a/common/file_util.h +++ b/common/file_util.h @@ -28,5 +28,6 @@ public: static void mkdir(const std::string& directory_name); }; +bool file_exists(const std::string& name); #endif //EQEMU_FILE_UTIL_H diff --git a/common/spdat.cpp b/common/spdat.cpp index 525c70c1c..b81b8167c 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1239,10 +1239,10 @@ bool IsEffectIgnoredInStacking(int spa) case SE_GravityEffect: case 425: //Spell effects implemented after ROF2, following same pattern, lets assume these should go here. - case SE_Fc_Spell_Damage_Pct_IncomingPC: + case SE_Fc_Spell_Damage_Pct_IncomingPC: case SE_Fc_Spell_Damage_Amt_IncomingPC: case SE_Ff_CasterClass: - case SE_Ff_Same_Caster: + case SE_Ff_Same_Caster: case SE_Proc_Timer_Modifier: case SE_Weapon_Stance: case SE_TwinCastBlocker: @@ -1417,8 +1417,8 @@ bool IsInstrumentModAppliedToSpellEffect(int32 spell_id, int effect) case SE_MinDamageModifier: case SE_ProcChance: case SE_PetFlurry: // ? Need verified - case SE_DiseaseCounter: - case SE_PoisonCounter: + case SE_DiseaseCounter: + case SE_PoisonCounter: case SE_CurseCounter: case SE_CorruptionCounter: return true; @@ -1576,7 +1576,7 @@ int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot) return 0; } -bool IsVirusSpell(int32 spell_id) +bool IsVirusSpell(int32 spell_id) { if (GetViralMinSpreadTime(spell_id) && GetViralMaxSpreadTime(spell_id) && GetViralSpreadRange(spell_id)){ return true; @@ -1584,17 +1584,17 @@ bool IsVirusSpell(int32 spell_id) return false; } -int32 GetViralMinSpreadTime(int32 spell_id) +int32 GetViralMinSpreadTime(int32 spell_id) { return spells[spell_id].viral_targets; } -int32 GetViralMaxSpreadTime(int32 spell_id) +int32 GetViralMaxSpreadTime(int32 spell_id) { return spells[spell_id].viral_timer; } -int32 GetViralSpreadRange(int32 spell_id) +int32 GetViralSpreadRange(int32 spell_id) { return spells[spell_id].viral_range; } @@ -1605,7 +1605,7 @@ uint32 GetProcLimitTimer(int32 spell_id, int proc_type) { if (!IsValidSpell(spell_id)) { return 0; } - + bool use_next_timer = false; for (int i = 0; i < EFFECT_COUNT; ++i) { @@ -1633,3 +1633,41 @@ uint32 GetProcLimitTimer(int32 spell_id, int proc_type) { } return 0; } + +bool CastRestrictedSpell(int spellid) +{ + switch (spellid) { + case SPELL_TOUCH_OF_VINITRAS: + case SPELL_DESPERATE_HOPE: + case SPELL_CHARM: + case SPELL_METAMORPHOSIS65: + case SPELL_JT_BUFF: + case SPELL_CAN_O_WHOOP_ASS: + case SPELL_PHOENIX_CHARM: + case SPELL_CAZIC_TOUCH: + case SPELL_AVATAR_KNOCKBACK: + case SPELL_SHAPECHANGE65: + case SPELL_SUNSET_HOME1218: + case SPELL_SUNSET_HOME819: + case SPELL_SHAPECHANGE75: + case SPELL_SHAPECHANGE80: + case SPELL_SHAPECHANGE85: + case SPELL_SHAPECHANGE90: + case SPELL_SHAPECHANGE95: + case SPELL_SHAPECHANGE100: + case SPELL_SHAPECHANGE25: + case SPELL_SHAPECHANGE30: + case SPELL_SHAPECHANGE35: + case SPELL_SHAPECHANGE40: + case SPELL_SHAPECHANGE45: + case SPELL_SHAPECHANGE50: + case SPELL_NPC_AEGOLISM: + case SPELL_SHAPECHANGE55: + case SPELL_SHAPECHANGE60: + case SPELL_COMMAND_OF_DRUZZIL: + case SPELL_SHAPECHANGE70: + return true; + default: + return false; + } +} diff --git a/common/spdat.h b/common/spdat.h index 2e6d59d5e..4efe92f94 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -219,292 +219,292 @@ enum SpellRestriction { UNKNOWN_3 = 3, // | caster restriction | seen in spell 30183 Mind Spiral IS_NOT_ON_HORSE = 5, // | caster restriction | - IS_ANIMAL_OR_HUMANOID = 100, // This spell will only work on animals or humanoid creatures. - IS_DRAGON = 101, // This spell will only work on dragons. - IS_ANIMAL_OR_INSECT = 102, // This spell will only work on animals or insects. - IS_BODY_TYPE_MISC = 103, // This spell will only work on humanoids, lycanthropes, giants, Kael Drakkel giants, Coldain, animals, insects, constructs, dragons, Skyshrine dragons, Muramites, or creatures constructed from magic. - IS_BODY_TYPE_MISC2 = 104, // This spell will only work on humanoids, lycanthropes, giants, Kael Drakkel giants, Coldain, animals, or insects. - IS_PLANT = 105, // This spell will only work on plants. + IS_ANIMAL_OR_HUMANOID = 100, // This spell will only work on animals or humanoid creatures. + IS_DRAGON = 101, // This spell will only work on dragons. + IS_ANIMAL_OR_INSECT = 102, // This spell will only work on animals or insects. + IS_BODY_TYPE_MISC = 103, // This spell will only work on humanoids, lycanthropes, giants, Kael Drakkel giants, Coldain, animals, insects, constructs, dragons, Skyshrine dragons, Muramites, or creatures constructed from magic. + IS_BODY_TYPE_MISC2 = 104, // This spell will only work on humanoids, lycanthropes, giants, Kael Drakkel giants, Coldain, animals, or insects. + IS_PLANT = 105, // This spell will only work on plants. IS_GIANT = 106, // This spell will only work on animals. | Live used to have this on spells restricted to Giants, but those spells were removed... We still have them - IS_NOT_ANIMAL_OR_HUMANOID = 108, // This spell will only work on targets that are neither animals or humanoid. - IS_BIXIE = 109, // This spell will only work on bixies. - IS_HARPY = 110, // This spell will only work on harpies. - IS_GNOLL = 111, // This spell will only work on gnolls. - IS_SPORALI = 112, // This spell will only work on fungusoids. - IS_KOBOLD = 113, // This spell will only work on kobolds. - IS_FROSTCRYPT_SHADE = 114, // This spell will only work on undead creatures or the Shades of Frostcrypt. - IS_DRAKKIN = 115, // This spell will only work on Drakkin. - IS_UNDEAD_OR_VALDEHOLM_GIANT = 116, // This spell will only work on undead creatures or the inhabitants of Valdeholm. - IS_ANIMAL_OR_PLANT = 117, // This spell will only work on plants or animals. - IS_SUMMONED = 118, // This spell will only work on constructs, elementals, or summoned elemental minions. - IS_WIZARD_USED_ON_MAGE_FIRE_PET = 119, // This spell will only work on wizards. | Live uses this on high level mage fire pets, which are wizard class - IS_UNDEAD = 120, // - IS_NOT_UNDEAD_OR_SUMMONED_OR_VAMPIRE = 121, // This spell will only work on creatures that are not undead, constructs, elementals, or vampires. - IS_FAE_OR_PIXIE = 122, // This spell will only work on Fae or pixies. - IS_HUMANOID = 123, // - IS_UNDEAD_AND_HP_LESS_THAN_10_PCT = 124, // The Essence Extractor whirrs but does not light up. - IS_CLOCKWORK_AND_HP_LESS_THAN_45_PCT = 125, // This spell will only work on clockwork gnomes. - IS_WISP_AND_HP_LESS_THAN_10_PCT = 126, // This spell will only work on wisps at or below 10% of their maximum HP. - IS_CLASS_MELEE_THAT_CAN_BASH_OR_KICK_EXCEPT_BARD = 127, // This spell will only work on non-bard targets that can bash or kick. - IS_CLASS_PURE_MELEE = 128, // This spell will only affect melee classes (warriors, monks, rogues, and berserkers). - IS_CLASS_PURE_CASTER = 129, // This spell will only affect pure caster classes (necromancers, wizards, magicians, and enchanters). - IS_CLASS_HYBRID_CLASS = 130, // This spell will only affect hybrid classes (paladins, rangers, shadow knights, bards, and beastlords). - IS_CLASS_WARRIOR = 131, // This spell will only affect Warriors. - IS_CLASS_CLERIC = 132, // This spell will only affect Clerics. - IS_CLASS_PALADIN = 133, // This spell will only affect Paladins. - IS_CLASS_RANGER = 134, // This spell will only affect Rangers. - IS_CLASS_SHADOWKNIGHT = 135, // This spell will only affect Shadow Knights. - IS_CLASS_DRUID = 136, // This spell will only affect Druids. - IS_CLASS_MONK = 137, // This spell will only affect Monks. - IS_CLASS_BARD = 138, // This spell will only affect Bards. - IS_CLASS_ROGUE = 139, // This spell will only affect Rogues. - IS_CLASS_SHAMAN = 140, // This spell will only affect Shamans. - IS_CLASS_NECRO = 141, // This spell will only affect Necromancers. - IS_CLASS_WIZARD = 142, // This spell will only affect Wizards. - IS_CLASS_MAGE = 143, // This spell will only affect Magicians. - IS_CLASS_ENCHANTER = 144, // This spell will only affect Enchanters. - IS_CLASS_BEASTLORD = 145, // This spell will only affect Beastlords. - IS_CLASS_BERSERKER = 146, // This spell will only affect Berserkers. - IS_CLASS_CLR_SHM_DRU = 147, // This spell will only affect priest classes (clerics, druids, and shaman). - IS_CLASS_NOT_WAR_PAL_SK = 148, // This spell will not affect Warriors, Paladins, or Shadow Knights. - IS_LEVEL_UNDER_100 = 150, // This spell will not affect any target over level 100. - IS_NOT_RAID_BOSS = 190, // This spell will not affect raid bosses. - IS_RAID_BOSS = 191, // This spell will only affect raid bosses. - FRENZIED_BURNOUT_ACTIVE = 192, // This spell will only cast if you have Frenzied Burnout active. - FRENZIED_BURNOUT_NOT_ACTIVE = 193, // This spell will only cast if you do not have Frenzied Burnout active. + IS_NOT_ANIMAL_OR_HUMANOID = 108, // This spell will only work on targets that are neither animals or humanoid. + IS_BIXIE = 109, // This spell will only work on bixies. + IS_HARPY = 110, // This spell will only work on harpies. + IS_GNOLL = 111, // This spell will only work on gnolls. + IS_SPORALI = 112, // This spell will only work on fungusoids. + IS_KOBOLD = 113, // This spell will only work on kobolds. + IS_FROSTCRYPT_SHADE = 114, // This spell will only work on undead creatures or the Shades of Frostcrypt. + IS_DRAKKIN = 115, // This spell will only work on Drakkin. + IS_UNDEAD_OR_VALDEHOLM_GIANT = 116, // This spell will only work on undead creatures or the inhabitants of Valdeholm. + IS_ANIMAL_OR_PLANT = 117, // This spell will only work on plants or animals. + IS_SUMMONED = 118, // This spell will only work on constructs, elementals, or summoned elemental minions. + IS_WIZARD_USED_ON_MAGE_FIRE_PET = 119, // This spell will only work on wizards. | Live uses this on high level mage fire pets, which are wizard class + IS_UNDEAD = 120, // + IS_NOT_UNDEAD_OR_SUMMONED_OR_VAMPIRE = 121, // This spell will only work on creatures that are not undead, constructs, elementals, or vampires. + IS_FAE_OR_PIXIE = 122, // This spell will only work on Fae or pixies. + IS_HUMANOID = 123, // + IS_UNDEAD_AND_HP_LESS_THAN_10_PCT = 124, // The Essence Extractor whirrs but does not light up. + IS_CLOCKWORK_AND_HP_LESS_THAN_45_PCT = 125, // This spell will only work on clockwork gnomes. + IS_WISP_AND_HP_LESS_THAN_10_PCT = 126, // This spell will only work on wisps at or below 10% of their maximum HP. + IS_CLASS_MELEE_THAT_CAN_BASH_OR_KICK_EXCEPT_BARD = 127, // This spell will only work on non-bard targets that can bash or kick. + IS_CLASS_PURE_MELEE = 128, // This spell will only affect melee classes (warriors, monks, rogues, and berserkers). + IS_CLASS_PURE_CASTER = 129, // This spell will only affect pure caster classes (necromancers, wizards, magicians, and enchanters). + IS_CLASS_HYBRID_CLASS = 130, // This spell will only affect hybrid classes (paladins, rangers, shadow knights, bards, and beastlords). + IS_CLASS_WARRIOR = 131, // This spell will only affect Warriors. + IS_CLASS_CLERIC = 132, // This spell will only affect Clerics. + IS_CLASS_PALADIN = 133, // This spell will only affect Paladins. + IS_CLASS_RANGER = 134, // This spell will only affect Rangers. + IS_CLASS_SHADOWKNIGHT = 135, // This spell will only affect Shadow Knights. + IS_CLASS_DRUID = 136, // This spell will only affect Druids. + IS_CLASS_MONK = 137, // This spell will only affect Monks. + IS_CLASS_BARD = 138, // This spell will only affect Bards. + IS_CLASS_ROGUE = 139, // This spell will only affect Rogues. + IS_CLASS_SHAMAN = 140, // This spell will only affect Shamans. + IS_CLASS_NECRO = 141, // This spell will only affect Necromancers. + IS_CLASS_WIZARD = 142, // This spell will only affect Wizards. + IS_CLASS_MAGE = 143, // This spell will only affect Magicians. + IS_CLASS_ENCHANTER = 144, // This spell will only affect Enchanters. + IS_CLASS_BEASTLORD = 145, // This spell will only affect Beastlords. + IS_CLASS_BERSERKER = 146, // This spell will only affect Berserkers. + IS_CLASS_CLR_SHM_DRU = 147, // This spell will only affect priest classes (clerics, druids, and shaman). + IS_CLASS_NOT_WAR_PAL_SK = 148, // This spell will not affect Warriors, Paladins, or Shadow Knights. + IS_LEVEL_UNDER_100 = 150, // This spell will not affect any target over level 100. + IS_NOT_RAID_BOSS = 190, // This spell will not affect raid bosses. + IS_RAID_BOSS = 191, // This spell will only affect raid bosses. + FRENZIED_BURNOUT_ACTIVE = 192, // This spell will only cast if you have Frenzied Burnout active. + FRENZIED_BURNOUT_NOT_ACTIVE = 193, // This spell will only cast if you do not have Frenzied Burnout active. UNKNOWN_199 = 199, // - IS_HP_ABOVE_75_PCT = 201, // + IS_HP_ABOVE_75_PCT = 201, // IS_HP_LESS_THAN_20_PCT = 203, // Your target's HP must be at 20% of its maximum or below. | caster restriction | IS_HP_LESS_THAN_50_PCT = 204, // Your target's HP must be at 50% of its maximum or below. | caster restriction | - IS_HP_LESS_THAN_75_PCT = 205, // Your target's HP must be at 75% of its maximum or below. - IS_NOT_IN_COMBAT = 216, // This spell will only affect creatures that are not in combat. - HAS_AT_LEAST_1_PET_ON_HATELIST = 221, // - HAS_AT_LEAST_2_PETS_ON_HATELIST = 222, // - HAS_AT_LEAST_3_PETS_ON_HATELIST = 223, // - HAS_AT_LEAST_4_PETS_ON_HATELIST = 224, // - HAS_AT_LEAST_5_PETS_ON_HATELIST = 225, // - HAS_AT_LEAST_6_PETS_ON_HATELIST = 226, // - HAS_AT_LEAST_7_PETS_ON_HATELIST = 227, // - HAS_AT_LEAST_8_PETS_ON_HATELIST = 228, // - HAS_AT_LEAST_9_PETS_ON_HATELIST = 229, // - HAS_AT_LEAST_10_PETS_ON_HATELIST = 230, // - HAS_AT_LEAST_11_PETS_ON_HATELIST = 231, // - HAS_AT_LEAST_12_PETS_ON_HATELIST = 232, // - HAS_AT_LEAST_13_PETS_ON_HATELIST = 233, // - HAS_AT_LEAST_14_PETS_ON_HATELIST = 234, // - HAS_AT_LEAST_15_PETS_ON_HATELIST = 235, // - HAS_AT_LEAST_16_PETS_ON_HATELIST = 236, // - HAS_AT_LEAST_17_PETS_ON_HATELIST = 237, // - HAS_AT_LEAST_18_PETS_ON_HATELIST = 238, // - HAS_AT_LEAST_19_PETS_ON_HATELIST = 239, // - HAS_AT_LEAST_20_PETS_ON_HATELIST = 240, // + IS_HP_LESS_THAN_75_PCT = 205, // Your target's HP must be at 75% of its maximum or below. + IS_NOT_IN_COMBAT = 216, // This spell will only affect creatures that are not in combat. + HAS_AT_LEAST_1_PET_ON_HATELIST = 221, // + HAS_AT_LEAST_2_PETS_ON_HATELIST = 222, // + HAS_AT_LEAST_3_PETS_ON_HATELIST = 223, // + HAS_AT_LEAST_4_PETS_ON_HATELIST = 224, // + HAS_AT_LEAST_5_PETS_ON_HATELIST = 225, // + HAS_AT_LEAST_6_PETS_ON_HATELIST = 226, // + HAS_AT_LEAST_7_PETS_ON_HATELIST = 227, // + HAS_AT_LEAST_8_PETS_ON_HATELIST = 228, // + HAS_AT_LEAST_9_PETS_ON_HATELIST = 229, // + HAS_AT_LEAST_10_PETS_ON_HATELIST = 230, // + HAS_AT_LEAST_11_PETS_ON_HATELIST = 231, // + HAS_AT_LEAST_12_PETS_ON_HATELIST = 232, // + HAS_AT_LEAST_13_PETS_ON_HATELIST = 233, // + HAS_AT_LEAST_14_PETS_ON_HATELIST = 234, // + HAS_AT_LEAST_15_PETS_ON_HATELIST = 235, // + HAS_AT_LEAST_16_PETS_ON_HATELIST = 236, // + HAS_AT_LEAST_17_PETS_ON_HATELIST = 237, // + HAS_AT_LEAST_18_PETS_ON_HATELIST = 238, // + HAS_AT_LEAST_19_PETS_ON_HATELIST = 239, // + HAS_AT_LEAST_20_PETS_ON_HATELIST = 240, // IS_HP_LESS_THAN_35_PCT = 250, // Your target's HP must be at 35% of its maximum or below. - HAS_BETWEEN_1_TO_2_PETS_ON_HATELIST = 260, // between 1 and 2 pets - HAS_BETWEEN_3_TO_5_PETS_ON_HATELIST = 261, // between 3 and 5 pets - HAS_BETWEEN_6_TO_9_PETS_ON_HATELIST = 262, // between 6 and 9 pets - HAS_BETWEEN_10_TO_14_PETS_ON_HATELIST = 263, // between 10 and 14 pets - HAS_MORE_THAN_14_PETS_ON_HATELIST = 264, // 15 or more pets - IS_CLASS_CHAIN_OR_PLATE = 304, // This spell will only affect plate or chain wearing classes. - IS_HP_BETWEEN_5_AND_9_PCT = 350, // Your target's HP must be between 5% and 9% of its maximum. - IS_HP_BETWEEN_10_AND_14_PCT = 351, // Your target's HP must be between 10% and 14% of its maximum. - IS_HP_BETWEEN_15_AND_19_PCT = 352, // Your target's HP must be between 15% and 19% of its maximum. - IS_HP_BETWEEN_20_AND_24_PCT = 353, // Your target's HP must be between 20% and 24% of its maximum. - IS_HP_BETWEEN_25_AND_29_PCT = 354, // Your target's HP must be between 25% and 29% of its maximum. - IS_HP_BETWEEN_30_AND_34_PCT = 355, // Your target's HP must be between 30% and 34% of its maximum. - IS_HP_BETWEEN_35_AND_39_PCT = 356, // Your target's HP must be between 35% and 39% of its maximum. - IS_HP_BETWEEN_40_AND_44_PCT = 357, // Your target's HP must be between 40% and 44% of its maximum. - IS_HP_BETWEEN_45_AND_49_PCT = 358, // Your target's HP must be between 45% and 49% of its maximum. - IS_HP_BETWEEN_50_AND_54_PCT = 359, // Your target's HP must be between 50% and 54% of its maximum. - IS_HP_BETWEEN_55_AND_59_PCT = 360, // Your target's HP must be between 55% and 59% of its maximum. - IS_HP_BETWEEN_5_AND_15_PCT = 398, // Your target's HP must be between 5% and 15% of its maximum. - IS_HP_BETWEEN_15_AND_25_PCT = 399, // Your target's HP must be between 15% and 25% of its maximum. - IS_HP_BETWEEN_1_AND_25_PCT = 400, // Your target's HP must be at 25% of its maximum or below. - IS_HP_BETWEEN_25_AND_35_PCT = 401, // Your target's HP must be between 25% and 35% of its maximum. - IS_HP_BETWEEN_35_AND_45_PCT = 402, // Your target's HP must be between 35% and 45% of its maximum. - IS_HP_BETWEEN_45_AND_55_PCT = 403, // Your target's HP must be between 45% and 55% of its maximum. - IS_HP_BETWEEN_55_AND_65_PCT = 404, // Your target's HP must be between 55% and 65% of its maximum. - IS_HP_BETWEEN_65_AND_75_PCT = 405, // Your target's HP must be between 65% and 75% of its maximum. - IS_HP_BETWEEN_75_AND_85_PCT = 406, // Your target's HP must be between 75% and 85% of its maximum. - IS_HP_BETWEEN_85_AND_95_PCT = 407, // Your target's HP must be between 85% and 95% of its maximum. - IS_HP_ABOVE_45_PCT = 408, // Your target's HP must be at least 45% of its maximum. - IS_HP_ABOVE_55_PCT = 409, // Your target's HP must be at least 55% of its maximum. - UNKNOWN_TOO_MUCH_HP_410 = 410, // Your target has too much HP to be affected by this spell. - UNKNOWN_TOO_MUCH_HP_411 = 411, // Your target has too much HP to be affected by this spell. - IS_HP_ABOVE_99_PCT = 412, // - IS_MANA_ABOVE_10_PCT = 429, // You must have at least 10% of your maximum mana available to cast this spell. | caster restriction | - IS_HP_BELOW_5_PCT = 501, // - IS_HP_BELOW_10_PCT = 502, // - IS_HP_BELOW_15_PCT = 503, // - IS_HP_BELOW_20_PCT = 504, // Your target's HP must be at 20% of its maximum or below. - IS_HP_BELOW_25_PCT = 505, // - IS_HP_BELOW_30_PCT = 506, // - IS_HP_BELOW_35_PCT = 507, // - IS_HP_BELOW_40_PCT = 508, // - IS_HP_BELOW_45_PCT = 509, // Your target's HP must be at 45% of its maximum or below. - IS_HP_BELOW_50_PCT = 510, // - IS_HP_BELOW_55_PCT = 511, // - IS_HP_BELOW_60_PCT = 512, // - IS_HP_BELOW_65_PCT = 513, // - IS_HP_BELOW_70_PCT = 514, // - IS_HP_BELOW_75_PCT = 515, // - IS_HP_BELOW_80_PCT = 516, // - IS_HP_BELOW_85_PCT = 517, // + HAS_BETWEEN_1_TO_2_PETS_ON_HATELIST = 260, // between 1 and 2 pets + HAS_BETWEEN_3_TO_5_PETS_ON_HATELIST = 261, // between 3 and 5 pets + HAS_BETWEEN_6_TO_9_PETS_ON_HATELIST = 262, // between 6 and 9 pets + HAS_BETWEEN_10_TO_14_PETS_ON_HATELIST = 263, // between 10 and 14 pets + HAS_MORE_THAN_14_PETS_ON_HATELIST = 264, // 15 or more pets + IS_CLASS_CHAIN_OR_PLATE = 304, // This spell will only affect plate or chain wearing classes. + IS_HP_BETWEEN_5_AND_9_PCT = 350, // Your target's HP must be between 5% and 9% of its maximum. + IS_HP_BETWEEN_10_AND_14_PCT = 351, // Your target's HP must be between 10% and 14% of its maximum. + IS_HP_BETWEEN_15_AND_19_PCT = 352, // Your target's HP must be between 15% and 19% of its maximum. + IS_HP_BETWEEN_20_AND_24_PCT = 353, // Your target's HP must be between 20% and 24% of its maximum. + IS_HP_BETWEEN_25_AND_29_PCT = 354, // Your target's HP must be between 25% and 29% of its maximum. + IS_HP_BETWEEN_30_AND_34_PCT = 355, // Your target's HP must be between 30% and 34% of its maximum. + IS_HP_BETWEEN_35_AND_39_PCT = 356, // Your target's HP must be between 35% and 39% of its maximum. + IS_HP_BETWEEN_40_AND_44_PCT = 357, // Your target's HP must be between 40% and 44% of its maximum. + IS_HP_BETWEEN_45_AND_49_PCT = 358, // Your target's HP must be between 45% and 49% of its maximum. + IS_HP_BETWEEN_50_AND_54_PCT = 359, // Your target's HP must be between 50% and 54% of its maximum. + IS_HP_BETWEEN_55_AND_59_PCT = 360, // Your target's HP must be between 55% and 59% of its maximum. + IS_HP_BETWEEN_5_AND_15_PCT = 398, // Your target's HP must be between 5% and 15% of its maximum. + IS_HP_BETWEEN_15_AND_25_PCT = 399, // Your target's HP must be between 15% and 25% of its maximum. + IS_HP_BETWEEN_1_AND_25_PCT = 400, // Your target's HP must be at 25% of its maximum or below. + IS_HP_BETWEEN_25_AND_35_PCT = 401, // Your target's HP must be between 25% and 35% of its maximum. + IS_HP_BETWEEN_35_AND_45_PCT = 402, // Your target's HP must be between 35% and 45% of its maximum. + IS_HP_BETWEEN_45_AND_55_PCT = 403, // Your target's HP must be between 45% and 55% of its maximum. + IS_HP_BETWEEN_55_AND_65_PCT = 404, // Your target's HP must be between 55% and 65% of its maximum. + IS_HP_BETWEEN_65_AND_75_PCT = 405, // Your target's HP must be between 65% and 75% of its maximum. + IS_HP_BETWEEN_75_AND_85_PCT = 406, // Your target's HP must be between 75% and 85% of its maximum. + IS_HP_BETWEEN_85_AND_95_PCT = 407, // Your target's HP must be between 85% and 95% of its maximum. + IS_HP_ABOVE_45_PCT = 408, // Your target's HP must be at least 45% of its maximum. + IS_HP_ABOVE_55_PCT = 409, // Your target's HP must be at least 55% of its maximum. + UNKNOWN_TOO_MUCH_HP_410 = 410, // Your target has too much HP to be affected by this spell. + UNKNOWN_TOO_MUCH_HP_411 = 411, // Your target has too much HP to be affected by this spell. + IS_HP_ABOVE_99_PCT = 412, // + IS_MANA_ABOVE_10_PCT = 429, // You must have at least 10% of your maximum mana available to cast this spell. | caster restriction | + IS_HP_BELOW_5_PCT = 501, // + IS_HP_BELOW_10_PCT = 502, // + IS_HP_BELOW_15_PCT = 503, // + IS_HP_BELOW_20_PCT = 504, // Your target's HP must be at 20% of its maximum or below. + IS_HP_BELOW_25_PCT = 505, // + IS_HP_BELOW_30_PCT = 506, // + IS_HP_BELOW_35_PCT = 507, // + IS_HP_BELOW_40_PCT = 508, // + IS_HP_BELOW_45_PCT = 509, // Your target's HP must be at 45% of its maximum or below. + IS_HP_BELOW_50_PCT = 510, // + IS_HP_BELOW_55_PCT = 511, // + IS_HP_BELOW_60_PCT = 512, // + IS_HP_BELOW_65_PCT = 513, // + IS_HP_BELOW_70_PCT = 514, // + IS_HP_BELOW_75_PCT = 515, // + IS_HP_BELOW_80_PCT = 516, // + IS_HP_BELOW_85_PCT = 517, // IS_HP_BELOW_90_PCT = 518, // This ability requires you to be at or below 90% of your maximum HP. | caster restriction | - IS_HP_BELOW_95_PCT = 519, // + IS_HP_BELOW_95_PCT = 519, // IS_MANA_BELOW_UNKNOWN_PCT = 521, // - IS_ENDURANCE_BELOW_40_PCT = 522, // - IS_MANA_BELOW_40_PCT = 523, // - IS_HP_ABOVE_20_PCT = 524, // Your target's HP must be at least 21% of its maximum. + IS_ENDURANCE_BELOW_40_PCT = 522, // + IS_MANA_BELOW_40_PCT = 523, // + IS_HP_ABOVE_20_PCT = 524, // Your target's HP must be at least 21% of its maximum. IS_BODY_TYPE_UNDEFINED = 600, // This spell will only work on creatures with an undefined body type. - IS_BODY_TYPE_HUMANOID = 601, // This spell will only work on humanoid creatures. - IS_BODY_TYPE_WEREWOLF = 602, // This spell will only work on lycanthrope creatures. - IS_BODY_TYPE_UNDEAD = 603, // This spell will only work on undead creatures. - IS_BODY_TYPE_GIANTS = 604, // This spell will only work on giants. - IS_BODY_TYPE_CONSTRUCTS = 605, // This spell will only work on constructs. - IS_BODY_TYPE_EXTRAPLANAR = 606, // This spell will only work on extraplanar creatures. - IS_BODY_TYPE_MAGICAL_CREATURE = 607, // This spell will only work on creatures constructed from magic. - IS_BODY_TYPE_UNDEADPET = 608, // This spell will only work on animated undead servants. - IS_BODY_TYPE_KAELGIANT = 609, // This spell will only work on the Giants of Kael Drakkal. - IS_BODY_TYPE_COLDAIN = 610, // This spell will only work on Coldain Dwarves. - IS_BODY_TYPE_VAMPIRE = 612, // This spell will only work on vampires. - IS_BODY_TYPE_ATEN_HA_RA = 613, // This spell will only work on Aten Ha Ra. - IS_BODY_TYPE_GREATER_AHKEVANS = 614, // This spell will only work on Greater Ahkevans. - IS_BODY_TYPE_KHATI_SHA = 615, // This spell will only work on Khati Sha. - IS_BODY_TYPE_LORD_INQUISITOR_SERU = 616, // This spell will only work on Lord Inquisitor Seru. - IS_BODY_TYPE_GRIEG_VENEFICUS = 617, // This spell will only work on Grieg Veneficus. + IS_BODY_TYPE_HUMANOID = 601, // This spell will only work on humanoid creatures. + IS_BODY_TYPE_WEREWOLF = 602, // This spell will only work on lycanthrope creatures. + IS_BODY_TYPE_UNDEAD = 603, // This spell will only work on undead creatures. + IS_BODY_TYPE_GIANTS = 604, // This spell will only work on giants. + IS_BODY_TYPE_CONSTRUCTS = 605, // This spell will only work on constructs. + IS_BODY_TYPE_EXTRAPLANAR = 606, // This spell will only work on extraplanar creatures. + IS_BODY_TYPE_MAGICAL_CREATURE = 607, // This spell will only work on creatures constructed from magic. + IS_BODY_TYPE_UNDEADPET = 608, // This spell will only work on animated undead servants. + IS_BODY_TYPE_KAELGIANT = 609, // This spell will only work on the Giants of Kael Drakkal. + IS_BODY_TYPE_COLDAIN = 610, // This spell will only work on Coldain Dwarves. + IS_BODY_TYPE_VAMPIRE = 612, // This spell will only work on vampires. + IS_BODY_TYPE_ATEN_HA_RA = 613, // This spell will only work on Aten Ha Ra. + IS_BODY_TYPE_GREATER_AHKEVANS = 614, // This spell will only work on Greater Ahkevans. + IS_BODY_TYPE_KHATI_SHA = 615, // This spell will only work on Khati Sha. + IS_BODY_TYPE_LORD_INQUISITOR_SERU = 616, // This spell will only work on Lord Inquisitor Seru. + IS_BODY_TYPE_GRIEG_VENEFICUS = 617, // This spell will only work on Grieg Veneficus. IS_BODY_TYPE_FROM_PLANE_OF_WAR = 619, // This spell will only work on creatures from the Plane of War. - IS_BODY_TYPE_LUGGALD = 620, // This spell will only work on Luggalds. - IS_BODY_TYPE_ANIMAL = 621, // This spell will only work on animals. - IS_BODY_TYPE_INSECT = 622, // This spell will only work on insects. - IS_BODY_TYPE_MONSTER = 623, // This spell will only work on monsters. - IS_BODY_TYPE_ELEMENTAL = 624, // This spell will only work on elemental creatures. - IS_BODY_TYPE_PLANT = 625, // This spell will only work on plants. - IS_BODY_TYPE_DRAGON2 = 626, // This spell will only work on dragons. - IS_BODY_TYPE_SUMMONED_ELEMENTAL = 627, // This spell will only work on summoned elementals. - IS_BODY_TYPE_WARDER = 628, // - IS_BODY_TYPE_DRAGON_OF_TOV = 630, // This spell will only work on Dragons of Veeshan's Temple. - IS_BODY_TYPE_FAMILIAR = 631, // This spell will only work on familiars. - IS_BODY_TYPE_MURAMITE = 634, // This spell will only work on Muramites. - IS_NOT_UNDEAD_OR_SUMMONED = 635, // - IS_NOT_PLANT = 636, // This spell will not affect plants. - IS_NOT_CLIENT = 700, // This spell will not work on adventurers. - IS_CLIENT = 701, // This spell will only work on adventurers. + IS_BODY_TYPE_LUGGALD = 620, // This spell will only work on Luggalds. + IS_BODY_TYPE_ANIMAL = 621, // This spell will only work on animals. + IS_BODY_TYPE_INSECT = 622, // This spell will only work on insects. + IS_BODY_TYPE_MONSTER = 623, // This spell will only work on monsters. + IS_BODY_TYPE_ELEMENTAL = 624, // This spell will only work on elemental creatures. + IS_BODY_TYPE_PLANT = 625, // This spell will only work on plants. + IS_BODY_TYPE_DRAGON2 = 626, // This spell will only work on dragons. + IS_BODY_TYPE_SUMMONED_ELEMENTAL = 627, // This spell will only work on summoned elementals. + IS_BODY_TYPE_WARDER = 628, // + IS_BODY_TYPE_DRAGON_OF_TOV = 630, // This spell will only work on Dragons of Veeshan's Temple. + IS_BODY_TYPE_FAMILIAR = 631, // This spell will only work on familiars. + IS_BODY_TYPE_MURAMITE = 634, // This spell will only work on Muramites. + IS_NOT_UNDEAD_OR_SUMMONED = 635, // + IS_NOT_PLANT = 636, // This spell will not affect plants. + IS_NOT_CLIENT = 700, // This spell will not work on adventurers. + IS_CLIENT = 701, // This spell will only work on adventurers. IS_LEVEL_ABOVE_42_AND_IS_CLIENT = 800, // This spell will only work on level 43 or higher adventurers. UNKNOWN_812 = 812, // | seen in spell 22616 Thaumatize Pet Mana Regen Base | UNKNOWN_814 = 814, // | seen in spell 22704 Vegetentacles I | - IS_TREANT = 815, // This spell will only work on treants. - IS_BIXIE2 = 816, // This spell will only work on bixies. - IS_SCARECROW = 817, // This spell will only work on scarecrows. - IS_VAMPIRE_OR_UNDEAD_OR_UNDEADPET = 818, // This spell will only work on vampires, undead, or animated undead creatures. - IS_NOT_VAMPIRE_OR_UNDEAD = 819, // This spell will not work on vampires or undead creatures. - IS_CLASS_KNIGHT_HYBRID_MELEE = 820, // This spell will only work on knights, hybrids, or melee classes. - IS_CLASS_WARRIOR_CASTER_PRIEST = 821, // This spell will only work on warriors, casters, or priests. + IS_TREANT = 815, // This spell will only work on treants. + IS_BIXIE2 = 816, // This spell will only work on bixies. + IS_SCARECROW = 817, // This spell will only work on scarecrows. + IS_VAMPIRE_OR_UNDEAD_OR_UNDEADPET = 818, // This spell will only work on vampires, undead, or animated undead creatures. + IS_NOT_VAMPIRE_OR_UNDEAD = 819, // This spell will not work on vampires or undead creatures. + IS_CLASS_KNIGHT_HYBRID_MELEE = 820, // This spell will only work on knights, hybrids, or melee classes. + IS_CLASS_WARRIOR_CASTER_PRIEST = 821, // This spell will only work on warriors, casters, or priests. UNKNOWN_822 = 822, // | seen in spell 22870 Morell's Distraction 822 | - IS_END_BELOW_21_PCT = 825, // This ability requires you to be at or below 21% of your maximum endurance. - IS_END_BELOW_25_PCT = 826, // This ability requires you to be at or below 25% of your maximum endurance. - IS_END_BELOW_29_PCT = 827, // This ability requires you to be at or below 29% of your maximum endurance. - IS_REGULAR_SERVER = 836, // - IS_PROGRESSION_SERVER = 837, // + IS_END_BELOW_21_PCT = 825, // This ability requires you to be at or below 21% of your maximum endurance. + IS_END_BELOW_25_PCT = 826, // This ability requires you to be at or below 25% of your maximum endurance. + IS_END_BELOW_29_PCT = 827, // This ability requires you to be at or below 29% of your maximum endurance. + IS_REGULAR_SERVER = 836, // + IS_PROGRESSION_SERVER = 837, // IS_GOD_EXPANSION_UNLOCKED = 839, // UNKNOWN_840 = 840, // | caster restriction | seen in spell 6883 Expedient Recovery UNKNOWN_841 = 841, // | caster restriction | seen in spell 32192 Merciless Blow - IS_HUMANOID_LEVEL_84_MAX = 842, // - IS_HUMANOID_LEVEL_86_MAX = 843, // - IS_HUMANOID_LEVEL_88_MAX = 844, // + IS_HUMANOID_LEVEL_84_MAX = 842, // + IS_HUMANOID_LEVEL_86_MAX = 843, // + IS_HUMANOID_LEVEL_88_MAX = 844, // HAS_CRYSTALLIZED_FLAME_BUFF = 845, // This spell will only work on targets afflicted by Crystallized Flame. | On live spell does not appear to be a buff - HAS_INCENDIARY_OOZE_BUFF = 847, // This spell will only work on targets afflicted by Incendiary Ooze. - IS_LEVEL_90_MAX = 860, // - IS_LEVEL_92_MAX = 861, // - IS_LEVEL_94_MAX = 862, // - IS_LEVEL_95_MAX = 863, // - IS_LEVEL_97_MAX = 864, // - IS_LEVEL_99_MAX = 865, // + HAS_INCENDIARY_OOZE_BUFF = 847, // This spell will only work on targets afflicted by Incendiary Ooze. + IS_LEVEL_90_MAX = 860, // + IS_LEVEL_92_MAX = 861, // + IS_LEVEL_94_MAX = 862, // + IS_LEVEL_95_MAX = 863, // + IS_LEVEL_97_MAX = 864, // + IS_LEVEL_99_MAX = 865, // HAS_WEAPONSTANCE_DEFENSIVE_PROFICIENCY = 866, // | caster restriction | - HAS_WEAPONSTANCE_TWO_HAND_PROFICIENCY = 867, // | caster restriction | + HAS_WEAPONSTANCE_TWO_HAND_PROFICIENCY = 867, // | caster restriction | HAS_WEAPONSTANCE_DUAL_WEILD_PROFICIENCY = 868, // | caster restriction | - IS_LEVEL_100_MAX = 869, // - IS_LEVEL_102_MAX = 870, // - IS_LEVEL_104_MAX = 871, // - IS_LEVEL_105_MAX = 872, // - IS_LEVEL_107_MAX = 873, // - IS_LEVEL_109_MAX = 874, // - IS_LEVEL_110_MAX = 875, // - IS_LEVEL_112_MAX = 876, // - IS_LEVEL_114_MAX = 877, // + IS_LEVEL_100_MAX = 869, // + IS_LEVEL_102_MAX = 870, // + IS_LEVEL_104_MAX = 871, // + IS_LEVEL_105_MAX = 872, // + IS_LEVEL_107_MAX = 873, // + IS_LEVEL_109_MAX = 874, // + IS_LEVEL_110_MAX = 875, // + IS_LEVEL_112_MAX = 876, // + IS_LEVEL_114_MAX = 877, // HAS_TBL_ESIANTI_ACCESS = 997, // This spell will only transport adventurers who have gained access to Esianti: Palace of the Winds. | not implemented - HAS_ITEM_CLOCKWORK_SCRAPS = 999, // - IS_BETWEEN_LEVEL_1_AND_75 = 1000, // - IS_BETWEEN_LEVEL_76_AND_85 = 1001, // - IS_BETWEEN_LEVEL_86_AND_95 = 1002, // - IS_BETWEEN_LEVEL_96_AND_105 = 1003, // - IS_HP_LESS_THAN_80_PCT = 1004, // - IS_LEVEL_ABOVE_34 = 1474, // Your target must be level 35 or higher. - IN_TWO_HANDED_STANCE = 2000, // You must be in your two-handed stance to use this ability. - IN_DUAL_WIELD_HANDED_STANCE = 2001, // You must be in your dual-wielding stance to use this ability. - IN_SHIELD_STANCE = 2002, // You must be in your shield stance to use this ability. - NOT_IN_TWO_HANDED_STANCE = 2010, // You may not use this ability if you are in your two-handed stance. - NOT_IN_DUAL_WIELD_HANDED_STANCE = 2011, // You may not use this ability if you are in your dual-wielding stance. - NOT_IN_SHIELD_STANCE = 2012, // You may not use this ability if you are in your shield stance. - LEVEL_46_MAX = 2761, // - DISABLED_UNTIL_EXPANSION_ROK = 7000, // This ability is disabled until Ruins of Kunark. - DISABLED_UNTIL_EXPANSION_SOV = 7001, // This ability is disabled until Scars of Velious. - DISABLED_UNTIL_EXPANSION_SOL = 7002, // This ability is disabled until Shadows of Luclin. - DISABLED_UNTIL_EXPANSION_POP = 7003, // This ability is disabled until Planes of Power. - DISABLED_UNTIL_EXPANSION_LOY = 7004, // This ability is disabled until Legacy of Ykesha. - DISABLED_UNTIL_EXPANSION_LDON = 7005, // This ability is disabled until Lost Dungeons of Norrath. - DISABLED_UNTIL_EXPANSION_GOD = 7006, // This ability is disabled until Gates of Discord. - DISABLED_UNTIL_EXPANSION_OOW = 7007, // This ability is disabled until Omens of War. - DISABLED_UNTIL_EXPANSION_DON = 7008, // This ability is disabled until Dragons of Norrath. - DISABLED_UNTIL_EXPANSION_DOD = 7009, // This ability is disabled until Depths of Darkhollow. - DISABLED_UNTIL_EXPANSION_POR = 7010, // This ability is disabled until Prophecy of Ro. - DISABLED_UNTIL_EXPANSION_TSS = 7011, // This ability is disabled until Serpent's Spine. - DISABLED_UNTIL_EXPANSION_TBS = 7012, // This ability is disabled until Buried Sea. - DISABLED_UNTIL_EXPANSION_SOF = 7013, // This ability is disabled until Secrets of Faydwer. - DISABLED_UNTIL_EXPANSION_SOD = 7014, // This ability is disabled until Seeds of Destruction. - DISABLED_UNTIL_EXPANSION_UF = 7015, // This ability is disabled until Underfoot. - DISABLED_UNTIL_EXPANSION_HOT = 7016, // This ability is disabled until House of Thule. - DISABLED_UNTIL_EXPANSION_VOA = 7017, // This ability is disabled until Veil of Alaris. - DISABLED_UNTIL_EXPANSION_ROF = 7018, // This ability is disabled until Rain of Fear. - DISABLED_UNTIL_EXPANSION_COF = 7019, // This ability is disabled until Call of the Forsaken. - DISABLED_UNTIL_EXPANSION_TDS = 7020, // This ability is disabled until Darkened Sea. - DISABLED_UNTIL_EXPANSION_TBM = 7021, // This ability is disabled until Broken Mirror. - DISABLED_UNTIL_EXPANSION_EOK = 7022, // This ability is disabled until Empires of Kunark. - DISABLED_UNTIL_EXPANSION_ROS = 7023, // This ability is disabled until Ring of Scale. - DISABLED_UNTIL_EXPANSION_TBL = 7024, // This ability is disabled until The Burning Lands. - DISABLED_UNTIL_EXPANSION_TOV = 7025, // This ability is disabled until Torment of Velious. - DISABLED_UNTIL_EXPANSION_COV = 7026, // This ability is disabled until Claws of Veeshan. - HAS_NO_MANA_BURN_BUFF = 8450, // This spell will not take hold until the effects of the previous Mana Burn have expired. + HAS_ITEM_CLOCKWORK_SCRAPS = 999, // + IS_BETWEEN_LEVEL_1_AND_75 = 1000, // + IS_BETWEEN_LEVEL_76_AND_85 = 1001, // + IS_BETWEEN_LEVEL_86_AND_95 = 1002, // + IS_BETWEEN_LEVEL_96_AND_105 = 1003, // + IS_HP_LESS_THAN_80_PCT = 1004, // + IS_LEVEL_ABOVE_34 = 1474, // Your target must be level 35 or higher. + IN_TWO_HANDED_STANCE = 2000, // You must be in your two-handed stance to use this ability. + IN_DUAL_WIELD_HANDED_STANCE = 2001, // You must be in your dual-wielding stance to use this ability. + IN_SHIELD_STANCE = 2002, // You must be in your shield stance to use this ability. + NOT_IN_TWO_HANDED_STANCE = 2010, // You may not use this ability if you are in your two-handed stance. + NOT_IN_DUAL_WIELD_HANDED_STANCE = 2011, // You may not use this ability if you are in your dual-wielding stance. + NOT_IN_SHIELD_STANCE = 2012, // You may not use this ability if you are in your shield stance. + LEVEL_46_MAX = 2761, // + DISABLED_UNTIL_EXPANSION_ROK = 7000, // This ability is disabled until Ruins of Kunark. + DISABLED_UNTIL_EXPANSION_SOV = 7001, // This ability is disabled until Scars of Velious. + DISABLED_UNTIL_EXPANSION_SOL = 7002, // This ability is disabled until Shadows of Luclin. + DISABLED_UNTIL_EXPANSION_POP = 7003, // This ability is disabled until Planes of Power. + DISABLED_UNTIL_EXPANSION_LOY = 7004, // This ability is disabled until Legacy of Ykesha. + DISABLED_UNTIL_EXPANSION_LDON = 7005, // This ability is disabled until Lost Dungeons of Norrath. + DISABLED_UNTIL_EXPANSION_GOD = 7006, // This ability is disabled until Gates of Discord. + DISABLED_UNTIL_EXPANSION_OOW = 7007, // This ability is disabled until Omens of War. + DISABLED_UNTIL_EXPANSION_DON = 7008, // This ability is disabled until Dragons of Norrath. + DISABLED_UNTIL_EXPANSION_DOD = 7009, // This ability is disabled until Depths of Darkhollow. + DISABLED_UNTIL_EXPANSION_POR = 7010, // This ability is disabled until Prophecy of Ro. + DISABLED_UNTIL_EXPANSION_TSS = 7011, // This ability is disabled until Serpent's Spine. + DISABLED_UNTIL_EXPANSION_TBS = 7012, // This ability is disabled until Buried Sea. + DISABLED_UNTIL_EXPANSION_SOF = 7013, // This ability is disabled until Secrets of Faydwer. + DISABLED_UNTIL_EXPANSION_SOD = 7014, // This ability is disabled until Seeds of Destruction. + DISABLED_UNTIL_EXPANSION_UF = 7015, // This ability is disabled until Underfoot. + DISABLED_UNTIL_EXPANSION_HOT = 7016, // This ability is disabled until House of Thule. + DISABLED_UNTIL_EXPANSION_VOA = 7017, // This ability is disabled until Veil of Alaris. + DISABLED_UNTIL_EXPANSION_ROF = 7018, // This ability is disabled until Rain of Fear. + DISABLED_UNTIL_EXPANSION_COF = 7019, // This ability is disabled until Call of the Forsaken. + DISABLED_UNTIL_EXPANSION_TDS = 7020, // This ability is disabled until Darkened Sea. + DISABLED_UNTIL_EXPANSION_TBM = 7021, // This ability is disabled until Broken Mirror. + DISABLED_UNTIL_EXPANSION_EOK = 7022, // This ability is disabled until Empires of Kunark. + DISABLED_UNTIL_EXPANSION_ROS = 7023, // This ability is disabled until Ring of Scale. + DISABLED_UNTIL_EXPANSION_TBL = 7024, // This ability is disabled until The Burning Lands. + DISABLED_UNTIL_EXPANSION_TOV = 7025, // This ability is disabled until Torment of Velious. + DISABLED_UNTIL_EXPANSION_COV = 7026, // This ability is disabled until Claws of Veeshan. + HAS_NO_MANA_BURN_BUFF = 8450, // This spell will not take hold until the effects of the previous Mana Burn have expired. IS_RACE_FIRST_CUSTOM = 10000, // | custom range to restrict targets or casters by race *not on live* | IS_RACE_LAST_CUSTOM = 11000, // | custom range to restrict targets or casters by race *not on live* | - IS_CLIENT_AND_MALE_PLATE_USER = 11044, // Your target wouldn't look right as that Jann. - IS_CLEINT_AND_MALE_DRUID_ENCHANTER_MAGICIAN_NECROANCER_SHAMAN_OR_WIZARD = 11090, // Your target wouldn't look right as that Jann. - IS_CLIENT_AND_MALE_BEASTLORD_BERSERKER_MONK_RANGER_OR_ROGUE = 11209, // Your target wouldn't look right as that Jann. - IS_CLIENT_AND_FEMALE_PLATE_USER = 11210, // Your target wouldn't look right as that Jann. - IS_CLIENT_AND_FEMALE_DRUID_ENCHANTER_MAGICIAN_NECROANCER_SHAMAN_OR_WIZARD = 11211, // Your target wouldn't look right as that Jann. - IS_CLIENT_AND_FEMALE_BEASTLORD_BERSERKER_MONK_RANGER_OR_ROGUE = 11248, // Your target wouldn't look right as that Jann. - HAS_TRAVELED_TO_STRATOS = 11260, // You must travel to Stratos at least once before wishing to go there. - HAS_TRAVELED_TO_AALISHAI = 11261, // You must travel to Aalishai at least once before wishing to go there. - HAS_TRAVELED_TO_MEARATS = 11268, // You must travel to Mearatas at least once before wishing to go there. - HAS_NO_ILLUSIONS_OF_GRANDEUR_BUFF = 12519, // - IS_HP_ABOVE_50_PCT = 16010, // - IS_HP_UNDER_50_PCT = 16031, // - IS_OFF_HAND_EQUIPED = 27672, // You must be wielding a weapon or shield in your offhand to use this ability. + IS_CLIENT_AND_MALE_PLATE_USER = 11044, // Your target wouldn't look right as that Jann. + IS_CLEINT_AND_MALE_DRUID_ENCHANTER_MAGICIAN_NECROANCER_SHAMAN_OR_WIZARD = 11090, // Your target wouldn't look right as that Jann. + IS_CLIENT_AND_MALE_BEASTLORD_BERSERKER_MONK_RANGER_OR_ROGUE = 11209, // Your target wouldn't look right as that Jann. + IS_CLIENT_AND_FEMALE_PLATE_USER = 11210, // Your target wouldn't look right as that Jann. + IS_CLIENT_AND_FEMALE_DRUID_ENCHANTER_MAGICIAN_NECROANCER_SHAMAN_OR_WIZARD = 11211, // Your target wouldn't look right as that Jann. + IS_CLIENT_AND_FEMALE_BEASTLORD_BERSERKER_MONK_RANGER_OR_ROGUE = 11248, // Your target wouldn't look right as that Jann. + HAS_TRAVELED_TO_STRATOS = 11260, // You must travel to Stratos at least once before wishing to go there. + HAS_TRAVELED_TO_AALISHAI = 11261, // You must travel to Aalishai at least once before wishing to go there. + HAS_TRAVELED_TO_MEARATS = 11268, // You must travel to Mearatas at least once before wishing to go there. + HAS_NO_ILLUSIONS_OF_GRANDEUR_BUFF = 12519, // + IS_HP_ABOVE_50_PCT = 16010, // + IS_HP_UNDER_50_PCT = 16031, // + IS_OFF_HAND_EQUIPED = 27672, // You must be wielding a weapon or shield in your offhand to use this ability. HAS_NO_PACT_OF_FATE_RECOURSE_BUFF = 29556, // This spell will not work while Pact of Fate Recourse is active. | caster restriction | - HAS_NO_SHROUD_OF_PRAYER_BUFF = 32339, // Your target cannot receive another Quiet Prayer this soon. - IS_MANA_BELOW_20_PCT = 38311, // This ability requires you to be at or below 20% of your maximum mana. - IS_MANA_ABOVE_50_PCT = 38312, // This ability requires you to be at or above 50% of your maximum mana. - COMPLETED_ACHIEVEMENT_LEGENDARY_ANSWERER = 39281, // You have completed Legendary Answerer. + HAS_NO_SHROUD_OF_PRAYER_BUFF = 32339, // Your target cannot receive another Quiet Prayer this soon. + IS_MANA_BELOW_20_PCT = 38311, // This ability requires you to be at or below 20% of your maximum mana. + IS_MANA_ABOVE_50_PCT = 38312, // This ability requires you to be at or above 50% of your maximum mana. + COMPLETED_ACHIEVEMENT_LEGENDARY_ANSWERER = 39281, // You have completed Legendary Answerer. HAS_NO_ROGUES_FURY_BUFF = 40297, // This spell will not affect anyone that currently has Rogue's Fury active. | caster restriction | - NOT_COMPLETED_ACHIEVEMENT_LEGENDARY_ANSWERER = 42280, // You must complete Legendary Answerer. - IS_SUMMONED_OR_UNDEAD = 49326, // - IS_CLASS_CASTER_PRIEST = 49529, // + NOT_COMPLETED_ACHIEVEMENT_LEGENDARY_ANSWERER = 42280, // You must complete Legendary Answerer. + IS_SUMMONED_OR_UNDEAD = 49326, // + IS_CLASS_CASTER_PRIEST = 49529, // IS_END_OR_MANA_ABOVE_20_PCT = 49543, // You must have at least 20% of your maximum mana and endurance to use this ability. //pure melee class check end, other check mana IS_END_OR_MANA_BELOW_30_PCT = 49573, // Your target already has 30% or more of their maximum mana or endurance. //pure melee class check the, other check more - IS_CLASS_BARD2 = 49574, // - IS_NOT_CLASS_BARD = 49575, // - HAS_NO_FURIOUS_RAMPAGE_BUFF = 49612, // This ability cannot be activated while Furious Rampage is active. - IS_END_OR_MANA_BELOW_30_PCT2 = 49809, // You can only perform this solo if you have less than 30% mana or endurance. - HAS_NO_HARMONIOUS_PRECISION_BUFF = 50003, // This spell will not work if you have the Harmonious Precision line active. + IS_CLASS_BARD2 = 49574, // + IS_NOT_CLASS_BARD = 49575, // + HAS_NO_FURIOUS_RAMPAGE_BUFF = 49612, // This ability cannot be activated while Furious Rampage is active. + IS_END_OR_MANA_BELOW_30_PCT2 = 49809, // You can only perform this solo if you have less than 30% mana or endurance. + HAS_NO_HARMONIOUS_PRECISION_BUFF = 50003, // This spell will not work if you have the Harmonious Precision line active. HAS_NO_HARMONIOUS_EXPANSE_BUFF = 50009, // This spell will not work if you have the Harmonious Expanse line active. UNKNOWN_99999 = 99999, // | caster restriction | works will spell 27672 Strike of Ire }; @@ -912,7 +912,7 @@ typedef enum { #define SE_FleshToBone 207 // implemented //#define SE_PurgePoison 208 // not used #define SE_DispelBeneficial 209 // implemented, @Dispel, removes only beneficial effects on a target, base: pct chance (950=95%), limit: none, max: none -#define SE_PetShield 210 // implmented, @ShieldAbility, allows pet to 'shield' owner for 50 pct of damage taken for a duration, base: Time multiplier 1=12 seconds, 2=24 ect, limit: mitigation on pet owner override (not on live), max: mitigation on pet overide (not on live) +#define SE_PetShield 210 // implmented, @ShieldAbility, allows pet to 'shield' owner for 50 pct of damage taken for a duration, base: Time multiplier 1=12 seconds, 2=24 ect, limit: mitigation on pet owner override (not on live), max: mitigation on pet overide (not on live) #define SE_AEMelee 211 // implemented TO DO: Implement to allow NPC use (client only atm). #define SE_FrenziedDevastation 212 // implemented - increase spell criticals + all DD spells cast 2x mana. #define SE_PetMaxHP 213 // implemented[AA] - increases the maximum hit points of your pet @@ -993,7 +993,7 @@ typedef enum { #define SE_SkillAttackProc 288 // implemented[AA] - Chance to proc spell on skill attack usage (ex. Dragon Punch) #define SE_CastOnFadeEffect 289 // implemented - Triggers only if fades after natural duration. #define SE_IncreaseRunSpeedCap 290 // implemented[AA] - increases run speed over the hard cap -#define SE_Purify 291 // implemented, @Dispel, remove up specified amount of detiremental spells, base: amt removed, limit: none, max: none, Note: excluding charm, fear, resurrection, and revival sickness +#define SE_Purify 291 // implemented, @Dispel, remove up specified amount of detiremental spells, base: amt removed, limit: none, max: none, Note: excluding charm, fear, resurrection, and revival sickness #define SE_StrikeThrough2 292 // implemented[AA] - increasing chance of bypassing an opponent's special defenses, such as dodge, block, parry, and riposte. #define SE_FrontalStunResist 293 // implemented[AA] - Reduce chance to be stunned from front. -- live descriptions sounds like this isn't limited to frontal anymore #define SE_CriticalSpellChance 294 // implemented - increase chance to critical hit and critical damage modifier. @@ -1041,7 +1041,7 @@ typedef enum { //#define SE_IllusionaryTarget 336 // not used #define SE_PercentXPIncrease 337 // implemented #define SE_SummonAndResAllCorpses 338 // implemented -#define SE_TriggerOnCast 339 // implemented, @Fc, On Caster, cast on spell use, base: chance pct limit: spellid +#define SE_TriggerOnCast 339 // implemented, @Fc, On Caster, cast on spell use, base: chance pct limit: spellid #define SE_SpellTrigger 340 // implemented - chance to trigger spell [Share rolls with 469] All base2 spells share roll chance, only 1 cast. #define SE_ItemAttackCapIncrease 341 // implemented[AA] - increases the maximum amount of attack you can gain from items. #define SE_ImmuneFleeing 342 // implemented - stop mob from fleeing @@ -1186,14 +1186,14 @@ typedef enum { #define SE_Fc_Cast_Spell_On_Land 481 // implemented, @Fc, On Target, cast spell if hit by spell, base: chance pct, limit: spellid #define SE_Skill_Base_Damage_Mod 482 // implemented, @OffBonus, modify base melee damage by percent, base: pct, limit: skill(-1=ALL), max: none #define SE_Fc_Spell_Damage_Pct_IncomingPC 483 // implemented, @Fc, On Target, spell damage taken mod pct, base: min pct, limit: max pct -#define SE_Fc_Spell_Damage_Amt_IncomingPC 484 // implemented, @Fc, On Target, damage taken flat amt, base: amt +#define SE_Fc_Spell_Damage_Amt_IncomingPC 484 // implemented, @Fc, On Target, damage taken flat amt, base: amt #define SE_Ff_CasterClass 485 // implemented, @Ff, Caster of spell on target with a focus effect that is checked by incoming spells must be specified class(es). base1: class(es), Note: Set multiple classes same as would for items #define SE_Ff_Same_Caster 486 // implemented, @Ff, Caster of spell on target with a focus effect that is checked by incoming spells, base1: 0=Must be different caster 1=Must be same caster //#define SE_Extend_Tradeskill_Cap 487 // //#define SE_Defender_Melee_Force_Pct_PC 488 // #define SE_Worn_Endurance_Regen_Cap 489 // implemented, modify worn regen cap, base: amt, limit: none, max: none #define SE_Ff_ReuseTimeMin 490 // implemented, @Ff, Minimum recast time of a spell that can be focused, base: recast time -#define SE_Ff_ReuseTimeMax 491 // implemented, @Ff, Max recast time of a spell that can be focused, base: recast time +#define SE_Ff_ReuseTimeMax 491 // implemented, @Ff, Max recast time of a spell that can be focused, base: recast time #define SE_Ff_Endurance_Min 492 // implemented, @Ff, Minimum endurance cost of a spell that can be focused, base: endurance cost #define SE_Ff_Endurance_Max 493 // implemented, @Ff, Max endurance cost of a spell that can be focused, base: endurance cost #define SE_Pet_Add_Atk 494 // implemented - Bonus on pet owner which gives their pet increased attack stat @@ -1213,7 +1213,7 @@ typedef enum { #define SE_Fc_Amplify_Amt 508 // implemented, @Fc, On Caster, damage-heal-dot mod flat amt, base: amt #define SE_Health_Transfer 509 // implemented - exchange health for damage or healing on a target. ie Lifeburn/Act of Valor #define SE_Fc_ResistIncoming 510 // implemented, @Fc, On Target, resist modifier, base: amt -#define SE_Ff_FocusTimerMin 511 // implemented, @Ff, sets a recast time until focus can be used again, base: 1, limit: time ms, Note: ie. limit to 1 trigger every 1.5 seconds +#define SE_Ff_FocusTimerMin 511 // implemented, @Ff, sets a recast time until focus can be used again, base: 1, limit: time ms, Note: ie. limit to 1 trigger every 1.5 seconds #define SE_Proc_Timer_Modifier 512 // implemented - limits procs per amount of a time based on timer value, base: 1, limit: time ms, Note:, ie limit to 1 proc every 55 seconds) //#define SE_Mana_Max_Percent 513 // //#define SE_Endurance_Max_Percent 514 // @@ -1531,5 +1531,6 @@ bool IsShortDurationBuff(uint16 spell_id); bool IsSpellUsableThisZoneType(uint16 spell_id, uint8 zone_type); const char *GetSpellName(uint16 spell_id); int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot = 0); +bool CastRestrictedSpell(int spellid); #endif diff --git a/utils/gm_commands/main.go b/utils/gm_commands/main.go new file mode 100644 index 000000000..4ef678d97 --- /dev/null +++ b/utils/gm_commands/main.go @@ -0,0 +1,183 @@ +package main + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "sort" + "strings" +) + +func main() { + // zone/command.cpp + commands, err := os.ReadFile("./zone/command.cpp") + if err != nil { + log.Fatal(err) + } + commandsString := string(commands) + + s := strings.Split(commandsString, "void command_") + commandFiles := []string{} + if len(s) > 1 { + startListing := false + for i, chunk := range s { + if strings.Contains(chunk, "logcommand(Client *c") { + startListing = true + } + + // get function name + functionName := "" + nameSplit := strings.Split(chunk, "(Client") + if len(nameSplit) > 0 { + functionName = strings.TrimSpace(nameSplit[0]) + } + + if startListing && + len(s[i-1]) > 0 && + !strings.Contains(s[i-1], "#ifdef") && + !strings.Contains(chunk, "#ifdef") && + !strings.Contains(chunk, "#ifdef BOTS") && + !strings.Contains(chunk, "#ifdef EQPROFILE") && + !strings.Contains(functionName, "bot") && + !strings.Contains(functionName, "help") && + !strings.Contains(functionName, "findaliases") { + + fmt.Println(functionName) + + // build command file name + commandFile := fmt.Sprintf("zone/gm_commands/%v.cpp", functionName) + + // append command file nam eto list + commandFiles = append(commandFiles, commandFile) + + includes := "" + if strings.Contains(chunk, "Client") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../client.h\"") + } + if strings.Contains(chunk, "parse->") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../quest_parser_collection.h\"") + } + if strings.Contains(chunk, "worldserver.") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../worldserver.h\"") + includes = fmt.Sprintf("%v%v\n", includes, "extern WorldServer worldserver;") + } + if strings.Contains(chunk, "RegionType") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../water_map.h\"") + } + if strings.Contains(chunk, "Corpse") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../corpse.h\"") + } + if strings.Contains(chunk, "Object") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../object.h\"") + } + if strings.Contains(chunk, "DoorManipulation") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"door_manipulation.h\"") + } + if strings.Contains(chunk, "Group") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../groups.h\"") + } + if strings.Contains(chunk, "httplib") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../../common/http/httplib.h\"") + } + if strings.Contains(chunk, "guild_mgr") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../guild_mgr.h\"") + } + if strings.Contains(chunk, "expedition") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../expedition.h\"") + } + if strings.Contains(chunk, "DataBucket::") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../data_bucket.h\"") + } + if strings.Contains(chunk, "file_exists") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../../common/file_util.h\"") + } + if strings.Contains(chunk, "std::thread") { + includes = fmt.Sprintf("%v%v\n", includes, "#include ") + } + if strings.Contains(chunk, "Door") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../doors.h\"") + } + if strings.Contains(chunk, "NOW_INVISIBLE") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../string_ids.h\"") + } + if strings.Contains(chunk, "Expansion::") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../../common/content/world_content_service.h\"") + } + if strings.Contains(chunk, "MobMovementManager::") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../mob_movement_manager.h\"") + } + if strings.Contains(chunk, "MobStuckBehavior::") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../mob_movement_manager.h\"") + } + if strings.Contains(chunk, "ReloadAllPatches") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../../common/patches/patches.h\"") + } + if strings.Contains(chunk, "ProfanityManager") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../../common/profanity_manager.h\"") + } + if strings.Contains(chunk, "npc_scale_manager") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../npc_scale_manager.h\"") + } + if strings.Contains(chunk, "g_Math") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../fastmath.h\"") + includes = fmt.Sprintf("%v%v\n", includes, "extern FastMath g_Math;") + } + if strings.Contains(chunk, "raid") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../raids.h\"") + } + if strings.Contains(chunk, "Raid") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../raids.h\"") + } + if strings.Contains(chunk, "GetOS") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../../common/serverinfo.h\"") + } + if strings.Contains(chunk, "LANG_") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../../common/languages.h\"") + } + if strings.Contains(chunk, "ServerOP_Shared") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../../common/shared_tasks.h\"") + } + if strings.Contains(chunk, "title_manager") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../titles.h\"") + } + if strings.Contains(chunk, "CatchSignal") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../../world/main.h\"") + } + + // build the contents of the command file + commandString := fmt.Sprintf("%v\nvoid command_%v", includes, chunk) + + //write file contents + err := ioutil.WriteFile(commandFile, []byte(commandString), 0777) + if err != nil { + fmt.Println(err) + } + + commandOnly := fmt.Sprintf("void command_%v", chunk) + commandsString = strings.ReplaceAll(commandsString, commandOnly, "") + + } + } + + // rewrite commands.cpp with functions removed + err := ioutil.WriteFile("zone/command.cpp", []byte(commandsString), 0777) + if err != nil { + fmt.Println(err) + } + + fmt.Println("# CmakeLists") + + // sort a-z + sort.Slice(commandFiles, func(i, j int) bool { + return commandFiles[i] < commandFiles[j] + }) + + for _, file := range commandFiles { + file = strings.ReplaceAll(file, "zone/", "") + fmt.Println(file) + } + } + + //fmt.Print(string(commands)) +} diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index dd4188c25..46c235455 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -1,293 +1,576 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.2) SET(zone_sources - aa.cpp - aa_ability.cpp - aggro.cpp - aggromanager.cpp - api_service.cpp - attack.cpp - aura.cpp - beacon.cpp - bonuses.cpp - bot.cpp - bot_command.cpp - bot_database.cpp - botspellsai.cpp - cheat_manager.cpp - client.cpp - client_mods.cpp - client_packet.cpp - client_process.cpp - command.cpp - corpse.cpp - data_bucket.cpp - doors.cpp - dialogue_window.cpp - dynamic_zone.cpp - effects.cpp - embparser.cpp - embparser_api.cpp - embperl.cpp - embxs.cpp - encounter.cpp - entity.cpp - exp.cpp - expedition.cpp - expedition_database.cpp - expedition_request.cpp - fastmath.cpp - fearpath.cpp - forage.cpp - groups.cpp - guild.cpp - guild_mgr.cpp - hate_list.cpp - heal_rotation.cpp - horse.cpp - inventory.cpp - loottables.cpp - lua_bot.cpp - lua_bit.cpp - lua_corpse.cpp - lua_client.cpp - lua_door.cpp - lua_encounter.cpp - lua_entity.cpp - lua_entity_list.cpp - lua_expedition.cpp - lua_general.cpp - lua_group.cpp - lua_hate_list.cpp - lua_inventory.cpp - lua_item.cpp - lua_iteminst.cpp - lua_mob.cpp - lua_mod.cpp - lua_npc.cpp - lua_object.cpp - lua_packet.cpp - lua_parser.cpp - lua_parser_events.cpp - lua_raid.cpp - lua_spawn.cpp - lua_spell.cpp - lua_stat_bonuses.cpp - embperl.cpp - embxs.cpp - entity.cpp - exp.cpp - fearpath.cpp - forage.cpp - global_loot_manager.cpp - gm_commands/door_manipulation.cpp - groups.cpp - guild.cpp - guild_mgr.cpp - hate_list.cpp - horse.cpp - inventory.cpp - loottables.cpp - main.cpp - map.cpp - merc.cpp - mob.cpp - mob_ai.cpp - mob_appearance.cpp - mob_movement_manager.cpp - mob_info.cpp - mod_functions.cpp - npc.cpp - npc_ai.cpp - npc_scale_manager.cpp - object.cpp - oriented_bounding_box.cpp - pathfinder_interface.cpp - pathfinder_nav_mesh.cpp - pathfinder_null.cpp - pathing.cpp - perl_bot.cpp - perl_client.cpp - perl_doors.cpp - perl_entity.cpp - perl_expedition.cpp - perl_groups.cpp - perl_hateentry.cpp - perl_inventory.cpp - perl_mob.cpp - perl_npc.cpp - perl_object.cpp - perl_perlpacket.cpp - perl_player_corpse.cpp - perl_questitem.cpp - perl_raids.cpp - perl_spell.cpp - perlpacket.cpp - petitions.cpp - pets.cpp - position.cpp - qglobals.cpp - queryserv.cpp - questmgr.cpp - quest_parser_collection.cpp - raids.cpp - raycast_mesh.cpp - shared_task_zone_messaging.cpp - spawn2.cpp - spawn2.h - spawngroup.cpp - special_attacks.cpp - spell_effects.cpp - spells.cpp - task_client_state.cpp - task_goal_list_manager.cpp - task_manager.cpp - task_proximity_manager.cpp - tasks.cpp - titles.cpp - tradeskills.cpp - trading.cpp - trap.cpp - tribute.cpp - tune.cpp - water_map.cpp - water_map_v1.cpp - water_map_v2.cpp - waypoints.cpp - worldserver.cpp - xtargetautohaters.cpp - zone.cpp - zone_config.cpp - zonedb.cpp - zone_event_scheduler.cpp - zone_reload.cpp - zone_store.cpp - zoning.cpp -) + aa.cpp + aa_ability.cpp + aggro.cpp + aggromanager.cpp + api_service.cpp + attack.cpp + aura.cpp + beacon.cpp + bonuses.cpp + bot.cpp + bot_command.cpp + bot_database.cpp + botspellsai.cpp + cheat_manager.cpp + client.cpp + client_mods.cpp + client_packet.cpp + client_process.cpp + command.cpp + corpse.cpp + data_bucket.cpp + doors.cpp + dialogue_window.cpp + dynamic_zone.cpp + effects.cpp + embparser.cpp + embparser_api.cpp + embperl.cpp + embxs.cpp + encounter.cpp + entity.cpp + exp.cpp + expedition.cpp + expedition_database.cpp + expedition_request.cpp + fastmath.cpp + fearpath.cpp + forage.cpp + groups.cpp + guild.cpp + guild_mgr.cpp + hate_list.cpp + heal_rotation.cpp + horse.cpp + inventory.cpp + loottables.cpp + lua_bot.cpp + lua_bit.cpp + lua_corpse.cpp + lua_client.cpp + lua_door.cpp + lua_encounter.cpp + lua_entity.cpp + lua_entity_list.cpp + lua_expedition.cpp + lua_general.cpp + lua_group.cpp + lua_hate_list.cpp + lua_inventory.cpp + lua_item.cpp + lua_iteminst.cpp + lua_mob.cpp + lua_mod.cpp + lua_npc.cpp + lua_object.cpp + lua_packet.cpp + lua_parser.cpp + lua_parser_events.cpp + lua_raid.cpp + lua_spawn.cpp + lua_spell.cpp + lua_stat_bonuses.cpp + embperl.cpp + embxs.cpp + entity.cpp + exp.cpp + fearpath.cpp + forage.cpp + global_loot_manager.cpp + gm_commands/door_manipulation.cpp + groups.cpp + guild.cpp + guild_mgr.cpp + hate_list.cpp + horse.cpp + inventory.cpp + loottables.cpp + main.cpp + map.cpp + merc.cpp + mob.cpp + mob_ai.cpp + mob_appearance.cpp + mob_movement_manager.cpp + mob_info.cpp + mod_functions.cpp + npc.cpp + npc_ai.cpp + npc_scale_manager.cpp + object.cpp + oriented_bounding_box.cpp + pathfinder_interface.cpp + pathfinder_nav_mesh.cpp + pathfinder_null.cpp + pathing.cpp + perl_bot.cpp + perl_client.cpp + perl_doors.cpp + perl_entity.cpp + perl_expedition.cpp + perl_groups.cpp + perl_hateentry.cpp + perl_inventory.cpp + perl_mob.cpp + perl_npc.cpp + perl_object.cpp + perl_perlpacket.cpp + perl_player_corpse.cpp + perl_questitem.cpp + perl_raids.cpp + perl_spell.cpp + perlpacket.cpp + petitions.cpp + pets.cpp + position.cpp + qglobals.cpp + queryserv.cpp + questmgr.cpp + quest_parser_collection.cpp + raids.cpp + raycast_mesh.cpp + shared_task_zone_messaging.cpp + spawn2.cpp + spawn2.h + spawngroup.cpp + special_attacks.cpp + spell_effects.cpp + spells.cpp + task_client_state.cpp + task_goal_list_manager.cpp + task_manager.cpp + task_proximity_manager.cpp + tasks.cpp + titles.cpp + tradeskills.cpp + trading.cpp + trap.cpp + tribute.cpp + tune.cpp + water_map.cpp + water_map_v1.cpp + water_map_v2.cpp + waypoints.cpp + worldserver.cpp + xtargetautohaters.cpp + zone.cpp + zone_config.cpp + zonedb.cpp + zone_event_scheduler.cpp + zone_reload.cpp + zone_store.cpp + zoning.cpp + ) SET(zone_headers - aa.h - aa_ability.h - aggromanager.h - api_service.h - aura.h - basic_functions.h - beacon.h - bot.h - bot_command.h - bot_database.h - bot_structs.h - cheat_manager.h - client.h - client_packet.h - command.h - common.h - corpse.h - data_bucket.h - doors.h - dialogue_window.h - dynamic_zone.h - embparser.h - embperl.h - embxs.h - encounter.h - entity.h - errmsg.h - event_codes.h - expedition.h - expedition_database.h - expedition_request.h - fastmath.h - forage.h - global_loot_manager.h - gm_commands/door_manipulation.h - groups.h - guild_mgr.h - hate_list.h - heal_rotation.h - horse.h - lua_bot.h - lua_bit.h - lua_client.h - lua_corpse.h - lua_door.h - lua_encounter.h - lua_entity.h - lua_entity_list.h - lua_expedition.h - lua_general.h - lua_group.h - lua_hate_list.h - lua_inventory.h - lua_item.h - lua_iteminst.h - lua_mob.h - lua_mod.h - lua_npc.h - lua_object.h - lua_packet.h - lua_parser.h - lua_parser_events.h - lua_ptr.h - lua_raid.h - lua_spawn.h - lua_spell.h - lua_stat_bonuses.h - map.h - masterentity.h - maxskill.h - message.h - merc.h - mob.h - mob_movement_manager.h - npc.h - npc_ai.h - npc_scale_manager.h - object.h - oriented_bounding_box.h - pathfinder_interface.h - pathfinder_nav_mesh.h - pathfinder_null.h - perlpacket.h - petitions.h - pets.h - position.h - qglobals.h - quest_interface.h - queryserv.h - quest_interface.h - questmgr.h - quest_parser_collection.h - raids.h - raycast_mesh.h - skills.h - shared_task_zone_messaging.h - spawn2.cpp - spawn2.h - spawngroup.h - string_ids.h - task_client_state.h - task_goal_list_manager.h - task_manager.h - task_proximity_manager.h - tasks.h - titles.h - trap.h - water_map.h - water_map_v1.h - water_map_v2.h - worldserver.h - xtargetautohaters.h - zone.h - zone_event_scheduler.h - zone_config.h - zonedb.h - zonedump.h - zone_reload.h - zone_store.h + aa.h + aa_ability.h + aggromanager.h + api_service.h + aura.h + basic_functions.h + beacon.h + bot.h + bot_command.h + bot_database.h + bot_structs.h + cheat_manager.h + client.h + client_packet.h + command.h + common.h + corpse.h + data_bucket.h + doors.h + dialogue_window.h + dynamic_zone.h + embparser.h + embperl.h + embxs.h + encounter.h + entity.h + errmsg.h + event_codes.h + expedition.h + expedition_database.h + expedition_request.h + fastmath.h + forage.h + global_loot_manager.h + gm_commands/door_manipulation.h + groups.h + guild_mgr.h + hate_list.h + heal_rotation.h + horse.h + lua_bot.h + lua_bit.h + lua_client.h + lua_corpse.h + lua_door.h + lua_encounter.h + lua_entity.h + lua_entity_list.h + lua_expedition.h + lua_general.h + lua_group.h + lua_hate_list.h + lua_inventory.h + lua_item.h + lua_iteminst.h + lua_mob.h + lua_mod.h + lua_npc.h + lua_object.h + lua_packet.h + lua_parser.h + lua_parser_events.h + lua_ptr.h + lua_raid.h + lua_spawn.h + lua_spell.h + lua_stat_bonuses.h + map.h + masterentity.h + maxskill.h + message.h + merc.h + mob.h + mob_movement_manager.h + npc.h + npc_ai.h + npc_scale_manager.h + object.h + oriented_bounding_box.h + pathfinder_interface.h + pathfinder_nav_mesh.h + pathfinder_null.h + perlpacket.h + petitions.h + pets.h + position.h + qglobals.h + quest_interface.h + queryserv.h + quest_interface.h + questmgr.h + quest_parser_collection.h + raids.h + raycast_mesh.h + skills.h + shared_task_zone_messaging.h + spawn2.cpp + spawn2.h + spawngroup.h + string_ids.h + task_client_state.h + task_goal_list_manager.h + task_manager.h + task_proximity_manager.h + tasks.h + titles.h + trap.h + water_map.h + water_map_v1.h + water_map_v2.h + worldserver.h + xtargetautohaters.h + zone.h + zone_event_scheduler.h + zone_config.h + zonedb.h + zonedump.h + zone_reload.h + zone_store.h + ) + +SET(gm_commands + gm_commands/acceptrules.cpp + gm_commands/advnpcspawn.cpp + gm_commands/aggro.cpp + gm_commands/aggrozone.cpp + gm_commands/ai.cpp + gm_commands/appearance.cpp + gm_commands/attack.cpp + gm_commands/augmentitem.cpp + gm_commands/ban.cpp + gm_commands/beard.cpp + gm_commands/beardcolor.cpp + gm_commands/bestz.cpp + gm_commands/bind.cpp + gm_commands/camerashake.cpp + gm_commands/castspell.cpp + gm_commands/chat.cpp + gm_commands/checklos.cpp + gm_commands/copycharacter.cpp + gm_commands/corpse.cpp + gm_commands/corpsefix.cpp + gm_commands/cvs.cpp + gm_commands/damage.cpp + gm_commands/databuckets.cpp + gm_commands/date.cpp + gm_commands/dbspawn2.cpp + gm_commands/delacct.cpp + gm_commands/deletegraveyard.cpp + gm_commands/delpetition.cpp + gm_commands/depop.cpp + gm_commands/depopzone.cpp + gm_commands/details.cpp + gm_commands/devtools.cpp + gm_commands/disablerecipe.cpp + gm_commands/disarmtrap.cpp + gm_commands/distance.cpp + gm_commands/doanim.cpp + gm_commands/door.cpp + gm_commands/dye.cpp + gm_commands/dz.cpp + gm_commands/dzkickplayers.cpp + gm_commands/editmassrespawn.cpp + gm_commands/emote.cpp + gm_commands/emotesearch.cpp + gm_commands/emoteview.cpp + gm_commands/enablerecipe.cpp + gm_commands/endurance.cpp + gm_commands/equipitem.cpp + gm_commands/face.cpp + gm_commands/faction.cpp + gm_commands/findclass.cpp + gm_commands/findfaction.cpp + gm_commands/findnpctype.cpp + gm_commands/findrace.cpp + gm_commands/findskill.cpp + gm_commands/findspell.cpp + gm_commands/findtask.cpp + gm_commands/findzone.cpp + gm_commands/fixmob.cpp + gm_commands/flag.cpp + gm_commands/flagedit.cpp + gm_commands/flags.cpp + gm_commands/flymode.cpp + gm_commands/fov.cpp + gm_commands/freeze.cpp + gm_commands/gassign.cpp + gm_commands/gearup.cpp + gm_commands/gender.cpp + gm_commands/getplayerburiedcorpsecount.cpp + gm_commands/getvariable.cpp + gm_commands/ginfo.cpp + gm_commands/giveitem.cpp + gm_commands/givemoney.cpp + gm_commands/globalview.cpp + gm_commands/gm.cpp + gm_commands/gmspeed.cpp + gm_commands/gmzone.cpp + gm_commands/goto.cpp + gm_commands/grid.cpp + gm_commands/guild.cpp + gm_commands/guildapprove.cpp + gm_commands/guildcreate.cpp + gm_commands/guildlist.cpp + gm_commands/hair.cpp + gm_commands/haircolor.cpp + gm_commands/haste.cpp + gm_commands/hatelist.cpp + gm_commands/heal.cpp + gm_commands/helm.cpp + gm_commands/heritage.cpp + gm_commands/heromodel.cpp + gm_commands/hideme.cpp + gm_commands/hp.cpp + gm_commands/incstat.cpp + gm_commands/instance.cpp + gm_commands/interrogateinv.cpp + gm_commands/interrupt.cpp + gm_commands/invsnapshot.cpp + gm_commands/invul.cpp + gm_commands/ipban.cpp + gm_commands/iplookup.cpp + gm_commands/iteminfo.cpp + gm_commands/itemsearch.cpp + gm_commands/kick.cpp + gm_commands/kill.cpp + gm_commands/killallnpcs.cpp + gm_commands/lastname.cpp + gm_commands/list.cpp + gm_commands/listpetition.cpp + gm_commands/loc.cpp + gm_commands/lock.cpp + gm_commands/logcommand.cpp + gm_commands/logs.cpp + gm_commands/makepet.cpp + gm_commands/mana.cpp + gm_commands/max_all_skills.cpp + gm_commands/memspell.cpp + gm_commands/merchantcloseshop.cpp + gm_commands/merchantopenshop.cpp + gm_commands/modifynpcstat.cpp + gm_commands/motd.cpp + gm_commands/movechar.cpp + gm_commands/movement.cpp + gm_commands/myskills.cpp + gm_commands/mysql.cpp + gm_commands/mystats.cpp + gm_commands/name.cpp + gm_commands/netstats.cpp + gm_commands/network.cpp + gm_commands/npccast.cpp + gm_commands/npcedit.cpp + gm_commands/npceditmass.cpp + gm_commands/npcemote.cpp + gm_commands/npcloot.cpp + gm_commands/npcsay.cpp + gm_commands/npcshout.cpp + gm_commands/npcspawn.cpp + gm_commands/npcspecialattk.cpp + gm_commands/npcstats.cpp + gm_commands/npctype_cache.cpp + gm_commands/npctypespawn.cpp + gm_commands/nudge.cpp + gm_commands/nukebuffs.cpp + gm_commands/nukeitem.cpp + gm_commands/object.cpp + gm_commands/oocmute.cpp + gm_commands/opcode.cpp + gm_commands/path.cpp + gm_commands/peekinv.cpp + gm_commands/peqzone.cpp + gm_commands/permaclass.cpp + gm_commands/permagender.cpp + gm_commands/permarace.cpp + gm_commands/petitioninfo.cpp + gm_commands/petname.cpp + gm_commands/pf.cpp + gm_commands/picklock.cpp + gm_commands/profanity.cpp + gm_commands/proximity.cpp + gm_commands/push.cpp + gm_commands/pvp.cpp + gm_commands/qglobal.cpp + gm_commands/questerrors.cpp + gm_commands/race.cpp + gm_commands/raidloot.cpp + gm_commands/randomfeatures.cpp + gm_commands/refreshgroup.cpp + gm_commands/reloadaa.cpp + gm_commands/reloadallrules.cpp + gm_commands/reloademote.cpp + gm_commands/reloadlevelmods.cpp + gm_commands/reloadmerchants.cpp + gm_commands/reloadperlexportsettings.cpp + gm_commands/reloadqst.cpp + gm_commands/reloadstatic.cpp + gm_commands/reloadtitles.cpp + gm_commands/reloadtraps.cpp + gm_commands/reloadworld.cpp + gm_commands/reloadworldrules.cpp + gm_commands/reloadzps.cpp + gm_commands/repop.cpp + gm_commands/resetaa.cpp + gm_commands/resetaa_timer.cpp + gm_commands/resetdisc_timer.cpp + gm_commands/revoke.cpp + gm_commands/roambox.cpp + gm_commands/rules.cpp + gm_commands/save.cpp + gm_commands/scale.cpp + gm_commands/scribespell.cpp + gm_commands/scribespells.cpp + gm_commands/sendzonespawns.cpp + gm_commands/sensetrap.cpp + gm_commands/serverinfo.cpp + gm_commands/serverrules.cpp + gm_commands/set_adventure_points.cpp + gm_commands/setaapts.cpp + gm_commands/setaaxp.cpp + gm_commands/setanim.cpp + gm_commands/setcrystals.cpp + gm_commands/setfaction.cpp + gm_commands/setgraveyard.cpp + gm_commands/setlanguage.cpp + gm_commands/setlsinfo.cpp + gm_commands/setpass.cpp + gm_commands/setpvppoints.cpp + gm_commands/setskill.cpp + gm_commands/setskillall.cpp + gm_commands/setstartzone.cpp + gm_commands/setstat.cpp + gm_commands/setxp.cpp + gm_commands/showbonusstats.cpp + gm_commands/showbuffs.cpp + gm_commands/shownpcgloballoot.cpp + gm_commands/shownumhits.cpp + gm_commands/showskills.cpp + gm_commands/showspellslist.cpp + gm_commands/showstats.cpp + gm_commands/showzonegloballoot.cpp + gm_commands/showzonepoints.cpp + gm_commands/shutdown.cpp + gm_commands/size.cpp + gm_commands/spawn.cpp + gm_commands/spawnfix.cpp + gm_commands/spawnstatus.cpp + gm_commands/spellinfo.cpp + gm_commands/stun.cpp + gm_commands/summon.cpp + gm_commands/summonburiedplayercorpse.cpp + gm_commands/summonitem.cpp + gm_commands/suspend.cpp + gm_commands/task.cpp + gm_commands/tattoo.cpp + gm_commands/tempname.cpp + gm_commands/texture.cpp + gm_commands/time.cpp + gm_commands/timers.cpp + gm_commands/timezone.cpp + gm_commands/title.cpp + gm_commands/titlesuffix.cpp + gm_commands/traindisc.cpp + gm_commands/trapinfo.cpp + gm_commands/tune.cpp + gm_commands/ucs.cpp + gm_commands/undye.cpp + gm_commands/undyeme.cpp + gm_commands/unfreeze.cpp + gm_commands/unlock.cpp + gm_commands/unscribespell.cpp + gm_commands/unscribespells.cpp + gm_commands/untraindisc.cpp + gm_commands/untraindiscs.cpp + gm_commands/uptime.cpp + gm_commands/version.cpp + gm_commands/viewnpctype.cpp + gm_commands/viewpetition.cpp + gm_commands/viewzoneloot.cpp + gm_commands/wc.cpp + gm_commands/weather.cpp + gm_commands/who.cpp + gm_commands/worldshutdown.cpp + gm_commands/worldwide.cpp + gm_commands/wp.cpp + gm_commands/wpadd.cpp + gm_commands/wpinfo.cpp + gm_commands/xtargets.cpp + gm_commands/zclip.cpp + gm_commands/zcolor.cpp + gm_commands/zheader.cpp + gm_commands/zonebootup.cpp + gm_commands/zonelock.cpp + gm_commands/zoneshutdown.cpp + gm_commands/zonespawn.cpp + gm_commands/zonestatus.cpp + gm_commands/zopp.cpp + gm_commands/zsafecoords.cpp + gm_commands/zsave.cpp + gm_commands/zsky.cpp + gm_commands/zstats.cpp + gm_commands/zunderworld.cpp + gm_commands/zuwcoords.cpp ) -ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers}) +ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers} ${gm_commands}) INSTALL(TARGETS zone RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) diff --git a/zone/command.cpp b/zone/command.cpp index e33e37cf9..1ed2629e2 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -1,36 +1,3 @@ -/* 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 @@ -47,16 +14,12 @@ #include "../common/global_define.h" #include "../common/eq_packet.h" #include "../common/features.h" -#include "../common/guilds.h" -#include "../common/patches/patches.h" #include "../common/ptimer.h" #include "../common/rulesys.h" -#include "../common/serverinfo.h" #include "../common/string_util.h" #include "../common/say_link.h" -#include "../common/eqemu_logsys.h" -#include "../common/profanity_manager.h" #include "../common/net/eqstream.h" +#include "../common/file_util.h" #include "../common/repositories/dynamic_zones_repository.h" #include "data_bucket.h" @@ -64,22 +27,15 @@ #include "dynamic_zone.h" #include "expedition.h" #include "guild_mgr.h" -#include "map.h" #include "qglobals.h" #include "queryserv.h" #include "quest_parser_collection.h" -#include "string_ids.h" #include "titles.h" #include "water_map.h" #include "worldserver.h" #include "fastmath.h" #include "mob_movement_manager.h" #include "npc_scale_manager.h" -#include "../common/content/world_content_service.h" -#include "../common/http/httplib.h" -#include "../common/shared_tasks.h" -#include "gm_commands/door_manipulation.h" -#include "../common/languages.h" extern QueryServ* QServ; extern WorldServer worldserver; @@ -94,10 +50,6 @@ int commandcount; // how many commands we have // 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; @@ -319,11 +271,6 @@ int command_init(void) command_add("object", "List|Add|Edit|Move|Rotate|Copy|Save|Undo|Delete - Manipulate static and tradeskill objects within the zone", AccountStatus::GMAdmin, command_object) || command_add("oocmute", "[1/0] - Mutes OOC chat", AccountStatus::GMMgmt, command_oocmute) || command_add("opcode", "- opcode management", AccountStatus::GMImpossible, command_opcode) || - -#ifdef PACKET_PROFILER - command_add("packetprofile", "- Dump packet profile for target or self.", AccountStatus::GMImpossible, command_packetprofile) || -#endif - command_add("path", "- view and edit pathing", AccountStatus::GMMgmt, 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", AccountStatus::GMAdmin, command_peekinv) || command_add("peqzone", "[zonename] - Go to specified zone, if you have > 75% health", AccountStatus::Player, command_peqzone) || @@ -334,12 +281,6 @@ int command_init(void) command_add("pf", "- Display additional mob coordinate and wandering data", AccountStatus::Player, command_pf) || command_add("picklock", "Analog for ldon pick lock for the newer clients since we still don't have it working.", AccountStatus::Player, command_picklock) || command_add("profanity", "Manage censored language.", AccountStatus::GMLeadAdmin, command_profanity) || - -#ifdef EQPROFILE - command_add("profiledump", "- Dump profiling info to logs", AccountStatus::GMImpossible, command_profiledump) || - command_add("profilereset", "- Reset profiling info", AccountStatus::GMImpossible, command_profilereset) || -#endif - command_add("push", "Lets you do spell push", AccountStatus::GMLeadAdmin, command_push) || command_add("proximity", "Shows NPC proximity", AccountStatus::GMLeadAdmin, command_proximity) || command_add("pvp", "[on/off] - Set your or your player target's PVP status", AccountStatus::GMAdmin, command_pvp) || @@ -679,279 +620,6 @@ int command_realdispatch(Client *c, const char *message) } -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 >= AccountStatus::GMLeadAdmin && - admin < AccountStatus::GMMgmt - ) { - continueevents = true; - } - break; - } - case 8: { // log only GM - if ( - admin >= AccountStatus::GMAdmin && - admin < AccountStatus::GMLeadAdmin - ) { - continueevents = true; - } - break; - } - case 1: { - if (admin >= AccountStatus::GMMgmt) { - continueevents = true; - } - break; - } - case 2: { - if (admin >= AccountStatus::GMLeadAdmin) { - continueevents = true; - } - break; - } - case 3: { - if (admin >= AccountStatus::GMAdmin) { - continueevents = true; - } - break; - } - case 4: { - if (admin >= AccountStatus::QuestTroupe) { - continueevents = true; - } - break; - } - case 5: { - if (admin >= AccountStatus::ApprenticeGuide) { - continueevents = true; - } - break; - } - case 6: { - if (admin >= AccountStatus::Steward) { - continueevents = true; - } - break; - } - case 7: { - continueevents = true; - break; - } - } - - if (continueevents) - database.logevents( - c->AccountName(), - c->AccountID(), - admin,c->GetName(), - c->GetTarget()?c->GetTarget()->GetName():"None", - "Command", - message, - 1 - ); -} - - -/* - * commands go below here - */ -void command_worldwide(Client *c, const Seperator *sep) -{ - std::string sub_command; - if (sep->arg[1]) { - sub_command = sep->arg[1]; - } - - if (sub_command == "cast") { - if (sep->arg[2] && Seperator::IsNumber(sep->arg[2])) { - uint8 update_type = WWSpellUpdateType_Cast; - auto spell_id = std::stoul(sep->arg[2]); - bool disable_message = false; - if (sep->arg[3] && Seperator::IsNumber(sep->arg[3])) { - disable_message = std::stoi(sep->arg[3]) ? true : false; - } - - c->Message( - Chat::White, - fmt::format( - "World Wide Cast Spell | Spell: {} ({})", - GetSpellName(spell_id), - spell_id - ).c_str() - ); - - quest_manager.WorldWideSpell(update_type, spell_id); - if (!disable_message) { - quest_manager.WorldWideMessage( - Chat::Yellow, - fmt::format( - "[SYSTEM] A GM has cast [{}] world-wide!", - GetSpellName(spell_id) - ).c_str() - ); - } - } else { - c->Message(Chat::White, "Usage: #worldwide cast [Spell ID] [Disable Message]"); - } - } else if (sub_command == "remove") { - if (sep->arg[2] && Seperator::IsNumber(sep->arg[2])) { - uint8 update_type = WWSpellUpdateType_Remove; - auto spell_id = std::stoul(sep->arg[2]); - - c->Message( - Chat::White, - fmt::format( - "World Wide Remove Spell | Spell: {} ({})", - GetSpellName(spell_id), - spell_id - ).c_str() - ); - - quest_manager.WorldWideSpell(update_type, spell_id); - } else { - c->Message(Chat::White, "Usage: #worldwide remove [Spell ID]"); - } - } else if (sub_command == "message") { - if (sep->arg[2]) { - std::string message = sep->arg[2]; - quest_manager.WorldWideMessage( - Chat::White, - fmt::format( - "{}", - message - ).c_str() - ); - } else { - c->Message(Chat::White, "Usage: #worldwide message [Message]"); - } - } else if (sub_command == "move") { - if (sep->arg[2]) { - uint8 update_type = WWMoveUpdateType_MoveZone; - uint32 zone_id = 0; - std::string zone_short_name; - if (Seperator::IsNumber(sep->arg[2])) { - zone_id = std::stoul(sep->arg[2]); - } - - if (zone_id) { - zone_short_name = ZoneName(zone_id); - } else { - zone_short_name = sep->arg[2]; - } - - c->Message( - Chat::White, - fmt::format( - "World Wide Zone | Zone: {} ({}) ID: {}", - ZoneLongName( - ZoneID(zone_short_name) - ), - zone_short_name, - ZoneID(zone_short_name) - ).c_str() - ); - - quest_manager.WorldWideMove(update_type, zone_short_name.c_str()); - } else { - c->Message( - Chat::White, - "Usage: #worldwide move [Zone ID] or #worldwide move [Zone Short Name]" - ); - } - } else if (sub_command == "moveinstance") { - if (Seperator::IsNumber(sep->arg[2])) { - uint8 update_type = WWMoveUpdateType_MoveZoneInstance; - const char* zone_short_name = ""; - uint16 instance_id = std::stoi(sep->arg[2]); - - c->Message( - Chat::White, - fmt::format( - "World Wide Zone Instance | Instance ID: {}", - instance_id - ).c_str() - ); - - quest_manager.WorldWideMove(update_type, zone_short_name, instance_id); - } else { - c->Message(Chat::White, "Usage: #worldwide moveinstance [Instance ID]"); - } - } - - if (!sep->arg[1]) { - c->Message(Chat::White, "This command is used to perform world-wide tasks."); - c->Message(Chat::White, "Usage: #worldwide cast [Spell ID] [Disable Message]"); - c->Message(Chat::White, "Usage: #worldwide remove [Spell ID]"); - c->Message(Chat::White, "Usage: #worldwide message [Message]"); - c->Message( - Chat::White, - "Usage: #worldwide move [Zone ID] or #worldwide move [Zone Short Name]" - ); - c->Message(Chat::White, "Usage: #worldwide moveinstance [Instance ID]"); - } -} - -void command_endurance(Client *c, const Seperator *sep) -{ - auto target = c->GetTarget() ? c->GetTarget() : c; - if (target->IsClient()) { - target->CastToClient()->SetEndurance(target->CastToClient()->GetMaxEndurance()); - } else { - target->SetEndurance(target->GetMaxEndurance()); - } - - if (c != target) { - c->Message( - Chat::White, - fmt::format( - "Set {} ({}) to full Endurance.", - target->GetCleanName(), - target->GetID() - ).c_str() - ); - } else { - c->Message(Chat::White, "Restored your Endurance to full."); - } -} - -void command_setstat(Client* c, const Seperator* sep){ - if(sep->arg[1][0] && sep->arg[2][0] && c->GetTarget()!=0 && c->GetTarget()->IsClient()){ - c->GetTarget()->CastToClient()->SetStats(atoi(sep->arg[1]),atoi(sep->arg[2])); - } - else{ - c->Message(Chat::White,"This command is used to permanently increase or decrease a players stats."); - c->Message(Chat::White,"Usage: #setstat {type} {value the stat should be}"); - c->Message(Chat::White,"Types: Str: 0, Sta: 1, Agi: 2, Dex: 3, Int: 4, Wis: 5, Cha: 6"); - } -} - -void command_incstat(Client* c, const Seperator* sep){ - if(sep->arg[1][0] && sep->arg[2][0] && c->GetTarget()!=0 && c->GetTarget()->IsClient()){ - c->GetTarget()->CastToClient()->IncStats(atoi(sep->arg[1]),atoi(sep->arg[2])); - } - else{ - c->Message(Chat::White,"This command is used to permanently increase or decrease a players stats."); - c->Message(Chat::White,"Usage: #setstat {type} {value by which to increase or decrease}"); - c->Message(Chat::White,"Note: The value is in increments of 2, so a value of 3 will actually increase the stat by 6"); - c->Message(Chat::White,"Types: Str: 0, Sta: 1, Agi: 2, Dex: 3, Int: 4, Wis: 5, Cha: 6"); - } -} - -void command_resetaa(Client* c,const Seperator *sep) { - if(c->GetTarget() && c->GetTarget()->IsClient()){ - c->GetTarget()->CastToClient()->ResetAA(); - c->Message(Chat::Red,"Successfully reset %s's AAs", c->GetTarget()->GetName()); - } - else - c->Message(Chat::White,"Usage: Target a client and use #resetaa to reset the AA data in their Profile."); -} - void command_help(Client *c, const Seperator *sep) { int commands_shown=0; @@ -984,335 +652,6 @@ void command_help(Client *c, const Seperator *sep) } -void command_version(Client *c, const Seperator *sep) -{ - c->Message(Chat::White, "Current version information."); - c->Message(Chat::White, " %s", CURRENT_VERSION); - c->Message(Chat::White, " Compiled on: %s at %s", COMPILE_DATE, COMPILE_TIME); - c->Message(Chat::White, " Last modified on: %s", LAST_MODIFIED); -} - -void command_setfaction(Client *c, const Seperator *sep) -{ - if((sep->arg[1][0] == 0 || strcasecmp(sep->arg[1],"*")==0) || ((c->GetTarget()==0) || (c->GetTarget()->IsClient()))) { - c->Message(Chat::White, "Usage: #setfaction [faction number]"); - return; - } - - auto npcTypeID = c->GetTarget()->CastToNPC()->GetNPCTypeID(); - c->Message(Chat::Yellow,"Setting NPC %u to faction %i", npcTypeID, atoi(sep->argplus[1])); - - std::string query = StringFormat("UPDATE npc_types SET npc_faction_id = %i WHERE id = %i", - atoi(sep->argplus[1]), npcTypeID); - content_db.QueryDatabase(query); -} - -void command_wc(Client *c, const Seperator *sep) -{ - if (sep->argnum < 2) { - c->Message( - 0, - "Usage: #wc [wear slot] [material] [ [hero_forge_model] [elite_material] [unknown06] [unknown18] ]" - ); - } - else if (c->GetTarget() == nullptr) { - c->Message(Chat::Red, "You must have a target to do a wear change."); - } - else { - uint32 hero_forge_model = 0; - uint32 wearslot = atoi(sep->arg[1]); - - // Hero Forge - if (sep->argnum > 2) { - hero_forge_model = atoi(sep->arg[3]); - - if (hero_forge_model != 0 && hero_forge_model < 1000) { - // Shorthand Hero Forge ID. Otherwise use the value the user entered. - hero_forge_model = (hero_forge_model * 100) + wearslot; - } - } - /* - // Leaving here to add color option to the #wc command eventually - uint32 Color; - if (c->GetTarget()->IsClient()) - Color = c->GetTarget()->GetEquipmentColor(atoi(sep->arg[1])); - else - Color = c->GetTarget()->GetArmorTint(atoi(sep->arg[1])); - */ - c->GetTarget()->SendTextureWC( - wearslot, - atoi(sep->arg[2]), - hero_forge_model, - atoi(sep->arg[4]), - atoi(sep->arg[5]), - atoi(sep->arg[6])); - } -} - -void command_heromodel(Client *c, const Seperator *sep) -{ - if (sep->argnum < 1) { - c->Message(Chat::White, "Usage: #heromodel [hero forge model] [ [slot] ] (example: #heromodel 63)"); - } - else if (c->GetTarget() == nullptr) { - c->Message(Chat::Red, "You must have a target to do a wear change for Hero's Forge Models."); - } - else { - uint32 hero_forge_model = atoi(sep->arg[1]); - - if (sep->argnum > 1) { - uint8 wearslot = (uint8) atoi(sep->arg[2]); - c->GetTarget()->SendTextureWC(wearslot, 0, hero_forge_model, 0, 0, 0); - } - else { - if (hero_forge_model > 0) { - // Conversion to simplify the command arguments - // Hero's Forge model is actually model * 1000 + texture * 100 + wearslot - // Hero's Forge Model slot 7 is actually for Robes, but it still needs to use wearslot 1 in the packet - hero_forge_model *= 100; - - for (uint8 wearslot = 0; wearslot < 7; wearslot++) { - c->GetTarget()->SendTextureWC(wearslot, 0, (hero_forge_model + wearslot), 0, 0, 0); - } - } - else { - c->Message(Chat::Red, "Hero's Forge Model must be greater than 0."); - } - } - } -} - -void command_setanim(Client *c, const Seperator *sep) -{ - if (c->GetTarget() && sep->IsNumber(1)) { - int num = atoi(sep->arg[1]); - if (num < 0 || num >= _eaMaxAppearance) { - c->Message(Chat::White, "Invalid animation number, between 0 and %d", _eaMaxAppearance - 1); - } - c->GetTarget()->SetAppearance(EmuAppearance(num)); - } - else { - c->Message(Chat::White, "Usage: #setanim [animnum]"); - } -} - -void command_serverinfo(Client *c, const Seperator *sep) -{ - auto os = EQ::GetOS(); - auto cpus = EQ::GetCPUs(); - auto pid = EQ::GetPID(); - auto rss = EQ::GetRSS(); - auto uptime = EQ::GetUptime(); - - c->Message(Chat::White, "Operating System Information"); - c->Message(Chat::White, "=================================================="); - c->Message(Chat::White, "System: %s", os.sysname.c_str()); - c->Message(Chat::White, "Release: %s", os.release.c_str()); - c->Message(Chat::White, "Version: %s", os.version.c_str()); - c->Message(Chat::White, "Machine: %s", os.machine.c_str()); - c->Message(Chat::White, "Uptime: %.2f seconds", uptime); - c->Message(Chat::White, "=================================================="); - c->Message(Chat::White, "CPU Information"); - c->Message(Chat::White, "=================================================="); - for (size_t i = 0; i < cpus.size(); ++i) { - auto &cp = cpus[i]; - c->Message(Chat::White, "CPU #%i: %s, Speed: %.2fGhz", i, cp.model.c_str(), cp.speed); - } - c->Message(Chat::White, "=================================================="); - c->Message(Chat::White, "Process Information"); - c->Message(Chat::White, "=================================================="); - c->Message(Chat::White, "PID: %u", pid); - c->Message(Chat::White, "RSS: %.2f MB", rss / 1048576.0); - c->Message(Chat::White, "=================================================="); -} - -void command_getvariable(Client *c, const Seperator *sep) -{ - std::string tmp; - if (database.GetVariable(sep->argplus[1], tmp)) - c->Message(Chat::White, "%s = %s", sep->argplus[1], tmp.c_str()); - else - c->Message(Chat::White, "GetVariable(%s) returned false", sep->argplus[1]); -} - -void command_chat(Client *c, const Seperator *sep) -{ - if (sep->arg[2][0] == 0) - c->Message(Chat::White, "Usage: #chat [channum] [message]"); - else - if (!worldserver.SendChannelMessage(0, 0, (uint8) atoi(sep->arg[1]), 0, 0, 100, sep->argplus[2])) - c->Message(Chat::White, "Error: World server disconnected"); -} - -void command_npcloot(Client *c, const Seperator *sep) -{ - if (c->GetTarget() == 0) - c->Message(Chat::White, "Error: No target"); - // #npcloot show - else if (strcasecmp(sep->arg[1], "show") == 0) - { - if (c->GetTarget()->IsNPC()) - c->GetTarget()->CastToNPC()->QueryLoot(c); - else if (c->GetTarget()->IsCorpse()) - c->GetTarget()->CastToCorpse()->QueryLoot(c); - else - c->Message(Chat::White, "Error: Target's type doesnt have loot"); - } - // These 2 types are *BAD* for the next few commands - else if (c->GetTarget()->IsClient() || c->GetTarget()->IsCorpse()) - c->Message(Chat::White, "Error: Invalid target type, try a NPC =)."); - // #npcloot add - else if (strcasecmp(sep->arg[1], "add") == 0) - { - // #npcloot add item - if (c->GetTarget()->IsNPC() && sep->IsNumber(2)) - { - uint32 item = atoi(sep->arg[2]); - if (database.GetItem(item)) - { - if (sep->arg[3][0] != 0 && sep->IsNumber(3)) - c->GetTarget()->CastToNPC()->AddItem(item, atoi(sep->arg[3]), 0); - else - c->GetTarget()->CastToNPC()->AddItem(item, 1, 0); - c->Message(Chat::White, "Added item(%i) to the %s's loot.", item, c->GetTarget()->GetName()); - } - else - c->Message(Chat::White, "Error: #npcloot add: Item(%i) does not exist!", item); - } - else if (!sep->IsNumber(2)) - c->Message(Chat::White, "Error: #npcloot add: Itemid must be a number."); - else - c->Message(Chat::White, "Error: #npcloot add: This is not a valid target."); - } - // #npcloot remove - else if (strcasecmp(sep->arg[1], "remove") == 0) - { - //#npcloot remove all - if (strcasecmp(sep->arg[2], "all") == 0) - c->Message(Chat::White, "Error: #npcloot remove all: Not yet implemented."); - //#npcloot remove itemid - else - { - if(c->GetTarget()->IsNPC() && sep->IsNumber(2)) - { - uint32 item = atoi(sep->arg[2]); - c->GetTarget()->CastToNPC()->RemoveItem(item); - c->Message(Chat::White, "Removed item(%i) from the %s's loot.", item, c->GetTarget()->GetName()); - } - else if (!sep->IsNumber(2)) - c->Message(Chat::White, "Error: #npcloot remove: Item must be a number."); - else - c->Message(Chat::White, "Error: #npcloot remove: This is not a valid target."); - } - } - // #npcloot money - else if (strcasecmp(sep->arg[1], "money") == 0) - { - if (c->GetTarget()->IsNPC() && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4) && sep->IsNumber(5)) - { - if ((atoi(sep->arg[2]) < 34465 && atoi(sep->arg[2]) >= 0) && (atoi(sep->arg[3]) < 34465 && atoi(sep->arg[3]) >= 0) && (atoi(sep->arg[4]) < 34465 && atoi(sep->arg[4]) >= 0) && (atoi(sep->arg[5]) < 34465 && atoi(sep->arg[5]) >= 0)) - { - c->GetTarget()->CastToNPC()->AddCash(atoi(sep->arg[5]), atoi(sep->arg[4]), atoi(sep->arg[3]), atoi(sep->arg[2])); - c->Message(Chat::White, "Set %i Platinum, %i Gold, %i Silver, and %i Copper as %s's money.", atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), atoi(sep->arg[5]), c->GetTarget()->GetName()); - } - else - c->Message(Chat::White, "Error: #npcloot money: Values must be between 0-34465."); - } - else - c->Message(Chat::White, "Usage: #npcloot money platinum gold silver copper"); - } - else - c->Message(Chat::White, "Usage: #npcloot [show/money/add/remove] [itemid/all/money: pp gp sp cp]"); -} - -void command_gm(Client *c, const Seperator *sep) -{ - bool state=atobool(sep->arg[1]); - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); - - if(sep->arg[1][0] != 0) { - t->SetGM(state); - c->Message(Chat::White, "%s is %s a GM.", t->GetName(), state?"now":"no longer"); - } - else - c->Message(Chat::White, "Usage: #gm [on/off]"); -} - -// there's no need for this, as /summon already takes care of it -// this command is here for reference but it is not added to the -// list above - -//To whoever wrote the above: And what about /kill, /zone, /zoneserver, etc? -//There is a reason for the # commands: so that admins can specifically enable certain -//commands for their users. Some might want users to #summon but not to /kill. Cant do that if they are a GM -void command_summon(Client *c, const Seperator *sep) -{ - Mob *t; - - if(sep->arg[1][0] != 0) // arg specified - { - Client* client = entity_list.GetClientByName(sep->arg[1]); - if (client != 0) // found player in zone - t=client->CastToMob(); - else - { - if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server disconnected."); - else - { // player is in another zone - //Taking this command out until we test the factor of 8 in ServerOP_ZonePlayer - //c->Message(Chat::White, "Summoning player from another zone not yet implemented."); - //return; - - auto pack = new ServerPacket(ServerOP_ZonePlayer, sizeof(ServerZonePlayer_Struct)); - ServerZonePlayer_Struct* szp = (ServerZonePlayer_Struct*) pack->pBuffer; - strcpy(szp->adminname, c->GetName()); - szp->adminrank = c->Admin(); - szp->ignorerestrictions = 2; - strcpy(szp->name, sep->arg[1]); - strcpy(szp->zone, zone->GetShortName()); - szp->x_pos = c->GetX(); // May need to add a factor of 8 in here.. - szp->y_pos = c->GetY(); - szp->z_pos = c->GetZ(); - szp->instance_id = zone->GetInstanceID(); - worldserver.SendPacket(pack); - safe_delete(pack); - } - return; - } - } - else if(c->GetTarget()) // have target - t=c->GetTarget(); - else - { - 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()) - { - 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 && @@ -1424,2656 +763,6 @@ void command_zone_instance(Client *c, const Seperator *sep) } } -void command_showbuffs(Client *c, const Seperator *sep) -{ - if (c->GetTarget() == 0) - c->CastToMob()->ShowBuffs(c); - else - c->GetTarget()->CastToMob()->ShowBuffs(c); -} - -void command_peqzone(Client *c, const Seperator *sep) -{ - uint32 timeleft = c->GetPTimers().GetRemainingTime(pTimerPeqzoneReuse)/60; - - if(!c->GetPTimers().Expired(&database, pTimerPeqzoneReuse, false)) { - c->Message(Chat::Red,"You must wait %i minute(s) before using this ability again.", timeleft); - return; - } - - if(c->GetHPRatio() < 75) { - c->Message(Chat::White, "You cannot use this command with less than 75 percent health."); - return; - } - - //this isnt perfect, but its better... - if( - c->IsInvisible(c) - || c->IsRooted() - || c->IsStunned() - || c->IsMezzed() - || c->AutoAttackEnabled() - || c->GetInvul() - ) { - c->Message(Chat::White, "You cannot use this command in your current state. Settle down and wait."); - return; - } - - uint16 zoneid = 0; - uint8 destzone = 0; - if (sep->IsNumber(1)) - { - zoneid = atoi(sep->arg[1]); - destzone = content_db.GetPEQZone(zoneid, 0); - if(destzone == 0){ - c->Message(Chat::Red, "You cannot use this command to enter that zone!"); - return; - } - if(zoneid == zone->GetZoneID()) { - c->Message(Chat::Red, "You cannot use this command on the zone you are in!"); - return; - } - } - else if (sep->arg[1][0] == 0 || sep->IsNumber(2) || sep->IsNumber(3) || sep->IsNumber(4) || sep->IsNumber(5)) - { - c->Message(Chat::White, "Usage: #peqzone [zonename]"); - c->Message(Chat::White, "Optional Usage: #peqzone [zoneid]"); - return; - } - else { - zoneid = ZoneID(sep->arg[1]); - destzone = content_db.GetPEQZone(zoneid, 0); - if(zoneid == 0) { - c->Message(Chat::White, "Unable to locate zone '%s'", sep->arg[1]); - return; - } - if(destzone == 0){ - c->Message(Chat::Red, "You cannot use this command to enter that zone!"); - return; - } - if(zoneid == zone->GetZoneID()) { - c->Message(Chat::Red, "You cannot use this command on the zone you are in!"); - return; - } - } - - if(RuleB (Zone, UsePEQZoneDebuffs)){ - c->SpellOnTarget(RuleI(Zone, PEQZoneDebuff1), c); - c->SpellOnTarget(RuleI(Zone, PEQZoneDebuff2), c); - } - - //zone to safe coords - c->GetPTimers().Start(pTimerPeqzoneReuse, RuleI(Zone, PEQZoneReuseTime)); - c->MovePC(zoneid, 0.0f, 0.0f, 0.0f, 0.0f, 0, ZoneToSafeCoords); -} - -void command_movechar(Client *c, const Seperator *sep) -{ - if(sep->arg[1][0]==0 || sep->arg[2][0] == 0) - c->Message(Chat::White, "Usage: #movechar [charactername] [zonename]"); - else if (c->Admin() < commandMovecharToSpecials && strcasecmp(sep->arg[2], "cshome") == 0 || strcasecmp(sep->arg[2], "load") == 0 || strcasecmp(sep->arg[2], "load2") == 0) - c->Message(Chat::White, "Invalid zone name"); - else - { - uint32 tmp = database.GetAccountIDByChar(sep->arg[1]); - if (tmp) - { - if (c->Admin() >= commandMovecharSelfOnly || tmp == c->AccountID()) - if (!database.MoveCharacterToZone((char*) sep->arg[1], ZoneID(sep->arg[2]))) - c->Message(Chat::White, "Character Move Failed!"); - else - c->Message(Chat::White, "Character has been moved."); - else - c->Message(Chat::Red,"You cannot move characters that are not on your account."); - } - else - c->Message(Chat::White, "Character Does Not Exist"); - } -} - -void command_movement(Client *c, const Seperator *sep) -{ - auto &mgr = MobMovementManager::Get(); - - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #movement stats/clearstats/walkto/runto/rotateto/stop/packet"); - return; - } - - if (strcasecmp(sep->arg[1], "stats") == 0) - { - mgr.DumpStats(c); - } - else if (strcasecmp(sep->arg[1], "clearstats") == 0) - { - mgr.ClearStats(); - } - else if (strcasecmp(sep->arg[1], "walkto") == 0) - { - auto target = c->GetTarget(); - if (target == nullptr) { - c->Message(Chat::White, "No target found."); - return; - } - - target->WalkTo(c->GetX(), c->GetY(), c->GetZ()); - } - else if (strcasecmp(sep->arg[1], "runto") == 0) - { - auto target = c->GetTarget(); - if (target == nullptr) { - c->Message(Chat::White, "No target found."); - return; - } - - target->RunTo(c->GetX(), c->GetY(), c->GetZ()); - } - else if (strcasecmp(sep->arg[1], "rotateto") == 0) - { - auto target = c->GetTarget(); - if (target == nullptr) { - c->Message(Chat::White, "No target found."); - return; - } - - target->RotateToWalking(target->CalculateHeadingToTarget(c->GetX(), c->GetY())); - } - else if (strcasecmp(sep->arg[1], "stop") == 0) - { - auto target = c->GetTarget(); - if (target == nullptr) { - c->Message(Chat::White, "No target found."); - return; - } - - target->StopNavigation(); - } - else if (strcasecmp(sep->arg[1], "packet") == 0) - { - auto target = c->GetTarget(); - if (target == nullptr) { - c->Message(Chat::White, "No target found."); - return; - } - - mgr.SendCommandToClients(target, atof(sep->arg[2]), atof(sep->arg[3]), atof(sep->arg[4]), atof(sep->arg[5]), atoi(sep->arg[6]), ClientRangeAny); - } - else { - c->Message(Chat::White, "Usage: #movement stats/clearstats/walkto/runto/rotateto/stop/packet"); - } -} - -void command_viewpetition(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #viewpetition (petition number) Type #listpetition for a list"); - return; - } - - c->Message(Chat::Red," ID : Character Name , Petition Text"); - - std::string query = "SELECT petid, charname, petitiontext FROM petitions ORDER BY petid"; - auto results = database.QueryDatabase(query); - if (!results.Success()) - return; - - LogInfo("View petition request from [{}], petition number: [{}]", c->GetName(), atoi(sep->argplus[1]) ); - - if (results.RowCount() == 0) { - c->Message(Chat::Red,"There was an error in your request: ID not found! Please check the Id and try again."); - return; - } - - for (auto row = results.begin(); row != results.end(); ++row) - if (strcasecmp(row[0], sep->argplus[1]) == 0) - c->Message(Chat::Yellow, " %s: %s , %s ", row[0], row[1], row[2]); - -} - -void command_petitioninfo(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #petitioninfo (petition number) Type #listpetition for a list"); - return; - } - - std::string query = "SELECT petid, charname, accountname, zone, charclass, charrace, charlevel FROM petitions ORDER BY petid"; - auto results = database.QueryDatabase(query); - if (!results.Success()) - return; - - LogInfo("Petition information request from [{}], petition number:", c->GetName(), atoi(sep->argplus[1]) ); - - if (results.RowCount() == 0) { - c->Message(Chat::Red,"There was an error in your request: ID not found! Please check the Id and try again."); - return; - } - - for (auto row = results.begin(); row != results.end(); ++row) - if (strcasecmp(row[0],sep->argplus[1])== 0) - c->Message(Chat::Red," ID : %s Character Name: %s Account Name: %s Zone: %s Character Class: %s Character Race: %s Character Level: %s", row[0],row[1],row[2],row[3],row[4],row[5],row[6]); - -} - -void command_delpetition(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == 0 || strcasecmp(sep->arg[1],"*") == 0) { - c->Message(Chat::White, "Usage: #delpetition (petition number) Type #listpetition for a list"); - return; - } - - c->Message(Chat::Red,"Attempting to delete petition number: %i", atoi(sep->argplus[1])); - std::string query = StringFormat("DELETE FROM petitions WHERE petid = %i", atoi(sep->argplus[1])); - auto results = database.QueryDatabase(query); - if (!results.Success()) - return; - - LogInfo("Delete petition request from [{}], petition number:", c->GetName(), atoi(sep->argplus[1]) ); - -} - -void command_list(Client *c, const Seperator *sep) -{ - std::string search_type; - if (strcasecmp(sep->arg[1], "npcs") == 0) { - search_type = "npcs"; - } - - if (strcasecmp(sep->arg[1], "players") == 0) { - search_type = "players"; - } - - if (strcasecmp(sep->arg[1], "corpses") == 0) { - search_type = "corpses"; - } - - if (strcasecmp(sep->arg[1], "doors") == 0) { - search_type = "doors"; - } - - if (strcasecmp(sep->arg[1], "objects") == 0) { - search_type = "objects"; - } - - if (search_type.length() > 0) { - - int entity_count = 0; - int found_count = 0; - - std::string search_string; - - if (sep->arg[2]) { - search_string = sep->arg[2]; - } - - /** - * NPC - */ - if (search_type.find("npcs") != std::string::npos) { - auto &entity_list_search = entity_list.GetMobList(); - - for (auto &itr : entity_list_search) { - Mob *entity = itr.second; - if (!entity->IsNPC()) { - continue; - } - - entity_count++; - - std::string entity_name = entity->GetName(); - - /** - * Filter by name - */ - if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { - continue; - } - - std::string saylink = StringFormat( - "#goto %.0f %0.f %.0f", - entity->GetX(), - entity->GetY(), - entity->GetZ() + (entity->IsBoat() ? 50 : 0)); - - c->Message( - 0, - "| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f", - EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), - entity->GetID(), - entity->GetName(), - entity->GetX(), - entity->GetY(), - entity->GetZ() - ); - - found_count++; - } - } - - /** - * Client - */ - if (search_type.find("players") != std::string::npos) { - auto &entity_list_search = entity_list.GetClientList(); - - for (auto &itr : entity_list_search) { - Client *entity = itr.second; - - entity_count++; - - std::string entity_name = entity->GetName(); - - /** - * Filter by name - */ - if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { - continue; - } - - std::string saylink = StringFormat( - "#goto %.0f %0.f %.0f", - entity->GetX(), - entity->GetY(), - entity->GetZ()); - - c->Message( - 0, - "| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f", - EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), - entity->GetID(), - entity->GetName(), - entity->GetX(), - entity->GetY(), - entity->GetZ() - ); - - found_count++; - } - } - - /** - * Corpse - */ - if (search_type.find("corpses") != std::string::npos) { - auto &entity_list_search = entity_list.GetCorpseList(); - - for (auto &itr : entity_list_search) { - Corpse *entity = itr.second; - - entity_count++; - - std::string entity_name = entity->GetName(); - - /** - * Filter by name - */ - if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { - continue; - } - - std::string saylink = StringFormat( - "#goto %.0f %0.f %.0f", - entity->GetX(), - entity->GetY(), - entity->GetZ()); - - c->Message( - 0, - "| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f", - EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), - entity->GetID(), - entity->GetName(), - entity->GetX(), - entity->GetY(), - entity->GetZ() - ); - - found_count++; - } - } - - /** - * Doors - */ - if (search_type.find("doors") != std::string::npos) { - auto &entity_list_search = entity_list.GetDoorsList(); - - for (auto &itr : entity_list_search) { - Doors * entity = itr.second; - - entity_count++; - - std::string entity_name = entity->GetDoorName(); - - /** - * Filter by name - */ - if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { - continue; - } - - std::string saylink = StringFormat( - "#goto %.0f %0.f %.0f", - entity->GetX(), - entity->GetY(), - entity->GetZ()); - - c->Message( - 0, - "| %s | Entity ID %5d | Door ID %i | %s | x %.0f | y %0.f | z %.0f", - EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), - entity->GetID(), - entity->GetDoorID(), - entity->GetDoorName(), - entity->GetX(), - entity->GetY(), - entity->GetZ() - ); - - found_count++; - } - } - - /** - * Objects - */ - if (search_type.find("objects") != std::string::npos) { - auto &entity_list_search = entity_list.GetObjectList(); - - for (auto &itr : entity_list_search) { - Object * entity = itr.second; - - entity_count++; - - std::string entity_name = entity->GetModelName(); - - /** - * Filter by name - */ - if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { - continue; - } - - std::string saylink = StringFormat( - "#goto %.0f %0.f %.0f", - entity->GetX(), - entity->GetY(), - entity->GetZ()); - - c->Message( - 0, - "| %s | Entity ID %5d | Object DBID %i | %s | x %.0f | y %0.f | z %.0f", - EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), - entity->GetID(), - entity->GetDBID(), - entity->GetModelName(), - entity->GetX(), - entity->GetY(), - entity->GetZ() - ); - - found_count++; - } - } - - if (found_count) { - c->Message( - 0, "Found (%i) of type (%s) in zone (%i) total", - found_count, - search_type.c_str(), - entity_count - ); - } - } - else { - c->Message(Chat::White, "Usage of #list"); - c->Message(Chat::White, "- #list [npcs|players|corpses|doors|objects] [search]"); - c->Message(Chat::White, "- Example: #list npcs (Blank for all)"); - } -} - -void command_date(Client *c, const Seperator *sep) -{ - //yyyy mm dd hh mm local - if(sep->arg[3][0]==0 || !sep->IsNumber(1) || !sep->IsNumber(2) || !sep->IsNumber(3)) { - c->Message(Chat::Red, "Usage: #date yyyy mm dd [HH MM]"); - } - else { - int h=0, m=0; - TimeOfDay_Struct eqTime; - zone->zone_time.GetCurrentEQTimeOfDay( time(0), &eqTime); - if(!sep->IsNumber(4)) - h=eqTime.hour; - else - h=atoi(sep->arg[4]); - if(!sep->IsNumber(5)) - m=eqTime.minute; - else - m=atoi(sep->arg[5]); - c->Message(Chat::Red, "Setting world time to %s-%s-%s %i:%i...", sep->arg[1], sep->arg[2], sep->arg[3], h, m); - zone->SetDate(atoi(sep->arg[1]), atoi(sep->arg[2]), atoi(sep->arg[3]), h, m); - } -} - -void command_timezone(Client *c, const Seperator *sep) -{ - if(sep->arg[1][0]==0 && !sep->IsNumber(1)) { - c->Message(Chat::Red, "Usage: #timezone HH [MM]"); - c->Message(Chat::Red, "Current timezone is: %ih %im", zone->zone_time.getEQTimeZoneHr(), zone->zone_time.getEQTimeZoneMin()); - } - else { - uint8 hours = atoi(sep->arg[1]); - uint8 minutes = atoi(sep->arg[2]); - if(!sep->IsNumber(2)) - minutes = 0; - c->Message(Chat::Red, "Setting timezone to %i h %i m", hours, minutes); - uint32 ntz=(hours*60)+minutes; - zone->zone_time.setEQTimeZone(ntz); - content_db.SetZoneTZ(zone->GetZoneID(), zone->GetInstanceVersion(), ntz); - - // Update all clients with new TZ. - auto outapp = new EQApplicationPacket(OP_TimeOfDay, sizeof(TimeOfDay_Struct)); - TimeOfDay_Struct* tod = (TimeOfDay_Struct*)outapp->pBuffer; - zone->zone_time.GetCurrentEQTimeOfDay(time(0), tod); - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } -} - -void command_invul(Client *c, const Seperator *sep) -{ - bool state=atobool(sep->arg[1]); - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); - - if(sep->arg[1][0] != 0) { - t->SetInvul(state); - c->Message(Chat::White, "%s is %s invulnerable from attack.", t->GetName(), state?"now":"no longer"); - } - else - c->Message(Chat::White, "Usage: #invulnerable [on/off]"); -} - -void command_hideme(Client *c, const Seperator *sep) -{ - bool state=atobool(sep->arg[1]); - - if(sep->arg[1][0]==0) - c->Message(Chat::White, "Usage: #hideme [on/off]"); - else - { - c->SetHideMe(state); - c->MessageString(Chat::Broadcasts, c->GetHideMe() ? NOW_INVISIBLE : NOW_VISIBLE, c->GetName()); - } -} - -void command_emote(Client *c, const Seperator *sep) -{ - if (sep->arg[3][0] == 0) - c->Message(Chat::White, "Usage: #emote [name | world | zone] type# message"); - else { - if (strcasecmp(sep->arg[1], "zone") == 0){ - char* newmessage=0; - if(strstr(sep->arg[3],"^")==0) - entity_list.Message(0, atoi(sep->arg[2]), sep->argplus[3]); - else{ - for(newmessage = strtok((char*)sep->arg[3],"^");newmessage!=nullptr;newmessage=strtok(nullptr, "^")) - entity_list.Message(0, atoi(sep->arg[2]), newmessage); - } - } else if (!worldserver.Connected()) { - c->Message(Chat::White, "Error: World server disconnected"); - } else if (!strcasecmp(sep->arg[1], "world")) { - worldserver.SendEmoteMessage( - 0, - 0, - atoi(sep->arg[2]), - sep->argplus[3] - ); - } else { - worldserver.SendEmoteMessage( - sep->arg[1], - 0, - atoi(sep->arg[2]), - sep->argplus[3] - ); - } - } -} - -void command_fov(Client *c, const Seperator *sep) -{ - if (c->GetTarget()) { - auto target = c->GetTarget(); - std::string behind_message = ( - c->BehindMob( - target, - c->GetX(), - c->GetY() - ) ? - "behind" : - "not behind" - ); - std::string gender_message = ( - target->GetGender() == MALE ? - "he" : - ( - target->GetGender() == FEMALE ? - "she" : - "it" - ) - ); - - c->Message( - Chat::White, - fmt::format( - "You are {} {} ({}), {} has a heading of {}.", - behind_message, - target->GetCleanName(), - target->GetID(), - gender_message, - target->GetHeading() - ).c_str() - ); - } else { - c->Message(Chat::White, "You must have a target to use this command."); - } -} - -void command_npcstats(Client *c, const Seperator *sep) -{ - if (c->GetTarget() && c->GetTarget()->IsNPC()) { - NPC* target = c->GetTarget()->CastToNPC(); - - // Stats - target->ShowStats(c); - - // Loot Data - if (target->GetLoottableID()) { - target->QueryLoot(c); - } - } else { - c->Message(Chat::White, "You must target an NPC to use this command."); - } -} - -void command_zclip(Client *c, const Seperator *sep) -{ - // modifys and resends zhdr packet - if(sep->arg[2][0]==0) - c->Message(Chat::White, "Usage: #zclip "); - else if(atoi(sep->arg[1])<=0) - c->Message(Chat::White, "ERROR: Min clip can not be zero or less!"); - else if(atoi(sep->arg[2])<=0) - c->Message(Chat::White, "ERROR: Max clip can not be zero or less!"); - else if(atoi(sep->arg[1])>atoi(sep->arg[2])) - c->Message(Chat::White, "ERROR: Min clip is greater than max clip!"); - else { - zone->newzone_data.minclip = atof(sep->arg[1]); - zone->newzone_data.maxclip = atof(sep->arg[2]); - if(sep->arg[3][0]!=0) - zone->newzone_data.fog_minclip[0]=atof(sep->arg[3]); - if(sep->arg[4][0]!=0) - zone->newzone_data.fog_minclip[1]=atof(sep->arg[4]); - if(sep->arg[5][0]!=0) - zone->newzone_data.fog_maxclip[0]=atof(sep->arg[5]); - if(sep->arg[6][0]!=0) - zone->newzone_data.fog_maxclip[1]=atof(sep->arg[6]); - auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); - memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } -} - -void command_npccast(Client *c, const Seperator *sep) -{ - if (c->GetTarget() && c->GetTarget()->IsNPC()) { - NPC* target = c->GetTarget()->CastToNPC(); - if (!sep->IsNumber(1) && sep->arg[1] && sep->IsNumber(2)) { - const char* entity_name = sep->arg[1] ? sep->arg[1] : 0; - auto spell_id = sep->arg[2] ? std::stoul(sep->arg[2]) : 0; - Mob* spell_target = entity_list.GetMob(entity_name); - if (spell_target && IsValidSpell(spell_id) && spell_id < SPDAT_RECORDS) { - c->Message( - Chat::White, - fmt::format( - "{} ({}) casting {} ({}) on {} ({}).", - target->GetCleanName(), - target->GetID(), - GetSpellName(static_cast(spell_id)), - spell_id, - spell_target->GetCleanName(), - spell_target->GetID() - ).c_str() - ); - - target->CastSpell(spell_id, spell_target->GetID()); - } else { - if (!spell_target) { - c->Message( - Chat::White, - fmt::format( - "Entity {} was not found", - entity_name - ).c_str() - ); - } else if (!spell_id || !IsValidSpell(spell_id)) { - c->Message( - Chat::White, - fmt::format( - "Spell ID {} was not found", - spell_id - ).c_str() - ); - } - } - } else if (sep->IsNumber(1) && sep->IsNumber(2) ) { - uint16 entity_id = sep->arg[1] ? std::stoul(sep->arg[1]) : 0; - auto spell_id = sep->arg[2] ? std::stoul(sep->arg[2]) : 0; - Mob* spell_target = entity_list.GetMob(entity_id); - if (spell_target && IsValidSpell(spell_id) && spell_id < SPDAT_RECORDS) { - c->Message( - Chat::White, - fmt::format( - "{} ({}) casting {} ({}) on {} ({}).", - target->GetCleanName(), - target->GetID(), - GetSpellName(static_cast(spell_id)), - spell_id, - spell_target->GetCleanName(), - spell_target->GetID() - ).c_str() - ); - - target->CastSpell(spell_id, spell_target->GetID()); - } else { - if (!spell_target) { - c->Message( - Chat::White, - fmt::format( - "Entity ID {} was not found", - entity_id - ).c_str() - ); - } else if (!spell_id || !IsValidSpell(spell_id)) { - c->Message( - Chat::White, - fmt::format( - "Spell ID {} was not found", - spell_id - ).c_str() - ); - } - } - } - } else { - c->Message(Chat::White, "You must target an NPC to use this command."); - } -} - -void command_zstats(Client *c, const Seperator *sep) -{ - // Zone - c->Message( - Chat::White, - fmt::format( - "Zone | ID: {} Instance ID: {} Name: {} ({})", - zone->GetZoneID(), - zone->GetInstanceID(), - zone->GetLongName(), - zone->GetShortName() - ).c_str() - ); - - // Type - c->Message( - Chat::White, - fmt::format( - "Type: {}", - zone->newzone_data.ztype - ).c_str() - ); - - // Fog Data - for (int fog_index = 0; fog_index < 4; fog_index++) { - int fog_number = (fog_index + 1); - c->Message( - Chat::White, - fmt::format( - "Fog {} Colors | Red: {} Blue: {} Green: {} ", - fog_number, - zone->newzone_data.fog_red[fog_index], - zone->newzone_data.fog_green[fog_index], - zone->newzone_data.fog_blue[fog_index] - ).c_str() - ); - - c->Message( - Chat::White, - fmt::format( - "Fog {} Clipping Distance | Min: {} Max: {}", - fog_number, - zone->newzone_data.fog_minclip[fog_index], - zone->newzone_data.fog_maxclip[fog_index] - ).c_str() - ); - } - - // Fog Density - c->Message( - Chat::White, - fmt::format( - "Fog Density: {}", - zone->newzone_data.fog_density - ).c_str() - ); - - - // Gravity - c->Message( - Chat::White, - fmt::format( - "Gravity: {}", - zone->newzone_data.gravity - ).c_str() - ); - - // Time Type - c->Message( - Chat::White, - fmt::format( - "Time Type: {}", - zone->newzone_data.time_type - ).c_str() - ); - - // Experience Multiplier - c->Message( - Chat::White, - fmt::format( - "Experience Multiplier: {}", - zone->newzone_data.zone_exp_multiplier - ).c_str() - ); - - // Safe Coordinates - c->Message( - Chat::White, - fmt::format( - "Safe Coordinates: {}, {}, {}", - zone->newzone_data.safe_x, - zone->newzone_data.safe_y, - zone->newzone_data.safe_z - ).c_str() - ); - - // Max Z - c->Message( - Chat::White, - fmt::format( - "Max Z: {}", - zone->newzone_data.max_z - ).c_str() - ); - - // Underworld Z - c->Message( - Chat::White, - fmt::format( - "Underworld Z: {}", - zone->newzone_data.underworld - ).c_str() - ); - - // Clipping Distance - c->Message( - Chat::White, - fmt::format( - "Clipping Distance | Min: {} Max: {}", - zone->newzone_data.minclip, - zone->newzone_data.maxclip - ).c_str() - ); - - // Weather Data - for (int weather_index = 0; weather_index < 4; weather_index++) { - int weather_number = (weather_index + 1); - c->Message( - Chat::White, - fmt::format( - "Rain {} | Chance: {} Duration: {} ", - weather_number, - zone->newzone_data.rain_chance[weather_index], - zone->newzone_data.rain_duration[weather_index] - ).c_str() - ); - - c->Message( - Chat::White, - fmt::format( - "Snow {} | Chance: {} Duration: {}", - weather_number, - zone->newzone_data.snow_chance[weather_index], - zone->newzone_data.snow_duration[weather_index] - ).c_str() - ); - } - - // Sky Type - c->Message( - Chat::White, - fmt::format( - "Sky Type: {}", - zone->newzone_data.sky - ).c_str() - ); - - // Suspend Buffs - c->Message( - Chat::White, - fmt::format( - "Suspend Buffs: {}", - zone->newzone_data.SuspendBuffs - ).c_str() - ); - - // Regeneration Data - c->Message( - Chat::White, - fmt::format( - "Regen | Health: {} Mana: {} Endurance: {}", - zone->newzone_data.FastRegenHP, - zone->newzone_data.FastRegenMana, - zone->newzone_data.FastRegenEndurance - ).c_str() - ); - - // NPC Max Aggro Distance - c->Message( - Chat::White, - fmt::format( - "NPC Max Aggro Distance: {}", - zone->newzone_data.NPCAggroMaxDist - ).c_str() - ); - - // Underworld Teleport Index - c->Message( - Chat::White, - fmt::format( - "Underworld Teleport Index: {}", - zone->newzone_data.underworld_teleport_index - ).c_str() - ); - - // Lava Damage - c->Message( - Chat::White, - fmt::format( - "Lava Damage | Min: {} Max: {}", - zone->newzone_data.MinLavaDamage, - zone->newzone_data.LavaDamage - ).c_str() - ); -} - -void command_permaclass(Client *c, const Seperator *sep) -{ - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); - - if(sep->arg[1][0]==0) { - c->Message(Chat::White,"Usage: #permaclass "); - } - else if(!t->IsClient()) - c->Message(Chat::White,"Target is not a client."); - else { - c->Message(Chat::White, "Setting %s's class...Sending to char select.", t->GetName()); - LogInfo("Class change request from [{}] for [{}], requested class:[{}]", c->GetName(), t->GetName(), atoi(sep->arg[1]) ); - t->SetBaseClass(atoi(sep->arg[1])); - t->Save(); - t->Kick("Class was changed."); - } -} - -void command_permarace(Client *c, const Seperator *sep) -{ - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); - - if(sep->arg[1][0]==0) { - c->Message(Chat::White,"Usage: #permarace "); - c->Message(Chat::White,"NOTE: Not all models are global. If a model is not global, it will appear as a human on character select and in zones without the model."); - } - else if(!t->IsClient()) - c->Message(Chat::White,"Target is not a client."); - else { - c->Message(Chat::White, "Setting %s's race - zone to take effect", t->GetName()); - LogInfo("Permanant race change request from [{}] for [{}], requested race:[{}]", c->GetName(), t->GetName(), atoi(sep->arg[1]) ); - uint32 tmp = Mob::GetDefaultGender(atoi(sep->arg[1]), t->GetBaseGender()); - t->SetBaseRace(atoi(sep->arg[1])); - t->SetBaseGender(tmp); - t->Save(); - t->SendIllusionPacket(atoi(sep->arg[1])); - } -} - -void command_permagender(Client *c, const Seperator *sep) -{ - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); - - if(sep->arg[1][0]==0) { - c->Message(Chat::White,"Usage: #permagender "); - c->Message(Chat::White,"Gender Numbers: 0=Male, 1=Female, 2=Neuter"); - } - else if(!t->IsClient()) - c->Message(Chat::White,"Target is not a client."); - else { - c->Message(Chat::White, "Setting %s's gender - zone to take effect", t->GetName()); - LogInfo("Permanant gender change request from [{}] for [{}], requested gender:[{}]", c->GetName(), t->GetName(), atoi(sep->arg[1]) ); - t->SetBaseGender(atoi(sep->arg[1])); - t->Save(); - t->SendIllusionPacket(atoi(sep->arg[1])); - } -} - -void command_weather(Client *c, const Seperator *sep) -{ - if (!(sep->arg[1][0] == '0' || sep->arg[1][0] == '1' || sep->arg[1][0] == '2' || sep->arg[1][0] == '3')) { - c->Message(Chat::White, "Usage: #weather <0/1/2/3> - Off/Rain/Snow/Manual."); - } - else if(zone->zone_weather == 0) { - if(sep->arg[1][0] == '3') { // Put in modifications here because it had a very good chance at screwing up the client's weather system if rain was sent during snow -T7 - if(sep->arg[2][0] != 0 && sep->arg[3][0] != 0) { - c->Message(Chat::White, "Sending weather packet... TYPE=%s, INTENSITY=%s", sep->arg[2], sep->arg[3]); - zone->zone_weather = atoi(sep->arg[2]); - auto outapp = new EQApplicationPacket(OP_Weather, 8); - outapp->pBuffer[0] = atoi(sep->arg[2]); - outapp->pBuffer[4] = atoi(sep->arg[3]); // This number changes in the packets, intensity? - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } - else { - c->Message(Chat::White, "Manual Usage: #weather 3 "); - } - } - else if(sep->arg[1][0] == '2') { - entity_list.Message(0, 0, "Snowflakes begin to fall from the sky."); - zone->zone_weather = 2; - auto outapp = new EQApplicationPacket(OP_Weather, 8); - outapp->pBuffer[0] = 0x01; - outapp->pBuffer[4] = 0x02; // This number changes in the packets, intensity? - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } - else if(sep->arg[1][0] == '1') { - entity_list.Message(0, 0, "Raindrops begin to fall from the sky."); - zone->zone_weather = 1; - auto outapp = new EQApplicationPacket(OP_Weather, 8); - outapp->pBuffer[4] = 0x01; // This is how it's done in Fear, and you can see a decent distance with it at this value - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } - } - else { - if(zone->zone_weather == 1) { // Doing this because if you have rain/snow on, you can only turn one off. - entity_list.Message(0, 0, "The sky clears as the rain ceases to fall."); - zone->zone_weather = 0; - auto outapp = new EQApplicationPacket(OP_Weather, 8); - // To shutoff weather you send an empty 8 byte packet (You get this everytime you zone even if the sky is clear) - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } - else if(zone->zone_weather == 2) { - entity_list.Message(0, 0, "The sky clears as the snow stops falling."); - zone->zone_weather = 0; - auto outapp = new EQApplicationPacket(OP_Weather, 8); - // To shutoff weather you send an empty 8 byte packet (You get this everytime you zone even if the sky is clear) - outapp->pBuffer[0] = 0x01; // Snow has it's own shutoff packet - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } - else { - entity_list.Message(0, 0, "The sky clears."); - zone->zone_weather = 0; - auto outapp = new EQApplicationPacket(OP_Weather, 8); - // To shutoff weather you send an empty 8 byte packet (You get this everytime you zone even if the sky is clear) - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } - } -} - -void command_zheader(Client *c, const Seperator *sep) -{ - // sends zhdr packet - if(sep->arg[1][0]==0) { - c->Message(Chat::White, "Usage: #zheader "); - } - else if(ZoneID(sep->argplus[1])==0) - c->Message(Chat::White, "Invalid Zone Name: %s", sep->argplus[1]); - else { - - if (zone->LoadZoneCFG(sep->argplus[1], 0)) - c->Message(Chat::White, "Successfully loaded zone header for %s from database.", sep->argplus[1]); - else - c->Message(Chat::White, "Failed to load zone header %s from database", sep->argplus[1]); - auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); - memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } -} - -void command_zsky(Client *c, const Seperator *sep) -{ - // modifys and resends zhdr packet - if(sep->arg[1][0]==0) - c->Message(Chat::White, "Usage: #zsky "); - else if(atoi(sep->arg[1])<0||atoi(sep->arg[1])>255) - c->Message(Chat::White, "ERROR: Sky type can not be less than 0 or greater than 255!"); - else { - zone->newzone_data.sky = atoi(sep->arg[1]); - auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); - memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } -} - -void command_zcolor(Client *c, const Seperator *sep) -{ - // modifys and resends zhdr packet - if (sep->arg[3][0]==0) - c->Message(Chat::White, "Usage: #zcolor "); - else if (atoi(sep->arg[1])<0||atoi(sep->arg[1])>255) - c->Message(Chat::White, "ERROR: Red can not be less than 0 or greater than 255!"); - else if (atoi(sep->arg[2])<0||atoi(sep->arg[2])>255) - c->Message(Chat::White, "ERROR: Green can not be less than 0 or greater than 255!"); - else if (atoi(sep->arg[3])<0||atoi(sep->arg[3])>255) - c->Message(Chat::White, "ERROR: Blue can not be less than 0 or greater than 255!"); - else { - for (int z=0; z<4; z++) { - zone->newzone_data.fog_red[z] = atoi(sep->arg[1]); - zone->newzone_data.fog_green[z] = atoi(sep->arg[2]); - zone->newzone_data.fog_blue[z] = atoi(sep->arg[3]); - } - auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); - memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } -} - -void command_gassign(Client *c, const Seperator *sep) -{ - if (sep->IsNumber(1) && c->GetTarget() && c->GetTarget()->IsNPC() && c->GetTarget()->CastToNPC()->GetSpawnPointID() > 0) { - int spawn2id = c->GetTarget()->CastToNPC()->GetSpawnPointID(); - database.AssignGrid(c, atoi(sep->arg[1]), spawn2id); - } - else - c->Message(Chat::White, "Usage: #gassign [num] - must have an npc target!"); -} - -void command_ai(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - - if (strcasecmp(sep->arg[1], "factionid") == 0) { - if (target && sep->IsNumber(2)) { - if (target->IsNPC()) - target->CastToNPC()->SetNPCFactionID(atoi(sep->arg[2])); - else - c->Message(Chat::White, "%s is not an NPC.", target->GetName()); - } - else - c->Message(Chat::White, "Usage: (targeted) #ai factionid [factionid]"); - } - else if (strcasecmp(sep->arg[1], "spellslist") == 0) { - if (target && sep->IsNumber(2) && atoi(sep->arg[2]) >= 0) { - if (target->IsNPC()) - target->CastToNPC()->AI_AddNPCSpells(atoi(sep->arg[2])); - else - c->Message(Chat::White, "%s is not an NPC.", target->GetName()); - } - else - c->Message(Chat::White, "Usage: (targeted) #ai spellslist [npc_spells_id]"); - } - else if (strcasecmp(sep->arg[1], "con") == 0) { - if (target && sep->arg[2][0] != 0) { - Mob* tar2 = entity_list.GetMob(sep->arg[2]); - if (tar2) - c->Message(Chat::White, "%s considering %s: %i", target->GetName(), tar2->GetName(), tar2->GetReverseFactionCon(target)); - else - c->Message(Chat::White, "Error: %s not found.", sep->arg[2]); - } - else - c->Message(Chat::White, "Usage: (targeted) #ai con [mob name]"); - } - else if (strcasecmp(sep->arg[1], "guard") == 0) { - if (target && target->IsNPC()) - target->CastToNPC()->SaveGuardSpot(target->GetPosition()); - else - c->Message(Chat::White, "Usage: (targeted) #ai guard - sets npc to guard the current location (use #summon to move)"); - } - else if (strcasecmp(sep->arg[1], "roambox") == 0) { - if (target && target->IsAIControlled() && target->IsNPC()) { - if ((sep->argnum == 6 || sep->argnum == 7 || sep->argnum == 8) && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4) && sep->IsNumber(5) && sep->IsNumber(6)) { - uint32 tmp = 2500; - uint32 tmp2 = 2500; - if (sep->IsNumber(7)) - tmp = atoi(sep->arg[7]); - if (sep->IsNumber(8)) - tmp2 = atoi(sep->arg[8]); - target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), atof(sep->arg[4]), atof(sep->arg[5]), atof(sep->arg[6]), tmp, tmp2); - } - else if ((sep->argnum == 3 || sep->argnum == 4) && sep->IsNumber(2) && sep->IsNumber(3)) { - uint32 tmp = 2500; - uint32 tmp2 = 2500; - if (sep->IsNumber(4)) - tmp = atoi(sep->arg[4]); - if (sep->IsNumber(5)) - tmp2 = atoi(sep->arg[5]); - target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), tmp, tmp2); - } - else { - c->Message(Chat::White, "Usage: #ai roambox dist max_x min_x max_y min_y [delay] [mindelay]"); - c->Message(Chat::White, "Usage: #ai roambox dist roamdist [delay] [mindelay]"); - } - } - else - c->Message(Chat::White, "You need a AI NPC targeted"); - } - else if (strcasecmp(sep->arg[1], "stop") == 0 && c->Admin() >= commandToggleAI) { - if (target) { - if (target->IsAIControlled()) - target->AI_Stop(); - else - c->Message(Chat::White, "Error: Target is not AI controlled"); - } - else - c->Message(Chat::White, "Usage: Target a Mob with AI enabled and use this to turn off their AI."); - } - else if (strcasecmp(sep->arg[1], "start") == 0 && c->Admin() >= commandToggleAI) { - if (target) { - if (!target->IsAIControlled()) - target->AI_Start(); - else - c->Message(Chat::White, "Error: Target is already AI controlled"); - } - else - c->Message(Chat::White, "Usage: Target a Mob with AI disabled and use this to turn on their AI."); - } - else { - c->Message(Chat::White, "#AI Sub-commands"); - c->Message(Chat::White, " factionid"); - c->Message(Chat::White, " spellslist"); - c->Message(Chat::White, " con"); - c->Message(Chat::White, " guard"); - } -} - -void command_worldshutdown(Client *c, const Seperator *sep) -{ - // GM command to shutdown world server and all zone servers - uint32 time = 0; - uint32 interval = 0; - if (worldserver.Connected()) { - if ( - sep->IsNumber(1) && - sep->IsNumber(2) && - (time = std::stoi(sep->arg[1]) > 0) && - (interval = std::stoi(sep->arg[2]) > 0) - ) { - int time_minutes = (time / 60); - quest_manager.WorldWideMessage( - Chat::Yellow, - fmt::format( - "[SYSTEM] World will be shutting down in {} minutes.", - time_minutes - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "World will be shutting down in {} minutes, notifying every {} seconds", - time_minutes, - interval - ).c_str() - ); - auto pack = new ServerPacket(ServerOP_ShutdownAll, sizeof(WorldShutDown_Struct)); - WorldShutDown_Struct* wsd = (WorldShutDown_Struct*)pack->pBuffer; - wsd->time = (time * 1000); - wsd->interval = (interval * 1000); - worldserver.SendPacket(pack); - safe_delete(pack); - } else if (!strcasecmp(sep->arg[1], "now")){ - quest_manager.WorldWideMessage( - Chat::Yellow, - "[SYSTEM] World is shutting down now." - ); - c->Message(Chat::White, "World is shutting down now."); - auto pack = new ServerPacket; - pack->opcode = ServerOP_ShutdownAll; - pack->size = 0; - worldserver.SendPacket(pack); - safe_delete(pack); - } else if (!strcasecmp(sep->arg[1], "disable")) { - c->Message(Chat::White, "World shutdown has been aborted."); - auto pack = new ServerPacket(ServerOP_ShutdownAll, sizeof(WorldShutDown_Struct)); - WorldShutDown_Struct* wsd = (WorldShutDown_Struct*)pack->pBuffer; - wsd->time = 0; - wsd->interval = 0; - worldserver.SendPacket(pack); - safe_delete(pack); - } else { - c->Message(Chat::White,"#worldshutdown - Shuts down the server and all zones."); - c->Message(Chat::White,"Usage: #worldshutdown now - Shuts down the server and all zones immediately."); - c->Message(Chat::White,"Usage: #worldshutdown disable - Stops the server from a previously scheduled shut down."); - c->Message(Chat::White,"Usage: #worldshutdown [timer] [interval] - Shuts down the server and all zones after [timer] seconds and notifies players every [interval] seconds."); - } - } else { - c->Message(Chat::White, "Error: World server is disconnected."); - } -} - -void command_sendzonespawns(Client *c, const Seperator *sep) -{ - entity_list.SendZoneSpawns(c); -} - -void command_zsave(Client *c, const Seperator *sep) -{ - if (zone->SaveZoneCFG()) { - c->Message(Chat::Red, "Zone header saved successfully."); - } - else { - c->Message(Chat::Red, "ERROR: Zone header data was NOT saved."); - } -} - -void command_dbspawn2(Client *c, const Seperator *sep) -{ - - if (sep->IsNumber(1) && sep->IsNumber(2) && sep->IsNumber(3)) { - LogInfo("Spawning database spawn"); - uint16 cond = 0; - int16 cond_min = 0; - if(sep->IsNumber(4)) { - cond = atoi(sep->arg[4]); - if(sep->IsNumber(5)) - cond_min = atoi(sep->arg[5]); - } - database.CreateSpawn2(c, atoi(sep->arg[1]), zone->GetShortName(), c->GetPosition(), atoi(sep->arg[2]), atoi(sep->arg[3]), cond, cond_min); - } - else { - c->Message(Chat::White, "Usage: #dbspawn2 spawngroup respawn variance [condition_id] [condition_min]"); - } -} - -void command_shutdown(Client *c, const Seperator *sep) -{ - CatchSignal(2); -} - -void command_delacct(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == 0) - c->Message(Chat::White, "Format: #delacct accountname"); - else { - std::string user; - std::string loginserver; - ParseAccountString(sep->arg[1], user, loginserver); - - if (database.DeleteAccount(user.c_str(), loginserver.c_str())) - c->Message(Chat::White, "The account was deleted."); - else - c->Message(Chat::White, "Unable to delete account."); - } -} - -void command_setpass(Client *c, const Seperator *sep) -{ - if(sep->argnum != 2) - c->Message(Chat::White, "Format: #setpass accountname password"); - else { - std::string user; - std::string loginserver; - ParseAccountString(sep->arg[1], user, loginserver); - - int16 tmpstatus = 0; - uint32 tmpid = database.GetAccountIDByName(user.c_str(), loginserver.c_str(), &tmpstatus); - if (!tmpid) - c->Message(Chat::White, "Error: Account not found"); - else if (tmpstatus > c->Admin()) - c->Message(Chat::White, "Cannot change password: Account's status is higher than yours"); - else if (database.SetLocalPassword(tmpid, sep->arg[2])) - c->Message(Chat::White, "Password changed."); - else - c->Message(Chat::White, "Error changing password."); - } -} - -void command_setlsinfo(Client *c, const Seperator *sep) -{ - if(sep->argnum != 2) - c->Message(Chat::White, "Format: #setlsinfo email password"); - else { - auto pack = new ServerPacket(ServerOP_LSAccountUpdate, sizeof(ServerLSAccountUpdate_Struct)); - ServerLSAccountUpdate_Struct* s = (ServerLSAccountUpdate_Struct *) pack->pBuffer; - s->useraccountid = c->LSAccountID(); - strn0cpy(s->useraccount, c->AccountName(), 30); - strn0cpy(s->user_email, sep->arg[1], 100); - strn0cpy(s->userpassword, sep->arg[2], 50); - worldserver.SendPacket(pack); - c->Message(Chat::White, "Login Server update packet sent."); - } -} - -void command_grid(Client *c, const Seperator *sep) -{ - auto command_type = sep->arg[1]; - auto zone_id = zone->GetZoneID(); - if (strcasecmp("max", command_type) == 0) { - c->Message( - Chat::White, - fmt::format( - "Highest grid ID in this zone is {}.", - content_db.GetHighestGrid(zone_id) - ).c_str() - ); - } else if (strcasecmp("add", command_type) == 0) { - auto grid_id = atoi(sep->arg[2]); - auto wander_type = atoi(sep->arg[3]); - auto pause_type = atoi(sep->arg[4]); - if (!content_db.GridExistsInZone(zone_id, grid_id)) { - content_db.ModifyGrid(c, false, grid_id, wander_type, pause_type, zone_id); - c->Message( - Chat::White, - fmt::format( - "Grid {} added to zone ID {} with wander type {} and pause type {}.", - grid_id, - zone_id, - wander_type, - pause_type - ).c_str() - ); - } else { - c->Message( - Chat::White, - fmt::format( - "Grid {} already exists in zone ID {}.", - grid_id, - zone_id - ).c_str() - ); - return; - } - } else if (strcasecmp("show", command_type) == 0) { - Mob *target = c->GetTarget(); - if (!target || !target->IsNPC()) { - c->Message(Chat::White, "You need to target an NPC!"); - return; - } - - auto grid_id = target->CastToNPC()->GetGrid(); - std::string query = fmt::format( - "SELECT `x`, `y`, `z`, `heading`, `number` " - "FROM `grid_entries` " - "WHERE `zoneid` = {} AND `gridid` = {} " - "ORDER BY `number`", - zone_id, - grid_id - ); - - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Error querying database."); - c->Message(Chat::White, query.c_str()); - } - - if (results.RowCount() == 0) { - c->Message(Chat::White, "No grid found."); - return; - } - - // Depop any node npc's already spawned - entity_list.DespawnGridNodes(grid_id); - - // Spawn grid nodes - std::map, int32> zoffset; - for (auto row : results) { - glm::vec4 node_position = glm::vec4(atof(row[0]), atof(row[1]), atof(row[2]), atof(row[3])); - std::vector node_loc { - node_position.x, - node_position.y, - node_position.z - }; - - // If we already have a node at this location, set the z offset - // higher from the existing one so we can see it. Adjust so if - // there is another at the same spot we adjust again. - auto search = zoffset.find(node_loc); - if (search != zoffset.end()) { - search->second = search->second + 3; - } else { - zoffset[node_loc] = 0.0; - } - - node_position.z += zoffset[node_loc]; - NPC::SpawnGridNodeNPC(node_position, grid_id, atoi(row[4]), zoffset[node_loc]); - } - c->Message( - Chat::White, - fmt::format( - "Spawning nodes for grid {}.", - grid_id - ).c_str() - ); - } else if (strcasecmp("hide", command_type) == 0) { - Mob* target = c->GetTarget(); - if (!target || !target->IsNPC()) { - c->Message(Chat::White, "You need to target an NPC!"); - return; - } - - auto grid_id = target->CastToNPC()->GetGrid(); - entity_list.DespawnGridNodes(grid_id); - c->Message( - Chat::White, - fmt::format( - "Depawning nodes for grid {}.", - grid_id - ).c_str() - ); - } else if (strcasecmp("delete", command_type) == 0) { - auto grid_id = atoi(sep->arg[2]); - content_db.ModifyGrid(c, true, grid_id, 0, 0, zone_id); - c->Message( - Chat::White, - fmt::format( - "Grid {} deleted from zone ID {}.", - grid_id, - zone_id - ).c_str() - ); - } else { - c->Message(Chat::White, "Usage: #grid [add|delete] [grid_id] [wander_type] [pause_type]"); - c->Message(Chat::White, "Usage: #grid [max] - displays the highest grid ID used in this zone (for add)"); - c->Message(Chat::White, "Usage: #grid [show] - displays wp nodes as boxes"); - } -} - -void command_wp(Client *c, const Seperator *sep) -{ - auto command_type = sep->arg[1]; - auto grid_id = atoi(sep->arg[2]); - if (grid_id != 0) { - auto pause = atoi(sep->arg[3]); - auto waypoint = atoi(sep->arg[4]); - auto zone_id = zone->GetZoneID(); - if (strcasecmp("add", command_type) == 0) { - if (waypoint == 0) { // Default to highest if it's left blank, or we enter 0 - waypoint = (content_db.GetHighestWaypoint(zone_id, grid_id) + 1); - } - - if (strcasecmp("-h", sep->arg[5]) == 0) { - content_db.AddWP(c, grid_id, waypoint, c->GetPosition(), pause, zone_id); - } else { - auto position = c->GetPosition(); - position.w = -1; - content_db.AddWP(c, grid_id, waypoint, position, pause, zone_id); - } - c->Message( - Chat::White, - fmt::format( - "Waypoint {} added to grid {} with a pause of {} {}.", - waypoint, - grid_id, - pause, - (pause == 1 ? "second" : "seconds") - ).c_str() - ); - } else if (strcasecmp("delete", command_type) == 0) { - content_db.DeleteWaypoint(c, grid_id, waypoint, zone_id); - c->Message( - Chat::White, - fmt::format( - "Waypoint {} deleted from grid {}.", - waypoint, - grid_id - ).c_str() - ); - } - } else { - c->Message(Chat::White,"Usage: #wp [add|delete] [grid_id] [pause] [waypoint_id] [-h]"); - } -} - -void command_iplookup(Client *c, const Seperator *sep) -{ - auto pack = - new ServerPacket(ServerOP_IPLookup, sizeof(ServerGenericWorldQuery_Struct) + strlen(sep->argplus[1]) + 1); - ServerGenericWorldQuery_Struct* s = (ServerGenericWorldQuery_Struct *) pack->pBuffer; - strcpy(s->from, c->GetName()); - s->admin = c->Admin(); - if (sep->argplus[1][0] != 0) - strcpy(s->query, sep->argplus[1]); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void command_size(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!sep->IsNumber(1)) - c->Message(Chat::White, "Usage: #size [0 - 255] (Decimal increments are allowed)"); - else { - float newsize = atof(sep->arg[1]); - if (newsize > 255) - c->Message(Chat::White, "Error: #size: Size can not be greater than 255."); - else if (newsize < 0) - c->Message(Chat::White, "Error: #size: Size can not be less than 0."); - else if (!target) - c->Message(Chat::White,"Error: this command requires a target"); - else { - uint16 Race = target->GetModel(); - uint8 Gender = target->GetGender(); - uint8 Texture = 0xFF; - uint8 HelmTexture = 0xFF; - uint8 HairColor = target->GetHairColor(); - uint8 BeardColor = target->GetBeardColor(); - uint8 EyeColor1 = target->GetEyeColor1(); - uint8 EyeColor2 = target->GetEyeColor2(); - uint8 HairStyle = target->GetHairStyle(); - uint8 LuclinFace = target->GetLuclinFace(); - uint8 Beard = target->GetBeard(); - uint32 DrakkinHeritage = target->GetDrakkinHeritage(); - uint32 DrakkinTattoo = target->GetDrakkinTattoo(); - uint32 DrakkinDetails = target->GetDrakkinDetails(); - - target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, - EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, - DrakkinHeritage, DrakkinTattoo, DrakkinDetails, newsize); - - c->Message(Chat::White,"Size = %f", atof(sep->arg[1])); - } - } -} - -void command_mana(Client *c, const Seperator *sep) -{ - auto target = c->GetTarget() ? c->GetTarget() : c; - if(target->IsClient()) { - target->CastToClient()->SetMana(target->CastToClient()->CalcMaxMana()); - } else { - target->SetMana(target->CalcMaxMana()); - } - - if (c != target) { - c->Message( - Chat::White, - fmt::format( - "Set {} ({}) to full Mana.", - target->GetCleanName(), - target->GetID() - ).c_str() - ); - } else { - c->Message(Chat::White, "Restored your Mana to full."); - } -} - -void command_flymode(Client *c, const Seperator *sep) -{ - Mob *t = c; - - if (strlen(sep->arg[1]) == 1 && sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 5) { - if (c->GetTarget()) { - t = c->GetTarget(); - } - - int fm = atoi(sep->arg[1]); - - t->SetFlyMode(static_cast(fm)); - t->SendAppearancePacket(AT_Levitate, fm); - if (sep->arg[1][0] == '0') { - c->Message(Chat::White, "Setting %s to Grounded", t->GetName()); - } - else if (sep->arg[1][0] == '1') { - c->Message(Chat::White, "Setting %s to Flying", t->GetName()); - } - else if (sep->arg[1][0] == '2') { - c->Message(Chat::White, "Setting %s to Levitating", t->GetName()); - } - else if (sep->arg[1][0] == '3') { - c->Message(Chat::White, "Setting %s to In Water", t->GetName()); - } - else if (sep->arg[1][0] == '4') { - c->Message(Chat::White, "Setting %s to Floating(Boat)", t->GetName()); - } - else if (sep->arg[1][0] == '5') { - c->Message(Chat::White, "Setting %s to Levitating While Running", t->GetName()); - } - } else { - c->Message(Chat::White, "#flymode [0/1/2/3/4/5]"); - } -} - - -void command_showskills(Client *c, const Seperator *sep) -{ - Client *target = c; - if (c->GetTarget() && c->GetTarget()->IsClient()) { - target = c->GetTarget()->CastToClient(); - } - - bool show_all = false; - - if (!strcasecmp("all", sep->arg[1])) { - show_all = true; - } - - c->Message( - Chat::White, - fmt::format( - "Skills | Name: {}", - target->GetCleanName() - ).c_str() - ); - - for ( - EQ::skills::SkillType skill_type = EQ::skills::Skill1HBlunt; - skill_type <= EQ::skills::HIGHEST_SKILL; - skill_type = (EQ::skills::SkillType)(skill_type + 1) - ) { - if (show_all || (target->CanHaveSkill(skill_type) && target->MaxSkill(skill_type))) { - c->Message( - Chat::White, - fmt::format( - "{} ({}) | Current: {} Max: {} Raw: {}", - EQ::skills::GetSkillName(skill_type), - skill_type, - target->GetSkill(skill_type), - target->MaxSkill(skill_type), - target->GetRawSkill(skill_type) - ).c_str() - ); - } - } -} - -void command_findclass(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - - if (arguments == 0) { - c->Message(Chat::White, "Command Syntax: #findclass [search criteria]"); - return; - } - - if (sep->IsNumber(1)) { - int class_id = std::stoi(sep->arg[1]); - if (class_id >= WARRIOR && class_id <= MERCERNARY_MASTER) { - std::string class_name = GetClassIDName(class_id); - c->Message( - Chat::White, - fmt::format( - "Class {}: {}", - class_id, - class_name - ).c_str() - ); - } else { - c->Message( - Chat::White, - fmt::format( - "Class ID {} was not found.", - class_id - ).c_str() - ); - } - } else { - std::string search_criteria = str_tolower(sep->argplus[1]); - int found_count = 0; - for (int class_id = WARRIOR; class_id <= MERCERNARY_MASTER; class_id++) { - std::string class_name = GetClassIDName(class_id); - std::string class_name_lower = str_tolower(class_name); - if (search_criteria.length() > 0 && class_name_lower.find(search_criteria) == std::string::npos) { - continue; - } - - c->Message( - Chat::White, - fmt::format( - "Class {}: {}", - class_id, - class_name - ).c_str() - ); - found_count++; - - if (found_count == 20) { - break; - } - } - - if (found_count == 20) { - c->Message(Chat::White, "20 Classes found... max reached."); - } else { - auto class_message = ( - found_count > 0 ? - ( - found_count == 1 ? - "A Class was" : - fmt::format("{} Classes were", found_count) - ) : - "No Classes were" - ); - - c->Message( - Chat::White, - fmt::format( - "{} found.", - class_message - ).c_str() - ); - } - } -} - -void command_findfaction(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - - if (arguments == 0) { - c->Message(Chat::White, "Command Syntax: #findfaction [search criteria]"); - return; - } - - if (sep->IsNumber(1)) { - int faction_id = std::stoi(sep->arg[1]); - auto faction_name = content_db.GetFactionName(faction_id); - if (!faction_name.empty()) { - c->Message( - Chat::White, - fmt::format( - "Faction {}: {}", - faction_id, - faction_name - ).c_str() - ); - } else { - c->Message( - Chat::White, - fmt::format( - "Faction ID {} was not found.", - faction_id - ).c_str() - ); - } - } else { - std::string search_criteria = str_tolower(sep->argplus[1]); - int found_count = 0; - int max_faction_id = content_db.GetMaxFaction(); - for (int faction_id = 0; faction_id < max_faction_id; faction_id++) { - std::string faction_name = content_db.GetFactionName(faction_id); - std::string faction_name_lower = str_tolower(faction_name); - if (faction_name.empty()) { - continue; - } - - if (faction_name.find(search_criteria) == std::string::npos) { - continue; - } - - c->Message( - Chat::White, - fmt::format( - "Faction {}: {}", - faction_id, - faction_name - ).c_str() - ); - found_count++; - - if (found_count == 20) { - break; - } - } - - if (found_count == 20) { - c->Message(Chat::White, "20 Factions found... max reached."); - } else { - auto faction_message = ( - found_count > 0 ? - ( - found_count == 1 ? - "A Faction was" : - fmt::format("{} Factions were", found_count) - ) : - "No Factions were" - ); - - c->Message( - Chat::White, - fmt::format( - "{} found.", - faction_message - ).c_str() - ); - } - } -} - -void command_findrace(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - - if (arguments == 0) { - c->Message(Chat::White, "Command Syntax: #findrace [search criteria]"); - return; - } - - if (sep->IsNumber(1)) { - int race_id = std::stoi(sep->arg[1]); - std::string race_name = GetRaceIDName(race_id); - if (race_id >= RACE_HUMAN_1 && race_id <= RACE_PEGASUS_732) { - c->Message( - Chat::White, - fmt::format( - "Race {}: {}", - race_id, - race_name - ).c_str() - ); - } else { - c->Message( - Chat::White, - fmt::format( - "Race ID {} was not found.", - race_id - ).c_str() - ); - } - } else { - std::string search_criteria = str_tolower(sep->argplus[1]); - int found_count = 0; - for (int race_id = RACE_HUMAN_1; race_id <= RACE_PEGASUS_732; race_id++) { - std::string race_name = GetRaceIDName(race_id); - std::string race_name_lower = str_tolower(race_name); - if (search_criteria.length() > 0 && race_name_lower.find(search_criteria) == std::string::npos) { - continue; - } - - c->Message( - Chat::White, - fmt::format( - "Race {}: {}", - race_id, - race_name - ).c_str() - ); - found_count++; - - if (found_count == 20) { - break; - } - } - - if (found_count == 20) { - c->Message(Chat::White, "20 Races found... max reached."); - } else { - auto race_message = ( - found_count > 0 ? - ( - found_count == 1 ? - "A Race was" : - fmt::format("{} Races were", found_count) - ) : - "No Races were" - ); - - c->Message( - Chat::White, - fmt::format( - "{} found.", - race_message - ).c_str() - ); - } - } -} - -void command_findskill(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - - if (arguments == 0) { - c->Message(Chat::White, "Command Syntax: #findskill [search criteria]"); - return; - } - - std::map skills = EQ::skills::GetSkillTypeMap(); - if (sep->IsNumber(1)) { - int skill_id = std::stoi(sep->arg[1]); - if (skill_id >= EQ::skills::Skill1HBlunt && skill_id < EQ::skills::SkillCount) { - for (auto skill : skills) { - if (skill_id == skill.first) { - c->Message( - Chat::White, - fmt::format( - "Skill {}: {}", - skill.first, - skill.second - ).c_str() - ); - break; - } - } - } else { - c->Message( - Chat::White, - fmt::format( - "Skill ID {} was not found.", - skill_id - ).c_str() - ); - } - } else { - std::string search_criteria = str_tolower(sep->argplus[1]); - if (!search_criteria.empty()) { - int found_count = 0; - for (auto skill : skills) { - std::string skill_name_lower = str_tolower(skill.second); - if (skill_name_lower.find(search_criteria) == std::string::npos) { - continue; - } - - c->Message( - Chat::White, - fmt::format( - "Skill {}: {}", - skill.first, - skill.second - ).c_str() - ); - found_count++; - - if (found_count == 20) { - break; - } - } - - if (found_count == 20) { - c->Message(Chat::White, "20 Skills were found, max reached."); - } else { - auto skill_message = ( - found_count > 0 ? - ( - found_count == 1 ? - "A Skill was" : - fmt::format("{} Skills were", found_count) - ) : - "No Skills were" - ); - - c->Message( - Chat::White, - fmt::format( - "{} found.", - skill_message - ).c_str() - ); - } - } - } -} - -void command_findspell(Client *c, const Seperator *sep) -{ - if (SPDAT_RECORDS <= 0) { - c->Message(Chat::White, "Spells not loaded"); - return; - } - - int arguments = sep->argnum; - - if (arguments == 0) { - c->Message(Chat::White, "Command Syntax: #findspell [search criteria]"); - return; - } - - if (sep->IsNumber(1)) { - int spell_id = std::stoi(sep->arg[1]); - if (!IsValidSpell(spell_id)) { - c->Message( - Chat::White, - fmt::format( - "Spell ID {} was not found.", - spell_id - ).c_str() - ); - } else { - c->Message( - Chat::White, - fmt::format( - "Spell {}: {}", - spell_id, - spells[spell_id].name - ).c_str() - ); - } - } else { - std::string search_criteria = str_tolower(sep->argplus[1]); - int found_count = 0; - for (int spell_id = 0; spell_id < SPDAT_RECORDS; spell_id++) { - auto current_spell = spells[spell_id]; - if (current_spell.name[0] != 0) { - std::string spell_name = current_spell.name; - std::string spell_name_lower = str_tolower(spell_name); - if (search_criteria.length() > 0 && spell_name_lower.find(search_criteria) == std::string::npos) { - continue; - } - - c->Message( - Chat::White, - fmt::format( - "Spell {}: {}", - spell_id, - spell_name - ).c_str() - ); - found_count++; - - if (found_count == 20) { - break; - } - } - } - - if (found_count == 20) { - c->Message(Chat::White, "20 Spells found... max reached."); - } else { - auto spell_message = ( - found_count > 0 ? - ( - found_count == 1 ? - "A Spell was" : - fmt::format("{} Spells were", found_count) - ) : - "No Spells were" - ); - - c->Message( - Chat::White, - fmt::format( - "{} found.", - spell_message - ).c_str() - ); - } - } -} - -inline bool CastRestrictedSpell(int spellid) -{ - switch (spellid) { - case SPELL_TOUCH_OF_VINITRAS: - case SPELL_DESPERATE_HOPE: - case SPELL_CHARM: - case SPELL_METAMORPHOSIS65: - case SPELL_JT_BUFF: - case SPELL_CAN_O_WHOOP_ASS: - case SPELL_PHOENIX_CHARM: - case SPELL_CAZIC_TOUCH: - case SPELL_AVATAR_KNOCKBACK: - case SPELL_SHAPECHANGE65: - case SPELL_SUNSET_HOME1218: - case SPELL_SUNSET_HOME819: - case SPELL_SHAPECHANGE75: - case SPELL_SHAPECHANGE80: - case SPELL_SHAPECHANGE85: - case SPELL_SHAPECHANGE90: - case SPELL_SHAPECHANGE95: - case SPELL_SHAPECHANGE100: - case SPELL_SHAPECHANGE25: - case SPELL_SHAPECHANGE30: - case SPELL_SHAPECHANGE35: - case SPELL_SHAPECHANGE40: - case SPELL_SHAPECHANGE45: - case SPELL_SHAPECHANGE50: - case SPELL_NPC_AEGOLISM: - case SPELL_SHAPECHANGE55: - case SPELL_SHAPECHANGE60: - case SPELL_COMMAND_OF_DRUZZIL: - case SPELL_SHAPECHANGE70: - return true; - default: - return false; - } -} - -void command_castspell(Client *c, const Seperator *sep) -{ - if (SPDAT_RECORDS <= 0) { - c->Message(Chat::White, "Spells not loaded."); - return; - } - - Mob *target = c; - if(c->GetTarget()) { - target = c->GetTarget(); - } - - if (!sep->IsNumber(1)) { - c->Message(Chat::White, "Usage: #castspell [Spell ID] [Instant (0 = False, 1 = True, Default is 1 if Unused)]"); - } else { - uint16 spell_id = std::stoul(sep->arg[1]); - - if (CastRestrictedSpell(spell_id) && c->Admin() < commandCastSpecials) { - c->Message(Chat::Red, "Unable to cast spell."); - } else if (spell_id >= SPDAT_RECORDS) { - c->Message(Chat::White, "Invalid Spell ID."); - } else { - bool instant_cast = (c->Admin() >= commandInstacast ? true : false); - if (instant_cast && sep->IsNumber(2)) { - instant_cast = std::stoi(sep->arg[2]) ? true : false; - c->Message(Chat::White, fmt::format("{}", std::stoi(sep->arg[2])).c_str()); - } - - if (c->Admin() >= commandInstacast && instant_cast) { - c->SpellFinished(spell_id, target, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty); - } else { - c->CastSpell(spell_id, target->GetID(), EQ::spells::CastingSlot::Item, spells[spell_id].cast_time); - } - - if (c != target) { - c->Message( - Chat::White, - fmt::format( - "Cast {} ({}) on {}{}.", - GetSpellName(spell_id), - spell_id, - target->GetCleanName(), - instant_cast ? " instantly" : "" - ).c_str() - ); - } else { - c->Message( - Chat::White, - fmt::format( - "Cast {} ({}) on yourself{}.", - GetSpellName(spell_id), - spell_id, - instant_cast ? " instantly" : "" - ).c_str() - ); - } - } - } -} - -void command_setlanguage(Client *c, const Seperator *sep) -{ - Client* target = c; - if (c->GetTarget() && c->GetTarget()->IsClient()) { - target = c->GetTarget()->CastToClient(); - } - - auto language_id = sep->IsNumber(1) ? std::stoi(sep->arg[1]) : -1; - auto language_value = sep->IsNumber(2) ? std::stoi(sep->arg[2]) : -1; - if (!strcasecmp(sep->arg[1], "list" )) { - for (int language = LANG_COMMON_TONGUE; language <= LANG_UNKNOWN; language++) { - c->Message( - Chat::White, - fmt::format( - "Language {}: {}", - language, - EQ::constants::GetLanguageName(language) - ).c_str() - ); - } - } else if ( - language_id < LANG_COMMON_TONGUE || - language_id > LANG_UNKNOWN || - language_value < 0 || - language_value > 100 - ) { - c->Message(Chat::White, "Usage: #setlanguage [Language ID] [Language Value]"); - c->Message(Chat::White, "Usage: #setlanguage [List]"); - c->Message(Chat::White, "Language ID = 0 to 27", LANG_UNKNOWN); - c->Message(Chat::White, "Language Value = 0 to 100", HIGHEST_CAN_SET_SKILL); - } else { - LogInfo( - "Set language request from [{}], Target: [{}] Language ID: [{}] Language Value: [{}]", - c->GetCleanName(), - target->GetCleanName(), - language_id, - language_value - ); - - target->SetLanguageSkill(language_id, language_value); - - if (c != target) { - c->Message( - Chat::White, - fmt::format( - "Set {} ({}) to {} for {}.", - EQ::constants::GetLanguageName(language_id), - language_id, - language_value, - target->GetCleanName() - ).c_str() - ); - } - } -} - -void command_setskill(Client* c, const Seperator* sep) -{ - Client* target = c; - if (c->GetTarget() && c->GetTarget()->IsClient()) { - target = c->GetTarget()->CastToClient(); - } - - auto skill_id = sep->IsNumber(1) ? std::stoi(sep->arg[1]) : -1; - auto skill_value = sep->IsNumber(2) ? std::stoi(sep->arg[2]) : -1; - if ( - skill_id < 0 || - skill_id > EQ::skills::HIGHEST_SKILL || - skill_value < 0 || - skill_value > HIGHEST_CAN_SET_SKILL - ) { - c->Message(Chat::White, "Usage: #setskill [Skill ID] [Skill Value]"); - c->Message(Chat::White, fmt::format("Skill ID = 0 to {}", EQ::skills::HIGHEST_SKILL).c_str()); - c->Message(Chat::White, fmt::format("Skill Value = 0 to {}", HIGHEST_CAN_SET_SKILL).c_str()); - } - else { - LogInfo( - "Set skill request from [{}], Target: [{}] Skill ID: [{}] Skill Value: [{}]", - c->GetCleanName(), - target->GetCleanName(), - skill_id, - skill_value - ); - - if ( - skill_id >= EQ::skills::Skill1HBlunt && - skill_id <= EQ::skills::HIGHEST_SKILL - ) { - target->SetSkill( - (EQ::skills::SkillType)skill_id, - skill_value - ); - - if (c != target) { - c->Message( - Chat::White, - fmt::format( - "Set {} ({}) to {} for {}.", - EQ::skills::GetSkillName((EQ::skills::SkillType)skill_id), - skill_id, - skill_value, - target->GetCleanName() - ).c_str() - ); - } - } - } -} - -void command_setskillall(Client *c, const Seperator *sep) -{ - if (c->GetTarget() == 0) - c->Message(Chat::White, "Error: #setallskill: No target."); - else if (!c->GetTarget()->IsClient()) - c->Message(Chat::White, "Error: #setskill: Target must be a client."); - else if (!sep->IsNumber(1) || atoi(sep->arg[1]) < 0 || atoi(sep->arg[1]) > HIGHEST_CAN_SET_SKILL) { - c->Message(Chat::White, "Usage: #setskillall value "); - c->Message(Chat::White, " value = 0 to %d", HIGHEST_CAN_SET_SKILL); - } - else { - if (c->Admin() >= commandSetSkillsOther || c->GetTarget()==c || c->GetTarget()==0) { - LogInfo("Set ALL skill request from [{}], target:[{}]", c->GetName(), c->GetTarget()->GetName()); - uint16 level = atoi(sep->arg[1]); - for (EQ::skills::SkillType skill_num = EQ::skills::Skill1HBlunt; skill_num <= EQ::skills::HIGHEST_SKILL; skill_num = (EQ::skills::SkillType)(skill_num + 1)) { - c->GetTarget()->CastToClient()->SetSkill(skill_num, level); - } - } - else - c->Message(Chat::White, "Error: Your status is not high enough to set anothers skills"); - } -} - -void command_race(Client *c, const Seperator *sep) -{ - Mob *target = c->CastToMob(); - - if (sep->IsNumber(1)) { - auto race = atoi(sep->arg[1]); - if ((race >= 0 && race <= RuleI(NPC, MaxRaceID)) || (race >= 2253 && race <= 2259)) { - if ((c->GetTarget()) && c->Admin() >= commandRaceOthers) { - target = c->GetTarget(); - } - target->SendIllusionPacket(race); - } - else { - c->Message(Chat::White, fmt::format("Usage: #race [0-{}, 2253-2259] (0 for back to normal)", RuleI(NPC, MaxRaceID)).c_str()); - } - } - else { - c->Message(Chat::White, fmt::format("Usage: #race [0-{}, 2253-2259] (0 for back to normal)", RuleI(NPC, MaxRaceID)).c_str()); - } -} - -void command_gearup(Client *c, const Seperator *sep) -{ - std::string tool_table_name = "tool_gearup_armor_sets"; - if (!database.DoesTableExist(tool_table_name)) { - c->Message( - Chat::Yellow, - fmt::format( - "Table [{}] does not exist. Downloading from Github and installing...", - tool_table_name - ).c_str() - ); - - // http get request - httplib::Client cli("https://raw.githubusercontent.com"); - cli.set_connection_timeout(0, 15000000); // 15 sec - cli.set_read_timeout(15, 0); // 15 seconds - cli.set_write_timeout(15, 0); // 15 seconds - - int sourced_queries = 0; - std::string url = "/EQEmu/Server/master/utils/sql/git/optional/2020_07_20_tool_gearup_armor_sets.sql"; - - if (auto res = cli.Get(url.c_str())) { - if (res->status == 200) { - for (auto &s: SplitString(res->body, ';')) { - if (!trim(s).empty()) { - auto results = database.QueryDatabase(s); - if (!results.ErrorMessage().empty()) { - c->Message( - Chat::Yellow, - fmt::format( - "Error sourcing SQL [{}]", results.ErrorMessage() - ).c_str() - ); - return; - } - sourced_queries++; - } - } - } - } - else { - c->Message( - Chat::Yellow, - fmt::format( - "Error retrieving URL [{}]", - url - ).c_str() - ); - } - - c->Message( - Chat::Yellow, - fmt::format( - "Table [{}] installed. Sourced [{}] queries", - tool_table_name, sourced_queries - ).c_str() - ); - } - - std::string expansion_arg = sep->arg[1]; - std::string expansion_filter; - if (expansion_arg.length() > 0) { - expansion_filter = fmt::format("and `expansion` = {}", expansion_arg); - } - - auto results = database.QueryDatabase( - fmt::format( - SQL ( - select - item_id, - slot - from - {} - where - `class` = {} - and `level` = {} - {} - order by score desc, expansion desc - ), - tool_table_name, - c->GetClass(), - c->GetLevel(), - expansion_filter - ) - ); - - int items_equipped = 0; - int items_already_have = 0; - std::set equipped; - - for (auto row = results.begin(); row != results.end(); ++row) { - int item_id = atoi(row[0]); - int slot_id = atoi(row[1]); - - if (equipped.find(slot_id) != equipped.end()) { - if (slot_id == EQ::invslot::slotEar1) { - slot_id = EQ::invslot::slotEar2; - } - if (slot_id == EQ::invslot::slotFinger1) { - slot_id = EQ::invslot::slotFinger2; - } - if (slot_id == EQ::invslot::slotWrist1) { - slot_id = EQ::invslot::slotWrist2; - } - } - - if (equipped.find(slot_id) == equipped.end()) { - const EQ::ItemData *item = database.GetItem(item_id); - bool has_item = (c->GetInv().HasItem(item_id, 1, invWhereWorn) != INVALID_INDEX); - bool can_wear_item = !c->CheckLoreConflict(item) && !has_item; - if (!can_wear_item) { - items_already_have++; - } - - if (c->CastToMob()->CanClassEquipItem(item_id) && can_wear_item) { - equipped.insert(slot_id); - c->SummonItem( - item_id, - 0, 0, 0, 0, 0, 0, 0, 0, - slot_id - ); - items_equipped++; - } - } - } - - c->Message( - Chat::White, - fmt::format( - "Equipped items [{}] already had [{}] items equipped", - items_equipped, - items_already_have - ).c_str() - ); - - if (expansion_arg.empty()) { - results = database.QueryDatabase( - fmt::format( - SQL ( - select - expansion - from - {} - where - class = {} - and level = {} - group by - expansion; - ), - tool_table_name, - c->GetClass(), - c->GetLevel() - ) - ); - - c->Message(Chat::White, "Choose armor from a specific era"); - std::string message; - for (auto row = results.begin(); row != results.end(); ++row) { - int expansion = atoi(row[0]); - message += "[" + EQ::SayLinkEngine::GenerateQuestSaylink( - fmt::format("#gearup {}", expansion), - false, - Expansion::ExpansionName[expansion] - ) + "] "; - - if (message.length() > 2000) { - c->Message(Chat::White, message.c_str()); - message = ""; - } - } - if (message.length() > 0) { - c->Message(Chat::White, message.c_str()); - } - } - -} - -void command_gender(Client *c, const Seperator *sep) -{ - Mob *t=c->CastToMob(); - - if (sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 500) { - if ((c->GetTarget()) && c->Admin() >= commandGenderOthers) - t=c->GetTarget(); - t->SendIllusionPacket(t->GetRace(), atoi(sep->arg[1])); - } - else - c->Message(Chat::White, "Usage: #gender [0/1/2]"); -} - -void command_makepet(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == '\0') - c->Message(Chat::White, "Usage: #makepet pet_type_name (will not survive across zones)"); - else - c->MakePet(0, sep->arg[1]); -} - void command_level(Client *c, const Seperator *sep) { uint16 level = atoi(sep->arg[1]); @@ -4238,4184 +927,6 @@ void command_spawneditmass(Client *c, const Seperator *sep) } } -void command_spawn(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] != 0){ - Client* client = entity_list.GetClientByName(sep->arg[1]); - if(client){ - c->Message(Chat::White,"You cannot spawn a mob with the same name as a character!"); - return; - } - } - - NPC* npc = NPC::SpawnNPC(sep->argplus[1], c->GetPosition(), c); - if (!npc) { - c->Message(Chat::White, "Format: #spawn name race level material hp gender class priweapon secweapon merchantid bodytype - spawns a npc those parameters."); - c->Message(Chat::White, "Name Format: NPCFirstname_NPCLastname - All numbers in a name are stripped and \"_\" characters become a space."); - c->Message(Chat::White, "Note: Using \"-\" for gender will autoselect the gender for the race. Using \"-\" for HP will use the calculated maximum HP."); - } -} - -void command_texture(Client *c, const Seperator *sep) -{ - - uint16 texture; - - if (sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 255) { - texture = atoi(sep->arg[1]); - uint8 helm = 0xFF; - - // Player Races Wear Armor, so Wearchange is sent instead - int i; - if (!c->GetTarget()) - for (i = EQ::textures::textureBegin; i <= EQ::textures::LastTintableTexture; i++) - { - c->SendTextureWC(i, texture); - } - else if ((c->GetTarget()->GetModel() > 0 && c->GetTarget()->GetModel() <= 12) || - c->GetTarget()->GetModel() == 128 || c->GetTarget()->GetModel() == 130 || - c->GetTarget()->GetModel() == 330 || c->GetTarget()->GetModel() == 522) { - for (i = EQ::textures::textureBegin; i <= EQ::textures::LastTintableTexture; i++) - { - c->GetTarget()->SendTextureWC(i, texture); - } - } - else // Non-Player Races only need Illusion Packets to be sent for texture - { - if (sep->IsNumber(2) && atoi(sep->arg[2]) >= 0 && atoi(sep->arg[2]) <= 255) - helm = atoi(sep->arg[2]); - else - helm = texture; - - if (texture == 255) { - texture = 0xFFFF; // Should be pulling these from the database instead - helm = 0xFF; - } - - if ((c->GetTarget()) && (c->Admin() >= commandTextureOthers)) - c->GetTarget()->SendIllusionPacket(c->GetTarget()->GetModel(), 0xFF, texture, helm); - else - c->SendIllusionPacket(c->GetRace(), 0xFF, texture, helm); - } - } - else - c->Message(Chat::White, "Usage: #texture [texture] [helmtexture] (0-255, 255 for show equipment)"); -} - -void command_npctypespawn(Client *c, const Seperator *sep) -{ - if (sep->IsNumber(1)) { - const NPCType* tmp = 0; - if ((tmp = content_db.LoadNPCTypesData(atoi(sep->arg[1])))) { - //tmp->fixedZ = 1; - auto npc = new NPC(tmp, 0, c->GetPosition(), GravityBehavior::Water); - if (npc && sep->IsNumber(2)) - npc->SetNPCFactionID(atoi(sep->arg[2])); - - npc->AddLootTable(); - if (npc->DropsGlobalLoot()) - npc->CheckGlobalLootTables(); - entity_list.AddNPC(npc); - } - else - c->Message(Chat::White, "NPC Type %i not found", atoi(sep->arg[1])); - } - else - c->Message(Chat::White, "Usage: #npctypespawn npctypeid factionid"); - -} - -void command_nudge(Client* c, const Seperator* sep) -{ - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #nudge [x=f] [y=f] [z=f] [h=f] (partial/mixed arguments allowed)"); - } - else { - - auto target = c->GetTarget(); - if (!target) { - - c->Message(Chat::Yellow, "This command requires a target."); - return; - } - if (target->IsMoving()) { - - c->Message(Chat::Yellow, "This command requires a stationary target."); - return; - } - - glm::vec4 position_offset(0.0f, 0.0f, 0.0f, 0.0f); - for (auto index = 1; index <= 4; ++index) { - - if (!sep->arg[index]) { - continue; - } - - Seperator argsep(sep->arg[index], '='); - if (!argsep.arg[1][0]) { - continue; - } - - switch (argsep.arg[0][0]) { - case 'x': - position_offset.x = atof(argsep.arg[1]); - break; - case 'y': - position_offset.y = atof(argsep.arg[1]); - break; - case 'z': - position_offset.z = atof(argsep.arg[1]); - break; - case 'h': - position_offset.w = atof(argsep.arg[1]); - break; - default: - break; - } - } - - const auto& current_position = target->GetPosition(); - glm::vec4 new_position( - (current_position.x + position_offset.x), - (current_position.y + position_offset.y), - (current_position.z + position_offset.z), - (current_position.w + position_offset.w) - ); - - target->GMMove(new_position.x, new_position.y, new_position.z, new_position.w); - - c->Message( - Chat::White, - "Nudging '%s' to {%1.3f, %1.3f, %1.3f, %1.2f} (adjustment: {%1.3f, %1.3f, %1.3f, %1.2f})", - target->GetName(), - new_position.x, - new_position.y, - new_position.z, - new_position.w, - position_offset.x, - position_offset.y, - position_offset.z, - position_offset.w - ); - } -} - -void command_heal(Client *c, const Seperator *sep) -{ - auto target = c->GetTarget() ? c->GetTarget() : c; - target->Heal(); - if (c != target) { - c->Message( - Chat::White, - fmt::format( - "Healed {} ({}) to full.", - target->GetCleanName(), - target->GetID() - ).c_str() - ); - } else { - c->Message(Chat::White, "Healed yourself to full."); - } -} - -void command_appearance(Client *c, const Seperator *sep) -{ - Mob *t=c->CastToMob(); - - // sends any appearance packet - // Dev debug command, for appearance types - if (sep->arg[2][0] == 0) - c->Message(Chat::White, "Usage: #appearance type value"); - else { - if ((c->GetTarget())) - t=c->GetTarget(); - t->SendAppearancePacket(atoi(sep->arg[1]), atoi(sep->arg[2])); - c->Message(Chat::White, "Sending appearance packet: target=%s, type=%s, value=%s", t->GetName(), sep->arg[1], sep->arg[2]); - } -} - -void command_nukeitem(Client *c, const Seperator *sep) -{ - int numitems, itemid; - - if (c->GetTarget() && c->GetTarget()->IsClient() && (sep->IsNumber(1) || sep->IsHexNumber(1))) { - itemid=sep->IsNumber(1)?atoi(sep->arg[1]):hextoi(sep->arg[1]); - numitems = c->GetTarget()->CastToClient()->NukeItem(itemid); - c->Message(Chat::White, " %u items deleted", numitems); - } - else - c->Message(Chat::White, "Usage: (targted) #nukeitem itemnum - removes the item from the player's inventory"); -} - -void command_peekinv(Client *c, const Seperator *sep) -{ - // this can be cleaned up once inventory is cleaned up - enum { - peekNone = 0x0000, - peekEquip = 0x0001, - peekGen = 0x0002, - peekCursor = 0x0004, - peekLimbo = 0x0008, - peekTrib = 0x0010, - peekBank = 0x0020, - peekShBank = 0x0040, - peekTrade = 0x0080, - peekWorld = 0x0100, - peekOutOfScope = (peekWorld * 2) // less than - }; - - static const char* scope_prefix[] = { "equip", "gen", "cursor", "limbo", "trib", "bank", "shbank", "trade", "world" }; - - static const int16 scope_range[][2] = { - { EQ::invslot::EQUIPMENT_BEGIN, EQ::invslot::EQUIPMENT_END }, - { EQ::invslot::GENERAL_BEGIN, EQ::invslot::GENERAL_END }, - { EQ::invslot::slotCursor, EQ::invslot::slotCursor }, - { EQ::invslot::SLOT_INVALID, EQ::invslot::SLOT_INVALID }, - { EQ::invslot::TRIBUTE_BEGIN, EQ::invslot::TRIBUTE_END }, - { EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END }, - { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, - { EQ::invslot::TRADE_BEGIN, EQ::invslot::TRADE_END }, - { EQ::invslot::SLOT_BEGIN, (EQ::invtype::WORLD_SIZE - 1) } - }; - - static const bool scope_bag[] = { false, true, true, true, false, true, true, true, true }; - - if (!c) - return; - - if (c->GetTarget() && !c->GetTarget()->IsClient()) { - c->Message(Chat::White, "You must target a PC for this command."); - return; - } - - int scopeMask = peekNone; - - if (strcasecmp(sep->arg[1], "all") == 0) { scopeMask = (peekOutOfScope - 1); } - else if (strcasecmp(sep->arg[1], "equip") == 0) { scopeMask |= peekEquip; } - else if (strcasecmp(sep->arg[1], "gen") == 0) { scopeMask |= peekGen; } - else if (strcasecmp(sep->arg[1], "cursor") == 0) { scopeMask |= peekCursor; } - else if (strcasecmp(sep->arg[1], "poss") == 0) { scopeMask |= (peekEquip | peekGen | peekCursor); } - else if (strcasecmp(sep->arg[1], "limbo") == 0) { scopeMask |= peekLimbo; } - else if (strcasecmp(sep->arg[1], "curlim") == 0) { scopeMask |= (peekCursor | peekLimbo); } - else if (strcasecmp(sep->arg[1], "trib") == 0) { scopeMask |= peekTrib; } - else if (strcasecmp(sep->arg[1], "bank") == 0) { scopeMask |= peekBank; } - else if (strcasecmp(sep->arg[1], "shbank") == 0) { scopeMask |= peekShBank; } - else if (strcasecmp(sep->arg[1], "allbank") == 0) { scopeMask |= (peekBank | peekShBank); } - else if (strcasecmp(sep->arg[1], "trade") == 0) { scopeMask |= peekTrade; } - else if (strcasecmp(sep->arg[1], "world") == 0) { scopeMask |= peekWorld; } - - if (!scopeMask) { - c->Message(Chat::White, "Usage: #peekinv [equip|gen|cursor|poss|limbo|curlim|trib|bank|shbank|allbank|trade|world|all]"); - c->Message(Chat::White, "- Displays a portion of the targeted user's inventory"); - c->Message(Chat::White, "- Caution: 'all' is a lot of information!"); - return; - } - - Client* targetClient = c; - if (c->GetTarget()) - targetClient = c->GetTarget()->CastToClient(); - - const EQ::ItemInstance* inst_main = nullptr; - const EQ::ItemInstance* inst_sub = nullptr; - const EQ::ItemInstance* inst_aug = nullptr; - const EQ::ItemData* item_data = nullptr; - - EQ::SayLinkEngine linker; - linker.SetLinkType(EQ::saylink::SayLinkItemInst); - - c->Message(Chat::White, "Displaying inventory for %s...", targetClient->GetName()); - - Object* objectTradeskill = targetClient->GetTradeskillObject(); - - bool itemsFound = false; - - for (int scopeIndex = 0, scopeBit = peekEquip; scopeBit < peekOutOfScope; ++scopeIndex, scopeBit <<= 1) { - if (scopeBit & ~scopeMask) - continue; - - if (scopeBit & peekWorld) { - if (objectTradeskill == nullptr) { - c->Message(Chat::Default, "No world tradeskill object selected..."); - continue; - } - else { - c->Message(Chat::White, "[WorldObject DBID: %i (entityid: %i)]", objectTradeskill->GetDBID(), objectTradeskill->GetID()); - } - } - - for (int16 indexMain = scope_range[scopeIndex][0]; indexMain <= scope_range[scopeIndex][1]; ++indexMain) { - if (indexMain == EQ::invslot::SLOT_INVALID) - continue; - - inst_main = ((scopeBit & peekWorld) ? objectTradeskill->GetItem(indexMain) : targetClient->GetInv().GetItem(indexMain)); - if (inst_main) { - itemsFound = true; - item_data = inst_main->GetItem(); - } - else { - item_data = nullptr; - } - - linker.SetItemInst(inst_main); - - c->Message( - (item_data == nullptr), - "%sSlot: %i, Item: %i (%s), Charges: %i", - scope_prefix[scopeIndex], - ((scopeBit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + indexMain) : indexMain), - ((item_data == nullptr) ? 0 : item_data->ID), - linker.GenerateLink().c_str(), - ((inst_main == nullptr) ? 0 : inst_main->GetCharges()) - ); - - if (inst_main && inst_main->IsClassCommon()) { - for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; indexAug <= EQ::invaug::SOCKET_END; ++indexAug) { - inst_aug = inst_main->GetItem(indexAug); - if (!inst_aug) // extant only - continue; - - item_data = inst_aug->GetItem(); - linker.SetItemInst(inst_aug); - - c->Message( - (item_data == nullptr), - ".%sAugSlot: %i (Slot #%i, Aug idx #%i), Item: %i (%s), Charges: %i", - scope_prefix[scopeIndex], - INVALID_INDEX, - ((scopeBit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + indexMain) : indexMain), - indexAug, - ((item_data == nullptr) ? 0 : item_data->ID), - linker.GenerateLink().c_str(), - ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) - ); - } - } - - if (!scope_bag[scopeIndex] || !(inst_main && inst_main->IsClassBag())) - continue; - - for (uint8 indexSub = EQ::invbag::SLOT_BEGIN; indexSub <= EQ::invbag::SLOT_END; ++indexSub) { - inst_sub = inst_main->GetItem(indexSub); - if (!inst_sub) // extant only - continue; - - item_data = inst_sub->GetItem(); - linker.SetItemInst(inst_sub); - - c->Message( - (item_data == nullptr), - "..%sBagSlot: %i (Slot #%i, Bag idx #%i), Item: %i (%s), Charges: %i", - scope_prefix[scopeIndex], - ((scopeBit & peekWorld) ? INVALID_INDEX : EQ::InventoryProfile::CalcSlotId(indexMain, indexSub)), - ((scopeBit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + indexMain) : indexMain), - indexSub, - ((item_data == nullptr) ? 0 : item_data->ID), - linker.GenerateLink().c_str(), - ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) - ); - - if (inst_sub->IsClassCommon()) { - for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; indexAug <= EQ::invaug::SOCKET_END; ++indexAug) { - inst_aug = inst_sub->GetItem(indexAug); - if (!inst_aug) // extant only - continue; - - item_data = inst_aug->GetItem(); - linker.SetItemInst(inst_aug); - - c->Message( - (item_data == nullptr), - "...%sAugSlot: %i (Slot #%i, Sub idx #%i, Aug idx #%i), Item: %i (%s), Charges: %i", - scope_prefix[scopeIndex], - INVALID_INDEX, - ((scopeBit & peekWorld) ? INVALID_INDEX : EQ::InventoryProfile::CalcSlotId(indexMain, indexSub)), - indexSub, - indexAug, - ((item_data == nullptr) ? 0 : item_data->ID), - linker.GenerateLink().c_str(), - ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) - ); - } - } - } - } - - if (scopeBit & peekLimbo) { - int limboIndex = 0; - for (auto it = targetClient->GetInv().cursor_cbegin(); (it != targetClient->GetInv().cursor_cend()); ++it, ++limboIndex) { - if (it == targetClient->GetInv().cursor_cbegin()) - continue; - - inst_main = *it; - if (inst_main) { - itemsFound = true; - item_data = inst_main->GetItem(); - } - else { - item_data = nullptr; - } - - linker.SetItemInst(inst_main); - - c->Message( - (item_data == nullptr), - "%sSlot: %i, Item: %i (%s), Charges: %i", - scope_prefix[scopeIndex], - (8000 + limboIndex), - ((item_data == nullptr) ? 0 : item_data->ID), - linker.GenerateLink().c_str(), - ((inst_main == nullptr) ? 0 : inst_main->GetCharges()) - ); - - if (inst_main && inst_main->IsClassCommon()) { - for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; indexAug <= EQ::invaug::SOCKET_END; ++indexAug) { - inst_aug = inst_main->GetItem(indexAug); - if (!inst_aug) // extant only - continue; - - item_data = inst_aug->GetItem(); - linker.SetItemInst(inst_aug); - - c->Message( - (item_data == nullptr), - ".%sAugSlot: %i (Slot #%i, Aug idx #%i), Item: %i (%s), Charges: %i", - scope_prefix[scopeIndex], - INVALID_INDEX, - (8000 + limboIndex), - indexAug, - ((item_data == nullptr) ? 0 : item_data->ID), - linker.GenerateLink().c_str(), - ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) - ); - } - } - - if (!scope_bag[scopeIndex] || !(inst_main && inst_main->IsClassBag())) - continue; - - for (uint8 indexSub = EQ::invbag::SLOT_BEGIN; indexSub <= EQ::invbag::SLOT_END; ++indexSub) { - inst_sub = inst_main->GetItem(indexSub); - if (!inst_sub) - continue; - - item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem(); - - linker.SetItemInst(inst_sub); - - c->Message( - (item_data == nullptr), - "..%sBagSlot: %i (Slot #%i, Bag idx #%i), Item: %i (%s), Charges: %i", - scope_prefix[scopeIndex], - INVALID_INDEX, - (8000 + limboIndex), - indexSub, - ((item_data == nullptr) ? 0 : item_data->ID), - linker.GenerateLink().c_str(), - ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) - ); - - if (inst_sub->IsClassCommon()) { - for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; indexAug <= EQ::invaug::SOCKET_END; ++indexAug) { - inst_aug = inst_sub->GetItem(indexAug); - if (!inst_aug) // extant only - continue; - - item_data = inst_aug->GetItem(); - linker.SetItemInst(inst_aug); - - c->Message( - (item_data == nullptr), - "...%sAugSlot: %i (Slot #%i, Sub idx #%i, Aug idx #%i), Item: %i (%s), Charges: %i", - scope_prefix[scopeIndex], - INVALID_INDEX, - (8000 + limboIndex), - indexSub, - indexAug, - ((item_data == nullptr) ? 0 : item_data->ID), - linker.GenerateLink().c_str(), - ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) - ); - } - } - } - } - } - } - - if (!itemsFound) - c->Message(Chat::White, "No items found."); -} - -void command_interrogateinv(Client *c, const Seperator *sep) -{ - // 'command_interrogateinv' is an in-memory inventory interrogation tool only. - // - // it does not verify against actual database entries..but, the output can be - // used to verify that something has been corrupted in a player's inventory. - // any error condition should be assumed that the item in question will be - // lost when the player logs out or zones (or incurrs any action that will - // consume the Client-Inventory object instance in question.) - // - // any item instances located at a greater depth than a reported error should - // be treated as an error themselves regardless of whether they report as the - // same or not. - - if (strcasecmp(sep->arg[1], "help") == 0) { - if (c->Admin() < commandInterrogateInv) { - c->Message(Chat::White, "Usage: #interrogateinv"); - c->Message(Chat::White, " Displays your inventory's current in-memory nested storage references"); - } - else { - c->Message(Chat::White, "Usage: #interrogateinv [log] [silent]"); - c->Message(Chat::White, " Displays your or your Player target inventory's current in-memory nested storage references"); - c->Message(Chat::White, " [log] - Logs interrogation to file"); - c->Message(Chat::White, " [silent] - Omits the in-game message portion of the interrogation"); - } - return; - } - - Client* target = nullptr; - std::map instmap; - bool log = false; - bool silent = false; - bool error = false; - bool allowtrip = false; - - if (c->Admin() < commandInterrogateInv) { - if (c->GetInterrogateInvState()) { - c->Message(Chat::Red, "The last use of #interrogateinv on this inventory instance discovered an error..."); - c->Message(Chat::Red, "Logging out, zoning or re-arranging items at this point will result in item loss!"); - return; - } - target = c; - allowtrip = true; - } - else { - if (c->GetTarget() == nullptr) { - target = c; - } - else if (c->GetTarget()->IsClient()) { - target = c->GetTarget()->CastToClient(); - } - else { - c->Message(Chat::Default, "Use of this command is limited to Client entities"); - return; - } - - if (strcasecmp(sep->arg[1], "log") == 0) - log = true; - if (strcasecmp(sep->arg[2], "silent") == 0) - silent = true; - } - - bool success = target->InterrogateInventory(c, log, silent, allowtrip, error); - - if (!success) - c->Message(Chat::Red, "An unknown error occurred while processing Client::InterrogateInventory()"); -} - -void command_invsnapshot(Client *c, const Seperator *sep) -{ - if (!c) - return; - - if (sep->argnum == 0 || strcmp(sep->arg[1], "help") == 0) { - std::string window_title = "Inventory Snapshot Argument Help Menu"; - - std::string window_text = - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - ""; - - if (c->Admin() >= commandInvSnapshot) - window_text.append( - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - ); - - window_text.append( - "
Usage:#invsnapshot arguments
(required optional)
helpthis menu
capturetakes snapshot of character inventory
gcountreturns global snapshot count
gclear
now
delete all snapshots - rule
delete all snapshots - now
countreturns character snapshot count
clear
now
delete character snapshots - rule
delete character snapshots - now
list
count
lists entry ids for current character
limits to count
parsetstmpdisplays slots and items in snapshot
comparetstmpcompares inventory against snapshot
restoretstmprestores slots and items in snapshot
" - ); - - c->SendPopupToClient(window_title.c_str(), window_text.c_str()); - - return; - } - - if (c->Admin() >= commandInvSnapshot) { // global arguments - - if (strcmp(sep->arg[1], "gcount") == 0) { - auto is_count = database.CountInvSnapshots(); - c->Message(Chat::White, "There %s %i inventory snapshot%s.", (is_count == 1 ? "is" : "are"), is_count, (is_count == 1 ? "" : "s")); - - return; - } - - if (strcmp(sep->arg[1], "gclear") == 0) { - if (strcmp(sep->arg[2], "now") == 0) { - database.ClearInvSnapshots(true); - c->Message(Chat::White, "Inventory snapshots cleared using current time."); - } - else { - database.ClearInvSnapshots(); - c->Message(Chat::White, "Inventory snapshots cleared using RuleI(Character, InvSnapshotHistoryD) (%i day%s).", - RuleI(Character, InvSnapshotHistoryD), (RuleI(Character, InvSnapshotHistoryD) == 1 ? "" : "s")); - } - - return; - } - } - - if (!c->GetTarget() || !c->GetTarget()->IsClient()) { - c->Message(Chat::White, "Target must be a client."); - return; - } - - auto tc = (Client*)c->GetTarget(); - - if (strcmp(sep->arg[1], "capture") == 0) { - if (database.SaveCharacterInvSnapshot(tc->CharacterID())) { - tc->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinIntervalM)); - c->Message(Chat::White, "Successful inventory snapshot taken of %s - setting next interval for %i minute%s.", - tc->GetName(), RuleI(Character, InvSnapshotMinIntervalM), (RuleI(Character, InvSnapshotMinIntervalM) == 1 ? "" : "s")); - } - else { - tc->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinRetryM)); - c->Message(Chat::White, "Failed to take inventory snapshot of %s - retrying in %i minute%s.", - tc->GetName(), RuleI(Character, InvSnapshotMinRetryM), (RuleI(Character, InvSnapshotMinRetryM) == 1 ? "" : "s")); - } - - return; - } - - if (c->Admin() >= commandInvSnapshot) { - if (strcmp(sep->arg[1], "count") == 0) { - auto is_count = database.CountCharacterInvSnapshots(tc->CharacterID()); - c->Message(Chat::White, "%s (id: %u) has %i inventory snapshot%s.", tc->GetName(), tc->CharacterID(), is_count, (is_count == 1 ? "" : "s")); - - return; - } - - if (strcmp(sep->arg[1], "clear") == 0) { - if (strcmp(sep->arg[2], "now") == 0) { - database.ClearCharacterInvSnapshots(tc->CharacterID(), true); - c->Message(Chat::White, "%s\'s (id: %u) inventory snapshots cleared using current time.", tc->GetName(), tc->CharacterID()); - } - else { - database.ClearCharacterInvSnapshots(tc->CharacterID()); - c->Message(Chat::White, "%s\'s (id: %u) inventory snapshots cleared using RuleI(Character, InvSnapshotHistoryD) (%i day%s).", - tc->GetName(), tc->CharacterID(), RuleI(Character, InvSnapshotHistoryD), (RuleI(Character, InvSnapshotHistoryD) == 1 ? "" : "s")); - } - - return; - } - - if (strcmp(sep->arg[1], "list") == 0) { - std::list> is_list; - database.ListCharacterInvSnapshots(tc->CharacterID(), is_list); - - if (is_list.empty()) { - c->Message(Chat::White, "No inventory snapshots for %s (id: %u)", tc->GetName(), tc->CharacterID()); - return; - } - - auto list_count = 0; - if (sep->IsNumber(2)) - list_count = atoi(sep->arg[2]); - if (list_count < 1 || list_count > is_list.size()) - list_count = is_list.size(); - - std::string window_title = StringFormat("Snapshots for %s", tc->GetName()); - - std::string window_text = - "" - "" - "" - "" - ""; - - for (auto iter : is_list) { - if (!list_count) - break; - - window_text.append(StringFormat( - "" - "" - "" - "", - iter.first, - iter.second - )); - - --list_count; - } - - window_text.append( - "
TimestampEntry Count
%u%i
" - ); - - c->SendPopupToClient(window_title.c_str(), window_text.c_str()); - - return; - } - - if (strcmp(sep->arg[1], "parse") == 0) { - if (!sep->IsNumber(2)) { - c->Message(Chat::White, "A timestamp is required to use this option."); - return; - } - - uint32 timestamp = atoul(sep->arg[2]); - - if (!database.ValidateCharacterInvSnapshotTimestamp(tc->CharacterID(), timestamp)) { - c->Message(Chat::White, "No inventory snapshots for %s (id: %u) exist at %u.", tc->GetName(), tc->CharacterID(), timestamp); - return; - } - - std::list> parse_list; - database.ParseCharacterInvSnapshot(tc->CharacterID(), timestamp, parse_list); - - std::string window_title = StringFormat("Snapshot Parse for %s @ %u", tc->GetName(), timestamp); - - std::string window_text = "Slot: ItemID - Description
"; - - for (auto iter : parse_list) { - auto item_data = database.GetItem(iter.second); - std::string window_line = StringFormat("%i: %u - %s
", iter.first, iter.second, (item_data ? item_data->Name : "[error]")); - - if (window_text.length() + window_line.length() < 4095) { - window_text.append(window_line); - } - else { - c->Message(Chat::White, "Too many snapshot entries to list..."); - break; - } - } - - c->SendPopupToClient(window_title.c_str(), window_text.c_str()); - - return; - } - - if (strcmp(sep->arg[1], "compare") == 0) { - if (!sep->IsNumber(2)) { - c->Message(Chat::White, "A timestamp is required to use this option."); - return; - } - - uint32 timestamp = atoul(sep->arg[2]); - - if (!database.ValidateCharacterInvSnapshotTimestamp(tc->CharacterID(), timestamp)) { - c->Message(Chat::White, "No inventory snapshots for %s (id: %u) exist at %u.", tc->GetName(), tc->CharacterID(), timestamp); - return; - } - - std::list> inv_compare_list; - database.DivergeCharacterInventoryFromInvSnapshot(tc->CharacterID(), timestamp, inv_compare_list); - - std::list> iss_compare_list; - database.DivergeCharacterInvSnapshotFromInventory(tc->CharacterID(), timestamp, iss_compare_list); - - std::string window_title = StringFormat("Snapshot Comparison for %s @ %u", tc->GetName(), timestamp); - - std::string window_text = "Slot: (action) Snapshot -> Inventory
"; - - auto inv_iter = inv_compare_list.begin(); - auto iss_iter = iss_compare_list.begin(); - - while (true) { - std::string window_line; - - if (inv_iter == inv_compare_list.end() && iss_iter == iss_compare_list.end()) { - break; - } - else if (inv_iter != inv_compare_list.end() && iss_iter == iss_compare_list.end()) { - window_line = StringFormat("%i: (delete) [empty] -> %u
", inv_iter->first, inv_iter->second); - ++inv_iter; - } - else if (inv_iter == inv_compare_list.end() && iss_iter != iss_compare_list.end()) { - window_line = StringFormat("%i: (insert) %u -> [empty]
", iss_iter->first, iss_iter->second); - ++iss_iter; - } - else { - if (inv_iter->first < iss_iter->first) { - window_line = StringFormat("%i: (delete) [empty] -> %u
", inv_iter->first, inv_iter->second); - ++inv_iter; - } - else if (inv_iter->first > iss_iter->first) { - window_line = StringFormat("%i: (insert) %u -> [empty]
", iss_iter->first, iss_iter->second); - ++iss_iter; - } - else { - window_line = StringFormat("%i: (replace) %u -> %u
", iss_iter->first, iss_iter->second, inv_iter->second); - ++inv_iter; - ++iss_iter; - } - } - - if (window_text.length() + window_line.length() < 4095) { - window_text.append(window_line); - } - else { - c->Message(Chat::White, "Too many comparison entries to list..."); - break; - } - } - - c->SendPopupToClient(window_title.c_str(), window_text.c_str()); - - return; - } - - if (strcmp(sep->arg[1], "restore") == 0) { - if (!sep->IsNumber(2)) { - c->Message(Chat::White, "A timestamp is required to use this option."); - return; - } - - uint32 timestamp = atoul(sep->arg[2]); - - if (!database.ValidateCharacterInvSnapshotTimestamp(tc->CharacterID(), timestamp)) { - c->Message(Chat::White, "No inventory snapshots for %s (id: %u) exist at %u.", tc->GetName(), tc->CharacterID(), timestamp); - return; - } - - if (database.SaveCharacterInvSnapshot(tc->CharacterID())) { - tc->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinIntervalM)); - } - else { - c->Message(Chat::Red, "Failed to take pre-restore inventory snapshot of %s (id: %u).", - tc->GetName(), tc->CharacterID()); - return; - } - - if (database.RestoreCharacterInvSnapshot(tc->CharacterID(), timestamp)) { - // cannot delete all valid item slots from client..so, we worldkick - tc->WorldKick(); // self restores update before the 'kick' is processed - - c->Message(Chat::White, "Successfully applied snapshot %u to %s's (id: %u) inventory.", - timestamp, tc->GetName(), tc->CharacterID()); - } - else { - c->Message(Chat::Red, "Failed to apply snapshot %u to %s's (id: %u) inventory.", - timestamp, tc->GetName(), tc->CharacterID()); - } - - return; - } - } -} - -void command_findnpctype(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (!arguments) { - c->Message(Chat::White, "Usage: #findnpctype [Search Criteria]"); - return; - } - - std::string query; - std::string search_criteria = sep->arg[1]; - if (sep->IsNumber(1)) { - query = fmt::format( - "SELECT id, name FROM npc_types WHERE id = {}", - search_criteria - ); - } else { - query = fmt::format( - "SELECT id, name FROM npc_types WHERE name LIKE '%%{}%%'", - search_criteria - ); - } - - auto results = content_db.QueryDatabase(query); - if (!results.Success() || !results.RowCount()) { - c->Message( - Chat::White, - fmt::format( - "No matches found for '{}'.", - search_criteria - ).c_str() - ); - return; - } - - int found_count = 0; - - for (auto row : results) { - int found_number = (found_count + 1); - if (found_count == 20) { - break; - } - - c->Message( - Chat::White, - fmt::format( - "NPC {} | {} ({})", - found_number, - row[1], - row[0] - ).c_str() - ); - found_count++; - } - - if (found_count == 20) { - c->Message(Chat::White, "20 NPCs were found, max reached."); - } else { - auto npc_message = ( - found_count == 1 ? - "An NPC was" : - fmt::format("{} NPCs were", found_count) - ); - - c->Message( - Chat::White, - fmt::format( - "{} found.", - npc_message - ).c_str() - ); - } -} - -void command_faction(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (!arguments) { - c->Message(Chat::White, "Usage: #faction review [Search Criteria | All] - Review Targeted Player's Faction Hits"); - c->Message(Chat::White, "Usage: #faction reset [Faction ID] - Reset Targeted Player's Faction to Base Faction Value"); - c->Message(Chat::White, "Usage: #faction view - Displays Target NPC's Primary Faction"); - return; - } - - std::string faction_filter; - if (sep->arg[2]) { - faction_filter = str_tolower(sep->arg[2]); - } - - if (!strcasecmp(sep->arg[1], "review")) { - if (!(c->GetTarget() && c->GetTarget()->IsClient())) { - c->Message(Chat::Red, "Player Target Required for faction review"); - return; - } - - Client* target = c->GetTarget()->CastToClient(); - uint32 character_id = target->CharacterID(); - std::string query; - if (!strcasecmp(faction_filter.c_str(), "all")) { - query = fmt::format( - "SELECT id, `name`, current_value FROM faction_list INNER JOIN faction_values ON faction_list.id = faction_values.faction_id WHERE char_id = {}", - character_id - ); - } else { - query = fmt::format( - "SELECT id, `name`, current_value FROM faction_list INNER JOIN faction_values ON faction_list.id = faction_values.faction_id WHERE `name` like '%{}%' and char_id = {}", - faction_filter.c_str(), - character_id - ); - } - - auto results = content_db.QueryDatabase(query); - if (!results.Success() || !results.RowCount()) { - c->Message(Chat::Yellow, "No faction hits found. All are at base level."); - return; - } - - uint32 found_count = 0; - for (auto row : results) { - uint32 faction_number = (found_count + 1); - auto faction_id = std::stoul(row[0]); - std::string faction_name = row[1]; - std::string faction_value = row[2]; - std::string reset_link = EQ::SayLinkEngine::GenerateQuestSaylink( - fmt::format("#faction reset {}", faction_id), - false, - "Reset" - ); - - c->Message( - Chat::White, - fmt::format( - "Faction {} | Name: {} ({}) Value: {} [{}]", - faction_number, - faction_name, - faction_id, - faction_value, - reset_link - ).c_str() - ); - found_count++; - } - - auto faction_message = ( - found_count > 0 ? - ( - found_count == 1 ? - "A Faction was" : - fmt::format("{} Factions were", found_count) - ) : - "No Factions were" - ); - c->Message( - Chat::White, - fmt::format( - "{} found.", - faction_message - ).c_str() - ); - } else if (!strcasecmp(sep->arg[1], "reset")) { - if (strlen(faction_filter.c_str()) > 0) { - if (c->GetTarget() && c->GetTarget()->IsClient()) { - Client* target = c->GetTarget()->CastToClient(); - if ( - ( - !c->GetFeigned() && - c->GetAggroCount() == 0 - ) || - ( - !target->GetFeigned() && - target->GetAggroCount() == 0 - ) - ) { - uint32 character_id = target->CharacterID(); - uint32 faction_id = std::stoul(faction_filter.c_str()); - if (target->ReloadCharacterFaction(target, faction_id, character_id)) { - c->Message( - Chat::White, - fmt::format( - "Faction Reset | {} ({}) was reset for {}.", - content_db.GetFactionName(faction_id), - faction_id, - target->GetCleanName() - ).c_str() - ); - } else { - c->Message( - Chat::White, - fmt::format( - "Faction Reset Failed | {} ({}) was unable to be reset for {}.", - content_db.GetFactionName(faction_id), - faction_id, - target->GetCleanName() - ).c_str() - ); - } - } else { - c->Message(Chat::White, "You cannot reset factions while you or your target is in combat or feigned."); - return; - } - } else { - c->Message(Chat::White, "You must target a PC for this command."); - return; - } - } else { - c->Message(Chat::White, "Usage: #faction reset [Faction ID] - Reset Targeted Player's Faction to Base Faction Value"); - } - } else if (!strcasecmp(sep->arg[1], "view")) { - if (c->GetTarget() && c->GetTarget()->IsNPC()) { - Mob* target = c->GetTarget(); - uint32 npc_id = target->GetNPCTypeID(); - uint32 npc_faction_id = target->CastToNPC()->GetPrimaryFaction(); - std::string npc_name = target->GetCleanName(); - c->Message( - Chat::White, - fmt::format( - "{} ({}) has a Primary Faction of {} ({}).", - npc_name, - npc_id, - content_db.GetFactionName(npc_faction_id), - npc_faction_id - ).c_str() - ); - } - } -} - -void command_findzone(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #findzone [search criteria]"); - c->Message(Chat::White, "Usage: #findzone expansion [expansion number]"); - return; - } - - std::string query; - int id = atoi((const char *) sep->arg[1]); - - std::string arg1 = sep->arg[1]; - - if (arg1 == "expansion") { - query = fmt::format( - "SELECT zoneidnumber, short_name, long_name, version FROM zone WHERE expansion = {}", - sep->arg[2] - ); - } - else { - - /** - * If id evaluates to 0, then search as if user entered a string - */ - if (id == 0) { - query = fmt::format( - "SELECT zoneidnumber, short_name, long_name, version FROM zone WHERE long_name LIKE '%{}%' OR `short_name` LIKE '%{}%'", - EscapeString(sep->arg[1]), - EscapeString(sep->arg[1]) - ); - } - else { - query = fmt::format( - "SELECT zoneidnumber, short_name, long_name, version FROM zone WHERE zoneidnumber = {}", - id - ); - } - } - - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Error querying database."); - c->Message(Chat::White, query.c_str()); - return; - } - - int count = 0; - const int maxrows = 100; - - for (auto row = results.begin(); row != results.end(); ++row) { - std::string zone_id = row[0]; - std::string short_name = row[1]; - std::string long_name = row[2]; - int version = atoi(row[3]); - - if (++count > maxrows) { - c->Message(Chat::White, "%i zones shown. Too many results.", maxrows); - break; - } - - std::string command_zone = EQ::SayLinkEngine::GenerateQuestSaylink("#zone " + short_name, false, "zone"); - std::string command_gmzone = EQ::SayLinkEngine::GenerateQuestSaylink( - fmt::format("#gmzone {} {}", short_name, version), - false, - "gmzone" - ); - - c->Message( - Chat::White, - fmt::format( - "[{}] [{}] [{}] ID ({}) Version ({}) [{}]", - (version == 0 ? command_zone : "zone"), - command_gmzone, - short_name, - zone_id, - version, - long_name - ).c_str() - ); - } - - if (count <= maxrows) { - c->Message( - Chat::White, - "Query complete. %i rows shown. %s", - count, - (arg1 == "expansion" ? "(expansion search)" : "")); - } - else if (count == 0) { - c->Message(Chat::White, "No matches found for %s.", sep->arg[1]); - } -} - -void command_viewnpctype(Client *c, const Seperator *sep) -{ - if (sep->IsNumber(1)) { - uint32 npc_id = std::stoul(sep->arg[1]); - const NPCType* npc_type_data = content_db.LoadNPCTypesData(npc_id); - if (npc_type_data) { - auto npc = new NPC( - npc_type_data, - nullptr, - c->GetPosition(), - GravityBehavior::Water - ); - npc->ShowStats(c); - } else { - c->Message( - Chat::White, - fmt::format( - "NPC ID {} was not found.", - npc_id - ).c_str() - ); - } - } else { - c->Message(Chat::White, "Usage: #viewnpctype [NPC ID]"); - } -} - -void command_reloadqst(Client *c, const Seperator *sep) -{ - bool stop_timers = false; - - if (sep->IsNumber(1)) { - stop_timers = std::stoi(sep->arg[1]) != 0 ? true : false; - } - - std::string stop_timers_message = stop_timers ? " and stopping timers" : ""; - c->Message( - Chat::White, - fmt::format( - "Clearing quest memory cache{}.", - stop_timers_message - ).c_str() - ); - entity_list.ClearAreas(); - parse->ReloadQuests(stop_timers); -} - -void command_corpsefix(Client *c, const Seperator *sep) -{ - entity_list.CorpseFix(c); -} - -void command_reloadworld(Client *c, const Seperator *sep) -{ - int world_repop = atoi(sep->arg[1]); - if (world_repop == 0) - c->Message(Chat::White, "Reloading quest cache worldwide."); - else - c->Message(Chat::White, "Reloading quest cache and repopping zones worldwide."); - - auto pack = new ServerPacket(ServerOP_ReloadWorld, sizeof(ReloadWorld_Struct)); - ReloadWorld_Struct* RW = (ReloadWorld_Struct*) pack->pBuffer; - RW->Option = world_repop; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void command_reloadmerchants(Client *c, const Seperator *sep) { - entity_list.ReloadMerchants(); - c->Message(Chat::Yellow, "Reloading merchants."); -} - -void command_reloadlevelmods(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == 0) - { - if(RuleB(Zone, LevelBasedEXPMods)){ - zone->LoadLevelEXPMods(); - c->Message(Chat::Yellow, "Level based EXP Mods have been reloaded zonewide"); - }else{ - c->Message(Chat::Yellow, "Level based EXP Mods are disabled in rules!"); - } - } -} - -void command_reloadzps(Client *c, const Seperator *sep) -{ - content_db.LoadStaticZonePoints(&zone->zone_point_list, zone->GetShortName(), zone->GetInstanceVersion()); - c->Message(Chat::White, "Reloading server zone_points."); -} - -void command_zoneshutdown(Client *c, const Seperator *sep) -{ - if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server disconnected"); - else if (sep->arg[1][0] == 0) - c->Message(Chat::White, "Usage: #zoneshutdown zoneshortname"); - else { - auto pack = new ServerPacket(ServerOP_ZoneShutdown, sizeof(ServerZoneStateChange_struct)); - ServerZoneStateChange_struct* s = (ServerZoneStateChange_struct *) pack->pBuffer; - strcpy(s->adminname, c->GetName()); - if (sep->arg[1][0] >= '0' && sep->arg[1][0] <= '9') - s->ZoneServerID = atoi(sep->arg[1]); - else - s->zoneid = ZoneID(sep->arg[1]); - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void command_zonebootup(Client *c, const Seperator *sep) -{ - if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server disconnected"); - else if (sep->arg[2][0] == 0) { - c->Message(Chat::White, "Usage: #zonebootup ZoneServerID# zoneshortname"); - } - else { - auto pack = new ServerPacket(ServerOP_ZoneBootup, sizeof(ServerZoneStateChange_struct)); - ServerZoneStateChange_struct* s = (ServerZoneStateChange_struct *) pack->pBuffer; - s->ZoneServerID = atoi(sep->arg[1]); - strcpy(s->adminname, c->GetName()); - s->zoneid = ZoneID(sep->arg[2]); - s->makestatic = (bool) (strcasecmp(sep->arg[3], "static") == 0); - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void command_kick(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == 0) - c->Message(Chat::White, "Usage: #kick [charname]"); - else { - Client* client = entity_list.GetClientByName(sep->arg[1]); - if (client != 0) { - if (client->Admin() <= c->Admin()) { - client->Message(Chat::White, "You have been kicked by %s", c->GetName()); - auto outapp = new EQApplicationPacket(OP_GMKick, 0); - client->QueuePacket(outapp); - client->Kick("Ordered kicked by command"); - c->Message(Chat::White, "Kick: local: kicking %s", sep->arg[1]); - } - } - else if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server disconnected"); - else { - auto pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); - ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*) pack->pBuffer; - strcpy(skp->adminname, c->GetName()); - strcpy(skp->name, sep->arg[1]); - skp->adminrank = c->Admin(); - worldserver.SendPacket(pack); - safe_delete(pack); - } - } -} - -void command_attack(Client *c, const Seperator *sep) -{ - if (c->GetTarget() && c->GetTarget()->IsNPC() && sep->arg[1] != 0) { - Mob* sictar = entity_list.GetMob(sep->argplus[1]); - if (sictar) - c->GetTarget()->CastToNPC()->AddToHateList(sictar, 1, 0); - else - c->Message(Chat::White, "Error: %s not found", sep->arg[1]); - } - else - c->Message(Chat::White, "Usage: (needs NPC targeted) #attack targetname"); -} - -void command_lock(Client *c, const Seperator *sep) -{ - auto outpack = new ServerPacket(ServerOP_Lock, sizeof(ServerLock_Struct)); - ServerLock_Struct* lss = (ServerLock_Struct*) outpack->pBuffer; - strcpy(lss->myname, c->GetName()); - lss->mode = 1; - worldserver.SendPacket(outpack); - safe_delete(outpack); -} - -void command_unlock(Client *c, const Seperator *sep) -{ - auto outpack = new ServerPacket(ServerOP_Lock, sizeof(ServerLock_Struct)); - ServerLock_Struct* lss = (ServerLock_Struct*) outpack->pBuffer; - strcpy(lss->myname, c->GetName()); - lss->mode = 0; - worldserver.SendPacket(outpack); - safe_delete(outpack); -} - -void command_motd(Client *c, const Seperator *sep) -{ - auto outpack = new ServerPacket(ServerOP_Motd, sizeof(ServerMotd_Struct)); - ServerMotd_Struct* mss = (ServerMotd_Struct*) outpack->pBuffer; - strn0cpy(mss->myname, c->GetName(),64); - strn0cpy(mss->motd, sep->argplus[1],512); - worldserver.SendPacket(outpack); - safe_delete(outpack); -} - -void command_listpetition(Client *c, const Seperator *sep) -{ - std::string query = "SELECT petid, charname, accountname FROM petitions ORDER BY petid"; - auto results = database.QueryDatabase(query); - if (!results.Success()) - return; - - LogInfo("Petition list requested by [{}]", c->GetName()); - - if (results.RowCount() == 0) - return; - - c->Message(Chat::Red," ID : Character Name , Account Name"); - - for (auto row = results.begin(); row != results.end(); ++row) - c->Message(Chat::Yellow, " %s: %s , %s ", row[0],row[1],row[2]); -} - -void command_equipitem(Client *c, const Seperator *sep) -{ - uint32 slot_id = atoi(sep->arg[1]); - if (sep->IsNumber(1) && (slot_id >= EQ::invslot::EQUIPMENT_BEGIN && slot_id <= EQ::invslot::EQUIPMENT_END)) { - const EQ::ItemInstance* from_inst = c->GetInv().GetItem(EQ::invslot::slotCursor); - const EQ::ItemInstance* to_inst = c->GetInv().GetItem(slot_id); // added (desync issue when forcing stack to stack) - bool partialmove = false; - int16 movecount; - - if (from_inst && from_inst->IsClassCommon()) { - auto outapp = new EQApplicationPacket(OP_MoveItem, sizeof(MoveItem_Struct)); - MoveItem_Struct* mi = (MoveItem_Struct*)outapp->pBuffer; - mi->from_slot = EQ::invslot::slotCursor; - mi->to_slot = slot_id; - // mi->number_in_stack = from_inst->GetCharges(); // replaced with con check for stacking - - // crude stackable check to only 'move' the difference count on client instead of entire stack when applicable - if (to_inst && to_inst->IsStackable() && - (to_inst->GetItem()->ID == from_inst->GetItem()->ID) && - (to_inst->GetCharges() < to_inst->GetItem()->StackSize) && - (from_inst->GetCharges() > to_inst->GetItem()->StackSize - to_inst->GetCharges())) { - movecount = to_inst->GetItem()->StackSize - to_inst->GetCharges(); - mi->number_in_stack = (uint32)movecount; - partialmove = true; - } - else - mi->number_in_stack = from_inst->GetCharges(); - - // Save move changes - // Added conditional check to packet send..would have sent change even on a swap failure..whoops! - - if (partialmove) { // remove this con check if someone can figure out removing charges from cursor stack issue below - // mi->number_in_stack is always from_inst->GetCharges() when partialmove is false - c->Message(Chat::Red, "Error: Partial stack added to existing stack exceeds allowable stacksize"); - safe_delete(outapp); - return; - } - else if(c->SwapItem(mi)) { - c->FastQueuePacket(&outapp); - - // if the below code is still needed..just send an an item trade packet to each slot..it should overwrite the client instance - - // below code has proper logic, but client does not like to have cursor charges changed - // (we could delete the cursor item and resend, but issues would arise if there are queued items) - //if (partialmove) { - // EQApplicationPacket* outapp2 = new EQApplicationPacket(OP_DeleteItem, sizeof(DeleteItem_Struct)); - // DeleteItem_Struct* di = (DeleteItem_Struct*)outapp2->pBuffer; - // di->from_slot = SLOT_CURSOR; - // di->to_slot = 0xFFFFFFFF; - // di->number_in_stack = 0xFFFFFFFF; - - // c->Message(Chat::White, "Deleting %i charges from stack", movecount); // debug line..delete - - // for (int16 deletecount=0; deletecount < movecount; deletecount++) - // have to use 'movecount' because mi->number_in_stack is 'ENCODED' at this point (i.e., 99 charges returns 22...) - // c->QueuePacket(outapp2); - - // safe_delete(outapp2); - //} - } - else { - c->Message(Chat::Red, "Error: Unable to equip current item"); - } - safe_delete(outapp); - - // also send out a wear change packet? - } - else if (from_inst == nullptr) - c->Message(Chat::Red, "Error: There is no item on your cursor"); - else - c->Message(Chat::Red, "Error: Item on your cursor cannot be equipped"); - } - else - c->Message(Chat::White, "Usage: #equipitem slotid[0-21] - equips the item on your cursor to the position"); -} - -void command_zonelock(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (!arguments) { - c->Message(Chat::White, "Usage: #zonelock list - Lists Locked Zones"); - if (c->Admin() >= commandLockZones) { - c->Message(Chat::White, "Usage: #zonelock lock [Zone ID] or #zonelock lock [Zone Short Name] - Locks a Zone by ID or Short Name"); - c->Message(Chat::White, "Usage: #zonelock unlock [Zone ID] or #zonelock unlock [Zone Short Name] - Unlocks a Zone by ID or Short Name"); - } - return; - } - - std::string lock_type = str_tolower(sep->arg[1]); - bool is_list = lock_type.find("list") != std::string::npos; - bool is_lock = lock_type.find("lock") != std::string::npos; - bool is_unlock = lock_type.find("unlock") != std::string::npos; - if (!is_list && !is_lock && !is_unlock) { - c->Message(Chat::White, "Usage: #zonelock list - Lists Locked Zones"); - if (c->Admin() >= commandLockZones) { - c->Message(Chat::White, "Usage: #zonelock lock [Zone ID] or #zonelock lock [Zone Short Name] - Locks a Zone by ID or Short Name"); - c->Message(Chat::White, "Usage: #zonelock unlock [Zone ID] or #zonelock unlock [Zone Short Name] - Unlocks a Zone by ID or Short Name"); - } - return; - } - - auto pack = new ServerPacket(ServerOP_LockZone, sizeof(ServerLockZone_Struct)); - ServerLockZone_Struct* lock_zone = (ServerLockZone_Struct*) pack->pBuffer; - strn0cpy(lock_zone->adminname, c->GetName(), sizeof(lock_zone->adminname)); - - if (is_list) { - lock_zone->op = ServerLockType::List; - worldserver.SendPacket(pack); - } else if (!is_list && c->Admin() >= commandLockZones) { - auto zone_id = ( - sep->IsNumber(2) ? - static_cast(std::stoul(sep->arg[2])) : - static_cast(ZoneID(sep->arg[2])) - ); - std::string zone_short_name = str_tolower(ZoneName(zone_id, true)); - bool is_unknown_zone = zone_short_name.find("unknown") != std::string::npos; - if (zone_id && !is_unknown_zone) { - lock_zone->op = is_lock ? ServerLockType::Lock : ServerLockType::Unlock; - lock_zone->zoneID = zone_id; - worldserver.SendPacket(pack); - } else { - c->Message( - Chat::White, - fmt::format( - "Usage: #zonelock {} [Zone ID] or #zonelock {} [Zone Short Name]", - is_lock ? "lock" : "unlock" - ).c_str() - ); - } - } - safe_delete(pack); -} - -void command_copycharacter(Client *c, const Seperator *sep) -{ - if (sep->argnum < 3) { - c->Message( - Chat::White, - "Usage: [source_character_name] [destination_character_name] [destination_account_name]" - ); - return; - } - - std::string source_character_name = sep->arg[1]; - std::string destination_character_name = sep->arg[2]; - std::string destination_account_name = sep->arg[3]; - - bool result = database.CopyCharacter( - source_character_name, - destination_character_name, - destination_account_name - ); - - c->Message( - Chat::Yellow, - fmt::format( - "Character Copy [{}] to [{}] via account [{}] [{}]", - source_character_name, - destination_character_name, - destination_account_name, - result ? "Success" : "Failed" - ).c_str() - ); -} - -void command_corpse(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - - if (strcasecmp(sep->arg[1], "DeletePlayerCorpses") == 0 && c->Admin() >= commandEditPlayerCorpses) { - int32 tmp = entity_list.DeletePlayerCorpses(); - if (tmp >= 0) - c->Message(Chat::White, "%i corpses deleted.", tmp); - else - c->Message(Chat::White, "DeletePlayerCorpses Error #%i", tmp); - } - else if (strcasecmp(sep->arg[1], "delete") == 0) { - if (target == 0 || !target->IsCorpse()) - c->Message(Chat::White, "Error: Target the corpse you wish to delete"); - else if (target->IsNPCCorpse()) { - - c->Message(Chat::White, "Depoping %s.", target->GetName()); - target->CastToCorpse()->Delete(); - } - else if (c->Admin() >= commandEditPlayerCorpses) { - c->Message(Chat::White, "Deleting %s.", target->GetName()); - target->CastToCorpse()->Delete(); - } - else - c->Message(Chat::White, "Insufficient status to delete player corpse."); - } - else if (strcasecmp(sep->arg[1], "ListNPC") == 0) { - entity_list.ListNPCCorpses(c); - } - else if (strcasecmp(sep->arg[1], "ListPlayer") == 0) { - entity_list.ListPlayerCorpses(c); - } - else if (strcasecmp(sep->arg[1], "DeleteNPCCorpses") == 0) { - int32 tmp = entity_list.DeleteNPCCorpses(); - if (tmp >= 0) - c->Message(Chat::White, "%d corpses deleted.", tmp); - else - c->Message(Chat::White, "DeletePlayerCorpses Error #%d", tmp); - } - else if (strcasecmp(sep->arg[1], "charid") == 0 && c->Admin() >= commandEditPlayerCorpses) { - if (target == 0 || !target->IsPlayerCorpse()) - c->Message(Chat::White, "Error: Target must be a player corpse."); - else if (!sep->IsNumber(2)) - c->Message(Chat::White, "Error: charid must be a number."); - else - c->Message(Chat::White, "Setting CharID=%u on PlayerCorpse '%s'", target->CastToCorpse()->SetCharID(atoi(sep->arg[2])), target->GetName()); - } - else if (strcasecmp(sep->arg[1], "ResetLooter") == 0) { - if (target == 0 || !target->IsCorpse()) - c->Message(Chat::White, "Error: Target the corpse you wish to reset"); - else - target->CastToCorpse()->ResetLooter(); - } - else if (strcasecmp(sep->arg[1], "RemoveCash") == 0) { - if (target == 0 || !target->IsCorpse()) - c->Message(Chat::White, "Error: Target the corpse you wish to remove the cash from"); - else if (!target->IsPlayerCorpse() || c->Admin() >= commandEditPlayerCorpses) { - c->Message(Chat::White, "Removing Cash from %s.", target->GetName()); - target->CastToCorpse()->RemoveCash(); - } - else - c->Message(Chat::White, "Insufficient status to modify player corpse."); - } - else if (strcasecmp(sep->arg[1], "InspectLoot") == 0) { - if (target == 0 || !target->IsCorpse()) - c->Message(Chat::White, "Error: Target must be a corpse."); - else - target->CastToCorpse()->QueryLoot(c); - } - else if (strcasecmp(sep->arg[1], "lock") == 0) { - if (target == 0 || !target->IsCorpse()) - c->Message(Chat::White, "Error: Target must be a corpse."); - else { - target->CastToCorpse()->Lock(); - c->Message(Chat::White, "Locking %s...", target->GetName()); - } - } - else if (strcasecmp(sep->arg[1], "unlock") == 0) { - if (target == 0 || !target->IsCorpse()) - c->Message(Chat::White, "Error: Target must be a corpse."); - else { - target->CastToCorpse()->UnLock(); - c->Message(Chat::White, "Unlocking %s...", target->GetName()); - } - } - else if (strcasecmp(sep->arg[1], "depop") == 0) { - if (target == 0 || !target->IsPlayerCorpse()) - c->Message(Chat::White, "Error: Target must be a player corpse."); - else if (c->Admin() >= commandEditPlayerCorpses && target->IsPlayerCorpse()) { - c->Message(Chat::White, "Depoping %s.", target->GetName()); - target->CastToCorpse()->DepopPlayerCorpse(); - if(!sep->arg[2][0] || atoi(sep->arg[2]) != 0) - target->CastToCorpse()->Bury(); - } - else - c->Message(Chat::White, "Insufficient status to depop player corpse."); - } - else if (strcasecmp(sep->arg[1], "depopall") == 0) { - if (target == 0 || !target->IsClient()) - c->Message(Chat::White, "Error: Target must be a player."); - else if (c->Admin() >= commandEditPlayerCorpses && target->IsClient()) { - c->Message(Chat::White, "Depoping %s\'s corpses.", target->GetName()); - target->CastToClient()->DepopAllCorpses(); - if(!sep->arg[2][0] || atoi(sep->arg[2]) != 0) - target->CastToClient()->BuryPlayerCorpses(); - } - else - c->Message(Chat::White, "Insufficient status to depop player corpse."); - - } - else if (strcasecmp(sep->arg[1], "moveallgraveyard") == 0) { - int count = entity_list.MovePlayerCorpsesToGraveyard(true); - c->Message(Chat::White, "Moved [%d] player corpse(s) to zone graveyard", count); - } - else if (sep->arg[1][0] == 0 || strcasecmp(sep->arg[1], "help") == 0) { - c->Message(Chat::White, "#Corpse Sub-Commands:"); - c->Message(Chat::White, " DeleteNPCCorpses"); - c->Message(Chat::White, " Delete - Delete targetted corpse"); - c->Message(Chat::White, " ListNPC"); - c->Message(Chat::White, " ListPlayer"); - c->Message(Chat::White, " Lock - GM locks the corpse - cannot be looted by non-GM"); - c->Message(Chat::White, " MoveAllGraveyard - move all player corpses to zone's graveyard or non-instance"); - c->Message(Chat::White, " UnLock"); - c->Message(Chat::White, " RemoveCash"); - c->Message(Chat::White, " InspectLoot"); - c->Message(Chat::White, " [to remove items from corpses, loot them]"); - c->Message(Chat::White, "Lead-GM status required to delete/modify player corpses"); - c->Message(Chat::White, " DeletePlayerCorpses"); - c->Message(Chat::White, " CharID [charid] - change player corpse's owner"); - c->Message(Chat::White, " Depop [bury] - Depops single target corpse."); - c->Message(Chat::White, " Depopall [bury] - Depops all target player's corpses."); - c->Message(Chat::White, "Set bury to 0 to skip burying the corpses."); - } - else - c->Message(Chat::White, "Error, #corpse sub-command not found"); -} - -void command_fixmob(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - const char* Usage = "Usage: #fixmob [race|gender|texture|helm|face|hair|haircolor|beard|beardcolor|heritage|tattoo|detail] [next|prev]"; - - if (!sep->arg[1]) - c->Message(Chat::White,Usage); - else if (!target) - c->Message(Chat::White,"Error: this command requires a target"); - else - { - - uint32 Adjustment = 1; // Previous or Next - char codeMove = 0; - - if (sep->arg[2]) - { - char* command2 = sep->arg[2]; - codeMove = (command2[0] | 0x20); // First character, lower-cased - if (codeMove == 'n') - Adjustment = 1; - else if (codeMove == 'p') - Adjustment = -1; - } - - 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(); - - const char* ChangeType = nullptr; // If it's still nullptr after processing, they didn't send a valid command - uint32 ChangeSetting; - char* command = sep->arg[1]; - - if (strcasecmp(command, "race") == 0) - { - if (Race == 1 && codeMove == 'p') - Race = RuleI(NPC, MaxRaceID); - else if (Race >= RuleI(NPC, MaxRaceID) && codeMove != 'p') - Race = 1; - else - Race += Adjustment; - ChangeType = "Race"; - ChangeSetting = Race; - } - else if (strcasecmp(command, "gender") == 0) - { - if (Gender == 0 && codeMove == 'p') - Gender = 2; - else if (Gender >= 2 && codeMove != 'p') - Gender = 0; - else - Gender += Adjustment; - ChangeType = "Gender"; - ChangeSetting = Gender; - } - else if (strcasecmp(command, "texture") == 0) - { - Texture = target->GetTexture(); - - if (Texture == 0 && codeMove == 'p') - Texture = 25; - else if (Texture >= 25 && codeMove != 'p') - Texture = 0; - else - Texture += Adjustment; - ChangeType = "Texture"; - ChangeSetting = Texture; - } - else if (strcasecmp(command, "helm") == 0) - { - HelmTexture = target->GetHelmTexture(); - if (HelmTexture == 0 && codeMove == 'p') - HelmTexture = 25; - else if (HelmTexture >= 25 && codeMove != 'p') - HelmTexture = 0; - else - HelmTexture += Adjustment; - ChangeType = "HelmTexture"; - ChangeSetting = HelmTexture; - } - else if (strcasecmp(command, "face") == 0) - { - if (LuclinFace == 0 && codeMove == 'p') - LuclinFace = 87; - else if (LuclinFace >= 87 && codeMove != 'p') - LuclinFace = 0; - else - LuclinFace += Adjustment; - ChangeType = "LuclinFace"; - ChangeSetting = LuclinFace; - } - else if (strcasecmp(command, "hair") == 0) - { - if (HairStyle == 0 && codeMove == 'p') - HairStyle = 8; - else if (HairStyle >= 8 && codeMove != 'p') - HairStyle = 0; - else - HairStyle += Adjustment; - ChangeType = "HairStyle"; - ChangeSetting = HairStyle; - } - else if (strcasecmp(command, "haircolor") == 0) - { - if (HairColor == 0 && codeMove == 'p') - HairColor = 24; - else if (HairColor >= 24 && codeMove != 'p') - HairColor = 0; - else - HairColor += Adjustment; - ChangeType = "HairColor"; - ChangeSetting = HairColor; - } - else if (strcasecmp(command, "beard") == 0) - { - if (Beard == 0 && codeMove == 'p') - Beard = 11; - else if (Beard >= 11 && codeMove != 'p') - Beard = 0; - else - Beard += Adjustment; - ChangeType = "Beard"; - ChangeSetting = Beard; - } - else if (strcasecmp(command, "beardcolor") == 0) - { - if (BeardColor == 0 && codeMove == 'p') - BeardColor = 24; - else if (BeardColor >= 24 && codeMove != 'p') - BeardColor = 0; - else - BeardColor += Adjustment; - ChangeType = "BeardColor"; - ChangeSetting = BeardColor; - } - else if (strcasecmp(command, "heritage") == 0) - { - if (DrakkinHeritage == 0 && codeMove == 'p') - DrakkinHeritage = 6; - else if (DrakkinHeritage >= 6 && codeMove != 'p') - DrakkinHeritage = 0; - else - DrakkinHeritage += Adjustment; - ChangeType = "DrakkinHeritage"; - ChangeSetting = DrakkinHeritage; - } - else if (strcasecmp(command, "tattoo") == 0) - { - if (DrakkinTattoo == 0 && codeMove == 'p') - DrakkinTattoo = 8; - else if (DrakkinTattoo >= 8 && codeMove != 'p') - DrakkinTattoo = 0; - else - DrakkinTattoo += Adjustment; - ChangeType = "DrakkinTattoo"; - ChangeSetting = DrakkinTattoo; - } - else if (strcasecmp(command, "detail") == 0) - { - if (DrakkinDetails == 0 && codeMove == 'p') - DrakkinDetails = 7; - else if (DrakkinDetails >= 7 && codeMove != 'p') - DrakkinDetails = 0; - else - DrakkinDetails += Adjustment; - ChangeType = "DrakkinDetails"; - ChangeSetting = DrakkinDetails; - } - - // Hack to fix some races that base features from face - switch (Race) - { - case 2: // Barbarian - if (LuclinFace > 10) { - LuclinFace -= ((DrakkinTattoo - 1) * 10); - } - LuclinFace += (DrakkinTattoo * 10); - break; - case 3: // Erudite - if (LuclinFace > 10) { - LuclinFace -= ((HairStyle - 1) * 10); - } - LuclinFace += (HairStyle * 10); - break; - case 5: // HighElf - case 6: // DarkElf - case 7: // HalfElf - if (LuclinFace > 10) { - LuclinFace -= ((Beard - 1) * 10); - } - LuclinFace += (Beard * 10); - break; - default: - break; - } - - - if (ChangeType == nullptr) - { - c->Message(Chat::White,Usage); - } - else - { - target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, - EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, - DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - - c->Message(Chat::White, "%s=%i", ChangeType, ChangeSetting); - } - } -} - -void command_gmspeed(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) { - database.SetGMSpeed(t->AccountID(), state ? 1 : 0); - c->Message(Chat::White, "Turning GMSpeed %s for %s (zone to take effect)", state ? "On" : "Off", t->GetName()); - } - else { - c->Message(Chat::White, "Usage: #gmspeed [on/off]"); - } -} - -void command_gmzone(Client *c, const Seperator *sep) -{ - if (!sep->arg[1]) { - c->Message(Chat::White, "Usage"); - c->Message(Chat::White, "-------"); - c->Message(Chat::White, "#gmzone [zone_short_name] [zone_version=0]"); - return; - } - - std::string zone_short_name_string = sep->arg[1]; - const char *zone_short_name = sep->arg[1]; - auto zone_version = static_cast(sep->arg[2] ? atoi(sep->arg[2]) : 0); - std::string identifier = "gmzone"; - uint32 zone_id = ZoneID(zone_short_name); - uint32 duration = 100000000; - uint16 instance_id = 0; - - if (zone_id == 0) { - c->Message(Chat::Red, "Invalid zone specified"); - return; - } - - if (sep->arg[3] && sep->arg[3][0]) { - identifier = sep->arg[3]; - } - - std::string bucket_key = StringFormat("%s-%s-%u-instance", zone_short_name, identifier.c_str(), zone_version); - std::string existing_zone_instance = DataBucket::GetData(bucket_key); - - if (existing_zone_instance.length() > 0) { - instance_id = std::stoi(existing_zone_instance); - - c->Message(Chat::Yellow, "Found already created instance (%s) (%u)", zone_short_name, instance_id); - } - - if (instance_id == 0) { - if (!database.GetUnusedInstanceID(instance_id)) { - c->Message(Chat::Red, "Server was unable to find a free instance id."); - return; - } - - if (!database.CreateInstance(instance_id, zone_id, zone_version, duration)) { - c->Message(Chat::Red, "Server was unable to create a new instance."); - return; - } - - c->Message(Chat::Yellow, "New private GM instance %s was created with id %lu.", zone_short_name, (unsigned long) instance_id); - DataBucket::SetData(bucket_key, std::to_string(instance_id)); - } - - if (instance_id > 0) { - float target_x = -1, target_y = -1, target_z = -1, target_heading = -1; - int16 min_status = AccountStatus::Player; - uint8 min_level = 0; - - if (!content_db.GetSafePoints( - zone_short_name, - zone_version, - &target_x, - &target_y, - &target_z, - &target_heading, - &min_status, - &min_level - )) { - c->Message(Chat::Red, "Failed to find safe coordinates for specified zone"); - } - - c->Message(Chat::Yellow, "Zoning to private GM instance (%s) (%u)", zone_short_name, instance_id); - - c->AssignToInstance(instance_id); - c->MovePC(zone_id, instance_id, target_x, target_y, target_z, target_heading, 1); - } -} - -void command_title(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0]==0) - c->Message(Chat::White, "Usage: #title [remove|text] [1 = Create row in title table] - remove or set title to 'text'"); - else { - bool Save = (atoi(sep->arg[2]) == 1); - - Mob *target_mob = c->GetTarget(); - if(!target_mob) - target_mob = c; - if(!target_mob->IsClient()) { - c->Message(Chat::Red, "#title only works on players."); - return; - } - Client *t = target_mob->CastToClient(); - - if(strlen(sep->arg[1]) > 31) { - c->Message(Chat::Red, "Title must be 31 characters or less."); - return; - } - - bool removed = false; - if(!strcasecmp(sep->arg[1], "remove")) { - t->SetAATitle(""); - removed = true; - } else { - for(unsigned int i=0; iarg[1]); i++) - if(sep->arg[1][i]=='_') - sep->arg[1][i] = ' '; - if(!Save) - t->SetAATitle(sep->arg[1]); - else - title_manager.CreateNewPlayerTitle(t, sep->arg[1]); - } - - t->Save(); - - if(removed) { - c->Message(Chat::Red, "%s's title has been removed.", t->GetName(), sep->arg[1]); - if(t != c) - t->Message(Chat::Red, "Your title has been removed.", sep->arg[1]); - } else { - c->Message(Chat::Red, "%s's title has been changed to '%s'.", t->GetName(), sep->arg[1]); - if(t != c) - t->Message(Chat::Red, "Your title has been changed to '%s'.", sep->arg[1]); - } - } -} - - -void command_titlesuffix(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0]==0) - c->Message(Chat::White, "Usage: #titlesuffix [remove|text] [1 = create row in title table] - remove or set title suffix to 'text'"); - else { - bool Save = (atoi(sep->arg[2]) == 1); - - Mob *target_mob = c->GetTarget(); - if(!target_mob) - target_mob = c; - if(!target_mob->IsClient()) { - c->Message(Chat::Red, "#titlesuffix only works on players."); - return; - } - Client *t = target_mob->CastToClient(); - - if(strlen(sep->arg[1]) > 31) { - c->Message(Chat::Red, "Title suffix must be 31 characters or less."); - return; - } - - bool removed = false; - if(!strcasecmp(sep->arg[1], "remove")) { - t->SetTitleSuffix(""); - removed = true; - } else { - for(unsigned int i=0; iarg[1]); i++) - if(sep->arg[1][i]=='_') - sep->arg[1][i] = ' '; - - if(!Save) - t->SetTitleSuffix(sep->arg[1]); - else - title_manager.CreateNewPlayerSuffix(t, sep->arg[1]); - } - - t->Save(); - - if(removed) { - c->Message(Chat::Red, "%s's title suffix has been removed.", t->GetName(), sep->arg[1]); - if(t != c) - t->Message(Chat::Red, "Your title suffix has been removed.", sep->arg[1]); - } else { - c->Message(Chat::Red, "%s's title suffix has been changed to '%s'.", t->GetName(), sep->arg[1]); - if(t != c) - t->Message(Chat::Red, "Your title suffix has been changed to '%s'.", sep->arg[1]); - } - } -} - -void command_spellinfo(Client *c, const Seperator *sep) -{ - if(sep->arg[1][0]==0) - c->Message(Chat::White, "Usage: #spellinfo [spell_id]"); - else { - short int spell_id=atoi(sep->arg[1]); - const struct SPDat_Spell_Struct *s=&spells[spell_id]; - c->Message(Chat::White, "Spell info for spell #%d:", spell_id); - c->Message(Chat::White, " name: %s", s->name); - c->Message(Chat::White, " player_1: %s", s->player_1); - c->Message(Chat::White, " teleport_zone: %s", s->teleport_zone); - c->Message(Chat::White, " you_cast: %s", s->you_cast); - c->Message(Chat::White, " other_casts: %s", s->other_casts); - c->Message(Chat::White, " cast_on_you: %s", s->cast_on_you); - c->Message(Chat::White, " spell_fades: %s", s->spell_fades); - c->Message(Chat::White, " range: %f", s->range); - c->Message(Chat::White, " aoe_range: %f", s->aoe_range); - c->Message(Chat::White, " push_back: %f", s->push_back); - c->Message(Chat::White, " push_up: %f", s->push_up); - c->Message(Chat::White, " cast_time: %d", s->cast_time); - c->Message(Chat::White, " recovery_time: %d", s->recovery_time); - c->Message(Chat::White, " recast_time: %d", s->recast_time); - c->Message(Chat::White, " buff_duration_formula: %d", s->buff_duration_formula); - c->Message(Chat::White, " buff_duration: %d", s->buff_duration); - c->Message(Chat::White, " AEDuration: %d", s->aoe_duration); - c->Message(Chat::White, " mana: %d", s->mana); - c->Message(Chat::White, " base[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", s->base_value[0], s->base_value[1], s->base_value[2], s->base_value[3], s->base_value[4], s->base_value[5], s->base_value[6], s->base_value[7], s->base_value[8], s->base_value[9], s->base_value[10], s->base_value[11]); - c->Message(Chat::White, " base22[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", s->limit_value[0], s->limit_value[1], s->limit_value[2], s->limit_value[3], s->limit_value[4], s->limit_value[5], s->limit_value[6], s->limit_value[7], s->limit_value[8], s->limit_value[9], s->limit_value[10], s->limit_value[11]); - c->Message(Chat::White, " max[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", s->max_value[0], s->max_value[1], s->max_value[2], s->max_value[3], s->max_value[4], s->max_value[5], s->max_value[6], s->max_value[7], s->max_value[8], s->max_value[9], s->max_value[10], s->max_value[11]); - c->Message(Chat::White, " components[4]: %d, %d, %d, %d", s->component[0], s->component[1], s->component[2], s->component[3]); - c->Message(Chat::White, " component_counts[4]: %d, %d, %d, %d", s->component_count[0], s->component_count[1], s->component_count[2], s->component_count[3]); - c->Message(Chat::White, " NoexpendReagent[4]: %d, %d, %d, %d", s->no_expend_reagent[0], s->no_expend_reagent[1], s->no_expend_reagent[2], s->no_expend_reagent[3]); - c->Message(Chat::White, " formula[12]: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x", s->formula[0], s->formula[1], s->formula[2], s->formula[3], s->formula[4], s->formula[5], s->formula[6], s->formula[7], s->formula[8], s->formula[9], s->formula[10], s->formula[11]); - c->Message(Chat::White, " goodEffect: %d", s->good_effect); - c->Message(Chat::White, " Activated: %d", s->activated); - c->Message(Chat::White, " resisttype: %d", s->resist_type); - c->Message(Chat::White, " effectid[12]: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x", s->effect_id[0], s->effect_id[1], s->effect_id[2], s->effect_id[3], s->effect_id[4], s->effect_id[5], s->effect_id[6], s->effect_id[7], s->effect_id[8], s->effect_id[9], s->effect_id[10], s->effect_id[11]); - c->Message(Chat::White, " targettype: %d", s->target_type); - c->Message(Chat::White, " basediff: %d", s->base_difficulty); - c->Message(Chat::White, " skill: %d", s->skill); - c->Message(Chat::White, " zonetype: %d", s->zone_type); - c->Message(Chat::White, " EnvironmentType: %d", s->environment_type); - c->Message(Chat::White, " TimeOfDay: %d", s->time_of_day); - c->Message(Chat::White, " classes[15]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", - s->classes[0], s->classes[1], s->classes[2], s->classes[3], s->classes[4], - s->classes[5], s->classes[6], s->classes[7], s->classes[8], s->classes[9], - s->classes[10], s->classes[11], s->classes[12], s->classes[13], s->classes[14]); - c->Message(Chat::White, " CastingAnim: %d", s->casting_animation); - c->Message(Chat::White, " SpellAffectIndex: %d", s->spell_affect_index); - c->Message(Chat::White, " RecourseLink: %d", s->recourse_link); - } -} - -void command_lastname(Client *c, const Seperator *sep) -{ - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); - LogInfo("#lastname request from [{}] for [{}]", c->GetName(), t->GetName()); - - if(strlen(sep->arg[1]) <= 70) - t->ChangeLastName(sep->arg[1]); - else - c->Message(Chat::White, "Usage: #lastname where is less than 70 chars long"); -} - -void command_memspell(Client *c, const Seperator *sep) -{ - uint32 slot; - uint16 spell_id; - - if (!(sep->IsNumber(1) && sep->IsNumber(2))) - { - c->Message(Chat::White, "Usage: #MemSpell slotid spellid"); - } - else - { - slot = atoi(sep->arg[1]) - 1; - spell_id = atoi(sep->arg[2]); - if (slot > EQ::spells::SPELL_GEM_COUNT || spell_id >= SPDAT_RECORDS) - { - c->Message(Chat::White, "Error: #MemSpell: Arguement out of range"); - } - else - { - c->MemSpell(spell_id, slot); - c->Message(Chat::White, "Spell slot changed, have fun!"); - } - } -} -void command_save(Client *c, const Seperator *sep) -{ - if (c->GetTarget() == 0) - c->Message(Chat::White, "Error: no target"); - else if (c->GetTarget()->IsClient()) { - if (c->GetTarget()->CastToClient()->Save(2)) - c->Message(Chat::White, "%s successfully saved.", c->GetTarget()->GetName()); - else - c->Message(Chat::White, "Manual save for %s failed.", c->GetTarget()->GetName()); - } - else if (c->GetTarget()->IsPlayerCorpse()) { - if (c->GetTarget()->CastToMob()->Save()) - c->Message(Chat::White, "%s successfully saved. (dbid=%u)", c->GetTarget()->GetName(), c->GetTarget()->CastToCorpse()->GetCorpseDBID()); - else - c->Message(Chat::White, "Manual save for %s failed.", c->GetTarget()->GetName()); - } - else - c->Message(Chat::White, "Error: target not a Client/PlayerCorpse"); -} - -void command_showstats(Client *c, const Seperator *sep) -{ - if (c->GetTarget() != 0 ) - c->GetTarget()->ShowStats(c); - else - c->ShowStats(c); -} - -void command_showzonegloballoot(Client *c, const Seperator *sep) -{ - c->Message(Chat::White, "GlobalLoot for %s (%d:%d)", zone->GetShortName(), zone->GetZoneID(), zone->GetInstanceVersion()); - zone->ShowZoneGlobalLoot(c); -} - -void command_showzonepoints(Client *c, const Seperator *sep) -{ - auto &mob_list = entity_list.GetMobList(); - for (auto itr : mob_list) { - Mob *mob = itr.second; - if (mob->IsNPC() && mob->GetRace() == 2254) { - mob->Depop(); - } - } - - int found_zone_points = 0; - - c->Message(Chat::White, "Listing zone points..."); - c->SendChatLineBreak(); - - for (auto &virtual_zone_point : zone->virtual_zone_point_list) { - std::string zone_long_name = zone_store.GetZoneLongName(virtual_zone_point.target_zone_id); - - c->Message( - Chat::White, - fmt::format( - "Virtual Zone Point x [{}] y [{}] z [{}] h [{}] width [{}] height [{}] | To [{}] ({}) x [{}] y [{}] z [{}] h [{}]", - virtual_zone_point.x, - virtual_zone_point.y, - virtual_zone_point.z, - virtual_zone_point.heading, - virtual_zone_point.width, - virtual_zone_point.height, - zone_long_name.c_str(), - virtual_zone_point.target_zone_id, - virtual_zone_point.target_x, - virtual_zone_point.target_y, - virtual_zone_point.target_z, - virtual_zone_point.target_heading - ).c_str() - ); - - std::string node_name = fmt::format("ZonePoint To [{}]", zone_long_name); - - float half_width = ((float) virtual_zone_point.width / 2); - - NPC::SpawnZonePointNodeNPC(node_name, glm::vec4( - (float) virtual_zone_point.x + half_width, - (float) virtual_zone_point.y + half_width, - virtual_zone_point.z, - virtual_zone_point.heading - )); - - NPC::SpawnZonePointNodeNPC(node_name, glm::vec4( - (float) virtual_zone_point.x + half_width, - (float) virtual_zone_point.y - half_width, - virtual_zone_point.z, - virtual_zone_point.heading - )); - - NPC::SpawnZonePointNodeNPC(node_name, glm::vec4( - (float) virtual_zone_point.x - half_width, - (float) virtual_zone_point.y - half_width, - virtual_zone_point.z, - virtual_zone_point.heading - )); - - NPC::SpawnZonePointNodeNPC(node_name, glm::vec4( - (float) virtual_zone_point.x - half_width, - (float) virtual_zone_point.y + half_width, - virtual_zone_point.z, - virtual_zone_point.heading - )); - - NPC::SpawnZonePointNodeNPC(node_name, glm::vec4( - (float) virtual_zone_point.x + half_width, - (float) virtual_zone_point.y + half_width, - (float) virtual_zone_point.z + (float) virtual_zone_point.height, - virtual_zone_point.heading - )); - - NPC::SpawnZonePointNodeNPC(node_name, glm::vec4( - (float) virtual_zone_point.x + half_width, - (float) virtual_zone_point.y - half_width, - (float) virtual_zone_point.z + (float) virtual_zone_point.height, - virtual_zone_point.heading - )); - - NPC::SpawnZonePointNodeNPC(node_name, glm::vec4( - (float) virtual_zone_point.x - half_width, - (float) virtual_zone_point.y - half_width, - (float) virtual_zone_point.z + (float) virtual_zone_point.height, - virtual_zone_point.heading - )); - - NPC::SpawnZonePointNodeNPC(node_name, glm::vec4( - (float) virtual_zone_point.x - half_width, - (float) virtual_zone_point.y + half_width, - (float) virtual_zone_point.z + (float) virtual_zone_point.height, - virtual_zone_point.heading - )); - - found_zone_points++; - } - - LinkedListIterator iterator(zone->zone_point_list); - iterator.Reset(); - while (iterator.MoreElements()) { - ZonePoint *zone_point = iterator.GetData(); - std::string zone_long_name = zone_store.GetZoneLongName(zone_point->target_zone_id); - std::string node_name = fmt::format("ZonePoint To [{}]", zone_long_name); - - NPC::SpawnZonePointNodeNPC( - node_name, glm::vec4( - zone_point->x, - zone_point->y, - zone_point->z, - zone_point->heading - ) - ); - - c->Message( - Chat::White, - fmt::format( - "Client Side Zone Point x [{}] y [{}] z [{}] h [{}] number [{}] | To [{}] ({}) x [{}] y [{}] z [{}] h [{}]", - zone_point->x, - zone_point->y, - zone_point->z, - zone_point->heading, - zone_point->number, - zone_long_name.c_str(), - zone_point->target_zone_id, - zone_point->target_x, - zone_point->target_y, - zone_point->target_z, - zone_point->target_heading - ).c_str() - ); - - iterator.Advance(); - - found_zone_points++; - } - - if (found_zone_points == 0) { - c->Message(Chat::White, "There were no zone points found..."); - } - - c->SendChatLineBreak(); - -} - -void command_mystats(Client *c, const Seperator *sep) -{ - if (c->GetTarget() && c->GetPet()) { - if (c->GetTarget()->IsPet() && c->GetTarget() == c->GetPet()) - c->GetTarget()->ShowStats(c); - else - c->ShowStats(c); - } - else - c->ShowStats(c); -} - -void command_myskills(Client *c, const Seperator *sep) -{ - c->ShowSkillsWindow(); -} - -void command_bind(Client *c, const Seperator *sep) -{ - if (c->GetTarget() != 0 ) { - if (c->GetTarget()->IsClient()) - c->GetTarget()->CastToClient()->SetBindPoint(); - else - c->Message(Chat::White, "Error: target not a Player"); - } else - c->SetBindPoint(); -} - -void command_depop(Client *c, const Seperator *sep) -{ - if (c->GetTarget() == 0 || !(c->GetTarget()->IsNPC() || c->GetTarget()->IsNPCCorpse())) - c->Message(Chat::White, "You must have a NPC target for this command. (maybe you meant #depopzone?)"); - else { - c->Message(Chat::White, "Depoping '%s'.", c->GetTarget()->GetName()); - c->GetTarget()->Depop(); - } -} - -void command_depopzone(Client *c, const Seperator *sep) -{ - zone->Depop(); - c->Message(Chat::White, "Zone depoped."); -} - -void command_devtools(Client *c, const Seperator *sep) -{ - std::string dev_tools_key = StringFormat("%i-dev-tools-disabled", c->AccountID()); - - /** - * Handle window toggle - */ - if (strcasecmp(sep->arg[1], "disable") == 0) { - DataBucket::SetData(dev_tools_key, "true"); - c->SetDevToolsEnabled(false); - } - if (strcasecmp(sep->arg[1], "enable") == 0) { - DataBucket::DeleteData(dev_tools_key); - c->SetDevToolsEnabled(true); - } - - c->ShowDevToolsMenu(); -} - -void command_repop(Client *c, const Seperator *sep) -{ - int timearg = 1; - int delay = 0; - - if (sep->arg[1] && strcasecmp(sep->arg[1], "force") == 0) { - timearg++; - - LinkedListIterator iterator(zone->spawn2_list); - iterator.Reset(); - while (iterator.MoreElements()) { - std::string query = StringFormat( - "DELETE FROM respawn_times WHERE id = %lu AND instance_id = %lu", - (unsigned long)iterator.GetData()->GetID(), - (unsigned long)zone->GetInstanceID() - ); - auto results = database.QueryDatabase(query); - iterator.Advance(); - } - c->Message(Chat::White, "Zone depop: Force resetting spawn timers."); - } - - if (!sep->IsNumber(timearg)) { - c->Message(Chat::White, "Zone depopped - repopping now."); - - zone->Repop(); - - /* Force a spawn2 timer trigger so we don't delay actually spawning the NPC's */ - zone->spawn2_timer.Trigger(); - return; - } - - c->Message(Chat::White, "Zone depoped. Repop in %i seconds", atoi(sep->arg[timearg])); - zone->Repop(atoi(sep->arg[timearg]) * 1000); - - zone->spawn2_timer.Trigger(); -} - -void command_spawnstatus(Client *c, const Seperator *sep) -{ - if((sep->arg[1][0] == 'e') | (sep->arg[1][0] == 'E')) - { - // show only enabled spawns - zone->ShowEnabledSpawnStatus(c); - } - else if((sep->arg[1][0] == 'd') | (sep->arg[1][0] == 'D')) - { - // show only disabled spawns - zone->ShowDisabledSpawnStatus(c); - } - else if((sep->arg[1][0] == 'a') | (sep->arg[1][0] == 'A')) - { - // show all spawn staus with no filters - zone->SpawnStatus(c); - } - else if(sep->IsNumber(1)) - { - // show spawn status by spawn2 id - zone->ShowSpawnStatusByID(c, atoi(sep->arg[1])); - } - else if(strcmp(sep->arg[1], "help") == 0) - { - c->Message(Chat::White, "Usage: #spawnstatus <[a]ll | [d]isabled | [e]nabled | {Spawn2 ID}>"); - } - else { - zone->SpawnStatus(c); - } -} - -void command_nukebuffs(Client *c, const Seperator *sep) -{ - if (c->GetTarget() == 0) - c->BuffFadeAll(); - else - c->GetTarget()->BuffFadeAll(); -} - -void command_zuwcoords(Client *c, const Seperator *sep) -{ - // modifys and resends zhdr packet - if(sep->arg[1][0]==0) - c->Message(Chat::White, "Usage: #zuwcoords "); - else { - zone->newzone_data.underworld = atof(sep->arg[1]); - //float newdata = atof(sep->arg[1]); - //memcpy(&zone->zone_header_data[130], &newdata, sizeof(float)); - 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_zunderworld(Client *c, const Seperator *sep) -{ - if(sep->arg[1][0]==0) - c->Message(Chat::White, "Usage: #zunderworld "); - else { - zone->newzone_data.underworld = atof(sep->arg[1]); - } -} - -void command_zsafecoords(Client *c, const Seperator *sep) -{ - // modifys and resends zhdr packet - if(sep->arg[3][0]==0) - c->Message(Chat::White, "Usage: #zsafecoords "); - else { - zone->newzone_data.safe_x = atof(sep->arg[1]); - zone->newzone_data.safe_y = atof(sep->arg[2]); - zone->newzone_data.safe_z = atof(sep->arg[3]); - //float newdatax = atof(sep->arg[1]); - //float newdatay = atof(sep->arg[2]); - //float newdataz = atof(sep->arg[3]); - //memcpy(&zone->zone_header_data[114], &newdatax, sizeof(float)); - //memcpy(&zone->zone_header_data[118], &newdatay, sizeof(float)); - //memcpy(&zone->zone_header_data[122], &newdataz, sizeof(float)); - //zone->SetSafeCoords(); - 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_freeze(Client *c, const Seperator *sep) -{ - if (c->GetTarget() != 0) - c->GetTarget()->SendAppearancePacket(AT_Anim, ANIM_FREEZE); - else - c->Message(Chat::White, "ERROR: Freeze requires a target."); -} - -void command_unfreeze(Client *c, const Seperator *sep) -{ - if (c->GetTarget() != 0) - c->GetTarget()->SendAppearancePacket(AT_Anim, ANIM_STAND); - else - c->Message(Chat::White, "ERROR: Unfreeze requires a target."); -} - -void command_push(Client *c, const Seperator *sep) -{ - Mob *t = c; - if (c->GetTarget() != nullptr) - t = c->GetTarget(); - - if (!sep->arg[1] || !sep->IsNumber(1)) { - c->Message(Chat::White, "ERROR: Must provide at least a push back."); - return; - } - - float back = atof(sep->arg[1]); - float up = 0.0f; - - if (sep->arg[2] && sep->IsNumber(2)) - up = atof(sep->arg[2]); - - if (t->IsNPC()) { - t->IncDeltaX(back * g_Math.FastSin(c->GetHeading())); - t->IncDeltaY(back * g_Math.FastCos(c->GetHeading())); - t->IncDeltaZ(up); - t->SetForcedMovement(6); - } else if (t->IsClient()) { - // TODO: send packet to push - } -} - -void command_proximity(Client *c, const Seperator *sep) -{ - if (!c->GetTarget() || (c->GetTarget() && !c->GetTarget()->IsNPC())) { - c->Message(Chat::White, "You must target an NPC"); - return; - } - - for (auto &iter : entity_list.GetNPCList()) { - auto npc = iter.second; - std::string name = npc->GetName(); - - if (name.find("Proximity") != std::string::npos) { - npc->Depop(); - } - } - - NPC *npc = c->GetTarget()->CastToNPC(); - - std::vector points; - - FindPerson_Point p{}; - - if (npc->IsProximitySet()) { - glm::vec4 position; - position.w = npc->GetHeading(); - position.x = npc->GetProximityMinX(); - position.y = npc->GetProximityMinY(); - position.z = npc->GetZ(); - - position.x = npc->GetProximityMinX(); - position.y = npc->GetProximityMinY(); - NPC::SpawnNodeNPC("Proximity", "", position); - - position.x = npc->GetProximityMinX(); - position.y = npc->GetProximityMaxY(); - NPC::SpawnNodeNPC("Proximity", "", position); - - position.x = npc->GetProximityMaxX(); - position.y = npc->GetProximityMinY(); - NPC::SpawnNodeNPC("Proximity", "", position); - - position.x = npc->GetProximityMaxX(); - position.y = npc->GetProximityMaxY(); - NPC::SpawnNodeNPC("Proximity", "", position); - - p.x = npc->GetProximityMinX(); - p.y = npc->GetProximityMinY(); - p.z = npc->GetZ(); - points.push_back(p); - - p.x = npc->GetProximityMinX(); - p.y = npc->GetProximityMaxY(); - points.push_back(p); - - p.x = npc->GetProximityMaxX(); - p.y = npc->GetProximityMaxY(); - points.push_back(p); - - p.x = npc->GetProximityMaxX(); - p.y = npc->GetProximityMinY(); - points.push_back(p); - - p.x = npc->GetProximityMinX(); - p.y = npc->GetProximityMinY(); - points.push_back(p); - } - - if (c->ClientVersion() >= EQ::versions::ClientVersion::RoF) { - c->SendPathPacket(points); - } -} - -void command_pvp(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->SetPVP(state); - c->Message(Chat::White, "%s now follows the ways of %s.", t->GetName(), state?"discord":"order"); - } - else - c->Message(Chat::White, "Usage: #pvp [on/off]"); -} - -void command_setxp(Client *c, const Seperator *sep) -{ - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); - - if (sep->IsNumber(1)) { - if (atoi(sep->arg[1]) > 9999999) - c->Message(Chat::White, "Error: Value too high."); - else - t->AddEXP(atoi(sep->arg[1])); - } - else - c->Message(Chat::White, "Usage: #setxp number"); -} - -void command_setpvppoints(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (!arguments || !sep->IsNumber(1)) { - c->Message(Chat::White, "Command Syntax: #setpvppoints [Amount]"); - return; - } - - Client *target = c; - if (c->GetTarget() && c->GetTarget()->IsClient()) { - target = c->GetTarget()->CastToClient(); - } - - uint32 pvp_points = static_cast(std::min(std::stoull(sep->arg[1]), (unsigned long long) 2000000000)); - target->SetPVPPoints(pvp_points); - target->Save(); - target->SendPVPStats(); - std::string pvp_message = fmt::format( - "{} now {} {} PVP Point{}.", - c == target ? "You" : target->GetCleanName(), - c == target ? "have" : "has", - pvp_points, - pvp_points != 1 ? "s" : "" - ); - c->Message( - Chat::White, - pvp_message.c_str() - ); -} - -void command_name(Client *c, const Seperator *sep) -{ - Client *target; - - if( (strlen(sep->arg[1]) == 0) || (!(c->GetTarget() && c->GetTarget()->IsClient())) ) - c->Message(Chat::White, "Usage: #name newname (requires player target)"); - else - { - target = c->GetTarget()->CastToClient(); - char *oldname = strdup(target->GetName()); - if(target->ChangeFirstName(sep->arg[1], c->GetName())) - { - c->Message(Chat::White, "Successfully renamed %s to %s", oldname, sep->arg[1]); - // until we get the name packet working right this will work - c->Message(Chat::White, "Sending player to char select."); - target->Kick("Name was changed"); - } - else - c->Message(Chat::Red, "ERROR: Unable to rename %s. Check that the new name '%s' isn't already taken.", oldname, sep->arg[2]); - free(oldname); - } -} - -void command_tempname(Client *c, const Seperator *sep) -{ - Mob *target; - target = c->GetTarget(); - - if(!target) - c->Message(Chat::White, "Usage: #tempname newname (requires a target)"); - else if(strlen(sep->arg[1]) > 0) - { - char *oldname = strdup(target->GetName()); - target->TempName(sep->arg[1]); - c->Message(Chat::White, "Renamed %s to %s", oldname, sep->arg[1]); - free(oldname); - } - else { - target->TempName(); - c->Message(Chat::White, "Restored the original name"); - } -} - -void command_petname(Client *c, const Seperator *sep) -{ - Mob *target; - target = c->GetTarget(); - - if(!target) - c->Message(Chat::White, "Usage: #petname newname (requires a target)"); - else if(target->IsPet() && (target->GetOwnerID() == c->GetID()) && strlen(sep->arg[1]) > 0) - { - char *oldname = strdup(target->GetName()); - target->TempName(sep->arg[1]); - c->Message(Chat::White, "Renamed %s to %s", oldname, sep->arg[1]); - free(oldname); - } - else { - target->TempName(); - c->Message(Chat::White, "Restored the original name"); - } -} - -void command_npcspecialattk(Client *c, const Seperator *sep) -{ - if (c->GetTarget()==0 || c->GetTarget()->IsClient() || strlen(sep->arg[1]) <= 0 || strlen(sep->arg[2]) <= 0) - c->Message(Chat::White, "Usage: #npcspecialattk *flagchar* *permtag* (Flags are E(nrage) F(lurry) R(ampage) S(ummon), permtag is 1 = True, 0 = False)."); - else { - c->GetTarget()->CastToNPC()->NPCSpecialAttacks(sep->arg[1],atoi(sep->arg[2])); - c->Message(Chat::White, "NPC Special Attack set."); - } -} - -void command_kill(Client *c, const Seperator *sep) -{ - if (!c->GetTarget()) { - c->Message(Chat::White, "Error: #Kill: No target."); - } - else - if (!c->GetTarget()->IsClient() || c->GetTarget()->CastToClient()->Admin() <= c->Admin()) - c->GetTarget()->Kill(); -} - -void command_killallnpcs(Client *c, const Seperator *sep) -{ - std::string search_string; - if (sep->arg[1]) { - search_string = sep->arg[1]; - } - - int count = 0; - for (auto &itr : entity_list.GetMobList()) { - Mob *entity = itr.second; - if (!entity->IsNPC()) { - continue; - } - - std::string entity_name = entity->GetName(); - - /** - * Filter by name - */ - if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { - continue; - } - - bool is_not_attackable = - ( - entity->IsInvisible() || - !entity->IsAttackAllowed(c) || - entity->GetRace() == 127 || - entity->GetRace() == 240 - ); - - if (is_not_attackable) { - continue; - } - - entity->Damage(c, 1000000000, 0, EQ::skills::SkillDragonPunch); - - count++; - } - - c->Message(Chat::Yellow, "Killed (%i) npc(s)", count); -} - -void command_haste(Client *c, const Seperator *sep) -{ - // #haste command to set client attack speed. Takes a percentage (100 = twice normal attack speed) - if(sep->arg[1][0] != 0) { - uint16 Haste = atoi(sep->arg[1]); - if(Haste > 85) - Haste = 85; - c->SetExtraHaste(Haste); - // SetAttackTimer must be called to make this take effect, so player needs to change - // the primary weapon. - c->Message(Chat::White, "Haste set to %d%% - Need to re-equip primary weapon before it takes effect", Haste); - } - else - c->Message(Chat::White, "Usage: #haste [percentage]"); -} - -void command_damage(Client *c, const Seperator *sep) -{ - if (c->GetTarget()==0) - c->Message(Chat::White, "Error: #Damage: No Target."); - else if (!sep->IsNumber(1)) { - c->Message(Chat::White, "Usage: #damage x"); - } - else { - int32 nkdmg = atoi(sep->arg[1]); - if (nkdmg > 2100000000) - c->Message(Chat::White, "Enter a value less then 2,100,000,000."); - else - c->GetTarget()->Damage(c, nkdmg, SPELL_UNKNOWN, EQ::skills::SkillHandtoHand, false); - } -} - -void command_zonespawn(Client *c, const Seperator *sep) -{ - c->Message(Chat::White, "This command is not yet implemented."); - return; - -/* this was kept from client.cpp verbatim (it was commented out) */ - // if (target && target->IsNPC()) { - // Message(0, "Inside main if."); - // if (strcasecmp(sep->arg[1], "add")==0) { - // Message(0, "Inside add if."); - // database.DBSpawn(1, StaticGetZoneName(this->GetPP().current_zone), target->CastToNPC()); - // } - // else if (strcasecmp(sep->arg[1], "update")==0) { - // database.DBSpawn(2, StaticGetZoneName(this->GetPP().current_zone), target->CastToNPC()); - // } - // else if (strcasecmp(sep->arg[1], "remove")==0) { - // if (strcasecmp(sep->arg[2], "all")==0) { - // database.DBSpawn(4, StaticGetZoneName(this->GetPP().current_zone)); - // } - // else { - // if (database.DBSpawn(3, StaticGetZoneName(this->GetPP().current_zone), target->CastToNPC())) { - // Message(0, "#zonespawn: %s removed successfully!", target->GetName()); - // target->CastToNPC()->Death(target, target->GetHP()); - // } - // } - // } - // else - // Message(0, "Error: #dbspawn: Invalid command. (Note: EDIT and REMOVE are NOT in yet.)"); - // if (target->CastToNPC()->GetNPCTypeID() > 0) { - // Message(0, "Spawn is type %i", target->CastToNPC()->GetNPCTypeID()); - // } - // } - // else if(!target || !target->IsNPC()) - // Message(0, "Error: #zonespawn: You must have a NPC targeted!"); - // else - // Message(0, "Usage: #zonespawn [add|edit|remove|remove all]"); -} - -void command_npcspawn(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (!arguments) { - c->Message(Chat::White, "Command Syntax: #npcspawn [Add|Create|Delete|Remove|Update]"); - return; - } - - if (!(c->GetTarget() && c->GetTarget()->IsNPC())) { - c->Message(Chat::White, "You must target an NPC to use this command."); - return; - } - - NPC* target = c->GetTarget()->CastToNPC(); - std::string spawn_type = str_tolower(sep->arg[1]); - uint32 extra = 0; - bool is_add = spawn_type.find("add") != std::string::npos; - bool is_create = spawn_type.find("create") != std::string::npos; - bool is_delete = spawn_type.find("delete") != std::string::npos; - bool is_remove = spawn_type.find("remove") != std::string::npos; - bool is_update = spawn_type.find("update") != std::string::npos; - if (!is_add && !is_create && !is_delete && !is_remove && !is_update) { - c->Message(Chat::White, "Command Syntax: #npcspawn [Add|Create|Delete|Remove|Update]"); - return; - } - - if (is_add || is_create) { - extra = ( - sep->IsNumber(2) ? - ( - is_add ? - std::stoi(sep->arg[2]) : - 1 - ) : ( - is_add ? - 1200 : - 0 - ) - ); // Default to 1200 for Add, 0 for Create if not set - content_db.NPCSpawnDB( - is_add ? NPCSpawnTypes::AddNewSpawngroup : NPCSpawnTypes::CreateNewSpawn, - zone->GetShortName(), - zone->GetInstanceVersion(), - c, - target, - extra - ); - c->Message( - Chat::White, - fmt::format( - "Spawn {} | Name: {} ({})", - is_add ? "Added" : "Created", - target->GetCleanName(), - target->GetID() - ).c_str() - ); - } else if (is_delete || is_remove || is_update) { - uint8 spawn_update_type = ( - is_delete ? - NPCSpawnTypes::DeleteSpawn : - ( - is_remove ? - NPCSpawnTypes::RemoveSpawn : - NPCSpawnTypes::UpdateAppearance - ) - ); - std::string spawn_message = ( - is_delete ? - "Deleted" : - ( - is_remove ? - "Removed" : - "Updated" - ) - ); - content_db.NPCSpawnDB( - spawn_update_type, - zone->GetShortName(), - zone->GetInstanceVersion(), - c, - target - ); - c->Message( - Chat::White, - fmt::format( - "Spawn {} | Name: {} ({})", - spawn_message, - target->GetCleanName(), - target->GetID() - ).c_str() - ); - } -} - -void command_spawnfix(Client *c, const Seperator *sep) { - Mob *target_mob = c->GetTarget(); - if (!target_mob || !target_mob->IsNPC()) { - c->Message(Chat::White, "Error: #spawnfix: Need an NPC target."); - return; - } - - Spawn2* s2 = target_mob->CastToNPC()->respawn2; - - if(!s2) { - c->Message(Chat::White, "#spawnfix FAILED -- cannot determine which spawn entry in the database this mob came from."); - return; - } - - std::string query = StringFormat( - "UPDATE spawn2 SET x = '%f', y = '%f', z = '%f', heading = '%f' WHERE id = '%i'", - c->GetX(), - c->GetY(), - target_mob->GetFixedZ(c->GetPosition()), - c->GetHeading(), - s2->GetID() - ); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::Red, "Update failed! MySQL gave the following error:"); - c->Message(Chat::Red, results.ErrorMessage().c_str()); - return; - } - - c->Message(Chat::White, "Updating coordinates successful."); - target_mob->Depop(false); -} - -void command_loc(Client *c, const Seperator *sep) -{ - Mob *target = c; - if (c->GetTarget()) { - target = c->GetTarget(); - } - - auto target_position = target->GetPosition(); - - c->Message( - Chat::White, - fmt::format( - "{} Location | XYZ: {:.2f}, {:.2f}, {:.2f} Heading: {:.2f}", - ( - c == target ? - "Your" : - fmt::format( - "{} ({})", - target->GetCleanName(), - target->GetID() - ) - ), - target_position.x, - target_position.y, - target_position.z, - target_position.w - ).c_str() - ); -} - -void command_goto(Client *c, const Seperator *sep) -{ - std::string arg1 = sep->arg[1]; - - bool goto_via_target_no_args = sep->arg[1][0] == '\0' && c->GetTarget(); - bool goto_via_player_name = !sep->IsNumber(1) && !arg1.empty(); - bool goto_via_x_y_z = sep->IsNumber(1) && sep->IsNumber(2) && sep->IsNumber(3); - - if (goto_via_target_no_args) { - c->MovePC( - zone->GetZoneID(), - zone->GetInstanceID(), - c->GetTarget()->GetX(), - c->GetTarget()->GetY(), - c->GetTarget()->GetZ(), - c->GetTarget()->GetHeading() - ); - } - else if (goto_via_player_name) { - - /** - * Find them in zone first - */ - const char *player_name = sep->arg[1]; - std::string player_name_string = sep->arg[1]; - Client *client = entity_list.GetClientByName(player_name); - if (client) { - c->MovePC( - zone->GetZoneID(), - zone->GetInstanceID(), - client->GetX(), - client->GetY(), - client->GetZ(), - client->GetHeading() - ); - - c->Message(Chat::Yellow, "Goto player '%s' same zone", player_name_string.c_str()); - } - else if (c->GotoPlayer(player_name_string)) { - c->Message(Chat::Yellow, "Goto player '%s' different zone", player_name_string.c_str()); - } - else { - c->Message(Chat::Yellow, "Player '%s' not found", player_name_string.c_str()); - } - } - else if (goto_via_x_y_z) { - c->MovePC( - zone->GetZoneID(), - zone->GetInstanceID(), - atof(sep->arg[1]), - atof(sep->arg[2]), - atof(sep->arg[3]), - (sep->arg[4] ? atof(sep->arg[4]) : c->GetHeading()) - ); - } - else { - c->Message(Chat::White, "Usage: #goto [x y z] [h]"); - c->Message(Chat::White, "Usage: #goto [player_name]"); - } -} - -void command_iteminfo(Client *c, const Seperator *sep) -{ - auto inst = c->GetInv()[EQ::invslot::slotCursor]; - if (!inst) { - c->Message(Chat::Red, "Error: You need an item on your cursor for this command"); - return; - } - auto item = inst->GetItem(); - if (!item) { - LogInventory("([{}]) Command #iteminfo processed an item with no data pointer"); - c->Message(Chat::Red, "Error: This item has no data reference"); - return; - } - - EQ::SayLinkEngine linker; - linker.SetLinkType(EQ::saylink::SayLinkItemInst); - linker.SetItemInst(inst); - - c->Message(Chat::White, "*** Item Info for [%s] ***", linker.GenerateLink().c_str()); - c->Message(Chat::White, ">> ID: %u, ItemUseType: %u, ItemClassType: %u", item->ID, item->ItemType, item->ItemClass); - c->Message(Chat::White, ">> IDFile: '%s', IconID: %u", item->IDFile, item->Icon); - c->Message(Chat::White, ">> Size: %u, Weight: %u, Price: %u, LDoNPrice: %u", item->Size, item->Weight, item->Price, item->LDoNPrice); - c->Message(Chat::White, ">> Material: 0x%02X, Color: 0x%08X, Tint: 0x%08X, Light: 0x%02X", item->Material, item->Color, inst->GetColor(), item->Light); - c->Message(Chat::White, ">> IsLore: %s, LoreGroup: %u, Lore: '%s'", (item->LoreFlag ? "TRUE" : "FALSE"), item->LoreGroup, item->Lore); - c->Message(Chat::White, ">> NoDrop: %u, NoRent: %u, NoPet: %u, NoTransfer: %u, FVNoDrop: %u", - item->NoDrop, item->NoRent, (uint8)item->NoPet, (uint8)item->NoTransfer, item->FVNoDrop); - - if (item->IsClassBook()) { - c->Message(Chat::White, "*** This item is a Book (filename:'%s') ***", item->Filename); - } - else if (item->IsClassBag()) { - c->Message(Chat::White, "*** This item is a Container (%u slots) ***", item->BagSlots); - } - else { - c->Message(Chat::White, "*** This item is Common ***"); - c->Message(Chat::White, ">> Classes: %u, Races: %u, Slots: %u", item->Classes, item->Races, item->Slots); - c->Message(Chat::White, ">> ReqSkill: %u, ReqLevel: %u, RecLevel: %u", item->RecSkill, item->ReqLevel, item->RecLevel); - c->Message(Chat::White, ">> SkillModType: %u, SkillModValue: %i", item->SkillModType, item->SkillModValue); - c->Message(Chat::White, ">> BaneRaceType: %u, BaneRaceDamage: %u, BaneBodyType: %u, BaneBodyDamage: %i", - item->BaneDmgRace, item->BaneDmgRaceAmt, item->BaneDmgBody, item->BaneDmgAmt); - c->Message(Chat::White, ">> Magic: %s, SpellID: %i, ProcLevel: %u, Charges: %u, MaxCharges: %u", - (item->Magic ? "TRUE" : "FALSE"), item->Click.Effect, item->Click.Level, inst->GetCharges(), item->MaxCharges); - c->Message(Chat::White, ">> EffectType: 0x%02X, CastTime: %.2f", (uint8)item->Click.Type, ((double)item->CastTime / 1000)); - } - - if (c->Admin() >= AccountStatus::GMMgmt) - c->Message(Chat::White, ">> MinStatus: %u", item->MinStatus); -} - -void command_uptime(Client *c, const Seperator *sep) -{ - if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server disconnected"); - else - { - auto pack = new ServerPacket(ServerOP_Uptime, sizeof(ServerUptime_Struct)); - ServerUptime_Struct* sus = (ServerUptime_Struct*) pack->pBuffer; - strcpy(sus->adminname, c->GetName()); - if (sep->IsNumber(1) && atoi(sep->arg[1]) > 0) - sus->zoneserverid = atoi(sep->arg[1]); - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void command_flag(Client *c, const Seperator *sep) -{ - if(sep->arg[2][0] == 0) { - if (!c->GetTarget() || (c->GetTarget() && c->GetTarget() == c)) { - c->UpdateAdmin(); - c->Message(Chat::White, "Refreshed your admin flag from DB."); - } else if (c->GetTarget() && c->GetTarget() != c && c->GetTarget()->IsClient()) { - c->GetTarget()->CastToClient()->UpdateAdmin(); - c->Message(Chat::White, "%s's admin flag has been refreshed.", c->GetTarget()->GetName()); - c->GetTarget()->Message(Chat::White, "%s refreshed your admin flag.", c->GetName()); - } - } - else if (!sep->IsNumber(1) || atoi(sep->arg[1]) < -2 || atoi(sep->arg[1]) > 255 || strlen(sep->arg[2]) == 0) - c->Message(Chat::White, "Usage: #flag [status] [acctname]"); - - else if (c->Admin() < commandChangeFlags) { - //this check makes banning players by less than this level - //impossible, but i'll leave it in anyways - c->Message(Chat::White, "You may only refresh your own flag, doing so now."); - c->UpdateAdmin(); - } - else { - if (atoi(sep->arg[1]) > c->Admin()) - c->Message(Chat::White, "You cannot set people's status to higher than your own"); - else if (atoi(sep->arg[1]) < 0 && c->Admin() < commandBanPlayers) - c->Message(Chat::White, "You have too low of status to suspend/ban"); - else if (!database.SetAccountStatus(sep->argplus[2], atoi(sep->arg[1]))) - c->Message(Chat::White, "Unable to set GM Flag."); - else { - c->Message(Chat::White, "Set GM Flag on account."); - - std::string user; - std::string loginserver; - ParseAccountString(sep->argplus[2], user, loginserver); - - ServerPacket pack(ServerOP_FlagUpdate, 6); - *((uint32*) pack.pBuffer) = database.GetAccountIDByName(user.c_str(), loginserver.c_str()); - *((int16*) &pack.pBuffer[4]) = atoi(sep->arg[1]); - worldserver.SendPacket(&pack); - } - } -} - -void command_time(Client *c, const Seperator *sep) -{ - char timeMessage[255]; - int minutes=0; - if(sep->IsNumber(1)) { - if(sep->IsNumber(2)) { - minutes=atoi(sep->arg[2]); - } - c->Message(Chat::Red, "Setting world time to %s:%i (Timezone: 0)...", sep->arg[1], minutes); - zone->SetTime(atoi(sep->arg[1])+1, minutes); - LogInfo("{} :: Setting world time to {}:{} (Timezone: 0)...", c->GetCleanName(), sep->arg[1], minutes); - } - else { - c->Message(Chat::Red, "To set the Time: #time HH [MM]"); - TimeOfDay_Struct eqTime; - zone->zone_time.GetCurrentEQTimeOfDay( time(0), &eqTime); - sprintf(timeMessage,"%02d:%s%d %s (Timezone: %ih %im)", - ((eqTime.hour - 1) % 12) == 0 ? 12 : ((eqTime.hour - 1) % 12), - (eqTime.minute < 10) ? "0" : "", - eqTime.minute, - (eqTime.hour >= 13) ? "pm" : "am", - zone->zone_time.getEQTimeZoneHr(), - zone->zone_time.getEQTimeZoneMin() - ); - c->Message(Chat::Red, "It is now %s.", timeMessage); - LogInfo("Current Time is: {}", timeMessage); - } -} - -void command_guild(Client *c, const Seperator *sep) -{ - int admin=c->Admin(); - Mob *target=c->GetTarget(); - - if (strcasecmp(sep->arg[1], "help") == 0) { - c->Message(Chat::White, "GM Guild commands:"); - c->Message(Chat::White, " #guild list - lists all guilds on the server"); - c->Message(Chat::White, " #guild create {guildleader charname or CharID} guildname"); - c->Message(Chat::White, " #guild delete guildID"); - c->Message(Chat::White, " #guild rename guildID newname"); - c->Message(Chat::White, " #guild set charname guildID (0=no guild)"); - c->Message(Chat::White, " #guild setrank charname rank"); - c->Message(Chat::White, " #guild setleader guildID {guildleader charname or CharID}"); - } - else if (strcasecmp(sep->arg[1], "status") == 0 || strcasecmp(sep->arg[1], "stat") == 0) { - Client* client = 0; - if (sep->arg[2][0] != 0) - client = entity_list.GetClientByName(sep->argplus[2]); - else if (target != 0 && target->IsClient()) - client = target->CastToClient(); - if (client == 0) - c->Message(Chat::White, "You must target someone or specify a character name"); - else if ((client->Admin() >= minStatusToEditOtherGuilds && admin < minStatusToEditOtherGuilds) && client->GuildID() != c->GuildID()) // no peeping for GMs, make sure tell message stays the same - c->Message(Chat::White, "You must target someone or specify a character name."); - else { - if (client->IsInAGuild()) - c->Message(Chat::White, "%s is not in a guild.", client->GetName()); - else if (guild_mgr.IsGuildLeader(client->GuildID(), client->CharacterID())) - c->Message(Chat::White, "%s is the leader of <%s> rank: %s", client->GetName(), guild_mgr.GetGuildName(client->GuildID()), guild_mgr.GetRankName(client->GuildID(), client->GuildRank())); - else - c->Message(Chat::White, "%s is a member of <%s> rank: %s", client->GetName(), guild_mgr.GetGuildName(client->GuildID()), guild_mgr.GetRankName(client->GuildID(), client->GuildRank())); - } - } - else if (strcasecmp(sep->arg[1], "info") == 0) { - if (sep->arg[2][0] == 0 && c->IsInAGuild()) { - if (admin >= minStatusToEditOtherGuilds) - c->Message(Chat::White, "Usage: #guildinfo guild_id"); - else - c->Message(Chat::White, "You're not in a guild"); - } - else { - uint32 tmp = GUILD_NONE; - if (sep->arg[2][0] == 0) - tmp = c->GuildID(); - else if (admin >= minStatusToEditOtherGuilds) - tmp = atoi(sep->arg[2]); - - if(tmp != GUILD_NONE) - guild_mgr.DescribeGuild(c, tmp); - } - } - else if (strcasecmp(sep->arg[1], "set") == 0) { - if (!sep->IsNumber(3)) - c->Message(Chat::White, "Usage: #guild set charname guildgbid (0 = clear guildtag)"); - else { - uint32 guild_id = atoi(sep->arg[3]); - - if(guild_id == 0) - guild_id = GUILD_NONE; - else if(!guild_mgr.GuildExists(guild_id)) { - c->Message(Chat::Red, "Guild %d does not exist.", guild_id); - return; - } - - uint32 charid = database.GetCharacterID(sep->arg[2]); - if(charid == 0) { - c->Message(Chat::Red, "Unable to find character '%s'", charid); - return; - } - - //we could do the checking we need for guild_mgr.CheckGMStatus, but im lazy right now - if(admin < minStatusToEditOtherGuilds) { - c->Message(Chat::Red, "Access denied."); - return; - } - - if(guild_id == GUILD_NONE) { - LogGuilds("[{}]: Removing [{}] ([{}]) from guild with GM command", c->GetName(), sep->arg[2], charid); - } else { - LogGuilds("[{}]: Putting [{}] ([{}]) into guild [{}] ([{}]) with GM command", c->GetName(), sep->arg[2], charid, guild_mgr.GetGuildName(guild_id), guild_id); - } - - if(!guild_mgr.SetGuild(charid, guild_id, GUILD_MEMBER)) { - c->Message(Chat::Red, "Error putting '%s' into guild %d", sep->arg[2], guild_id); - } else { - c->Message(Chat::White, "%s has been put into guild %d", sep->arg[2], guild_id); - } - } - } - /*else if (strcasecmp(sep->arg[1], "setdoor") == 0 && admin >= minStatusToEditOtherGuilds) { - - if (!sep->IsNumber(2)) - c->Message(Chat::White, "Usage: #guild setdoor guildEQid (0 = delete guilddoor)"); - else { -// guild doors - if((!guilds[atoi(sep->arg[2])].databaseID) && (atoi(sep->arg[2])!=0) ) - { - - c->Message(Chat::White, "These is no guild with this guildEQid"); - } - else { - c->SetIsSettingGuildDoor(true); - c->Message(Chat::White, "Click on a door you want to become a guilddoor"); - c->SetSetGuildDoorID(atoi(sep->arg[2])); - } - } - }*/ - else if (strcasecmp(sep->arg[1], "setrank") == 0) { - int rank = atoi(sep->arg[3]); - if (!sep->IsNumber(3)) - c->Message(Chat::White, "Usage: #guild setrank charname rank"); - else if (rank < 0 || rank > GUILD_MAX_RANK) - c->Message(Chat::White, "Error: invalid rank #."); - else { - uint32 charid = database.GetCharacterID(sep->arg[2]); - if(charid == 0) { - c->Message(Chat::Red, "Unable to find character '%s'", charid); - return; - } - - //we could do the checking we need for guild_mgr.CheckGMStatus, but im lazy right now - if(admin < minStatusToEditOtherGuilds) { - c->Message(Chat::Red, "Access denied."); - return; - } - - LogGuilds("[{}]: Setting [{}] ([{}])'s guild rank to [{}] with GM command", c->GetName(), sep->arg[2], charid, rank); - - if(!guild_mgr.SetGuildRank(charid, rank)) - c->Message(Chat::Red, "Error while setting rank %d on '%s'.", rank, sep->arg[2]); - else - c->Message(Chat::White, "%s has been set to rank %d", sep->arg[2], rank); - } - } - else if (strcasecmp(sep->arg[1], "create") == 0) { - if (sep->arg[3][0] == 0) - c->Message(Chat::White, "Usage: #guild create {guildleader charname or CharID} guild name"); - else if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server dirconnected"); - else { - uint32 leader = 0; - if (sep->IsNumber(2)) { - leader = atoi(sep->arg[2]); - } else if((leader=database.GetCharacterID(sep->arg[2])) != 0) { - //got it from the db.. - } else { - c->Message(Chat::Red, "Unable to find char '%s'", sep->arg[2]); - return; - } - if (leader == 0) { - c->Message(Chat::White, "Guild leader not found."); - return; - } - - uint32 tmp = guild_mgr.FindGuildByLeader(leader); - if (tmp != GUILD_NONE) { - c->Message(Chat::White, "Error: %s already is the leader of DB# %i '%s'.", sep->arg[2], tmp, guild_mgr.GetGuildName(tmp)); - } - else { - - if(admin < minStatusToEditOtherGuilds) { - c->Message(Chat::Red, "Access denied."); - return; - } - - uint32 id = guild_mgr.CreateGuild(sep->argplus[3], leader); - - LogGuilds("[{}]: Creating guild [{}] with leader [{}] with GM command. It was given id [{}]", c->GetName(), - sep->argplus[3], leader, (unsigned long)id); - - if (id == GUILD_NONE) - c->Message(Chat::White, "Guild creation failed."); - else { - c->Message(Chat::White, "Guild created: Leader: %i, number %i: %s", leader, id, sep->argplus[3]); - - if(!guild_mgr.SetGuild(leader, id, GUILD_LEADER)) - c->Message(Chat::White, "Unable to set guild leader's guild in the database. Your going to have to run #guild set"); - } - - } - } - } - else if (strcasecmp(sep->arg[1], "delete") == 0) { - if (!sep->IsNumber(2)) - c->Message(Chat::White, "Usage: #guild delete guildID"); - else if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server dirconnected"); - else { - uint32 id = atoi(sep->arg[2]); - - if(!guild_mgr.GuildExists(id)) { - c->Message(Chat::White, "Guild %d does not exist!", id); - return; - } - - if(admin < minStatusToEditOtherGuilds) { - //this person is not allowed to just edit any guild, check this guild's min status. - if(c->GuildID() != id) { - c->Message(Chat::Red, "Access denied to edit other people's guilds"); - return; - } else if(!guild_mgr.CheckGMStatus(id, admin)) { - c->Message(Chat::Red, "Access denied to edit your guild with GM commands."); - return; - } - } - - LogGuilds("[{}]: Deleting guild [{}] ([{}]) with GM command", c->GetName(), - guild_mgr.GetGuildName(id), id); - - if (!guild_mgr.DeleteGuild(id)) - c->Message(Chat::White, "Guild delete failed."); - else { - c->Message(Chat::White, "Guild %d deleted.", id); - } - } - } - else if (strcasecmp(sep->arg[1], "rename") == 0) { - if ((!sep->IsNumber(2)) || sep->arg[3][0] == 0) - c->Message(Chat::White, "Usage: #guild rename guildID newname"); - else if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server dirconnected"); - else { - uint32 id = atoi(sep->arg[2]); - - if(!guild_mgr.GuildExists(id)) { - c->Message(Chat::White, "Guild %d does not exist!", id); - return; - } - - if(admin < minStatusToEditOtherGuilds) { - //this person is not allowed to just edit any guild, check this guild's min status. - if(c->GuildID() != id) { - c->Message(Chat::Red, "Access denied to edit other people's guilds"); - return; - } else if(!guild_mgr.CheckGMStatus(id, admin)) { - c->Message(Chat::Red, "Access denied to edit your guild with GM commands."); - return; - } - } - - LogGuilds("[{}]: Renaming guild [{}] ([{}]) to [{}] with GM command", c->GetName(), - guild_mgr.GetGuildName(id), id, sep->argplus[3]); - - if (!guild_mgr.RenameGuild(id, sep->argplus[3])) - c->Message(Chat::White, "Guild rename failed."); - else { - c->Message(Chat::White, "Guild %d renamed to %s", id, sep->argplus[3]); - } - } - } - else if (strcasecmp(sep->arg[1], "setleader") == 0) { - if (sep->arg[3][0] == 0 || !sep->IsNumber(2)) - c->Message(Chat::White, "Usage: #guild setleader guild_id {guildleader charname or CharID}"); - else if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server dirconnected"); - else { - uint32 leader = 0; - if (sep->IsNumber(2)) { - leader = atoi(sep->arg[2]); - } else if((leader=database.GetCharacterID(sep->arg[2])) != 0) { - //got it from the db.. - } else { - c->Message(Chat::Red, "Unable to find char '%s'", sep->arg[2]); - return; - } - - uint32 tmpdb = guild_mgr.FindGuildByLeader(leader); - if (leader == 0) - c->Message(Chat::White, "New leader not found."); - else if (tmpdb != 0) { - c->Message(Chat::White, "Error: %s already is the leader of guild # %i", sep->arg[2], tmpdb); - } - else { - uint32 id = atoi(sep->arg[2]); - - if(!guild_mgr.GuildExists(id)) { - c->Message(Chat::White, "Guild %d does not exist!", id); - return; - } - - if(admin < minStatusToEditOtherGuilds) { - //this person is not allowed to just edit any guild, check this guild's min status. - if(c->GuildID() != id) { - c->Message(Chat::Red, "Access denied to edit other people's guilds"); - return; - } else if(!guild_mgr.CheckGMStatus(id, admin)) { - c->Message(Chat::Red, "Access denied to edit your guild with GM commands."); - return; - } - } - - LogGuilds("[{}]: Setting leader of guild [{}] ([{}]) to [{}] with GM command", c->GetName(), - guild_mgr.GetGuildName(id), id, leader); - - if(!guild_mgr.SetGuildLeader(id, leader)) - c->Message(Chat::White, "Guild leader change failed."); - else { - c->Message(Chat::White, "Guild leader changed: guild # %d, Leader: %s", id, sep->argplus[3]); - } - } - } - } - else if (strcasecmp(sep->arg[1], "list") == 0) { - if(admin < minStatusToEditOtherGuilds) { - c->Message(Chat::Red, "Access denied."); - return; - } - guild_mgr.ListGuilds(c); - } - else { - c->Message(Chat::White, "Unknown guild command, try #guild help"); - } -} -/* -bool helper_guild_edit(Client *c, uint32 dbid, uint32 eqid, uint8 rank, const char* what, const char* value) { - struct GuildRankLevel_Struct grl; - strcpy(grl.rankname, guild_mgr.GetRankName(eqid, rank)); - grl.demote = guilds[eqid].rank[rank].demote; - grl.heargu = guilds[eqid].rank[rank].heargu; - grl.invite = guilds[eqid].rank[rank].invite; - grl.motd = guilds[eqid].rank[rank].motd; - grl.promote = guilds[eqid].rank[rank].promote; - grl.remove = guilds[eqid].rank[rank].remove; - grl.speakgu = guilds[eqid].rank[rank].speakgu; - grl.warpeace = guilds[eqid].rank[rank].warpeace; - - if (strcasecmp(what, "title") == 0) { - if (strlen(value) > 100) - c->Message(Chat::White, "Error: Title has a maxium length of 100 characters."); - else - strcpy(grl.rankname, value); - } - else if (rank == 0) - c->Message(Chat::White, "Error: Rank 0's permissions can not be changed."); - else { - if (!(strlen(value) == 1 && (value[0] == '0' || value[0] == '1'))) - - return false; - if (strcasecmp(what, "demote") == 0) - grl.demote = (value[0] == '1'); - else if (strcasecmp(what, "heargu") == 0) - grl.heargu = (value[0] == '1'); - else if (strcasecmp(what, "invite") == 0) - grl.invite = (value[0] == '1'); - else if (strcasecmp(what, "motd") == 0) - grl.motd = (value[0] == '1'); - else if (strcasecmp(what, "promote") == 0) - grl.promote = (value[0] == '1'); - else if (strcasecmp(what, "remove") == 0) - - grl.remove = (value[0] == '1'); - else if (strcasecmp(what, "speakgu") == 0) - grl.speakgu = (value[0] == '1'); - else if (strcasecmp(what, "warpeace") == 0) - grl.warpeace = (value[0] == '1'); - else - c->Message(Chat::White, "Error: Permission name not recognized."); - } - if (!database.EditGuild(dbid, rank, &grl)) - c->Message(Chat::White, "Error: database.EditGuild() failed"); - return true; -}*/ - -void command_zonestatus(Client *c, const Seperator *sep) -{ - if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server disconnected"); - else { - auto pack = new ServerPacket(ServerOP_ZoneStatus, strlen(c->GetName()) + 2); - memset(pack->pBuffer, (uint8) c->Admin(), 1); - strcpy((char *) &pack->pBuffer[1], c->GetName()); - worldserver.SendPacket(pack); - delete pack; - } -} - -void command_doanim(Client *c, const Seperator *sep) -{ - if (!sep->IsNumber(1)) - c->Message(Chat::White, "Usage: #DoAnim [number]"); - else - if (c->Admin() >= commandDoAnimOthers) - if (c->GetTarget() == 0) - c->Message(Chat::White, "Error: You need a target."); - else - c->GetTarget()->DoAnim(atoi(sep->arg[1]),atoi(sep->arg[2])); - else - c->DoAnim(atoi(sep->arg[1]),atoi(sep->arg[2])); -} - -void command_dz(Client* c, const Seperator* sep) -{ - if (!c || !zone) { - return; - } - - if (strcasecmp(sep->arg[1], "cache") == 0) - { - if (strcasecmp(sep->arg[2], "reload") == 0) - { - DynamicZone::CacheAllFromDatabase(); - Expedition::CacheAllFromDatabase(); - c->Message(Chat::White, fmt::format( - "Reloaded [{}] dynamic zone(s) and [{}] expedition(s) from database", - zone->dynamic_zone_cache.size(), zone->expedition_cache.size() - ).c_str()); - } - } - else if (strcasecmp(sep->arg[1], "expedition") == 0) - { - if (strcasecmp(sep->arg[2], "list") == 0) - { - std::vector expeditions; - for (const auto& expedition : zone->expedition_cache) - { - expeditions.emplace_back(expedition.second.get()); - } - - std::sort(expeditions.begin(), expeditions.end(), - [](const Expedition* lhs, const Expedition* rhs) { - return lhs->GetID() < rhs->GetID(); - }); - - c->Message(Chat::White, fmt::format("Total Active Expeditions: [{}]", expeditions.size()).c_str()); - for (const auto& expedition : expeditions) - { - auto dz = expedition->GetDynamicZone(); - if (!dz) - { - LogExpeditions("Expedition [{}] has an invalid dz [{}] in cache", expedition->GetID(), expedition->GetDynamicZoneID()); - continue; - } - - auto leader_saylink = EQ::SayLinkEngine::GenerateQuestSaylink(fmt::format( - "#goto {}", expedition->GetLeaderName()), false, expedition->GetLeaderName()); - auto zone_saylink = EQ::SayLinkEngine::GenerateQuestSaylink(fmt::format( - "#zoneinstance {}", dz->GetInstanceID()), false, "zone"); - - auto seconds = dz->GetSecondsRemaining(); - - c->Message(Chat::White, fmt::format( - "expedition id: [{}] dz id: [{}] name: [{}] leader: [{}] {}: [{}]:[{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", - expedition->GetID(), - expedition->GetDynamicZoneID(), - expedition->GetName(), - leader_saylink, - zone_saylink, - ZoneName(dz->GetZoneID()), - dz->GetZoneID(), - dz->GetInstanceID(), - dz->GetZoneVersion(), - dz->GetMemberCount(), - seconds / 3600, // hours - (seconds / 60) % 60, // minutes - seconds % 60 // seconds - ).c_str()); - } - } - else if (strcasecmp(sep->arg[2], "reload") == 0) - { - Expedition::CacheAllFromDatabase(); - c->Message(Chat::White, fmt::format( - "Reloaded [{}] expeditions to cache from database.", zone->expedition_cache.size() - ).c_str()); - } - else if (strcasecmp(sep->arg[2], "destroy") == 0 && sep->IsNumber(3)) - { - auto expedition_id = std::strtoul(sep->arg[3], nullptr, 10); - auto expedition = Expedition::FindCachedExpeditionByID(expedition_id); - if (expedition) - { - c->Message(Chat::White, fmt::format("Destroying expedition [{}] ({})", - expedition_id, expedition->GetName()).c_str()); - expedition->GetDynamicZone()->RemoveAllMembers(); - } - else - { - c->Message(Chat::Red, fmt::format("Failed to destroy expedition [{}]", sep->arg[3]).c_str()); - } - } - else if (strcasecmp(sep->arg[2], "unlock") == 0 && sep->IsNumber(3)) - { - auto expedition_id = std::strtoul(sep->arg[3], nullptr, 10); - auto expedition = Expedition::FindCachedExpeditionByID(expedition_id); - if (expedition) - { - c->Message(Chat::White, fmt::format("Unlocking expedition [{}]", expedition_id).c_str()); - expedition->SetLocked(false, ExpeditionLockMessage::None, true); - } - else - { - c->Message(Chat::Red, fmt::format("Failed to find expedition [{}]", sep->arg[3]).c_str()); - } - } - } - else if (strcasecmp(sep->arg[1], "list") == 0) - { - c->Message(Chat::White, fmt::format("Total Dynamic Zones (cache): [{}]", zone->dynamic_zone_cache.size()).c_str()); - - std::vector dynamic_zones; - for (const auto& dz : zone->dynamic_zone_cache) - { - dynamic_zones.emplace_back(dz.second.get()); - } - - std::sort(dynamic_zones.begin(), dynamic_zones.end(), - [](const DynamicZone* lhs, const DynamicZone* rhs) { - return lhs->GetID() < rhs->GetID(); - }); - - for (const auto& dz : dynamic_zones) - { - auto seconds = dz->GetSecondsRemaining(); - auto zone_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( - fmt::format("#zoneinstance {}", dz->GetInstanceID()), false, "zone"); - - std::string aligned_type = fmt::format("[{}]", DynamicZone::GetDynamicZoneTypeName(static_cast(dz->GetType()))); - c->Message(Chat::White, fmt::format( - "id: [{}] type: {:>10} {}: [{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", - dz->GetID(), - aligned_type, - zone_saylink, - dz->GetZoneID(), - dz->GetInstanceID(), - dz->GetZoneVersion(), - dz->GetMemberCount(), - seconds / 3600, // hours - (seconds / 60) % 60, // minutes - seconds % 60 // seconds - ).c_str()); - } - } - else if (strcasecmp(sep->arg[1], "listdb") == 0) - { - auto dz_list = DynamicZonesRepository::AllDzInstancePlayerCounts(database); - c->Message(Chat::White, fmt::format("Total Dynamic Zones (database): [{}]", dz_list.size()).c_str()); - - auto now = std::chrono::system_clock::now(); - - for (const auto& dz : dz_list) - { - auto expire_time = std::chrono::system_clock::from_time_t(dz.start_time + dz.duration); - auto remaining = std::chrono::duration_cast(expire_time - now); - auto seconds = std::max(0, static_cast(remaining.count())); - bool is_expired = now > expire_time; - - if (!is_expired || strcasecmp(sep->arg[2], "all") == 0) - { - auto zone_saylink = is_expired ? "zone" : EQ::SayLinkEngine::GenerateQuestSaylink( - fmt::format("#zoneinstance {}", dz.instance), false, "zone"); - - c->Message(Chat::White, fmt::format( - "id: [{}] type: [{}] {}: [{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", - dz.id, - DynamicZone::GetDynamicZoneTypeName(static_cast(dz.type)), - zone_saylink, - dz.zone, - dz.instance, - dz.version, - dz.member_count, - seconds / 3600, // hours - (seconds / 60) % 60, // minutes - seconds % 60 // seconds - ).c_str()); - } - } - } - else if (strcasecmp(sep->arg[1], "lockouts") == 0) - { - if (strcasecmp(sep->arg[2], "remove") == 0 && sep->arg[3][0] != '\0') - { - if (sep->arg[5][0] == '\0') - { - c->Message(Chat::White, fmt::format( - "Removing [{}] lockouts on [{}].", sep->arg[4][0] ? sep->arg[4] : "all", sep->arg[3] - ).c_str()); - } - else - { - c->Message(Chat::White, fmt::format( - "Removing [{}]:[{}] lockout on [{}].", sep->arg[4], sep->arg[5], sep->arg[3] - ).c_str()); - } - Expedition::RemoveLockoutsByCharacterName(sep->arg[3], sep->arg[4], sep->arg[5]); - } - } - else if (strcasecmp(sep->arg[1], "makeleader") == 0 && sep->IsNumber(2) && sep->arg[3][0] != '\0') - { - auto expedition_id = std::strtoul(sep->arg[2], nullptr, 10); - auto expedition = Expedition::FindCachedExpeditionByID(expedition_id); - if (expedition) - { - auto char_name = FormatName(sep->arg[3]); - c->Message(Chat::White, fmt::format("Setting expedition [{}] leader to [{}]", expedition_id, char_name).c_str()); - expedition->SendWorldMakeLeaderRequest(c->CharacterID(), char_name); - } - else - { - c->Message(Chat::Red, fmt::format("Failed to find expedition [{}]", expedition_id).c_str()); - } - } - else - { - c->Message(Chat::White, "#dz usage:"); - c->Message(Chat::White, "#dz cache reload - reload the current zone cache from db (also reloads expedition cache dependency)"); - c->Message(Chat::White, "#dz expedition list - list expeditions in current zone cache"); - c->Message(Chat::White, "#dz expedition reload - reload expedition zone cache from database"); - c->Message(Chat::White, "#dz expedition destroy - destroy expedition globally (must be in cache)"); - c->Message(Chat::White, "#dz expedition unlock - unlock expedition"); - c->Message(Chat::White, "#dz list - list all dynamic zone instances from current zone cache"); - c->Message(Chat::White, "#dz listdb [all] - list dynamic zone instances from database -- 'all' includes expired"); - c->Message(Chat::White, "#dz lockouts remove - delete all of character's expedition lockouts"); - c->Message(Chat::White, "#dz lockouts remove \"\" - delete lockouts by expedition"); - c->Message(Chat::White, "#dz lockouts remove \"\" \"\" - delete lockout by expedition event"); - c->Message(Chat::White, "#dz makeleader - set new expedition leader"); - } -} - -void command_dzkickplayers(Client* c, const Seperator* sep) -{ - if (c) - { - auto expedition = c->GetExpedition(); - if (expedition) - { - expedition->DzKickPlayers(c); - } - } -} - -void command_editmassrespawn(Client* c, const Seperator* sep) -{ - if (strcasecmp(sep->arg[1], "usage") == 0) { - c->Message(Chat::White, "#editmassrespawn [exact_match: =]npc_type_name new_respawn_seconds (apply)"); - return; - } - - std::string search_npc_type; - if (sep->arg[1]) { - search_npc_type = sep->arg[1]; - } - - int change_respawn_seconds = 0; - if (sep->arg[2] && sep->IsNumber(2)) { - change_respawn_seconds = atoi(sep->arg[2]); - } - - bool change_apply = false; - if (sep->arg[3] && strcasecmp(sep->arg[3], "apply") == 0) { - change_apply = true; - } - - std::string search_encapsulator = "%"; - if (search_npc_type[0] == '=') { - - search_npc_type = search_npc_type.substr(1); - search_encapsulator = ""; - } - - std::string query = fmt::format( - SQL( - SELECT npc_types.id, spawn2.spawngroupID, spawn2.id, npc_types.name, spawn2.respawntime - FROM spawn2 - INNER JOIN spawnentry ON spawn2.spawngroupID = spawnentry.spawngroupID - INNER JOIN npc_types ON spawnentry.npcID = npc_types.id - WHERE spawn2.zone LIKE '{}' - AND spawn2.version = '{}' - AND npc_types.name LIKE '{}{}{}' - ORDER BY npc_types.id, spawn2.spawngroupID, spawn2.id - ), - zone->GetShortName(), - zone->GetInstanceVersion(), - search_encapsulator, - search_npc_type, - search_encapsulator - ); - - std::string status = "(Searching)"; - if (change_apply) { - status = "(Applying)"; - } - - int results_count = 0; - - auto results = content_db.QueryDatabase(query); - if (results.Success() && results.RowCount()) { - - results_count = results.RowCount(); - - for (auto row : results) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC (npcid:{}) (sgid:{}) (s2id:{}) [{}] Respawn: Current [{}] New [{}] {}", - row[0], - row[1], - row[2], - row[3], - row[4], - change_respawn_seconds, - status - ).c_str() - ); - } - - c->Message(Chat::Yellow, "Found (%i) NPC's that match this search...", results_count); - - if (change_respawn_seconds > 0) { - - if (change_apply) { - - results = content_db.QueryDatabase( - fmt::format( - SQL( - UPDATE spawn2 - SET respawntime = '{}' - WHERE id IN ( - SELECT spawn2.id - FROM spawn2 - INNER JOIN spawnentry ON spawn2.spawngroupID = spawnentry.spawngroupID - INNER JOIN npc_types ON spawnentry.npcID = npc_types.id - WHERE spawn2.zone LIKE '{}' - AND spawn2.version = '{}' - AND npc_types.name LIKE '{}{}{}' - ) - ), - change_respawn_seconds, - zone->GetShortName(), - zone->GetInstanceVersion(), - search_encapsulator, - search_npc_type, - search_encapsulator - ) - ); - - if (results.Success()) { - - c->Message(Chat::Yellow, "Changes applied to (%i) NPC 'Spawn2' entries", results_count); - zone->Repop(); - } - else { - - c->Message(Chat::Yellow, "Found (0) NPC's that match this search..."); - } - } - else { - - std::string saylink = fmt::format( - "#editmassrespawn {}{} {} apply", - (search_encapsulator.empty() ? "=" : ""), - search_npc_type, - change_respawn_seconds - ); - - c->Message( - Chat::Yellow, "To apply these changes, click <%s> or type [%s]", - EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Apply").c_str(), - saylink.c_str() - ); - } - } - } - else { - - c->Message(Chat::Yellow, "Found (0) NPC's that match this search..."); - } -} - -void command_randomfeatures(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!target) - c->Message(Chat::White,"Error: This command requires a target"); - else - { - if (target->RandomizeFeatures()) - c->Message(Chat::White,"Features Randomized"); - else - c->Message(Chat::White,"This command requires a Playable Race as the target"); - } -} - -void command_face(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!sep->IsNumber(1)) - c->Message(Chat::White,"Usage: #face [number of face]"); - 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 = atoi(sep->arg[1]); - 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); - - c->Message(Chat::White,"Face = %i", atoi(sep->arg[1])); - } -} - void command_findaliases(Client *c, const Seperator *sep) { if (!sep->arg[1][0]) { @@ -8448,6845 +959,6 @@ void command_findaliases(Client *c, const Seperator *sep) c->Message(Chat::White, "%d command alias%s listed.", commandaliasesshown, commandaliasesshown != 1 ? "es" : ""); } -void command_details(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!sep->IsNumber(1)) - c->Message(Chat::White,"Usage: #details [number of drakkin detail]"); - 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 = atoi(sep->arg[1]); - - target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, - EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, - DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - - c->Message(Chat::White,"Details = %i", atoi(sep->arg[1])); - } -} - -void command_heritage(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!sep->IsNumber(1)) - c->Message(Chat::White,"Usage: #heritage [number of Drakkin heritage]"); - 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 = atoi(sep->arg[1]); - 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); - - c->Message(Chat::White,"Heritage = %i", atoi(sep->arg[1])); - } -} - -void command_tattoo(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!sep->IsNumber(1)) - c->Message(Chat::White,"Usage: #tattoo [number of Drakkin tattoo]"); - 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 = atoi(sep->arg[1]); - uint32 DrakkinDetails = target->GetDrakkinDetails(); - - target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, - EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, - DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - - c->Message(Chat::White,"Tattoo = %i", atoi(sep->arg[1])); - } -} - -void command_helm(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!sep->IsNumber(1)) - c->Message(Chat::White,"Usage: #helm [number of helm texture]"); - 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 = atoi(sep->arg[1]); - 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); - - c->Message(Chat::White,"Helm = %i", atoi(sep->arg[1])); - } -} - -void command_hair(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!sep->IsNumber(1)) - c->Message(Chat::White,"Usage: #hair [number of hair style]"); - 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 = atoi(sep->arg[1]); - 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); - - c->Message(Chat::White,"Hair = %i", atoi(sep->arg[1])); - } -} - -void command_haircolor(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!sep->IsNumber(1)) - c->Message(Chat::White,"Usage: #haircolor [number of hair color]"); - 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 = atoi(sep->arg[1]); - 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); - - c->Message(Chat::White,"Hair Color = %i", atoi(sep->arg[1])); - } -} - -void command_beard(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!sep->IsNumber(1)) - c->Message(Chat::White,"Usage: #beard [number of beard style]"); - 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 = atoi(sep->arg[1]); - 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); - - c->Message(Chat::White,"Beard = %i", atoi(sep->arg[1])); - } -} - -void command_beardcolor(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!sep->IsNumber(1)) - c->Message(Chat::White,"Usage: #beardcolor [number of beard color]"); - 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 = atoi(sep->arg[1]); - 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); - - c->Message(Chat::White,"Beard Color = %i", atoi(sep->arg[1])); - } -} - -void command_scribespells(Client *c, const Seperator *sep) -{ - Client *target = c; - if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { - target = c->GetTarget()->CastToClient(); - } - - if(sep->argnum < 1 || !sep->IsNumber(1)) { - c->Message(Chat::White, "FORMAT: #scribespells "); - return; - } - - uint8 rule_max_level = (uint8) RuleI(Character, MaxLevel); - uint8 max_level = (uint8) std::stoi(sep->arg[1]); - uint8 min_level = ( - sep->IsNumber(2) ? - (uint8) - std::stoi(sep->arg[2]) : - 1 - ); // Default to Level 1 if there isn't a 2nd argument - - if (!c->GetGM()) { // Default to Character:MaxLevel if we're not a GM and Level is higher than the max level - if (max_level > rule_max_level) { - max_level = rule_max_level; - } - - if (min_level > rule_max_level) { - min_level = rule_max_level; - } - } - - if(max_level < 1 || min_level < 1) { - c->Message(Chat::White, "ERROR: Level must be greater than or equal to 1."); - return; - } - - if (min_level > max_level) { - c->Message(Chat::White, "ERROR: Minimum Level must be less than or equal to Maximum Level."); - return; - } - - uint16 scribed_spells = target->ScribeSpells(min_level, max_level); - if (target != c) { - std::string spell_message = ( - scribed_spells > 0 ? - ( - scribed_spells == 1 ? - "A new spell" : - fmt::format("{} New spells", scribed_spells) - ) : - "No new spells" - ); - c->Message( - Chat::White, - fmt::format( - "{} scribed for {}.", - spell_message, - target->GetCleanName() - ).c_str() - ); - } -} - -void command_scribespell(Client *c, const Seperator *sep) { - uint16 spell_id = 0; - uint16 book_slot = -1; - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) - t=c->GetTarget()->CastToClient(); - - if(!sep->arg[1][0]) { - c->Message(Chat::White, "FORMAT: #scribespell "); - return; - } - - spell_id = atoi(sep->arg[1]); - - if(IsValidSpell(spell_id)) { - t->Message(Chat::White, "Scribing spell: %s (%i) to spellbook.", spells[spell_id].name, spell_id); - - if(t != c) - c->Message(Chat::White, "Scribing spell: %s (%i) for %s.", spells[spell_id].name, spell_id, t->GetName()); - - LogInfo("Scribe spell: [{}] ([{}]) request for [{}] from [{}]", spells[spell_id].name, spell_id, t->GetName(), c->GetName()); - - if (spells[spell_id].classes[WARRIOR] != 0 && spells[spell_id].skill != 52 && spells[spell_id].classes[t->GetPP().class_ - 1] > 0 && !IsDiscipline(spell_id)) { - book_slot = t->GetNextAvailableSpellBookSlot(); - - if(book_slot >= 0 && t->FindSpellBookSlotBySpellID(spell_id) < 0) - t->ScribeSpell(spell_id, book_slot); - else { - t->Message(Chat::Red, "Unable to scribe spell: %s (%i) to your spellbook.", spells[spell_id].name, spell_id); - - if(t != c) - c->Message(Chat::Red, "Unable to scribe spell: %s (%i) for %s.", spells[spell_id].name, spell_id, t->GetName()); - } - } - else - c->Message(Chat::Red, "Your target can not scribe this spell."); - } - else - c->Message(Chat::Red, "Spell ID: %i is an unknown spell and cannot be scribed.", spell_id); -} - -void command_unscribespell(Client *c, const Seperator *sep) { - uint16 spell_id = 0; - uint16 book_slot = -1; - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) - t=c->GetTarget()->CastToClient(); - - if(!sep->arg[1][0]) { - c->Message(Chat::White, "FORMAT: #unscribespell "); - return; - } - - spell_id = atoi(sep->arg[1]); - - if(IsValidSpell(spell_id)) { - book_slot = t->FindSpellBookSlotBySpellID(spell_id); - - if(book_slot >= 0) { - t->UnscribeSpell(book_slot); - - t->Message(Chat::White, "Unscribing spell: %s (%i) from spellbook.", spells[spell_id].name, spell_id); - - if(t != c) - c->Message(Chat::White, "Unscribing spell: %s (%i) for %s.", spells[spell_id].name, spell_id, t->GetName()); - - LogInfo("Unscribe spell: [{}] ([{}]) request for [{}] from [{}]", spells[spell_id].name, spell_id, t->GetName(), c->GetName()); - } - else { - t->Message(Chat::Red, "Unable to unscribe spell: %s (%i) from your spellbook. This spell is not scribed.", spells[spell_id].name, spell_id); - - if(t != c) - c->Message(Chat::Red, "Unable to unscribe spell: %s (%i) for %s due to spell not scribed.", spells[spell_id].name, spell_id, t->GetName()); - } - } -} - -void command_unscribespells(Client *c, const Seperator *sep) -{ - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) - t=c->GetTarget()->CastToClient(); - - t->UnscribeSpellAll(); -} - -void command_untraindisc(Client *c, const Seperator *sep) { - Client *t = c; - if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) - t = c->GetTarget()->CastToClient(); - - for (int i = 0; i < MAX_PP_DISCIPLINES; i++) { - if (t->GetPP().disciplines.values[i] == atoi(sep->arg[1])) { - t->UntrainDisc(i, 1); - return; - } - } -} - -void command_untraindiscs(Client *c, const Seperator *sep) { - Client *t = c; - if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) - t = c->GetTarget()->CastToClient(); - - t->UntrainDiscAll(); - t->Message(Chat::Yellow, "All disciplines removed."); -} - -void command_wpinfo(Client *c, const Seperator *sep) -{ - Mob *t=c->GetTarget(); - - if (t == nullptr || !t->IsNPC()) { - c->Message(Chat::White,"You must target an NPC to use this."); - return; - } - - NPC *n = t->CastToNPC(); - n->DisplayWaypointInfo(c); -} - -void command_wpadd(Client *c, const Seperator *sep) -{ - int type1 = 0, type2 = 0, pause = 0; // Defaults for a new grid - Mob *target = c->GetTarget(); - if (target && target->IsNPC()) { - Spawn2 *s2info = target->CastToNPC()->respawn2; - if (s2info == nullptr) { - c->Message( - Chat::White, - "#wpadd Failed, you must target a valid spawn." - ); - return; - } - - if (sep->arg[1][0]) { - if (atoi(sep->arg[1]) >= 0) { - pause = atoi(sep->arg[1]); - } else { - c->Message(Chat::White, "Usage: #wpadd [pause] [-h]"); - return; - } - } - auto position = c->GetPosition(); - if (strcmp("-h", sep->arg[2]) != 0) { - position.w = -1; - } - - auto zone_id = zone->GetZoneID(); - uint32 tmp_grid = content_db.AddWPForSpawn(c, s2info->GetID(), position, pause, type1, type2, zone_id); - if (tmp_grid) { - target->CastToNPC()->SetGrid(tmp_grid); - } - - auto grid_id = target->CastToNPC()->GetGrid(); - target->CastToNPC()->AssignWaypoints(grid_id); - c->Message( - Chat::White, - fmt::format( - "Waypoint added to grid {} in zone ID {}. Use #wpinfo to see waypoints for this NPC (may need to #repop first).", - grid_id, - zone_id - ).c_str() - ); - } else { - c->Message(Chat::White, "You must target an NPC to use this."); - } -} - -void command_interrupt(Client *c, const Seperator *sep) -{ - uint16 ci_message=0x01b7, ci_color=0x0121; - - if(sep->arg[1][0]) - ci_message=atoi(sep->arg[1]); - if(sep->arg[2][0]) - ci_color=atoi(sep->arg[2]); - - c->InterruptSpell(ci_message, ci_color); -} - -void command_summonitem(Client *c, const Seperator *sep) -{ - uint32 item_id = 0; - int16 charges = -1; - uint32 augment_one = 0; - uint32 augment_two = 0; - uint32 augment_three = 0; - uint32 augment_four = 0; - uint32 augment_five = 0; - uint32 augment_six = 0; - int arguments = sep->argnum; - std::string cmd_msg = sep->msg; - size_t link_open = cmd_msg.find('\x12'); - size_t link_close = cmd_msg.find_last_of('\x12'); - if (link_open != link_close && (cmd_msg.length() - link_open) > EQ::constants::SAY_LINK_BODY_SIZE) { - EQ::SayLinkBody_Struct link_body; - EQ::saylink::DegenerateLinkBody(link_body, cmd_msg.substr(link_open + 1, EQ::constants::SAY_LINK_BODY_SIZE)); - item_id = link_body.item_id; - augment_one = link_body.augment_1; - augment_two = link_body.augment_2; - augment_three = link_body.augment_3; - augment_four = link_body.augment_4; - augment_five = link_body.augment_5; - augment_six = link_body.augment_6; - } else if (!sep->IsNumber(1)) { - c->Message(Chat::White, "Usage: #summonitem [item id | link] [charges] [augment_one_id] [augment_two_id] [augment_three_id] [augment_four_id] [augment_five_id] [augment_six_id] (Charges are optional.)"); - return; - } else { - item_id = atoi(sep->arg[1]); - } - - if (!item_id) { - c->Message(Chat::White, "Enter a valid item ID."); - return; - } - - uint8 item_status = 0; - uint8 current_status = c->Admin(); - const EQ::ItemData* item = database.GetItem(item_id); - if (item) { - item_status = item->MinStatus; - } - - if (item_status > current_status) { - c->Message( - Chat::White, - fmt::format( - "Insufficient status to summon this item, current status is {}, required status is {}.", - current_status, - item_status - ).c_str() - ); - } - - if (arguments >= 2 && sep->IsNumber(2)) { - charges = atoi(sep->arg[2]); - } - - if (arguments >= 3 && sep->IsNumber(3)) { - augment_one = atoi(sep->arg[3]); - } - - if (arguments >= 4 && sep->IsNumber(4)) { - augment_two = atoi(sep->arg[4]); - } - - if (arguments >= 5 && sep->IsNumber(5)) { - augment_three = atoi(sep->arg[5]); - } - - if (arguments >= 6 && sep->IsNumber(6)) { - augment_four = atoi(sep->arg[6]); - } - - if (arguments >= 7 && sep->IsNumber(7)) { - augment_five = atoi(sep->arg[7]); - } - - if (arguments == 8 && sep->IsNumber(8)) { - augment_six = atoi(sep->arg[8]); - } - - c->SummonItem(item_id, charges, augment_one, augment_two, augment_three, augment_four, augment_five, augment_six); -} - -void command_giveitem(Client *c, const Seperator *sep) -{ - uint32 item_id = 0; - int16 charges = -1; - uint32 augment_one = 0; - uint32 augment_two = 0; - uint32 augment_three = 0; - uint32 augment_four = 0; - uint32 augment_five = 0; - uint32 augment_six = 0; - int arguments = sep->argnum; - std::string cmd_msg = sep->msg; - size_t link_open = cmd_msg.find('\x12'); - size_t link_close = cmd_msg.find_last_of('\x12'); - if (c->GetTarget()) { - if (!c->GetTarget()->IsClient()) { - c->Message(Chat::Red, "You can only give items to players with this command."); - return; - } - - if (link_open != link_close && (cmd_msg.length() - link_open) > EQ::constants::SAY_LINK_BODY_SIZE) { - EQ::SayLinkBody_Struct link_body; - EQ::saylink::DegenerateLinkBody(link_body, cmd_msg.substr(link_open + 1, EQ::constants::SAY_LINK_BODY_SIZE)); - item_id = link_body.item_id; - augment_one = link_body.augment_1; - augment_two = link_body.augment_2; - augment_three = link_body.augment_3; - augment_four = link_body.augment_4; - augment_five = link_body.augment_5; - augment_six = link_body.augment_6; - } else if (sep->IsNumber(1)) { - item_id = atoi(sep->arg[1]); - } else if (!sep->IsNumber(1)) { - c->Message(Chat::Red, "Usage: #giveitem [item id | link] [charges] [augment_one_id] [augment_two_id] [augment_three_id] [augment_four_id] [augment_five_id] [augment_six_id] (Charges are optional.)"); - return; - } - - Client *client_target = c->GetTarget()->CastToClient(); - uint8 item_status = 0; - uint8 current_status = c->Admin(); - const EQ::ItemData* item = database.GetItem(item_id); - if (item) { - item_status = item->MinStatus; - } - - if (item_status > current_status) { - c->Message( - Chat::White, - fmt::format( - "Insufficient status to summon this item, current status is {}, required status is {}.", - current_status, - item_status - ).c_str() - ); - return; - } - - if (arguments >= 2 && sep->IsNumber(2)) { - charges = atoi(sep->arg[2]); - } - - if (arguments >= 3 && sep->IsNumber(3)) { - augment_one = atoi(sep->arg[3]); - } - - if (arguments >= 4 && sep->IsNumber(4)) { - augment_two = atoi(sep->arg[4]); - } - - if (arguments >= 5 && sep->IsNumber(5)) { - augment_three = atoi(sep->arg[5]); - } - - if (arguments >= 6 && sep->IsNumber(6)) { - augment_four = atoi(sep->arg[6]); - } - - if (arguments >= 7 && sep->IsNumber(7)) { - augment_five = atoi(sep->arg[7]); - } - - if (arguments == 8 && sep->IsNumber(8)) { - augment_six = atoi(sep->arg[8]); - } - - client_target->SummonItem(item_id, charges, augment_one, augment_two, augment_three, augment_four, augment_five, augment_six); - } else { - c->Message(Chat::Red, "You must target a client to give the item to."); - return; - } -} - -void command_givemoney(Client *c, const Seperator *sep) -{ - if (!sep->IsNumber(1)) { //as long as the first one is a number, we'll just let atoi convert the rest to 0 or a number - c->Message(Chat::Red, "Usage: #Usage: #givemoney [pp] [gp] [sp] [cp]"); - } - else if(c->GetTarget() == nullptr) { - c->Message(Chat::Red, "You must target a player to give money to."); - } - else if(!c->GetTarget()->IsClient()) { - c->Message(Chat::Red, "You can only give money to players with this command."); - } - else { - //TODO: update this to the client, otherwise the client doesn't show any weight change until you zone, move an item, etc - c->GetTarget()->CastToClient()->AddMoneyToPP(atoi(sep->arg[4]), atoi(sep->arg[3]), atoi(sep->arg[2]), atoi(sep->arg[1]), true); - c->Message(Chat::White, "Added %i Platinum, %i Gold, %i Silver, and %i Copper to %s's inventory.", atoi(sep->arg[1]), atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), c->GetTarget()->GetName()); - } -} - -void command_itemsearch(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == 0) - c->Message(Chat::White, "Usage: #itemsearch [search string]"); - else - { - const char *search_criteria=sep->argplus[1]; - - const EQ::ItemData* item = nullptr; - EQ::SayLinkEngine linker; - linker.SetLinkType(EQ::saylink::SayLinkItemData); - - if (Seperator::IsNumber(search_criteria)) { - item = database.GetItem(atoi(search_criteria)); - if (item) { - linker.SetItemData(item); - std::string item_id = std::to_string(item->ID); - std::string saylink_commands = - "[" + - EQ::SayLinkEngine::GenerateQuestSaylink( - "#si " + item_id, - false, - "X" - ) + - "] "; - - if (item->Stackable && item->StackSize > 1) { - std::string stack_size = std::to_string(item->StackSize); - saylink_commands += - "[" + - EQ::SayLinkEngine::GenerateQuestSaylink( - "#si " + item_id + " " + stack_size, - false, - stack_size - ) + - "]"; - } - - c->Message( - Chat::White, - fmt::format( - " Summon {} [{}] [{}]", - saylink_commands, - linker.GenerateLink(), - item->ID - ).c_str() - ); - } - else { - c->Message( - Chat::White, - fmt::format( - "Item {} not found", - search_criteria - ).c_str() - ); - } - - return; - } - - int count = 0; - char sName[64]; - char sCriteria[255]; - strn0cpy(sCriteria, search_criteria, sizeof(sCriteria)); - strupr(sCriteria); - char* pdest; - uint32 it = 0; - while ((item = database.IterateItems(&it))) { - strn0cpy(sName, item->Name, sizeof(sName)); - strupr(sName); - pdest = strstr(sName, sCriteria); - if (pdest != nullptr) { - linker.SetItemData(item); - std::string item_id = std::to_string(item->ID); - std::string saylink_commands = - "[" + - EQ::SayLinkEngine::GenerateQuestSaylink( - "#si " + item_id, - false, - "X" - ) + - "] "; - if (item->Stackable && item->StackSize > 1) { - std::string stack_size = std::to_string(item->StackSize); - saylink_commands += - "[" + - EQ::SayLinkEngine::GenerateQuestSaylink( - "#si " + item_id + " " + stack_size, - false, - stack_size - ) + - "]"; - } - - c->Message( - Chat::White, - fmt::format( - " Summon {} [{}] [{}]", - saylink_commands, - linker.GenerateLink(), - item->ID - ).c_str() - ); - - ++count; - } - - if (count == 50) - break; - } - - if (count == 50) - c->Message(Chat::White, "50 items shown...too many results."); - else - c->Message(Chat::White, "%i items found", count); - - } -} - -void command_setaaxp(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (arguments <= 1 || !sep->IsNumber(2)) { - c->Message(Chat::White, "Usage: #setaaxp [AA|Group|Raid] [AA Experience]"); - return; - } - - Client *target = c; - if (c->GetTarget() && c->GetTarget()->IsClient()) { - target = c->GetTarget()->CastToClient(); - } - - std::string aa_type = str_tolower(sep->arg[1]); - std::string group_raid_string; - uint32 aa_experience = static_cast(std::min(std::stoull(sep->arg[2]), (unsigned long long) 2000000000)); - bool is_aa = aa_type.find("aa") != std::string::npos; - bool is_group = aa_type.find("group") != std::string::npos; - bool is_raid = aa_type.find("raid") != std::string::npos; - if (!is_aa && !is_group && !is_raid) { - c->Message(Chat::White, "Usage: #setaaxp [AA|Group|Raid] [AA Experience]"); - return; - } - - if (is_aa) { - target->SetEXP( - target->GetEXP(), - aa_experience, - false - ); - } else if (is_group) { - group_raid_string = "Group "; - target->SetLeadershipEXP( - aa_experience, - target->GetRaidEXP() - ); - } else if (is_raid) { - group_raid_string = "Raid "; - target->SetLeadershipEXP( - target->GetGroupEXP(), - aa_experience - ); - } - - std::string aa_exp_message = fmt::format( - "{} now {} {} {}AA Experience.", - c == target ? "You" : target->GetCleanName(), - c == target ? "have" : "has", - aa_experience, - group_raid_string - ); - c->Message( - Chat::White, - aa_exp_message.c_str() - ); -} - -void command_setaapts(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (arguments <= 1 || !sep->IsNumber(2)) { - c->Message(Chat::White, "Usage: #setaapts [AA|Group|Raid] [AA Amount]"); - return; - } - - Client *target = c; - if (c->GetTarget() && c->GetTarget()->IsClient()) { - target = c->GetTarget()->CastToClient(); - } - - std::string aa_type = str_tolower(sep->arg[1]); - std::string group_raid_string; - uint32 aa_points = static_cast(std::min(std::stoull(sep->arg[2]), (unsigned long long) 2000000000)); - bool is_aa = aa_type.find("aa") != std::string::npos; - bool is_group = aa_type.find("group") != std::string::npos; - bool is_raid = aa_type.find("raid") != std::string::npos; - if (!is_aa && !is_group && !is_raid) { - c->Message(Chat::White, "Usage: #setaapts [AA|Group|Raid] [AA Amount]"); - return; - } - - if (is_aa) { - target->GetPP().aapoints = aa_points; - target->GetPP().expAA = 0; - target->SendAlternateAdvancementStats(); - } else if (is_group || is_raid) { - if (is_group) { - group_raid_string = "Group "; - target->GetPP().group_leadership_points = aa_points; - target->GetPP().group_leadership_exp = 0; - } else if (is_raid) { - group_raid_string = "Raid "; - target->GetPP().raid_leadership_points = aa_points; - target->GetPP().raid_leadership_exp = 0; - } - target->SendLeadershipEXPUpdate(); - } - - std::string aa_message = fmt::format( - "{} now {} {} {}AA Point{}.", - c == target ? "You" : target->GetCleanName(), - c == target ? "have" : "has", - aa_points, - group_raid_string, - aa_points != 1 ? "s" : "" - - ); - c->Message( - Chat::White, - aa_message.c_str() - ); -} - -void command_setcrystals(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (arguments <= 1 || !sep->IsNumber(2)) { - c->Message(Chat::White, "Usage: #setcrystals [Ebon|Radiant] [Crystal Amount]"); - return; - } - - Client *target = c; - if(c->GetTarget() && c->GetTarget()->IsClient()) { - target = c->GetTarget()->CastToClient(); - } - - std::string crystal_type = str_tolower(sep->arg[1]); - uint32 crystal_amount = static_cast(std::min(std::stoull(sep->arg[2]), (unsigned long long) 2000000000)); - bool is_ebon = crystal_type.find("ebon") != std::string::npos; - bool is_radiant = crystal_type.find("radiant") != std::string::npos; - if (!is_ebon && !is_radiant) { - c->Message(Chat::White, "Usage: #setcrystals [Ebon|Radiant] [Crystal Amount]"); - return; - } - - uint32 crystal_item_id = ( - is_ebon ? - RuleI(Zone, EbonCrystalItemID) : - RuleI(Zone, RadiantCrystalItemID) - ); - - auto crystal_link = database.CreateItemLink(crystal_item_id); - if (is_radiant) { - target->SetRadiantCrystals(crystal_amount); - } else { - target->SetEbonCrystals(crystal_amount); - } - - c->Message( - Chat::White, - fmt::format( - "{} now {} {} {}.", - c == target ? "You" : target->GetCleanName(), - c == target ? "have" : "has", - crystal_amount, - crystal_link - ).c_str() - ); -} - -void command_stun(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (!arguments || !sep->IsNumber(1)) { - c->Message(Chat::White, "Usage: #stun [Duration]"); - return; - } - - Mob* target = c; - int duration = static_cast(std::min(std::stoll(sep->arg[1]), (long long) 2000000000)); - - if (duration < 0) { - duration = 0; - } - - if (c->GetTarget()) { - target = c->GetTarget(); - if (target->IsClient()) { - target->CastToClient()->Stun(duration); - } else if (target->IsNPC()) { - target->CastToNPC()->Stun(duration); - } - } else { - c->Stun(duration); - } - - std::string stun_message = ( - duration ? - fmt::format( - "You stunned {} for {}.", - ( - c == target ? - "yourself" : - fmt::format( - "{} ({})", - target->GetCleanName(), - target->GetID() - ) - ), - ConvertSecondsToTime(duration) - ) : - fmt::format( - "You unstunned {}.", - ( - c == target ? - "yourself" : - fmt::format( - "{} ({})", - target->GetCleanName(), - target->GetID() - ) - ) - ) - ); - c->Message( - Chat::White, - stun_message.c_str() - ); -} - - -void command_ban(Client *c, const Seperator *sep) -{ -if(sep->arg[1][0] == 0 || sep->arg[2][0] == 0) { - c->Message(Chat::White, "Usage: #ban "); - return; - } - - auto account_id = database.GetAccountIDByChar(sep->arg[1]); - - std::string message; - int i = 2; - while(1) { - if(sep->arg[i][0] == 0) { - break; - } - - if(message.length() > 0) { - message.push_back(' '); - } - - message += sep->arg[i]; - ++i; - } - - if(message.length() == 0) { - c->Message(Chat::White, "Usage: #ban "); - return; - } - - if(account_id == 0) { - c->Message(Chat::Red, "Character does not exist."); - return; - } - - std::string query = StringFormat("UPDATE account SET status = -2, ban_reason = '%s' " - "WHERE id = %i", EscapeString(message).c_str(), account_id); - auto results = database.QueryDatabase(query); - - c->Message(Chat::Red, "Account number %i with the character %s has been banned with message: \"%s\"", account_id, sep->arg[1], message.c_str()); - - ServerPacket flagUpdatePack(ServerOP_FlagUpdate, 6); - *((uint32*)&flagUpdatePack.pBuffer[0]) = account_id; - *((int16*)&flagUpdatePack.pBuffer[4]) = -2; - worldserver.SendPacket(&flagUpdatePack); - - Client *client = nullptr; - client = entity_list.GetClientByName(sep->arg[1]); - if(client) { - client->WorldKick(); - return; - } - - ServerPacket kickPlayerPack(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); - ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*)kickPlayerPack.pBuffer; - strcpy(skp->adminname, c->GetName()); - strcpy(skp->name, sep->arg[1]); - skp->adminrank = c->Admin(); - worldserver.SendPacket(&kickPlayerPack); -} - -void command_suspend(Client *c, const Seperator *sep) -{ - if((sep->arg[1][0] == 0) || (sep->arg[2][0] == 0)) { - c->Message(Chat::White, "Usage: #suspend (Specify 0 days to lift the suspension immediately) "); - return; - } - - int duration = atoi(sep->arg[2]); - - if(duration < 0) - duration = 0; - - std::string message; - - if(duration > 0) { - int i = 3; - while(1) { - if(sep->arg[i][0] == 0) { - break; - } - - if(message.length() > 0) { - message.push_back(' '); - } - - message += sep->arg[i]; - ++i; - } - - if(message.length() == 0) { - c->Message(Chat::White, "Usage: #suspend (Specify 0 days to lift the suspension immediately) "); - return; - } - } - - auto escName = new char[strlen(sep->arg[1]) * 2 + 1]; - database.DoEscapeString(escName, sep->arg[1], strlen(sep->arg[1])); - int accountID = database.GetAccountIDByChar(escName); - safe_delete_array(escName); - - if (accountID <= 0) { - c->Message(Chat::Red,"Character does not exist."); - return; - } - - std::string query = StringFormat("UPDATE `account` SET `suspendeduntil` = DATE_ADD(NOW(), INTERVAL %i DAY), " - "suspend_reason = '%s' WHERE `id` = %i", - duration, EscapeString(message).c_str(), accountID); - auto results = database.QueryDatabase(query); - - if(duration) - c->Message(Chat::Red,"Account number %i with the character %s has been temporarily suspended for %i day(s).", accountID, sep->arg[1], duration); - else - c->Message(Chat::Red,"Account number %i with the character %s is no longer suspended.", accountID, sep->arg[1]); - - Client *bannedClient = entity_list.GetClientByName(sep->arg[1]); - - if(bannedClient) { - bannedClient->WorldKick(); - return; - } - - auto pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); - ServerKickPlayer_Struct *sks = (ServerKickPlayer_Struct *)pack->pBuffer; - - strn0cpy(sks->adminname, c->GetName(), sizeof(sks->adminname)); - strn0cpy(sks->name, sep->arg[1], sizeof(sks->name)); - sks->adminrank = c->Admin(); - - worldserver.SendPacket(pack); - - safe_delete(pack); -} - -void command_ipban(Client *c, const Seperator *sep) -{ - if(sep->arg[1] == 0) - { - c->Message(Chat::White, "Usage: #ipban [xxx.xxx.xxx.xxx]"); - } else { - if(database.AddBannedIP(sep->arg[1], c->GetName())) { - c->Message(Chat::White, "%s has been successfully added to the banned_ips table by %s", sep->arg[1], c->GetName()); - } else { - c->Message(Chat::White, "IPBan Failed (IP address is possibly already in the table?)"); - } - } -} - -void command_revoke(Client *c, const Seperator *sep) -{ - if(sep->arg[1][0] == 0 || sep->arg[2][0] == 0) { - c->Message(Chat::White, "Usage: #revoke [charname] [1/0]"); - return; - } - - uint32 characterID = database.GetAccountIDByChar(sep->arg[1]); - if(characterID == 0) { - c->Message(Chat::Red,"Character does not exist."); - return; - } - - int flag = sep->arg[2][0] == '1' ? true : false; - std::string query = StringFormat("UPDATE account SET revoked = %d WHERE id = %i", flag, characterID); - auto results = database.QueryDatabase(query); - - c->Message(Chat::Red,"%s account number %i with the character %s.", flag? "Revoking": "Unrevoking", characterID, sep->arg[1]); - - Client* revokee = entity_list.GetClientByAccID(characterID); - if(revokee) { - c->Message(Chat::White, "Found %s in this zone.", revokee->GetName()); - revokee->SetRevoked(flag); - return; - } - - c->Message(Chat::Red, "#revoke: Couldn't find %s in this zone, passing request to worldserver.", sep->arg[1]); - - auto outapp = new ServerPacket(ServerOP_Revoke, sizeof(RevokeStruct)); - RevokeStruct *revoke = (RevokeStruct *)outapp->pBuffer; - strn0cpy(revoke->adminname, c->GetName(), 64); - strn0cpy(revoke->name, sep->arg[1], 64); - revoke->toggle = flag; - worldserver.SendPacket(outapp); - safe_delete(outapp); -} - -void command_roambox(Client *c, const Seperator *sep) -{ - std::string arg1 = sep->arg[1]; - - Mob *target = c->GetTarget(); - if (!target || !target->IsNPC()) { - c->Message(Chat::Red, "You need a valid NPC target for this command"); - return; - } - - NPC *npc = dynamic_cast(target); - int spawn_group_id = npc->GetSpawnGroupId(); - if (spawn_group_id <= 0) { - c->Message(Chat::Red, "NPC needs a valid SpawnGroup!"); - return; - } - - if (arg1 == "set") { - int box_size = (sep->arg[2] ? atoi(sep->arg[2]) : 0); - int delay = (sep->arg[3] ? atoi(sep->arg[3]) : 15000); - if (box_size > 0) { - std::string query = fmt::format( - SQL( - UPDATE spawngroup SET - dist = {}, - min_x = {}, - max_x = {}, - min_y = {}, - max_y = {}, - delay = {} - WHERE id = {} - ), - (box_size / 2), - npc->GetX() - (box_size / 2), - npc->GetX() + (box_size / 2), - npc->GetY() - (box_size / 2), - npc->GetY() + (box_size / 2), - delay, - spawn_group_id - ); - - database.QueryDatabase(query); - - c->Message( - Chat::Yellow, - "NPC (%s) Roam Box set to box size of [%i] SpawnGroupId [%i] delay [%i]", - npc->GetCleanName(), - box_size, - spawn_group_id, - delay - ); - - return; - } - - c->Message(Chat::Red, "Box size must be set!"); - } - - if (arg1 == "remove") { - std::string query = fmt::format( - SQL( - UPDATE spawngroup SET - dist = 0, - min_x = 0, - max_x = 0, - min_y = 0, - max_y = 0, - delay = 0 - WHERE id = {} - ), - spawn_group_id - ); - - database.QueryDatabase(query); - - c->Message( - Chat::Yellow, - "NPC (%s) Roam Box has been removed from SpawnGroupID [%i]", - npc->GetCleanName(), - spawn_group_id - ); - - return; - } - - c->Message(Chat::Yellow, "> Command Usage"); - c->Message(Chat::Yellow, "#roambox set box_size [delay = 0]"); - c->Message(Chat::Yellow, "#roambox remove"); -} - -void command_oocmute(Client *c, const Seperator *sep) -{ - if(sep->arg[1][0] == 0 || !(sep->arg[1][0] == '1' || sep->arg[1][0] == '0')) - c->Message(Chat::White, "Usage: #oocmute [1/0]"); - else { - auto outapp = new ServerPacket(ServerOP_OOCMute, 1); - *(outapp->pBuffer) = atoi(sep->arg[1]); - worldserver.SendPacket(outapp); - safe_delete(outapp); - } -} - -void command_checklos(Client *c, const Seperator *sep) -{ - if (!c->GetTarget()) { - c->Message(Chat::White, "You must have a target to use this command."); - } - - bool has_los = c->CheckLosFN(c->GetTarget()); - c->Message( - Chat::White, - fmt::format( - "You {}have line of sight to {} ({}).", - has_los ? "" : "do not ", - c->GetTarget()->GetCleanName(), - c->GetTarget()->GetID() - ).c_str() - ); -} - -void command_set_adventure_points(Client *c, const Seperator *sep) -{ - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); - - if(!sep->arg[1][0]) - { - c->Message(Chat::White, "Usage: #setadventurepoints [theme] [points]"); - return; - } - - if(!sep->IsNumber(1) || !sep->IsNumber(2)) - { - c->Message(Chat::White, "Usage: #setadventurepoints [theme] [points]"); - return; - } - - c->Message(Chat::White, "Updating adventure points for %s", t->GetName()); - t->UpdateLDoNPoints(atoi(sep->arg[1]), atoi(sep->arg[2])); -} - -void command_npcsay(Client *c, const Seperator *sep) -{ - if(c->GetTarget() && c->GetTarget()->IsNPC() && sep->arg[1][0]) - { - c->GetTarget()->Say(sep->argplus[1]); - } - else - { - c->Message(Chat::White, "Usage: #npcsay message (requires NPC target"); - } -} - -void command_npcshout(Client *c, const Seperator *sep) -{ - if(c->GetTarget() && c->GetTarget()->IsNPC() && sep->arg[1][0]) - { - c->GetTarget()->Shout(sep->argplus[1]); - } - else - { - c->Message(Chat::White, "Usage: #npcshout message (requires NPC target"); - } -} - -void command_timers(Client *c, const Seperator *sep) { - if(!c->GetTarget() || !c->GetTarget()->IsClient()) { - c->Message(Chat::White,"Need a player target for timers."); - return; - } - Client *them = c->GetTarget()->CastToClient(); - - std::vector< std::pair > res; - them->GetPTimers().ToVector(res); - - c->Message(Chat::White,"Timers for target:"); - - int r; - int l = res.size(); - for(r = 0; r < l; r++) { - c->Message(Chat::White,"Timer %d: %d seconds remain.", res[r].first, res[r].second->GetRemainingTime()); - } -} - -void command_npcemote(Client *c, const Seperator *sep) -{ - if(c->GetTarget() && c->GetTarget()->IsNPC() && sep->arg[1][0]) - { - c->GetTarget()->Emote(sep->argplus[1]); - } - else - { - c->Message(Chat::White, "Usage: #npcemote message (requires NPC target"); - } -} - -void command_npceditmass(Client *c, const Seperator *sep) -{ - if (strcasecmp(sep->arg[1], "usage") == 0) { - c->Message(Chat::White, "#npceditmass search_column [exact_match: =]search_value change_column change_value (apply)"); - return; - } - - std::string query = SQL( - SELECT - COLUMN_NAME - FROM - INFORMATION_SCHEMA.COLUMNS - WHERE - table_name = 'npc_types' - AND - COLUMN_NAME != 'id' - ); - - std::string search_column, search_value, change_column, change_value; - if (sep->arg[1]) { - search_column = sep->arg[1]; - } - if (sep->arg[2]) { - search_value = sep->arg[2]; - } - if (sep->arg[3]) { - change_column = sep->arg[3]; - } - if (sep->arg[4]) { - change_value = sep->arg[4]; - } - - bool valid_change_column = false; - bool valid_search_column = false; - auto results = content_db.QueryDatabase(query); - - std::vector possible_column_options; - - for (auto row = results.begin(); row != results.end(); ++row) { - if (row[0] == change_column) { - valid_change_column = true; - } - if (row[0] == search_column) { - valid_search_column = true; - } - - possible_column_options.push_back(row[0]); - } - - std::string options_glue = ", "; - - if (!valid_search_column) { - c->Message(Chat::Red, "You must specify a valid search column. [%s] is not valid", search_column.c_str()); - c->Message(Chat::Yellow, "Possible columns [%s]", implode(options_glue, possible_column_options).c_str()); - return; - } - - if (!valid_change_column) { - c->Message(Chat::Red, "You must specify a valid change column. [%s] is not valid", change_column.c_str()); - c->Message(Chat::Yellow, "Possible columns [%s]", implode(options_glue, possible_column_options).c_str()); - return; - } - - if (!valid_search_column || !valid_change_column) { - c->Message(Chat::Red, "One requested column is invalid"); - return; - } - - query = fmt::format( - SQL( - select - id, - name, - {0}, - {1} - from - npc_types - where - id IN( - select - spawnentry.npcID - from - spawnentry - join spawn2 on spawn2.spawngroupID = spawnentry.spawngroupID - where - spawn2.zone = '{2}' and spawn2.version = {3} - ) - ), - search_column, - change_column, - zone->GetShortName(), - zone->GetInstanceVersion() - ); - - std::string status = "(Searching)"; - - if (strcasecmp(sep->arg[5], "apply") == 0) { - status = "(Applying)"; - } - - std::vector npc_ids; - - bool exact_match = false; - if (search_value[0] == '=') { - exact_match = true; - search_value = search_value.substr(1); - } - - int found_count = 0; - results = content_db.QueryDatabase(query); - for (auto row = results.begin(); row != results.end(); ++row) { - - std::string npc_id = row[0]; - std::string npc_name = row[1]; - std::string search_column_value = str_tolower(row[2]); - std::string change_column_current_value = row[3]; - - if (exact_match) { - if (search_column_value.compare(search_value) != 0) { - continue; - } - } - else { - if (search_column_value.find(search_value) == std::string::npos) { - continue; - } - } - - c->Message( - Chat::Yellow, - fmt::format( - "NPC ({0}) [{1}] ({2}) [{3}] Current ({4}) [{5}] New [{6}] {7}", - npc_id, - npc_name, - search_column, - search_column_value, - change_column, - change_column_current_value, - change_value, - status - ).c_str() - ); - - npc_ids.push_back(npc_id); - - found_count++; - } - - std::string saylink = fmt::format( - "#npceditmass {} {}{} {} {} apply", - search_column, - (exact_match ? "=" : ""), - search_value, - change_column, - change_value - ); - - if (strcasecmp(sep->arg[5], "apply") == 0) { - std::string npc_ids_string = implode(",", npc_ids); - if (npc_ids_string.empty()) { - c->Message(Chat::Red, "Error: Ran into an unknown error compiling NPC IDs"); - return; - } - - content_db.QueryDatabase( - fmt::format( - "UPDATE `npc_types` SET {} = '{}' WHERE id IN ({})", - change_column, - change_value, - npc_ids_string - ) - ); - - c->Message(Chat::Yellow, "Changes applied to (%i) NPC's", found_count); - zone->Repop(); - } - else { - c->Message(Chat::Yellow, "Found (%i) NPC's that match this search...", found_count); - - if (found_count > 0) { - c->Message( - Chat::Yellow, "To apply these changes, click <%s> or type [%s]", - EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Apply").c_str(), - saylink.c_str() - ); - } - } -} - -void command_npcedit(Client *c, const Seperator *sep) -{ if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { - c->Message(Chat::White, "Error: Must have NPC targeted"); - return; - } - - if (strcasecmp(sep->arg[1], "help") == 0) { - - c->Message(Chat::White, "Help File for #npcedit. Syntax for commands are:"); - c->Message(Chat::White, "#npcedit name - Sets an NPC's Name"); - c->Message(Chat::White, "#npcedit lastname - Sets an NPC's Lastname"); - c->Message(Chat::White, "#npcedit level - Sets an NPC's Level"); - c->Message(Chat::White, "#npcedit race - Sets an NPC's Race"); - c->Message(Chat::White, "#npcedit class - Sets an NPC's Class"); - c->Message(Chat::White, "#npcedit bodytype - Sets an NPC's Bodytype"); - c->Message(Chat::White, "#npcedit hp - Sets an NPC's Hitpoints"); - c->Message(Chat::White, "#npcedit mana - Sets an NPC's Mana"); - c->Message(Chat::White, "#npcedit gender - Sets an NPC's Gender"); - c->Message(Chat::White, "#npcedit texture - Sets an NPC's Texture"); - c->Message(Chat::White, "#npcedit helmtexture - Sets an NPC's Helmet Texture"); - c->Message(Chat::White, "#npcedit herosforgemodel - Sets an NPC's Hero's Forge Model"); - c->Message(Chat::White, "#npcedit size - Sets an NPC's Size"); - c->Message(Chat::White, "#npcedit hpregen - Sets an NPC's Hitpoints Regeneration Rate Per Tick"); - c->Message(Chat::White, "#npcedit manaregen - Sets an NPC's Mana Regeneration Rate Per Tick"); - c->Message(Chat::White, "#npcedit loottable - Sets an NPC's Loottable ID"); - c->Message(Chat::White, "#npcedit merchantid - Sets an NPC's Merchant ID"); - c->Message(Chat::White, "#npcedit alt_currency_id - Sets an NPC's Alternate Currency ID"); - c->Message(Chat::White, "#npcedit spell - Sets an NPC's Spells List ID"); - c->Message(Chat::White, "#npcedit npc_spells_effects_id - Sets an NPC's Spell Effects ID"); - c->Message(Chat::White, "#npcedit faction - Sets an NPC's Faction ID"); - c->Message(Chat::White, "#npcedit adventure_template_id - Sets an NPC's Adventure Template ID"); - c->Message(Chat::White, "#npcedit trap_template - Sets an NPC's Trap Template ID"); - c->Message(Chat::White, "#npcedit damage [minimum] [maximum] - Sets an NPC's Damage"); - c->Message(Chat::White, "#npcedit attackcount - Sets an NPC's Attack Count"); - c->Message(Chat::White, "#npcedit special_attacks - Sets an NPC's Special Attacks"); - c->Message(Chat::White, "#npcedit special_abilities - Sets an NPC's Special Abilities"); - c->Message(Chat::White, "#npcedit aggroradius - Sets an NPC's Aggro Radius"); - c->Message(Chat::White, "#npcedit assistradius - Sets an NPC's Assist Radius"); - c->Message(Chat::White, "#npcedit featuresave - Saves an NPC's current facial features to the database"); - c->Message(Chat::White, "#npcedit armortint_id - Sets an NPC's Armor Tint ID"); - c->Message(Chat::White, "#npcedit color [red] [green] [blue] - Sets an NPC's Red, Green, and Blue armor tint"); - c->Message(Chat::White, "#npcedit ammoidfile - Sets an NPC's Ammo ID File"); - c->Message(Chat::White, "#npcedit weapon [primary_model] [secondary_model] - Sets an NPC's Primary and Secondary Weapon Model"); - c->Message(Chat::White, "#npcedit meleetype [primary_type] [secondary_type] - Sets an NPC's Melee Types"); - c->Message(Chat::White, "#npcedit rangedtype - Sets an NPC's Ranged Type"); - c->Message(Chat::White, "#npcedit runspeed - Sets an NPC's Run Speed"); - c->Message(Chat::White, "#npcedit mr - Sets an NPC's Magic Resistance"); - c->Message(Chat::White, "#npcedit pr - Sets an NPC's Poison Resistance"); - c->Message(Chat::White, "#npcedit dr - Sets an NPC's Disease Resistance"); - c->Message(Chat::White, "#npcedit fr - Sets an NPC's Fire Resistance"); - c->Message(Chat::White, "#npcedit cr - Sets an NPC's Cold Resistance"); - c->Message(Chat::White, "#npcedit corrup - Sets an NPC's Corruption Resistance"); - c->Message(Chat::White, "#npcedit phr - Sets and NPC's Physical Resistance"); - c->Message(Chat::White, "#npcedit seeinvis - Sets an NPC's See Invisible Flag [0 = Cannot See Invisible, 1 = Can See Invisible]"); - c->Message(Chat::White, "#npcedit seeinvisundead - Sets an NPC's See Invisible vs. Undead Flag [0 = Cannot See Invisible vs. Undead, 1 = Can See Invisible vs. Undead]"); - c->Message(Chat::White, "#npcedit qglobal - Sets an NPC's Quest Global Flag [0 = Quest Globals Off, 1 = Quest Globals On]"); - c->Message(Chat::White, "#npcedit ac - Sets an NPC's Armor Class"); - c->Message(Chat::White, "#npcedit npcaggro - Sets an NPC's NPC Aggro Flag [0 = Aggro NPCs Off, 1 = Aggro NPCs On]"); - c->Message(Chat::White, "#npcedit spawn_limit - Sets an NPC's Spawn Limit Counter"); - c->Message(Chat::White, "#npcedit attackspeed - Sets an NPC's Attack Speed Modifier"); - c->Message(Chat::White, "#npcedit attackdelay - Sets an NPC's Attack Delay"); - c->Message(Chat::White, "#npcedit findable - Sets an NPC's Findable Flag [0 = Not Findable, 1 = Findable]"); - c->Message(Chat::White, "#npcedit str - Sets an NPC's Strength"); - c->Message(Chat::White, "#npcedit sta - Sets an NPC's Stamina"); - c->Message(Chat::White, "#npcedit dex - Sets an NPC's Dexterity"); - c->Message(Chat::White, "#npcedit agi - Sets an NPC's Agility"); - c->Message(Chat::White, "#npcedit int - Sets an NPC's Intelligence"); - c->Message(Chat::White, "#npcedit wis - Sets an NPC's Wisdom"); - c->Message(Chat::White, "#npcedit cha - Sets an NPC's Charisma"); - c->Message(Chat::White, "#npcedit seehide - Sets an NPC's See Hide Flag [0 = Cannot See Hide, 1 = Can See Hide]"); - c->Message(Chat::White, "#npcedit seeimprovedhide - Sets an NPC's See Improved Hide Flag [0 = Cannot See Improved Hide, 1 = Can See Improved Hide]"); - c->Message(Chat::White, "#npcedit trackable - Sets an NPC's Trackable Flag [0 = Not Trackable, 1 = Trackable]"); - c->Message(Chat::White, "#npcedit atk - Sets an NPC's Attack"); - c->Message(Chat::White, "#npcedit accuracy - Sets an NPC's Accuracy"); - c->Message(Chat::White, "#npcedit avoidance - Sets an NPC's Avoidance"); - c->Message(Chat::White, "#npcedit slow_mitigation - Sets an NPC's Slow Mitigation"); - c->Message(Chat::White, "#npcedit version - Sets an NPC's Version"); - c->Message(Chat::White, "#npcedit maxlevel - Sets an NPC's Maximum Level"); - c->Message(Chat::White, "#npcedit scalerate - Sets an NPC's Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]"); - c->Message(Chat::White, "#npcedit spellscale - Sets an NPC's Spell Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]"); - c->Message(Chat::White, "#npcedit healscale - Sets an NPC's Heal Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]"); - c->Message(Chat::White, "#npcedit no_target - Sets an NPC's No Target Hotkey Flag [0 = Not Targetable with Target Hotkey, 1 = Targetable with Target Hotkey]"); - c->Message(Chat::White, "#npcedit raidtarget - Sets an NPC's Raid Target Flag [0 = Not a Raid Target, 1 = Raid Target]"); - c->Message(Chat::White, "#npcedit armtexture - Sets an NPC's Arm Texture"); - c->Message(Chat::White, "#npcedit bracertexture - Sets an NPC's Bracer Texture"); - c->Message(Chat::White, "#npcedit handtexture - Sets an NPC's Hand Texture"); - c->Message(Chat::White, "#npcedit legtexture - Sets an NPC's Leg Texture"); - c->Message(Chat::White, "#npcedit feettexture - Sets an NPC's Feet Texture"); - c->Message(Chat::White, "#npcedit walkspeed - Sets an NPC's Walk Speed"); - c->Message(Chat::White, "#npcedit show_name - Sets an NPC's Show Name Flag [0 = Hidden, 1 = Shown]"); - c->Message(Chat::White, "#npcedit untargetable - Sets an NPC's Untargetable Flag [0 = Targetable, 1 = Untargetable]"); - c->Message(Chat::White, "#npcedit charm_ac - Sets an NPC's Armor Class while Charmed"); - c->Message(Chat::White, "#npcedit charm_min_dmg - Sets an NPC's Minimum Damage while Charmed"); - c->Message(Chat::White, "#npcedit charm_max_dmg - Sets an NPC's Max Damage while Charmed"); - c->Message(Chat::White, "#npcedit charm_attack_delay - Sets an NPC's Attack Delay while Charmed"); - c->Message(Chat::White, "#npcedit charm_accuracy_rating - Sets an NPC's Accuracy Rating while Charmed"); - c->Message(Chat::White, "#npcedit charm_avoidance_rating - Sets an NPC's Avoidance Rating while Charmed"); - c->Message(Chat::White, "#npcedit charm_atk - Sets an NPC's Attack while Charmed"); - c->Message(Chat::White, "#npcedit skip_global_loot - Sets an NPC's Skip Global Loot Flag [0 = Don't Skip, 1 = Skip"); - c->Message(Chat::White, "#npcedit rarespawn - Sets an NPC's Rare Spawn Flag [0 = Not a Rare Spawn, 1 = Rare Spawn]"); - c->Message(Chat::White, "#npcedit stuck_behavior - Sets an NPC's Stuck Behavior [0 = Run to Target, 1 = Warp to Target, 2 = Take No Action, 3 = Evade Combat]"); - c->Message(Chat::White, "#npcedit flymode - Sets an NPC's flymode [0 = Ground, 1 = Flying, 2 = Levitating, 3 = Water, 4 = Floating, 5 = Levitating While Running]"); - c->Message(Chat::White, "#npcedit always_aggro - Sets an NPC's Always Aggro Flag [0 = Does not Always Aggro, 1 = Always Aggro]"); - c->Message(Chat::White, "#npcedit exp_mod - Sets an NPC's Experience Modifier [50 = 50%, 100 = 100%, 200 = 200%]"); - c->Message(Chat::White, "#npcedit setanimation - Sets an NPC's Animation on Spawn (Stored in spawn2 table)"); - c->Message(Chat::White, "#npcedit respawntime - Sets an NPC's Respawn Timer in Seconds (Stored in spawn2 table)"); - } - - uint32 npc_id = c->GetTarget()->CastToNPC()->GetNPCTypeID(); - if (strcasecmp(sep->arg[1], "name") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has the name '{}'.", npc_id, sep->arg[2]).c_str()); - std::string query = fmt::format("UPDATE npc_types SET name = '{}' WHERE id = {}", sep->arg[2], npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "lastname") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has the lastname '{}'.", npc_id, sep->arg[2]).c_str()); - std::string query = fmt::format("UPDATE npc_types SET lastname = '{}' WHERE id = {}", sep->arg[2], npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "level") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now level {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET level = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "race") == 0) { - auto race_id = atoi(sep->arg[2]); - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now a {} ({}).", npc_id, GetRaceIDName(race_id), race_id).c_str()); - std::string query = fmt::format("UPDATE npc_types SET race = {} WHERE id = {}", race_id, npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "class") == 0) { - auto class_id = atoi(sep->arg[2]); - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now a {} ({}).", npc_id, GetClassIDName(class_id), class_id).c_str()); - std::string query = fmt::format("UPDATE npc_types SET class = {} WHERE id = {}", class_id, npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "bodytype") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Bodytype {} .", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET bodytype = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "hp") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Health.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET hp = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "mana") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Mana.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET mana = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "gender") == 0) { - auto gender_id = atoi(sep->arg[2]); - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now a {} ({}).", npc_id, gender_id, GetGenderName(gender_id)).c_str()); - std::string query = fmt::format("UPDATE npc_types SET gender = {} WHERE id = {}", gender_id, npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "texture") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET texture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "helmtexture") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Helmet Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET helmtexture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "herosforgemodel") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Hero's Forge Model {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET herosforgemodel = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "size") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now Size {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET size = {:.2f} WHERE id = {}", atof(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "hpregen") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now regenerates {} Health per Tick.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET hp_regen_rate = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "manaregen") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now regenerates {} Mana per Tick.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET mana_regen_rate = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "loottable") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using loottable ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET loottable_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "merchantid") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using merchant ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET merchant_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "alt_currency_id") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Alternate Currency ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET alt_currency_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "spell") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Spell List ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET npc_spells_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "npc_spells_effects_id") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using NPC Spells Effects ID {}.", npc_id, sep->arg[2]).c_str()); - std::string query = fmt::format("UPDATE npc_types SET npc_spells_effects_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "faction") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Faction ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET npc_faction_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "adventure_template_id") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Adventure Template ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET adventure_template_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "trap_template") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Trap Template ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET trap_template = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "damage") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now hits from {} to {} damage.", npc_id, atoi(sep->arg[2]), atoi(sep->arg[3])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET mindmg = {}, maxdmg = {} WHERE id = {}", atoi(sep->arg[2]), atoi(sep->arg[3]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "attackcount") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has an Attack Count of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET attack_count = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "special_attacks") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using the following Special Attacks '{}'.", npc_id, sep->arg[2]).c_str()); - std::string query = fmt::format("UPDATE npc_types SET npcspecialattks = '{}' WHERE id = {}", sep->arg[2], npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "special_abilities") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using the following Special Abilities '{}'.", npc_id, sep->arg[2]).c_str()); - std::string query = fmt::format("UPDATE npc_types SET special_abilities = '{}' WHERE id = {}", sep->arg[2], npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "aggroradius") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has an Aggro Radius of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET aggroradius = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "assistradius") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has an Assist Radius of {}", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET assistradius = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "featuresave") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} saved with all current facial feature settings.", npc_id).c_str()); - Mob* target = c->GetTarget(); - std::string query = fmt::format( - "UPDATE npc_types " - "SET luclin_haircolor = {}, luclin_beardcolor = {}, " - "luclin_hairstyle = {}, luclin_beard = {}, " - "face = {}, drakkin_heritage = {}, " - "drakkin_tattoo = {}, drakkin_details = {} " - "WHERE id = {}", - target->GetHairColor(), target->GetBeardColor(), - target->GetHairStyle(), target->GetBeard(), - target->GetLuclinFace(), target->GetDrakkinHeritage(), - target->GetDrakkinTattoo(), target->GetDrakkinDetails(), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "armortint_id") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Armor Tint ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET armortint_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "color") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Red, {} Green, and {} Blue tinting on their armor.", npc_id, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET armortint_red = {}, armortint_green = {}, armortint_blue = {} WHERE id = {}", atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "ammoidfile") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Ammo ID File {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET ammo_idfile = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "weapon") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} will have Model {} set to their Primary and Model {} set to their Secondary on repop.", npc_id, atoi(sep->arg[2]), atoi(sep->arg[3])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET d_melee_texture1 = {}, d_melee_texture2 = {} WHERE id = {}", atoi(sep->arg[2]), atoi(sep->arg[3]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "meleetype") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Primary Melee Type of {} and a Secondary Melee Type of {}.", npc_id, atoi(sep->arg[2]), atoi(sep->arg[3])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET prim_melee_type = {}, sec_melee_type = {} WHERE id = {}", atoi(sep->arg[2]), atoi(sep->arg[3]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "rangedtype") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Ranged Type of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET ranged_type = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "runspeed") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now runs at {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET runspeed = {:.2f} WHERE id = {}", atof(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "mr") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Magic Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET MR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "pr") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Poison Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET PR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "dr") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Disease Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET DR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "fr") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Fire Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET FR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "cr") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Cold Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET CR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "corrup") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Corruption Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET corrup = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "phr") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Physical Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET PhR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "seeinvis") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} can {} See Invisible.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET see_invis = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "seeinvisundead") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} can {} See Invisible vs. Undead.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET see_invis_undead = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "qglobal") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} can {} use Quest Globals.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET qglobal = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "ac") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Armor Class.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET ac = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "npcaggro") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} will {} aggro other NPCs that have a hostile faction.", npc_id, (atoi(sep->arg[2]) == 1 ? "now": "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET npc_aggro = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "spawn_limit") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Spawn Limit of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET spawn_limit = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "attackspeed") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has an Attack Speed of {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET attack_speed = {:.2f} WHERE id = {}", atof(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "attackdelay") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has an Attack Delay of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET attack_delay = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "findable") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is {} Findable.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET findable = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "str") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Strength.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET STR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "sta") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Stamina.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET STA = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "agi") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Agility.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET AGI = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "dex") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Dexterity.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET DEX = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "int") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Intelligence.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET _INT = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "wis") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Magic Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET WIS = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "cha") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Charisma.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET CHA = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "seehide") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} can {} See Hide.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET see_hide = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "seeimprovedhide") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} can {} See Improved Hide.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET see_improved_hide = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "trackable") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is {} Trackable.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET trackable = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "atk") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Attack.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET atk = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "accuracy") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Accuracy.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET accuracy = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "avoidance") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Avoidance.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET avoidance = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "slow_mitigation") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Slow Mitigation.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET slow_mitigation = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "version") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Version {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET version = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "maxlevel") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Maximum Level of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET maxlevel = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "scalerate") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Scaling Rate of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET scalerate = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "spellscale") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Spell Scaling Rate of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET spellscale = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "healscale") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Heal Scaling Rate of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET healscale = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "no_target") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is {} Targetable with Target Hotkey.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET no_target_hotkey = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "raidtarget") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is {} designated as a Raid Target.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET raid_target = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "armtexture") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Arm Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET armtexture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "bracertexture") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Bracer Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET bracertexture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "handtexture") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Hand Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET handtexture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "legtexture") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Leg Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET legtexture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "feettexture") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Feet Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET feettexture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "walkspeed") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now walks at {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET walkspeed = {:.2f} WHERE id = {}", atof(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "show_name") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} will {} show their name.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET show_name = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "untargetable") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} will {} be untargetable.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET untargetable = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "charm_ac") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Armor Class while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET charm_ac = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "charm_min_dmg") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now does {} Minimum Damage while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET charm_min_dmg = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "charm_max_dmg") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now does {} Maximum Damage while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET charm_max_dmg = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "charm_attack_delay") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Attack Delay while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET charm_attack_delay = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "charm_accuracy_rating") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Accuracy Rating while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET charm_accuracy_rating = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "charm_avoidance_rating") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Avoidance Rating while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET charm_avoidance_rating = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "charm_atk") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Attack while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET charm_atk = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "skip_global_loot") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} will {} skip Global Loot.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET skip_global_loot = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "rarespawn") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is {} designated as a Rare Spawn.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET rare_spawn = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "stuck_behavior") == 0) { - auto behavior_id = atoi(sep->arg[2]); - std::string behavior_name = "Unknown"; - if (behavior_id == MobStuckBehavior::RunToTarget) { - behavior_name = "Run To Target"; - } else if (behavior_id == MobStuckBehavior::WarpToTarget) { - behavior_name = "Warp To Target"; - } else if (behavior_id == MobStuckBehavior::TakeNoAction) { - behavior_name = "Take No Action"; - } else if (behavior_id == MobStuckBehavior::EvadeCombat) { - behavior_name = "Evade Combat"; - } - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Stuck Behavior {} ({}).", npc_id, behavior_name, behavior_id).c_str()); - std::string query = fmt::format("UPDATE npc_types SET stuck_behavior = {} WHERE id = {}", behavior_id, npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "flymode") == 0) { - auto flymode_id = atoi(sep->arg[2]); - std::string flymode_name = "Unknown"; - if (flymode_id == GravityBehavior::Ground) { - flymode_name = "Ground"; - } else if (flymode_id == GravityBehavior::Flying) { - flymode_name = "Flying"; - } else if (flymode_id == GravityBehavior::Levitating) { - flymode_name = "Levitating"; - } else if (flymode_id == GravityBehavior::Water) { - flymode_name = "Water"; - } else if (flymode_id == GravityBehavior::Floating) { - flymode_name = "Floating"; - } else if (flymode_id == GravityBehavior::LevitateWhileRunning) { - flymode_name = "Levitating While Running"; - } - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Fly Mode {} ({}).", npc_id, flymode_name, flymode_id).c_str()); - std::string query = fmt::format("UPDATE npc_types SET flymode = {} WHERE id = {}", flymode_id, npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "always_aggro") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} will {} Always Aggro.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET always_aggro = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "exp_mod") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has an Experience Modifier of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET exp_mod = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "setanimation") == 0) { - int animation = 0; - std::string animation_name = "Unknown"; - if(sep->arg[2] && atoi(sep->arg[2]) <= 4) { - if(strcasecmp(sep->arg[2], "stand") == 0 || atoi(sep->arg[2]) == 0) { // Stand - animation = 0; - animation_name = "Standing"; - } else if(strcasecmp(sep->arg[2], "sit") == 0 || atoi(sep->arg[2]) == 1) { // Sit - animation = 1; - animation_name = "Sitting"; - } else if(strcasecmp(sep->arg[2], "crouch") == 0 || atoi(sep->arg[2]) == 2) { // Crouch - animation = 2; - animation_name = "Crouching"; - } else if(strcasecmp(sep->arg[2], "dead") == 0 || atoi(sep->arg[2]) == 3) { // Dead - animation = 3; - animation_name = "Dead"; - } else if(strcasecmp(sep->arg[2], "loot") == 0 || atoi(sep->arg[2]) == 4) { // Looting Animation - animation = 4; - animation_name = "Looting"; - } - } else { - c->Message(Chat::White, "You must specify an Animation (0 = Stand, 1 = Sit, 2 = Crouch, 3 = Dead, 4 = Loot)"); - c->Message(Chat::White, "Example: #npcedit setanimation sit"); - c->Message(Chat::White, "Example: #npcedit setanimation 0"); - return; - } - - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} now has their Spawn Animation set to {} ({}) on Spawn Group ID {}.", - npc_id, - animation_name, - animation, - c->GetTarget()->CastToNPC()->GetSpawnGroupId() - ).c_str() - ); - std::string query = fmt::format( - "UPDATE spawn2 SET animation = {} WHERE spawngroupID = {}", - animation, - c->GetTarget()->CastToNPC()->GetSpawnGroupId() - ); - content_db.QueryDatabase(query); - - c->GetTarget()->SetAppearance(EmuAppearance(animation)); - return; - } - - if (strcasecmp(sep->arg[1], "respawntime") == 0) { - if (sep->arg[2][0] && sep->IsNumber(sep->arg[2]) && atoi(sep->arg[2]) > 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Respawn Timer of {} Seconds on Spawn Group ID {}.", npc_id, atoi(sep->arg[2]), c->GetTarget()->CastToNPC()->GetSpawnGroupId()).c_str()); - std::string query = fmt::format("UPDATE spawn2 SET respawntime = {} WHERE spawngroupID = {} AND version = {}", atoi(sep->arg[2]), c->GetTarget()->CastToNPC()->GetSpawnGroupId(), zone->GetInstanceVersion()); - content_db.QueryDatabase(query); - return; - } - } - - if((sep->arg[1][0] == 0 || strcasecmp(sep->arg[1],"*")==0) || ((c->GetTarget()==0) || (c->GetTarget()->IsClient()))) - c->Message(Chat::White, "Type #npcedit help for more info"); - -} - -#ifdef PACKET_PROFILER -void command_packetprofile(Client *c, const Seperator *sep) { - Client *t = c; - if(c->GetTarget() && c->GetTarget()->IsClient()) { - t = c->GetTarget()->CastToClient(); - } - c->DumpPacketProfile(); -} -#endif - -#ifdef EQPROFILE -void command_profiledump(Client *c, const Seperator *sep) { - DumpZoneProfile(); -} - -void command_profilereset(Client *c, const Seperator *sep) { - ResetZoneProfile(); -} -#endif - -void command_opcode(Client *c, const Seperator *sep) { - if(!strcasecmp(sep->arg[1], "reload" )) { - ReloadAllPatches(); - c->Message(Chat::White, "Opcodes for all patches have been reloaded"); - } -} - -void command_qglobal(Client *c, const Seperator *sep) { - //In-game switch for qglobal column - if(sep->arg[1][0] == 0) { - c->Message(Chat::White, "Syntax: #qglobal [on/off/view]. Requires NPC target."); - return; - } - - Mob *target = c->GetTarget(); - - if(!target || !target->IsNPC()) { - c->Message(Chat::Red, "NPC Target Required!"); - return; - } - - if(!strcasecmp(sep->arg[1], "on")) { - std::string query = StringFormat("UPDATE npc_types SET qglobal = 1 WHERE id = '%i'", - target->GetNPCTypeID()); - auto results = content_db.QueryDatabase(query); - if(!results.Success()) { - c->Message(Chat::Yellow, "Could not update database."); - return; - } - - c->Message(Chat::Yellow, "Success! Changes take effect on zone reboot."); - return; - } - - if(!strcasecmp(sep->arg[1], "off")) { - std::string query = StringFormat("UPDATE npc_types SET qglobal = 0 WHERE id = '%i'", - target->GetNPCTypeID()); - auto results = content_db.QueryDatabase(query); - if(!results.Success()) { - c->Message(Chat::Yellow, "Could not update database."); - return; - } - - c->Message(Chat::Yellow, "Success! Changes take effect on zone reboot."); - return; - } - - if(!strcasecmp(sep->arg[1], "view")) { - const NPCType *type = content_db.LoadNPCTypesData(target->GetNPCTypeID()); - if(!type) - c->Message(Chat::Yellow, "Invalid NPC type."); - else if(type->qglobal) - c->Message(Chat::Yellow, "This NPC has quest globals active."); - else - c->Message(Chat::Yellow, "This NPC has quest globals disabled."); - return; - } - - c->Message(Chat::Yellow, "Invalid action specified."); -} - -void command_path(Client *c, const Seperator *sep) -{ - if (zone->pathing) { - zone->pathing->DebugCommand(c, sep); - } -} - -void Client::Undye() { - for (int cur_slot = EQ::textures::textureBegin; cur_slot <= EQ::textures::LastTexture; cur_slot++) { - uint8 slot2=SlotConvert(cur_slot); - EQ::ItemInstance* inst = m_inv.GetItem(slot2); - - if(inst != nullptr) { - inst->SetColor(inst->GetItem()->Color); - database.SaveInventory(CharacterID(), inst, slot2); - } - - m_pp.item_tint.Slot[cur_slot].Color = 0; - SendWearChange(cur_slot); - } - - database.DeleteCharacterDye(this->CharacterID()); -} - -void command_undye(Client *c, const Seperator *sep) -{ - if(c->GetTarget() && c->GetTarget()->IsClient()) - { - c->GetTarget()->CastToClient()->Undye(); - } - else - { - c->Message(Chat::White, "ERROR: Client target required"); - } -} - -void command_ucs(Client *c, const Seperator *sep) -{ - if (!c) - return; - - LogInfo("Character [{}] attempting ucs reconnect while ucs server is [{}] available", - c->GetName(), (zone->IsUCSServerAvailable() ? "" : "un")); - - if (zone->IsUCSServerAvailable()) { - EQApplicationPacket* outapp = nullptr; - std::string buffer; - - std::string MailKey = database.GetMailKey(c->CharacterID(), true); - EQ::versions::UCSVersion ConnectionType = EQ::versions::ucsUnknown; - - // chat server packet - switch (c->ClientVersion()) { - case EQ::versions::ClientVersion::Titanium: - ConnectionType = EQ::versions::ucsTitaniumChat; - break; - case EQ::versions::ClientVersion::SoF: - ConnectionType = EQ::versions::ucsSoFCombined; - break; - case EQ::versions::ClientVersion::SoD: - ConnectionType = EQ::versions::ucsSoDCombined; - break; - case EQ::versions::ClientVersion::UF: - ConnectionType = EQ::versions::ucsUFCombined; - break; - case EQ::versions::ClientVersion::RoF: - ConnectionType = EQ::versions::ucsRoFCombined; - break; - case EQ::versions::ClientVersion::RoF2: - ConnectionType = EQ::versions::ucsRoF2Combined; - break; - default: - ConnectionType = EQ::versions::ucsUnknown; - break; - } - - buffer = StringFormat("%s,%i,%s.%s,%c%s", - Config->ChatHost.c_str(), - Config->ChatPort, - Config->ShortName.c_str(), - c->GetName(), - ConnectionType, - MailKey.c_str() - ); - - outapp = new EQApplicationPacket(OP_SetChatServer, (buffer.length() + 1)); - memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); - outapp->pBuffer[buffer.length()] = '\0'; - - c->QueuePacket(outapp); - safe_delete(outapp); - - // mail server packet - switch (c->ClientVersion()) { - case EQ::versions::ClientVersion::Titanium: - ConnectionType = EQ::versions::ucsTitaniumMail; - break; - default: - // retain value from previous switch - break; - } - - buffer = StringFormat("%s,%i,%s.%s,%c%s", - Config->MailHost.c_str(), - Config->MailPort, - Config->ShortName.c_str(), - c->GetName(), - ConnectionType, - MailKey.c_str() - ); - - outapp = new EQApplicationPacket(OP_SetChatServer2, (buffer.length() + 1)); - memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); - outapp->pBuffer[buffer.length()] = '\0'; - - c->QueuePacket(outapp); - safe_delete(outapp); - } -} - -void command_undyeme(Client *c, const Seperator *sep) -{ - if(c) { - c->Undye(); - c->Message(Chat::Red, "Dye removed from all slots. Please zone for the process to complete."); - } -} - -void command_ginfo(Client *c, const Seperator *sep) -{ - Client *t; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t = c->GetTarget()->CastToClient(); - else - t = c; - - Group *g = t->GetGroup(); - if(!g) { - c->Message(Chat::White, "This client is not in a group"); - return; - } - - c->Message(Chat::White, "Player: %s is in Group #%lu: with %i members", t->GetName(), (unsigned long)g->GetID(), g->GroupCount()); - - uint32 r; - for(r = 0; r < MAX_GROUP_MEMBERS; r++) { - if(g->members[r] == nullptr) { - if(g->membername[r][0] == '\0') - continue; - c->Message(Chat::White, "...Zoned Member: %s, Roles: %s %s %s", g->membername[r], - (g->MemberRoles[r] & RoleAssist) ? "Assist" : "", - (g->MemberRoles[r] & RoleTank) ? "Tank" : "", - (g->MemberRoles[r] & RolePuller) ? "Puller" : ""); - } else { - c->Message(Chat::White, "...In-Zone Member: %s (0x%x) Roles: %s %s %s", g->membername[r], g->members[r], - (g->MemberRoles[r] & RoleAssist) ? "Assist" : "", - (g->MemberRoles[r] & RoleTank) ? "Tank" : "", - (g->MemberRoles[r] & RolePuller) ? "Puller" : ""); - - } - } -} - -void command_hp(Client *c, const Seperator *sep) -{ - c->SendHPUpdate(); - c->CheckManaEndUpdate(); -} - -void command_aggro(Client *c, const Seperator *sep) -{ - if(c->GetTarget() == nullptr || !c->GetTarget()->IsNPC()) { - c->Message(Chat::White, "Error: you must have an NPC target."); - return; - } - float d = atof(sep->arg[1]); - if(d == 0.0f) { - c->Message(Chat::Red, "Error: distance argument required."); - return; - } - bool verbose = false; - if(sep->arg[2][0] == '-' && sep->arg[2][1] == 'v' && sep->arg[2][2] == '\0') { - verbose = true; - } - - entity_list.DescribeAggro(c, c->GetTarget()->CastToNPC(), d, verbose); -} - -void command_pf(Client *c, const Seperator *sep) -{ - if(c->GetTarget()) - { - Mob *who = c->GetTarget(); - c->Message(Chat::White, "POS: (%.2f, %.2f, %.2f)", who->GetX(), who->GetY(), who->GetZ()); - c->Message(Chat::White, "WP: %s (%d/%d)", to_string(who->GetCurrentWayPoint()).c_str(), who->IsNPC()?who->CastToNPC()->GetMaxWp():-1); - c->Message(Chat::White, "pause=%d RAspeed=%d", who->GetCWPP(), who->GetRunAnimSpeed()); - //who->DumpMovement(c); - } else { - c->Message(Chat::White, "ERROR: target required"); - } -} - -void command_bestz(Client *c, const Seperator *sep) { - if (zone->zonemap == nullptr) { - c->Message(Chat::White,"Map not loaded for this zone"); - } else { - glm::vec3 me; - me.x = c->GetX(); - me.y = c->GetY(); - me.z = c->GetZ() + (c->GetSize() == 0.0 ? 6 : c->GetSize()) * HEAD_POSITION; - glm::vec3 hit; - glm::vec3 bme(me); - bme.z -= 500; - - float best_z = zone->zonemap->FindBestZ(me, &hit); - - if (best_z != BEST_Z_INVALID) - { - c->Message(Chat::White, "Z is %.3f at (%.3f, %.3f).", best_z, me.x, me.y); - } - else - { - c->Message(Chat::White, "Found no Z."); - } - } - - if(zone->watermap == nullptr) { - c->Message(Chat::White,"Water Region Map not loaded for this zone"); - } else { - WaterRegionType RegionType; - float z; - - if(c->GetTarget()) { - z=c->GetTarget()->GetZ(); - auto position = glm::vec3(c->GetTarget()->GetX(), c->GetTarget()->GetY(), z); - RegionType = zone->watermap->ReturnRegionType(position); - c->Message(Chat::White,"InWater returns %d", zone->watermap->InWater(position)); - c->Message(Chat::White,"InLava returns %d", zone->watermap->InLava(position)); - - } - else { - z=c->GetZ(); - auto position = glm::vec3(c->GetX(), c->GetY(), z); - RegionType = zone->watermap->ReturnRegionType(position); - c->Message(Chat::White,"InWater returns %d", zone->watermap->InWater(position)); - c->Message(Chat::White,"InLava returns %d", zone->watermap->InLava(position)); - - } - - switch(RegionType) { - case RegionTypeNormal: { c->Message(Chat::White,"There is nothing special about the region you are in!"); break; } - case RegionTypeWater: { c->Message(Chat::White,"You/your target are in Water."); break; } - case RegionTypeLava: { c->Message(Chat::White,"You/your target are in Lava."); break; } - case RegionTypeVWater: { c->Message(Chat::White,"You/your target are in VWater (Icy Water?)."); break; } - case RegionTypePVP: { c->Message(Chat::White, "You/your target are in a pvp enabled area."); break; } - case RegionTypeSlime: { c->Message(Chat::White, "You/your target are in slime."); break; } - case RegionTypeIce: { c->Message(Chat::White, "You/your target are in ice."); break; } - default: c->Message(Chat::White,"You/your target are in an unknown region type."); - } - } - - -} - - -void command_reloadstatic(Client *c, const Seperator *sep) { - c->Message(Chat::White, "Reloading zone static data..."); - zone->ReloadStaticData(); -} - -void command_flags(Client *c, const Seperator *sep) { - Client *t = c; - - if(c->Admin() >= minStatusToSeeOthersZoneFlags) { - Mob *tgt = c->GetTarget(); - if(tgt != nullptr && tgt->IsClient()) - t = tgt->CastToClient(); - } - - t->SendZoneFlagInfo(c); -} - -void command_flagedit(Client *c, const Seperator *sep) { - //super-command for editing zone flags - if(sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { - c->Message(Chat::White, "Syntax: #flagedit [lockzone|unlockzone|listzones|give|take]."); - c->Message(Chat::White, "...lockzone [zone id/short] [flag name] - Set the specified flag name on the zone, locking the zone"); - c->Message(Chat::White, "...unlockzone [zone id/short] - Removes the flag requirement from the specified zone"); - c->Message(Chat::White, "...listzones - List all zones which require a flag, and their flag's name"); - c->Message(Chat::White, "...give [zone id/short] - Give your target the zone flag for the specified zone."); - c->Message(Chat::White, "...take [zone id/short] - Take the zone flag for the specified zone away from your target"); - c->Message(Chat::White, "...Note: use #flags to view flags on a person"); - return; - } - - if(!strcasecmp(sep->arg[1], "lockzone")) { - uint32 zoneid = 0; - if(sep->arg[2][0] != '\0') { - zoneid = atoi(sep->arg[2]); - if(zoneid < 1) { - zoneid = ZoneID(sep->arg[2]); - } - } - if(zoneid < 1) { - c->Message(Chat::Red, "zone required. see help."); - return; - } - - char flag_name[128]; - if(sep->argplus[3][0] == '\0') { - c->Message(Chat::Red, "flag name required. see help."); - return; - } - database.DoEscapeString(flag_name, sep->argplus[3], 64); - flag_name[127] = '\0'; - - std::string query = StringFormat("UPDATE zone SET flag_needed = '%s' " - "WHERE zoneidnumber = %d AND version = %d", - flag_name, zoneid, zone->GetInstanceVersion()); - auto results = content_db.QueryDatabase(query); - if(!results.Success()) { - c->Message(Chat::Red, "Error updating zone: %s", results.ErrorMessage().c_str()); - return; - } - - c->Message(Chat::Yellow, "Success! Zone %s now requires a flag, named %s", ZoneName(zoneid), flag_name); - return; - } - - if(!strcasecmp(sep->arg[1], "unlockzone")) { - uint32 zoneid = 0; - if(sep->arg[2][0] != '\0') { - zoneid = atoi(sep->arg[2]); - if(zoneid < 1) { - zoneid = ZoneID(sep->arg[2]); - } - } - - if(zoneid < 1) { - c->Message(Chat::Red, "zone required. see help."); - return; - } - - std::string query = StringFormat("UPDATE zone SET flag_needed = '' " - "WHERE zoneidnumber = %d AND version = %d", - zoneid, zone->GetInstanceVersion()); - auto results = content_db.QueryDatabase(query); - if(!results.Success()) { - c->Message(Chat::Yellow, "Error updating zone: %s", results.ErrorMessage().c_str()); - return; - } - - c->Message(Chat::Yellow, "Success! Zone %s no longer requires a flag.", ZoneName(zoneid)); - return; - } - - if(!strcasecmp(sep->arg[1], "listzones")) { - std::string query = "SELECT zoneidnumber, short_name, long_name, version, flag_needed " - "FROM zone WHERE flag_needed != ''"; - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - return; - } - - c->Message(Chat::White, "Zones which require flags:"); - for (auto row = results.begin(); row != results.end(); ++row) - c->Message(Chat::White, "Zone %s (%s,%s) version %s requires key %s", row[2], row[0], row[1], row[3], row[4]); - - return; - } - - if(!strcasecmp(sep->arg[1], "give")) { - uint32 zoneid = 0; - if(sep->arg[2][0] != '\0') { - zoneid = atoi(sep->arg[2]); - if(zoneid < 1) { - zoneid = ZoneID(sep->arg[2]); - } - } - if(zoneid < 1) { - c->Message(Chat::Red, "zone required. see help."); - return; - } - - Mob *t = c->GetTarget(); - if(t == nullptr || !t->IsClient()) { - c->Message(Chat::Red, "client target required"); - return; - } - - t->CastToClient()->SetZoneFlag(zoneid); - return; - } - - if(!strcasecmp(sep->arg[1], "give")) { - uint32 zoneid = 0; - if(sep->arg[2][0] != '\0') { - zoneid = atoi(sep->arg[2]); - if(zoneid < 1) { - zoneid = ZoneID(sep->arg[2]); - } - } - if(zoneid < 1) { - c->Message(Chat::Red, "zone required. see help."); - return; - } - - Mob *t = c->GetTarget(); - if(t == nullptr || !t->IsClient()) { - c->Message(Chat::Red, "client target required"); - return; - } - - t->CastToClient()->ClearZoneFlag(zoneid); - return; - } - - c->Message(Chat::Yellow, "Invalid action specified. use '#flagedit help' for help"); -} - -void command_serverrules(Client *c, const Seperator *sep) -{ - c->SendRules(c); -} - -void command_acceptrules(Client *c, const Seperator *sep) -{ - if(!database.GetAgreementFlag(c->AccountID())) - { - database.SetAgreementFlag(c->AccountID()); - c->SendAppearancePacket(AT_Anim, ANIM_STAND); - c->Message(Chat::White,"It is recorded you have agreed to the rules."); - } -} - -void command_guildcreate(Client *c, const Seperator *sep) -{ - if(strlen(sep->argplus[1])>4 && strlen(sep->argplus[1])<16) - { - guild_mgr.AddGuildApproval(sep->argplus[1],c); - } - else - { - c->Message(Chat::White,"Guild name must be more than 4 characters and less than 16."); - } -} - -void command_guildapprove(Client *c, const Seperator *sep) -{ - guild_mgr.AddMemberApproval(atoi(sep->arg[1]),c); -} - -void command_guildlist(Client *c, const Seperator *sep) -{ - GuildApproval* tmp = guild_mgr.FindGuildByIDApproval(atoi(sep->arg[1])); - if(tmp) - { - tmp->ApprovedMembers(c); - } - else - c->Message(Chat::White,"Could not find reference id."); -} - -void command_hatelist(Client *c, const Seperator *sep) { - Mob *target = c->GetTarget(); - if(target == nullptr) { - c->Message(Chat::White, "Error: you must have a target."); - return; - } - - c->Message(Chat::White, "Display hate list for %s..", target->GetName()); - target->PrintHateListToClient(c); -} - - -void command_rules(Client *c, const Seperator *sep) { - //super-command for managing rules settings - if(sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { - c->Message(Chat::White, "Syntax: #rules [subcommand]."); - c->Message(Chat::White, "-- Rule Set Manipulation --"); - c->Message(Chat::White, "...listsets - List avaliable rule sets"); - c->Message(Chat::White, "...current - gives the name of the ruleset currently running in this zone"); - c->Message(Chat::White, "...reload - Reload the selected ruleset in this zone"); - c->Message(Chat::White, "...switch (ruleset name) - Change the selected ruleset and load it"); - c->Message(Chat::White, "...load (ruleset name) - Load a ruleset in just this zone without changing the selected set"); -//too lazy to write this right now: -// c->Message(Chat::White, "...wload (ruleset name) - Load a ruleset in all zones without changing the selected set"); - c->Message(Chat::White, "...store [ruleset name] - Store the running ruleset as the specified name"); - c->Message(Chat::White, "---------------------"); - c->Message(Chat::White, "-- Running Rule Manipulation --"); - c->Message(Chat::White, "...reset - Reset all rules to their default values"); - c->Message(Chat::White, "...get [rule] - Get the specified rule's local value"); - c->Message(Chat::White, "...set (rule) (value) - Set the specified rule to the specified value locally only"); - c->Message(Chat::White, "...setdb (rule) (value) - Set the specified rule to the specified value locally and in the DB"); - c->Message(Chat::White, "...list [catname] - List all rules in the specified category (or all categiries if omitted)"); - c->Message(Chat::White, "...values [catname] - List the value of all rules in the specified category"); - return; - } - - if(!strcasecmp(sep->arg[1], "current")) { - c->Message(Chat::White, "Currently running ruleset '%s' (%d)", RuleManager::Instance()->GetActiveRuleset(), - RuleManager::Instance()->GetActiveRulesetID()); - } else if(!strcasecmp(sep->arg[1], "listsets")) { - std::map sets; - if(!RuleManager::Instance()->ListRulesets(&database, sets)) { - c->Message(Chat::Red, "Failed to list rule sets!"); - return; - } - - c->Message(Chat::White, "Avaliable rule sets:"); - std::map::iterator cur, end; - cur = sets.begin(); - end = sets.end(); - for(; cur != end; ++cur) { - c->Message(Chat::White, "(%d) %s", cur->first, cur->second.c_str()); - } - } else if(!strcasecmp(sep->arg[1], "reload")) { - RuleManager::Instance()->LoadRules(&database, RuleManager::Instance()->GetActiveRuleset(), true); - c->Message(Chat::White, "The active ruleset (%s (%d)) has been reloaded", RuleManager::Instance()->GetActiveRuleset(), - RuleManager::Instance()->GetActiveRulesetID()); - } else if(!strcasecmp(sep->arg[1], "switch")) { - //make sure this is a valid rule set.. - int rsid = RuleManager::Instance()->GetRulesetID(&database, sep->arg[2]); - if(rsid < 0) { - c->Message(Chat::Red, "Unknown rule set '%s'", sep->arg[2]); - return; - } - if(!database.SetVariable("RuleSet", sep->arg[2])) { - c->Message(Chat::Red, "Failed to update variables table to change selected rule set"); - return; - } - - //TODO: we likely want to reload this ruleset everywhere... - RuleManager::Instance()->LoadRules(&database, sep->arg[2], true); - - c->Message(Chat::White, "The selected ruleset has been changed to (%s (%d)) and reloaded locally", sep->arg[2], rsid); - } else if(!strcasecmp(sep->arg[1], "load")) { - //make sure this is a valid rule set.. - int rsid = RuleManager::Instance()->GetRulesetID(&database, sep->arg[2]); - if(rsid < 0) { - c->Message(Chat::Red, "Unknown rule set '%s'", sep->arg[2]); - return; - } - RuleManager::Instance()->LoadRules(&database, sep->arg[2], true); - c->Message(Chat::White, "Loaded ruleset '%s' (%d) locally", sep->arg[2], rsid); - } else if(!strcasecmp(sep->arg[1], "store")) { - if(sep->argnum == 1) { - //store current rule set. - RuleManager::Instance()->SaveRules(&database); - c->Message(Chat::White, "Rules saved"); - } else if(sep->argnum == 2) { - RuleManager::Instance()->SaveRules(&database, sep->arg[2]); - int prersid = RuleManager::Instance()->GetActiveRulesetID(); - int rsid = RuleManager::Instance()->GetRulesetID(&database, sep->arg[2]); - if(rsid < 0) { - c->Message(Chat::Red, "Unable to query ruleset ID after store, it most likely failed."); - } else { - c->Message(Chat::White, "Stored rules as ruleset '%s' (%d)", sep->arg[2], rsid); - if(prersid != rsid) { - c->Message(Chat::White, "Rule set %s (%d) is now active in this zone", sep->arg[2], rsid); - } - } - } else { - c->Message(Chat::Red, "Invalid argument count, see help."); - return; - } - } else if(!strcasecmp(sep->arg[1], "reset")) { - RuleManager::Instance()->ResetRules(true); - c->Message(Chat::White, "The running ruleset has been set to defaults"); - - } else if(!strcasecmp(sep->arg[1], "get")) { - if(sep->argnum != 2) { - c->Message(Chat::Red, "Invalid argument count, see help."); - return; - } - std::string value; - if(!RuleManager::Instance()->GetRule(sep->arg[2], value)) - c->Message(Chat::Red, "Unable to find rule %s", sep->arg[2]); - else - c->Message(Chat::White, "%s - %s", sep->arg[2], value.c_str()); - - } else if(!strcasecmp(sep->arg[1], "set")) { - if(sep->argnum != 3) { - c->Message(Chat::Red, "Invalid argument count, see help."); - return; - } - if(!RuleManager::Instance()->SetRule(sep->arg[2], sep->arg[3], nullptr, false, true)) { - c->Message(Chat::Red, "Failed to modify rule"); - } else { - c->Message(Chat::White, "Rule modified locally."); - } - } else if(!strcasecmp(sep->arg[1], "setdb")) { - if(sep->argnum != 3) { - c->Message(Chat::Red, "Invalid argument count, see help."); - return; - } - if(!RuleManager::Instance()->SetRule(sep->arg[2], sep->arg[3], &database, true, true)) { - c->Message(Chat::Red, "Failed to modify rule"); - } else { - c->Message(Chat::White, "Rule modified locally and in the database."); - } - } else if(!strcasecmp(sep->arg[1], "list")) { - if(sep->argnum == 1) { - std::vector rule_list; - if(!RuleManager::Instance()->ListCategories(rule_list)) { - c->Message(Chat::Red, "Failed to list categories!"); - return; - } - c->Message(Chat::White, "Rule Categories:"); - std::vector::iterator cur, end; - cur = rule_list.begin(); - end = rule_list.end(); - for(; cur != end; ++cur) { - c->Message(Chat::White, " %s", *cur); - } - } else if(sep->argnum == 2) { - const char *catfilt = nullptr; - if(std::string("all") != sep->arg[2]) - catfilt = sep->arg[2]; - std::vector rule_list; - if(!RuleManager::Instance()->ListRules(catfilt, rule_list)) { - c->Message(Chat::Red, "Failed to list rules!"); - return; - } - c->Message(Chat::White, "Rules in category %s:", sep->arg[2]); - std::vector::iterator cur, end; - cur = rule_list.begin(); - end = rule_list.end(); - for(; cur != end; ++cur) { - c->Message(Chat::White, " %s", *cur); - } - } else { - c->Message(Chat::Red, "Invalid argument count, see help."); - } - } else if(!strcasecmp(sep->arg[1], "values")) { - if(sep->argnum != 2) { - c->Message(Chat::Red, "Invalid argument count, see help."); - return; - } else { - const char *catfilt = nullptr; - if(std::string("all") != sep->arg[2]) - catfilt = sep->arg[2]; - std::vector rule_list; - if(!RuleManager::Instance()->ListRules(catfilt, rule_list)) { - c->Message(Chat::Red, "Failed to list rules!"); - return; - } - c->Message(Chat::White, "Rules & values in category %s:", sep->arg[2]); - std::vector::iterator cur, end; - cur = rule_list.begin(); - end = rule_list.end(); - for(std::string tmp_value; cur != end; ++cur) { - if (RuleManager::Instance()->GetRule(*cur, tmp_value)) - c->Message(Chat::White, " %s - %s", *cur, tmp_value.c_str()); - } - } - - } else { - c->Message(Chat::Yellow, "Invalid action specified. use '#rules help' for help"); - } -} - - -void command_task(Client *c, const Seperator *sep) { - //super-command for managing tasks - if(sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { - c->Message(Chat::White, "Syntax: #task [subcommand]"); - c->Message(Chat::White, "------------------------------------------------"); - c->Message(Chat::White, "# Task System Commands"); - c->Message(Chat::White, "------------------------------------------------"); - c->Message( - Chat::White, - fmt::format( - "--- [{}] List active tasks for a client", - EQ::SayLinkEngine::GenerateQuestSaylink("#task show", false, "show") - ).c_str() - ); - c->Message(Chat::White, "--- update [count] | Updates task"); - c->Message(Chat::White, "--- assign | Assigns task to client"); - c->Message( - Chat::White, - fmt::format( - "--- [{}] Reload all Task information from the database", - EQ::SayLinkEngine::GenerateQuestSaylink("#task reloadall", false, "reloadall") - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "--- [{}] Reload Task and Activity information for a single task", - EQ::SayLinkEngine::GenerateQuestSaylink("#task reload task", false, "reload task") - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "--- [{}] Reload goal/reward list information", - EQ::SayLinkEngine::GenerateQuestSaylink("#task reload lists", false, "reload lists") - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "--- [{}] Reload proximity information", - EQ::SayLinkEngine::GenerateQuestSaylink("#task reload prox", false, "reload prox") - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "--- [{}] Reload task set information", - EQ::SayLinkEngine::GenerateQuestSaylink("#task reload sets", false, "reload sets") - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "--- [{}] Purges targeted characters task timers", - EQ::SayLinkEngine::GenerateQuestSaylink("#task purgetimers", false, "purgetimers") - ).c_str() - ); - - c->Message(Chat::White, "------------------------------------------------"); - c->Message(Chat::White, "# Shared Task System Commands"); - c->Message(Chat::White, "------------------------------------------------"); - c->Message( - Chat::White, - fmt::format( - "--- [{}] Purges all active Shared Tasks in memory and database ", - EQ::SayLinkEngine::GenerateQuestSaylink("#task sharedpurge", false, "sharedpurge") - ).c_str() - ); - - return; - } - - Client *client_target = c; - if (c->GetTarget() && c->GetTarget()->IsClient()) { - client_target = c->GetTarget()->CastToClient(); - } - - if (!strcasecmp(sep->arg[1], "show")) { - c->ShowClientTasks(client_target); - return; - } - - if (!strcasecmp(sep->arg[1], "purgetimers")) { - c->Message(15, fmt::format("{}'s task timers have been purged", client_target->GetCleanName()).c_str()); - if (client_target != c) { - client_target->Message(15, "[GM] Your task timers have been purged by a GM"); - } - client_target->PurgeTaskTimers(); - return; - } - - if (!strcasecmp(sep->arg[1], "update")) { - if (sep->argnum >= 3) { - int task_id = atoi(sep->arg[2]); - int activity_id = atoi(sep->arg[3]); - int count = 1; - - if (sep->argnum >= 4) { - count = atoi(sep->arg[4]); - if (count <= 0) { - count = 1; - } - } - c->Message( - Chat::Yellow, - "Updating Task [%i] Activity [%i] Count [%i] for client [%s]", - task_id, - activity_id, - count, - client_target->GetCleanName() - ); - client_target->UpdateTaskActivity(task_id, activity_id, count); - c->ShowClientTasks(client_target); - } - return; - } - - if (!strcasecmp(sep->arg[1], "sharedpurge")) { - if (!strcasecmp(sep->arg[2], "confirm")) { - LogTasksDetail("Sending purge request"); - auto pack = new ServerPacket(ServerOP_SharedTaskPurgeAllCommand, 0); - worldserver.SendPacket(pack); - safe_delete(pack); - - return; - } - - c->Message( - Chat::White, - fmt::format( - "[WARNING] This will purge all active Shared Tasks [{}]?", - EQ::SayLinkEngine::GenerateQuestSaylink("#task sharedpurge confirm", false, "confirm") - ).c_str() - ); - - return; - } - - if (!strcasecmp(sep->arg[1], "assign")) { - int task_id = atoi(sep->arg[2]); - if ((task_id > 0) && (task_id < MAXTASKS)) { - client_target->AssignTask(task_id, 0, false); - c->Message(Chat::Yellow, "Assigned task [%i] to [%s]", task_id, client_target->GetCleanName()); - } - return; - } - - if (!strcasecmp(sep->arg[1], "reloadall")) { - c->Message(Chat::Yellow, "Sending reloadtasks to world"); - worldserver.SendReloadTasks(RELOADTASKS); - c->Message(Chat::Yellow, "Back again"); - return; - } - - if (!strcasecmp(sep->arg[1], "reload")) { - if (sep->arg[2][0] != '\0') { - if (!strcasecmp(sep->arg[2], "lists")) { - c->Message(Chat::Yellow, "Sending reload lists to world"); - worldserver.SendReloadTasks(RELOADTASKGOALLISTS); - c->Message(Chat::Yellow, "Reloaded"); - return; - } - if (!strcasecmp(sep->arg[2], "prox")) { - c->Message(Chat::Yellow, "Sending reload proximities to world"); - worldserver.SendReloadTasks(RELOADTASKPROXIMITIES); - c->Message(Chat::Yellow, "Reloaded"); - return; - } - if (!strcasecmp(sep->arg[2], "sets")) { - c->Message(Chat::Yellow, "Sending reload task sets to world"); - worldserver.SendReloadTasks(RELOADTASKSETS); - c->Message(Chat::Yellow, "Reloaded"); - return; - } - if (!strcasecmp(sep->arg[2], "task") && (sep->arg[3][0] != '\0')) { - int task_id = atoi(sep->arg[3]); - if ((task_id > 0) && (task_id < MAXTASKS)) { - c->Message(Chat::Yellow, "Sending reload task %i to world", task_id); - worldserver.SendReloadTasks(RELOADTASKS, task_id); - c->Message(Chat::Yellow, "Reloaded"); - return; - } - } - } - - } - c->Message(Chat::White, "Unable to interpret command. Type #task help"); - -} -void command_reloadtitles(Client *c, const Seperator *sep) -{ - auto pack = new ServerPacket(ServerOP_ReloadTitles, 0); - worldserver.SendPacket(pack); - safe_delete(pack); - c->Message(Chat::Yellow, "Player Titles Reloaded."); - -} - -void command_traindisc(Client *c, const Seperator *sep) -{ - Client *target = c; - if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { - target = c->GetTarget()->CastToClient(); - } - - if(sep->argnum < 1 || !sep->IsNumber(1)) { - c->Message(Chat::White, "FORMAT: #traindisc "); - return; - } - - uint8 rule_max_level = (uint8) RuleI(Character, MaxLevel); - uint8 max_level = (uint8) std::stoi(sep->arg[1]); - uint8 min_level = ( - sep->IsNumber(2) ? - (uint8) - std::stoi(sep->arg[2]) : - 1 - ); // Default to Level 1 if there isn't a 2nd argument - - if (!c->GetGM()) { // Default to Character:MaxLevel if we're not a GM and Level is higher than the max level - if (max_level > rule_max_level) { - max_level = rule_max_level; - } - - if (min_level > rule_max_level) { - min_level = rule_max_level; - } - } - - if(max_level < 1 || min_level < 1) { - c->Message(Chat::White, "ERROR: Level must be greater than or equal to 1."); - return; - } - - if (min_level > max_level) { - c->Message(Chat::White, "ERROR: Minimum Level must be less than or equal to Maximum Level."); - return; - } - - uint16 learned_disciplines = target->LearnDisciplines(min_level, max_level); - if (target != c) { - std::string discipline_message = ( - learned_disciplines > 0 ? - ( - learned_disciplines == 1 ? - "A new discipline" : - fmt::format("{} New disciplines", learned_disciplines) - ) : - "No new disciplines" - ); - c->Message( - Chat::White, - fmt::format( - "{} learned for {}.", - discipline_message, - target->GetCleanName() - ).c_str() - ); - } -} - -void command_setgraveyard(Client *c, const Seperator *sep) -{ - uint32 zoneid = 0; - uint32 graveyard_id = 0; - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) - t=c->GetTarget()->CastToClient(); - - if(!sep->arg[1][0]) { - c->Message(Chat::White, "Usage: #setgraveyard [zonename]"); - return; - } - - zoneid = ZoneID(sep->arg[1]); - - if(zoneid > 0) { - graveyard_id = content_db.CreateGraveyardRecord(zoneid, t->GetPosition()); - - if(graveyard_id > 0) { - c->Message(Chat::White, "Successfuly added a new record for this graveyard!"); - if(content_db.AddGraveyardIDToZone(zoneid, graveyard_id) > 0) { - c->Message(Chat::White, "Successfuly added this new graveyard for the zone %s.", sep->arg[1]); - // TODO: Set graveyard data to the running zone process. - c->Message(Chat::White, "Done!"); - } - else - c->Message(Chat::White, "Unable to add this new graveyard to the zone %s.", sep->arg[1]); - } - else { - c->Message(Chat::White, "Unable to create a new graveyard record in the database."); - } - } - else { - c->Message(Chat::White, "Unable to retrieve a ZoneID for the zone: %s", sep->arg[1]); - } - - return; -} - -void command_deletegraveyard(Client *c, const Seperator *sep) -{ - uint32 zoneid = 0; - uint32 graveyard_id = 0; - - if(!sep->arg[1][0]) { - c->Message(Chat::White, "Usage: #deletegraveyard [zonename]"); - return; - } - - zoneid = ZoneID(sep->arg[1]); - graveyard_id = content_db.GetZoneGraveyardID(zoneid, 0); - - if(zoneid > 0 && graveyard_id > 0) { - if(content_db.DeleteGraveyard(zoneid, graveyard_id)) - c->Message(Chat::White, "Successfuly deleted graveyard %u for zone %s.", graveyard_id, sep->arg[1]); - else - c->Message(Chat::White, "Unable to delete graveyard %u for zone %s.", graveyard_id, sep->arg[1]); - } - else { - if(zoneid <= 0) - c->Message(Chat::White, "Unable to retrieve a ZoneID for the zone: %s", sep->arg[1]); - else if(graveyard_id <= 0) - c->Message(Chat::White, "Unable to retrieve a valid GraveyardID for the zone: %s", sep->arg[1]); - } - - return; -} - -void command_summonburiedplayercorpse(Client *c, const Seperator *sep) -{ - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) - t = c->GetTarget()->CastToClient(); - else { - c->Message(Chat::White, "You must first select a target!"); - return; - } - - Corpse* PlayerCorpse = database.SummonBuriedCharacterCorpses(t->CharacterID(), t->GetZoneID(), zone->GetInstanceID(), t->GetPosition()); - - if(!PlayerCorpse) - c->Message(Chat::White, "Your target doesn't have any buried corpses."); - - return; -} - -void command_getplayerburiedcorpsecount(Client *c, const Seperator *sep) -{ - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) - t = c->GetTarget()->CastToClient(); - else { - c->Message(Chat::White, "You must first select a target!"); - return; - } - - uint32 CorpseCount = database.GetCharacterBuriedCorpseCount(t->CharacterID()); - - if(CorpseCount > 0) - c->Message(Chat::White, "Your target has a total of %u buried corpses.", CorpseCount); - else - c->Message(Chat::White, "Your target doesn't have any buried corpses."); - - return; -} - -void command_refreshgroup(Client *c, const Seperator *sep) -{ - if(!c) - return; - - Group *g = c->GetGroup(); - - if(!g) - return; - - database.RefreshGroupFromDB(c); - //g->SendUpdate(7, c); -} - -void command_advnpcspawn(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (!arguments) { - c->Message(Chat::White, "Usage: #advnpcspawn addentry [Spawngroup ID] [NPC ID] [Spawn Chance] - Adds a new Spawngroup Entry"); - c->Message(Chat::White, "Usage: #advnpcspawn addspawn [Spawngroup ID] - Adds a new Spawngroup Entry from an existing Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn clearbox [Spawngroup ID] - Clears the roambox of a Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn deletespawn - Deletes a Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn editbox [Spawngroup ID] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Edit the roambox of a Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn editrespawn [Respawn Timer] [Variance] - Edit the Respawn Timer of a Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn makegroup [Spawn Group Name] [Spawn Limit] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Makes a new Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn makenpc - Makes a new NPC"); - c->Message(Chat::White, "Usage: #advnpcspawn movespawn - Moves a Spawngroup to your current location"); - c->Message(Chat::White, "Usage: #advnpcspawn setversion [Version] - Sets a Spawngroup's Version"); - return; - } - - std::string spawn_command = str_tolower(sep->arg[1]); - bool is_add_entry = spawn_command.find("addentry") != std::string::npos; - bool is_add_spawn = spawn_command.find("addspawn") != std::string::npos; - bool is_clear_box = spawn_command.find("clearbox") != std::string::npos; - bool is_delete_spawn = spawn_command.find("deletespawn") != std::string::npos; - bool is_edit_box = spawn_command.find("editgroup") != std::string::npos; - bool is_edit_respawn = spawn_command.find("editrespawn") != std::string::npos; - bool is_make_group = spawn_command.find("makegroup") != std::string::npos; - bool is_make_npc = spawn_command.find("makenpc") != std::string::npos; - bool is_move_spawn = spawn_command.find("movespawn") != std::string::npos; - bool is_set_version = spawn_command.find("setversion") != std::string::npos; - if ( - !is_add_entry && - !is_add_spawn && - !is_clear_box && - !is_delete_spawn && - !is_edit_box && - !is_edit_respawn && - !is_make_group && - !is_make_npc && - !is_move_spawn && - !is_set_version - ) { - c->Message(Chat::White, "Usage: #advnpcspawn addentry [Spawngroup ID] [NPC ID] [Spawn Chance] - Adds a new Spawngroup Entry"); - c->Message(Chat::White, "Usage: #advnpcspawn addspawn [Spawngroup ID] - Adds a new Spawngroup Entry from an existing Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn clearbox [Spawngroup ID] - Clears the roambox of a Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn deletespawn - Deletes a Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn editbox [Spawngroup ID] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Edit the roambox of a Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn editrespawn [Respawn Timer] [Variance] - Edit the Respawn Timer of a Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn makegroup [Spawn Group Name] [Spawn Limit] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Makes a new Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn makenpc - Makes a new NPC"); - c->Message(Chat::White, "Usage: #advnpcspawn movespawn - Moves a Spawngroup to your current location"); - c->Message(Chat::White, "Usage: #advnpcspawn setversion [Version] - Sets a Spawngroup's Version"); - return; - } - - - if (is_add_entry) { - if(arguments < 4) { - c->Message(Chat::White, "Usage: #advnpcspawn addentry [Spawngroup ID] [NPC ID] [Spawn Chance]"); - return; - } - - auto spawngroup_id = std::stoi(sep->arg[2]); - auto npc_id = std::stoi(sep->arg[2]); - auto spawn_chance = std::stoi(sep->arg[2]); - - std::string query = fmt::format( - SQL( - INSERT INTO spawnentry (spawngroupID, npcID, chance) - VALUES ({}, {}, {}) - ), - spawngroup_id, - npc_id, - spawn_chance - ); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Failed to add entry to Spawngroup."); - return; - } - - c->Message( - Chat::White, - fmt::format( - "{} ({}) added to Spawngroup {}, its spawn chance is {}%%.", - database.GetCleanNPCNameByID(npc_id), - npc_id, - spawngroup_id, - spawn_chance - ).c_str() - ); - return; - } else if (is_add_spawn) { - content_db.NPCSpawnDB( - NPCSpawnTypes::AddSpawnFromSpawngroup, - zone->GetShortName(), - zone->GetInstanceVersion(), - c, - 0, - std::stoi(sep->arg[2]) - ); - c->Message( - Chat::White, - fmt::format( - "Spawn Added | Added spawn from Spawngroup ID {}.", - std::stoi(sep->arg[2]) - ).c_str() - ); - return; - } else if (is_clear_box) { - if (arguments != 2 || !sep->IsNumber(2)) { - c->Message(Chat::White, "Usage: #advnpcspawn clearbox [Spawngroup ID]"); - return; - } - - std::string query = fmt::format( - "UPDATE spawngroup SET dist = 0, min_x = 0, max_x = 0, min_y = 0, max_y = 0, delay = 0 WHERE id = {}", - std::stoi(sep->arg[2]) - ); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Failed to clear Spawngroup box."); - return; - } - - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Roambox Cleared | Delay: 0 Distance: 0.00", - std::stoi(sep->arg[2]) - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Roambox Cleared | Minimum X: 0.00 Maximum X: 0.00", - std::stoi(sep->arg[2]) - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Roambox Cleared | Minimum Y: 0.00 Maximum Y: 0.00", - std::stoi(sep->arg[2]) - ).c_str() - ); - return; - } else if (is_delete_spawn) { - if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { - c->Message(Chat::White, "You must target an NPC to use this command."); - return; - } - - NPC *target = c->GetTarget()->CastToNPC(); - Spawn2* spawn2 = target->respawn2; - if(!spawn2) { - c->Message(Chat::White, "Failed to delete spawn because NPC has no Spawn2."); - return; - } - - auto spawn2_id = spawn2->GetID(); - std::string query = fmt::format( - "DELETE FROM spawn2 WHERE id = {}", - spawn2_id - ); - auto results = content_db.QueryDatabase(query); - if(!results.Success()) { - c->Message(Chat::White, "Failed to delete spawn."); - return; - } - - c->Message( - Chat::White, - fmt::format( - "Spawn2 {} Deleted | Name: {} ({})", - spawn2_id, - target->GetCleanName(), - target->GetID() - ).c_str() - ); - target->Depop(false); - return; - } else if (is_edit_box) { - if ( - arguments != 8 || - !sep->IsNumber(3) || - !sep->IsNumber(4) || - !sep->IsNumber(5) || - !sep->IsNumber(6) || - !sep->IsNumber(7) || - !sep->IsNumber(8) - ) { - c->Message(Chat::White, "Usage: #advnpcspawn editbox [Spawngroup ID] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay]"); - return; - } - auto spawngroup_id = std::stoi(sep->arg[2]); - auto distance = std::stof(sep->arg[3]); - auto minimum_x = std::stof(sep->arg[4]); - auto maximum_x = std::stof(sep->arg[5]); - auto minimum_y = std::stof(sep->arg[6]); - auto maximum_y = std::stof(sep->arg[7]); - auto delay = std::stoi(sep->arg[8]); - - std::string query = fmt::format( - "UPDATE spawngroup SET dist = {:.2f}, min_x = {:.2f}, max_x = {:.2f}, max_y = {:.2f}, min_y = {:.2f}, delay = {} WHERE id = {}", - distance, - minimum_x, - maximum_x, - minimum_y, - maximum_y, - delay, - spawngroup_id - ); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Failed to edit Spawngroup box."); - return; - } - - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Roambox Edited | Delay: {} Distance: {:.2f}", - spawngroup_id, - delay, - distance - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Roambox Edited | Minimum X: {:.2f} Maximum X: {:.2f}", - spawngroup_id, - minimum_x, - maximum_x - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Roambox Edited | Minimum Y: {:.2f} Maximum Y: {:.2f}", - spawngroup_id, - minimum_y, - maximum_y - ).c_str() - ); - return; - } else if (is_edit_respawn) { - if (arguments < 2 || !sep->IsNumber(2)) { - c->Message(Chat::White, "Usage: #advnpcspawn editrespawn [Respawn Timer] [Variance]"); - return; - } - - if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { - c->Message(Chat::White, "You must target an NPC to use this command."); - return; - } - - NPC *target = c->GetTarget()->CastToNPC(); - Spawn2* spawn2 = target->respawn2; - if(!spawn2) { - c->Message(Chat::White, "Failed to edit respawn because NPC has no Spawn2."); - return; - } - - auto spawn2_id = spawn2->GetID(); - uint32 respawn_timer = std::stoi(sep->arg[2]); - uint32 variance = ( - sep->IsNumber(3) ? - std::stoi(sep->arg[3]) : - spawn2->GetVariance() - ); - std::string query = fmt::format( - "UPDATE spawn2 SET respawntime = {}, variance = {} WHERE id = {}", - respawn_timer, - variance, - spawn2_id - ); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Failed to edit respawn."); - return; - } - - c->Message( - Chat::White, - fmt::format( - "Spawn2 {} Respawn Modified | Name: {} ({}) Respawn Timer: {} Variance: {}", - spawn2_id, - target->GetCleanName(), - target->GetID(), - respawn_timer, - variance - ).c_str() - ); - spawn2->SetRespawnTimer(respawn_timer); - spawn2->SetVariance(variance); - return; - } else if (is_make_group) { - if ( - arguments != 9 || - !sep->IsNumber(3) || - !sep->IsNumber(4) || - !sep->IsNumber(5) || - !sep->IsNumber(6) || - !sep->IsNumber(7) || - !sep->IsNumber(8) || - !sep->IsNumber(9) - ) { - c->Message(Chat::White, "Usage: #advncspawn makegroup [Spawn Group Name] [Spawn Limit] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay]"); - return; - } - std::string spawngroup_name = sep->arg[2]; - auto spawn_limit = std::stoi(sep->arg[3]); - auto distance = std::stof(sep->arg[4]); - auto minimum_x = std::stof(sep->arg[5]); - auto maximum_x = std::stof(sep->arg[6]); - auto minimum_y = std::stof(sep->arg[7]); - auto maximum_y = std::stof(sep->arg[8]); - auto delay = std::stoi(sep->arg[9]); - - std::string query = fmt::format( - "INSERT INTO spawngroup" - "(name, spawn_limit, dist, min_x, max_x, min_y, max_y, delay)" - "VALUES ('{}', {}, {:.2f}, {:.2f}, {:.2f}, {:.2f}, {:.2f}, {})", - spawngroup_name, - spawn_limit, - distance, - minimum_x, - maximum_x, - minimum_y, - maximum_y, - delay - ); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Failed to make Spawngroup."); - return; - } - - auto spawngroup_id = results.LastInsertedID(); - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Created | Name: {} Spawn Limit: {}", - spawngroup_id, - spawngroup_name, - spawn_limit - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Created | Delay: {} Distance: {:.2f}", - spawngroup_id, - delay, - distance - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Created | Minimum X: {:.2f} Maximum X: {:.2f}", - spawngroup_id, - minimum_x, - maximum_x - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Created | Minimum Y: {:.2f} Maximum Y: {:.2f}", - spawngroup_id, - minimum_y, - maximum_y - ).c_str() - ); - return; - } else if (is_make_npc) { - if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { - c->Message(Chat::White, "You must target an NPC to use this command."); - return; - } - - NPC *target = c->GetTarget()->CastToNPC(); - content_db.NPCSpawnDB( - NPCSpawnTypes::CreateNewNPC, - zone->GetShortName(), - zone->GetInstanceVersion(), - c, - target - ); - return; - } else if (is_move_spawn) { - if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { - c->Message(Chat::White, "You must target an NPC to use this command."); - return; - } - - NPC *target = c->GetTarget()->CastToNPC(); - Spawn2* spawn2 = target->respawn2; - if(!spawn2) { - c->Message(Chat::White, "Failed to move spawn because NPC has no Spawn2."); - return; - } - - auto client_position = c->GetPosition(); - auto spawn2_id = spawn2->GetID(); - std::string query = fmt::format( - "UPDATE spawn2 SET x = {:.2f}, y = {:.2f}, z = {:.2f}, heading = {:.2f} WHERE id = {}", - client_position.x, - client_position.y, - client_position.z, - client_position.w, - spawn2_id - ); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Failed to move spawn."); - return; - } - - c->Message( - Chat::White, - fmt::format( - "Spawn2 {} Moved | Name: {} ({})", - spawn2_id, - target->GetCleanName(), - target->GetID() - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "Spawn2 {} Moved | XYZ: {}, {}, {} Heading: {}", - spawn2_id, - client_position.x, - client_position.y, - client_position.z, - client_position.w - ).c_str() - ); - target->GMMove( - client_position.x, - client_position.y, - client_position.z, - client_position.w - ); - return; - } else if (is_set_version) { - if (arguments != 2 || !sep->IsNumber(2)) { - c->Message(Chat::White, "Usage: #advnpcspawn setversion [Version]"); - return; - } - - if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { - c->Message(Chat::White, "You must target an NPC to use this command."); - return; - } - - NPC* target = c->GetTarget()->CastToNPC(); - auto version = std::stoi(sep->arg[2]); - std::string query = fmt::format( - "UPDATE spawn2 SET version = {} WHERE spawngroupID = {}", - version, - target->GetSpawnGroupId() - ); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Failed to set version."); - return; - } - - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Version Modified | Name: {} ({}) Version: {}", - target->GetSpawnGroupId(), - target->GetCleanName(), - target->GetID(), - version - ).c_str() - ); - target->Depop(false); - return; - } -} - -void command_aggrozone(Client *c, const Seperator *sep) { - if(!c) - return; - - Mob *m = c->CastToMob(); - - if (!m) - return; - - uint32 hate = atoi(sep->arg[1]); //should default to 0 if we don't enter anything - entity_list.AggroZone(m, hate); - c->Message(Chat::White, "Train to you! Last chance to go invulnerable..."); -} - -void command_modifynpcstat(Client *c, const Seperator *sep) -{ - if(!c) - return; - - if(sep->arg[1][0] == '\0') - { - c->Message(Chat::White, "usage #modifynpcstat arg value"); - c->Message(Chat::White, "Args: ac, str, sta, agi, dex, wis, _int, cha, max_hp, mr, fr, cr, pr, dr, runspeed, special_attacks, " - "attack_speed, atk, accuracy, trackable, min_hit, max_hit, see_invis_undead, see_hide, see_improved_hide, " - "hp_regen, mana_regen, aggro, assist, slow_mitigation, loottable_id, healscale, spellscale"); - return; - } - - if(!c->GetTarget()) - return; - - if(!c->GetTarget()->IsNPC()) - return; - - c->GetTarget()->CastToNPC()->ModifyNPCStat(sep->arg[1], sep->arg[2]); -} - -void command_instance(Client *c, const Seperator *sep) -{ - if(!c) - return; - - //options: - //help - //create [zone_id] [version] - //destroy [instance_id] - //add [instance_id] [player_name] - //remove [instance_id] [player_name] - //list [player_name] - - if(strcasecmp(sep->arg[1], "help") == 0) - { - c->Message(Chat::White, "#instance usage:"); - c->Message(Chat::White, "#instance create zone_id version duration - Creates an instance of version 'version' in the " - "zone with id matching zone_id, will last for duration seconds."); - c->Message(Chat::White, "#instance destroy instance_id - Destroys the instance with id matching instance_id."); - c->Message(Chat::White, "#instance add instance_id player_name - adds the player 'player_name' to the instance " - "with id matching instance_id."); - c->Message(Chat::White, "#instance remove instance_id player_name - removes the player 'player_name' from the " - "instance with id matching instance_id."); - c->Message(Chat::White, "#instance list player_name - lists all the instances 'player_name' is apart of."); - return; - } - else if(strcasecmp(sep->arg[1], "create") == 0) - { - if(!sep->IsNumber(3) || !sep->IsNumber(4)) - { - c->Message(Chat::White, "#instance create zone_id version duration - Creates an instance of version 'version' in the " - "zone with id matching zone_id, will last for duration seconds."); - return; - } - - const char * zn = nullptr; - uint32 zone_id = 0; - - if(sep->IsNumber(2)) - { - zone_id = atoi(sep->arg[2]); - } - else - { - zone_id = ZoneID(sep->arg[2]); - } - - uint32 version = atoi(sep->arg[3]); - uint32 duration = atoi(sep->arg[4]); - zn = ZoneName(zone_id); - - if(!zn) - { - c->Message(Chat::White, "Zone with id %lu was not found by the server.", (unsigned long)zone_id); - return; - } - - uint16 id = 0; - if(!database.GetUnusedInstanceID(id)) - { - c->Message(Chat::White, "Server was unable to find a free instance id."); - return; - } - - if(!database.CreateInstance(id, zone_id, version, duration)) - { - c->Message(Chat::White, "Server was unable to create a new instance."); - return; - } - - c->Message(Chat::White, "New instance %s was created with id %lu.", zn, (unsigned long)id); - } - else if(strcasecmp(sep->arg[1], "destroy") == 0) - { - if(!sep->IsNumber(2)) - { - c->Message(Chat::White, "#instance destroy instance_id - Destroys the instance with id matching instance_id."); - return; - } - - uint16 id = atoi(sep->arg[2]); - database.DeleteInstance(id); - c->Message(Chat::White, "Destroyed instance with id %lu.", (unsigned long)id); - } - else if(strcasecmp(sep->arg[1], "add") == 0) - { - if(!sep->IsNumber(2)) - { - c->Message(Chat::White, "#instance add instance_id player_name - adds the player 'player_name' to the instance " - "with id matching instance_id."); - return; - } - - uint16 id = atoi(sep->arg[2]); - uint32 charid = database.GetCharacterID(sep->arg[3]); - - if(id <= 0 || charid <= 0) - { - c->Message(Chat::White, "Must enter a valid instance id and player name."); - return; - } - - if(!database.CheckInstanceExists(id)) - { - c->Message(Chat::White, "Instance does not exist."); - return; - } - - uint32 zone_id = database.ZoneIDFromInstanceID(id); - uint32 version = database.VersionFromInstanceID(id); - uint32 cur_id = database.GetInstanceID(zone_id, charid, version); - if(cur_id == 0) - { - if(database.AddClientToInstance(id, charid)) - { - c->Message(Chat::White, "Added client to instance."); - } - else - { - c->Message(Chat::White, "Failed to add client to instance."); - } - } - else - { - c->Message(Chat::White, "Client was already saved to %u which has uses the same zone and version as that instance.", cur_id); - } - } - else if(strcasecmp(sep->arg[1], "remove") == 0) - { - if(!sep->IsNumber(2)) - { - c->Message(Chat::White, "#instance remove instance_id player_name - removes the player 'player_name' from the " - "instance with id matching instance_id."); - return; - } - - uint16 id = atoi(sep->arg[2]); - uint32 charid = database.GetCharacterID(sep->arg[3]); - - if(id <= 0 || charid <= 0) - { - c->Message(Chat::White, "Must enter a valid instance id and player name."); - } - - if(database.RemoveClientFromInstance(id, charid)) - { - c->Message(Chat::White, "Removed client from instance."); - } - else - { - c->Message(Chat::White, "Failed to remove client from instance."); - } - } - else if(strcasecmp(sep->arg[1], "list") == 0) - { - uint32 charid = database.GetCharacterID(sep->arg[2]); - if(charid <= 0) - { - if(c->GetTarget() == nullptr || (c->GetTarget() && !c->GetTarget()->IsClient())) - { - c->Message(Chat::White, "Character not found."); - return; - } - else - charid = c->GetTarget()->CastToClient()->CharacterID(); - } - - database.ListAllInstances(c, charid); - } - else - { - c->Message(Chat::White, "Invalid Argument."); - c->Message(Chat::White, "#instance usage:"); - c->Message(Chat::White, "#instance create zone_id version duration - Creates an instance of version 'version' in the " - "zone with id matching zone_id, will last for duration seconds."); - c->Message(Chat::White, "#instance destroy instance_id - Destroys the instance with id matching instance_id."); - c->Message(Chat::White, "#instance add instance_id player_name - adds the player 'player_name' to the instance " - "with id matching instance_id."); - c->Message(Chat::White, "#instance remove instance_id player_name - removes the player 'player_name' from the " - "instance with id matching instance_id."); - c->Message(Chat::White, "#instance list player_name - lists all the instances 'player_name' is apart of."); - return; - } -} - -void command_setstartzone(Client *c, const Seperator *sep) -{ - uint32 startzone = 0; - Client* target = nullptr; - if(c->GetTarget() && c->GetTarget()->IsClient() && sep->arg[1][0] != 0) - target = c->GetTarget()->CastToClient(); - else { - c->Message(Chat::White, "Usage: (needs PC target) #setstartzone zonename"); - c->Message(Chat::White, "Optional Usage: Use '#setstartzone reset' or '#setstartzone 0' to clear a starting zone. Player can select a starting zone using /setstartcity"); - return; - } - - if(sep->IsNumber(1)) { - startzone = atoi(sep->arg[1]); - } - else if(strcasecmp(sep->arg[1],"reset") == 0) { - startzone = 0; - } - else { - startzone = ZoneID(sep->arg[1]); - if(startzone == 0) { - c->Message(Chat::White, "Unable to locate zone '%s'", sep->arg[1]); - return; - } - } - - target->SetStartZone(startzone); -} - -void command_netstats(Client *c, const Seperator *sep) -{ - if(c) - { - auto client = c; - if (c->GetTarget() && c->GetTarget()->IsClient()) { - client = c->GetTarget()->CastToClient(); - } - - if (strcasecmp(sep->arg[1], "reset") == 0) { - auto connection = c->Connection(); - c->Message(Chat::White, "Resetting client stats (packet loss will not read correctly after reset)."); - connection->ResetStats(); - return; - } - - auto connection = c->Connection(); - auto opts = connection->GetManager()->GetOptions(); - auto eqs_stats = connection->GetStats(); - auto &stats = eqs_stats.DaybreakStats; - auto now = EQ::Net::Clock::now(); - auto sec_since_stats_reset = std::chrono::duration_cast>(now - stats.created).count(); - - c->Message(Chat::White, "Netstats:"); - c->Message(Chat::White, "--------------------------------------------------------------------"); - c->Message(Chat::White, "Sent Bytes: %u (%.2f/sec)", stats.sent_bytes, stats.sent_bytes / sec_since_stats_reset); - c->Message(Chat::White, "Recv Bytes: %u (%.2f/sec)", stats.recv_bytes, stats.recv_bytes / sec_since_stats_reset); - c->Message(Chat::White, "Bytes Before Encode (Sent): %u, Compression Rate: %.2f%%", stats.bytes_before_encode, - static_cast(stats.bytes_before_encode - stats.sent_bytes) / static_cast(stats.bytes_before_encode) * 100.0); - c->Message(Chat::White, "Bytes After Decode (Recv): %u, Compression Rate: %.2f%%", stats.bytes_after_decode, - static_cast(stats.bytes_after_decode - stats.recv_bytes) / static_cast(stats.bytes_after_decode) * 100.0); - c->Message(Chat::White, "Min Ping: %u", stats.min_ping); - c->Message(Chat::White, "Max Ping: %u", stats.max_ping); - c->Message(Chat::White, "Last Ping: %u", stats.last_ping); - c->Message(Chat::White, "Average Ping: %u", stats.avg_ping); - c->Message(Chat::White, "--------------------------------------------------------------------"); - c->Message(Chat::White, "(Realtime) Recv Packets: %u (%.2f/sec)", stats.recv_packets, stats.recv_packets / sec_since_stats_reset); - c->Message(Chat::White, "(Realtime) Sent Packets: %u (%.2f/sec)", stats.sent_packets, stats.sent_packets / sec_since_stats_reset); - c->Message(Chat::White, "(Sync) Recv Packets: %u", stats.sync_recv_packets); - c->Message(Chat::White, "(Sync) Sent Packets: %u", stats.sync_sent_packets); - c->Message(Chat::White, "(Sync) Remote Recv Packets: %u", stats.sync_remote_recv_packets); - c->Message(Chat::White, "(Sync) Remote Sent Packets: %u", stats.sync_remote_sent_packets); - c->Message(Chat::White, "Packet Loss In: %.2f%%", 100.0 * (1.0 - static_cast(stats.sync_recv_packets) / static_cast(stats.sync_remote_sent_packets))); - c->Message(Chat::White, "Packet Loss Out: %.2f%%", 100.0 * (1.0 - static_cast(stats.sync_remote_recv_packets) / static_cast(stats.sync_sent_packets))); - c->Message(Chat::White, "--------------------------------------------------------------------"); - c->Message(Chat::White, "Resent Packets: %u (%.2f/sec)", stats.resent_packets, stats.resent_packets / sec_since_stats_reset); - c->Message(Chat::White, "Resent Fragments: %u (%.2f/sec)", stats.resent_fragments, stats.resent_fragments / sec_since_stats_reset); - c->Message(Chat::White, "Resent Non-Fragments: %u (%.2f/sec)", stats.resent_full, stats.resent_full / sec_since_stats_reset); - c->Message(Chat::White, "Dropped Datarate Packets: %u (%.2f/sec)", stats.dropped_datarate_packets, stats.dropped_datarate_packets / sec_since_stats_reset); - - if (opts.daybreak_options.outgoing_data_rate > 0.0) { - c->Message(Chat::White, "Outgoing Link Saturation %.2f%% (%.2fkb/sec)", 100.0 * (1.0 - ((opts.daybreak_options.outgoing_data_rate - stats.datarate_remaining) / opts.daybreak_options.outgoing_data_rate)), opts.daybreak_options.outgoing_data_rate); - } - - if (strcasecmp(sep->arg[1], "full") == 0) { - c->Message(Chat::White, "--------------------------------------------------------------------"); - c->Message(Chat::White, "Sent Packet Types"); - for (auto i = 0; i < _maxEmuOpcode; ++i) { - auto cnt = eqs_stats.SentCount[i]; - if (cnt > 0) { - c->Message(Chat::White, "%s: %u (%.2f / sec)", OpcodeNames[i], cnt, cnt / sec_since_stats_reset); - } - } - - c->Message(Chat::White, "--------------------------------------------------------------------"); - c->Message(Chat::White, "Recv Packet Types"); - for (auto i = 0; i < _maxEmuOpcode; ++i) { - auto cnt = eqs_stats.RecvCount[i]; - if (cnt > 0) { - c->Message(Chat::White, "%s: %u (%.2f / sec)", OpcodeNames[i], cnt, cnt / sec_since_stats_reset); - } - } - } - - c->Message(Chat::White, "--------------------------------------------------------------------"); - } -} - -void command_object(Client *c, const Seperator *sep) -{ - if (!c) - return; // Crash Suppressant: No client. How did we get here? - - // Save it here. We sometimes have need to refer to it in multiple places. - const char *usage_string = "Usage: #object List|Add|Edit|Move|Rotate|Save|Copy|Delete|Undo"; - - if ((!sep) || (sep->argnum == 0)) { - c->Message(Chat::White, usage_string); - return; - } - - Object *o = nullptr; - Object_Struct od; - Door_Struct *ds; - uint32 id = 0; - uint32 itemid = 0; - uint32 icon = 0; - uint32 instance = 0; - uint32 newid = 0; - uint16 radius; - EQApplicationPacket *app; - - bool bNewObject = false; - - float x2; - float y2; - - // Temporary object type for static objects to allow manipulation - // NOTE: Zone::LoadZoneObjects() currently loads this as an uint8, so max value is 255! - static const uint32 staticType = 255; - - // Case insensitive commands (List == list == LIST) - strlwr(sep->arg[1]); - - if (strcasecmp(sep->arg[1], "list") == 0) { - // Insufficient or invalid args - if ((sep->argnum < 2) || (sep->arg[2][0] < '0') || - ((sep->arg[2][0] > '9') && ((sep->arg[2][0] & 0xDF) != 'A'))) { - c->Message(Chat::White, "Usage: #object List All|(radius)"); - return; - } - - if ((sep->arg[2][0] & 0xDF) == 'A') - radius = 0; // List All - else if ((radius = atoi(sep->arg[2])) <= 0) - radius = 500; // Invalid radius. Default to 500 units. - - if (radius == 0) - c->Message(Chat::White, "Objects within this zone:"); - else - c->Message(Chat::White, "Objects within %u units of your current location:", radius); - - std::string query; - if (radius) - query = StringFormat( - "SELECT id, xpos, ypos, zpos, heading, itemid, " - "objectname, type, icon, unknown08, unknown10, unknown20 " - "FROM object WHERE zoneid = %u AND version = %u " - "AND (xpos BETWEEN %.1f AND %.1f) " - "AND (ypos BETWEEN %.1f AND %.1f) " - "AND (zpos BETWEEN %.1f AND %.1f) " - "ORDER BY id", - zone->GetZoneID(), zone->GetInstanceVersion(), - c->GetX() - radius, // Yes, we're actually using a bounding box instead of a radius. - c->GetX() + radius, // Much less processing power used this way. - c->GetY() - radius, c->GetY() + radius, c->GetZ() - radius, c->GetZ() + radius); - else - query = StringFormat("SELECT id, xpos, ypos, zpos, heading, itemid, " - "objectname, type, icon, unknown08, unknown10, unknown20 " - "FROM object WHERE zoneid = %u AND version = %u " - "ORDER BY id", - zone->GetZoneID(), zone->GetInstanceVersion()); - - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Error in objects query"); - return; - } - - for (auto row = results.begin(); row != results.end(); ++row) { - id = atoi(row[0]); - od.x = atof(row[1]); - od.y = atof(row[2]); - od.z = atof(row[3]); - od.heading = atof(row[4]); - itemid = atoi(row[5]); - strn0cpy(od.object_name, row[6], sizeof(od.object_name)); - od.object_name[sizeof(od.object_name) - 1] = - '\0'; // Required if strlen(row[col++]) exactly == sizeof(object_name) - - od.object_type = atoi(row[7]); - icon = atoi(row[8]); - od.size = atoi(row[9]); - od.solidtype = atoi(row[10]); - od.unknown020 = atoi(row[11]); - - switch (od.object_type) { - case 0: // Static Object - case staticType: // Static Object unlocked for changes - if (od.size == 0) // Unknown08 field is optional Size parameter for static objects - od.size = 100; // Static object default Size is 100% - - c->Message(Chat::White, "- STATIC Object (%s): id %u, x %.1f, y %.1f, z %.1f, h %.1f, model %s, " - "size %u, solidtype %u, incline %u", - (od.object_type == 0) ? "locked" : "unlocked", id, od.x, od.y, od.z, - od.heading, od.object_name, od.size, od.solidtype, od.unknown020); - break; - - case OT_DROPPEDITEM: // Ground Spawn - c->Message(Chat::White, "- TEMPORARY Object: id %u, x %.1f, y %.1f, z %.1f, h %.1f, itemid %u, " - "model %s, icon %u", - id, od.x, od.y, od.z, od.heading, itemid, od.object_name, icon); - break; - - default: // All others == Tradeskill Objects - c->Message(Chat::White, "- TRADESKILL Object: id %u, x %.1f, y %.1f, z %.1f, h %.1f, model %s, " - "type %u, icon %u", - id, od.x, od.y, od.z, od.heading, od.object_name, od.object_type, icon); - break; - } - } - - c->Message(Chat::White, "%u object%s found", results.RowCount(), (results.RowCount() == 1) ? "" : "s"); - return; - } - - if (strcasecmp(sep->arg[1], "add") == 0) { - // Insufficient or invalid arguments - if ((sep->argnum < 3) || - ((sep->arg[3][0] == '\0') && (sep->arg[4][0] < '0') && (sep->arg[4][0] > '9'))) { - c->Message(Chat::White, "Usage: (Static Object): #object Add [ObjectID] 0 Model [SizePercent] " - "[SolidType] [Incline]"); - c->Message(Chat::White, "Usage: (Tradeskill Object): #object Add [ObjectID] TypeNum Model Icon"); - c->Message(Chat::White, "- Notes: Model must start with a letter, max length 16. SolidTypes = 0 (Solid), " - "1 (Sometimes Non-Solid)"); - return; - } - - int col; - - if (sep->argnum > 3) { // Model name in arg3? - if ((sep->arg[3][0] <= '9') && (sep->arg[3][0] >= '0')) { - // Nope, user must have specified ObjectID. Extract it. - id = atoi(sep->arg[2]); - col = 1; // Bump all other arguments one to the right. Model is in arg4. - } else { - // Yep, arg3 is non-numeric, ObjectID must be omitted and model must be arg3 - id = 0; - col = 0; - } - } else { - // Nope, only 3 args. Object ID must be omitted and arg3 must be model. - id = 0; - col = 0; - } - - memset(&od, 0, sizeof(od)); - - od.object_type = atoi(sep->arg[2 + col]); - - switch (od.object_type) { - case 0: // Static Object - if ((sep->argnum - col) > 3) { - od.size = atoi(sep->arg[4 + col]); // Size specified - - if ((sep->argnum - col) > 4) { - od.solidtype = atoi(sep->arg[5 + col]); // SolidType specified - - if ((sep->argnum - col) > 5) - od.unknown020 = atoi(sep->arg[6 + col]); // Incline specified - } - } - break; - - case 1: // Ground Spawn - c->Message(Chat::White, "ERROR: Object Type 1 is used for temporarily spawned ground spawns and dropped " - "items, which are not supported with #object. See the 'ground_spawns' table in " - "the database."); - return; - - default: // Everything else == Tradeskill Object - icon = ((sep->argnum - col) > 3) ? atoi(sep->arg[4 + col]) : 0; - - if (icon == 0) { - c->Message(Chat::White, "ERROR: Required property 'Icon' not specified for Tradeskill Object"); - return; - } - - break; - } - - od.x = c->GetX(); - od.y = c->GetY(); - od.z = c->GetZ() - (c->GetSize() * 0.625f); - od.heading = c->GetHeading(); - - std::string query; - if (id) { - // ID specified. Verify that it doesn't already exist. - query = StringFormat("SELECT COUNT(*) FROM object WHERE ID = %u", id); - auto results = content_db.QueryDatabase(query); - if (results.Success() && results.RowCount() != 0) { - auto row = results.begin(); - if (atoi(row[0]) > 0) // Yep, in database already. - id = 0; - } - - // Not in database. Already spawned, just not saved? - // Yep, already spawned. - if (id && entity_list.FindObject(id)) - id = 0; - - if (id == 0) { - c->Message(Chat::White, "ERROR: An object already exists with the id %u", atoi(sep->arg[2])); - return; - } - } - - int objectsFound = 0; - // Verify no other objects already in this spot (accidental double-click of Hotkey?) - query = StringFormat( - "SELECT COUNT(*) FROM object WHERE zoneid = %u " - "AND version=%u AND (xpos BETWEEN %.1f AND %.1f) " - "AND (ypos BETWEEN %.1f AND %.1f) " - "AND (zpos BETWEEN %.1f AND %.1f)", - zone->GetZoneID(), zone->GetInstanceVersion(), od.x - 0.2f, - od.x + 0.2f, // Yes, we're actually using a bounding box instead of a radius. - od.y - 0.2f, od.y + 0.2f, // Much less processing power used this way. - od.z - 0.2f, od.z + 0.2f); // It's pretty forgiving, though, allowing for close-proximity objects - - auto results = content_db.QueryDatabase(query); - if (results.Success() && results.RowCount() != 0) { - auto row = results.begin(); - objectsFound = atoi(row[0]); // Number of nearby objects from database - } - - // No objects found in database too close. How about spawned but not yet saved? - if (objectsFound == 0 && entity_list.FindNearbyObject(od.x, od.y, od.z, 0.2f)) - objectsFound = 1; - - if (objectsFound) { - c->Message(Chat::White, "ERROR: Object already at this location."); - return; - } - - // Strip any single quotes from objectname (SQL injection FTL!) - strn0cpy(od.object_name, sep->arg[3 + col], sizeof(od.object_name)); - - uint32 len = strlen(od.object_name); - for (col = 0; col < (uint32)len; col++) { - if (od.object_name[col] != '\'') - continue; - - // Uh oh, 1337 h4x0r monkeying around! Strip that apostrophe! - memcpy(&od.object_name[col], &od.object_name[col + 1], len - col); - len--; - col--; - } - - strupr(od.object_name); // Model names are always upper-case. - - if ((od.object_name[0] < 'A') || (od.object_name[0] > 'Z')) { - c->Message(Chat::White, "ERROR: Model name must start with a letter."); - return; - } - - if (id == 0) { - // No ID specified. Get a best-guess next number from the database - // If there's a problem retrieving an ID from the database, it'll end up being object # 1. No - // biggie. - - query = "SELECT MAX(id) FROM object"; - results = content_db.QueryDatabase(query); - if (results.Success() && results.RowCount() != 0) { - auto row = results.begin(); - id = atoi(row[0]); - } - - id++; - } - - // Make sure not to overwrite already-spawned objects that haven't been saved yet. - while (o = entity_list.FindObject(id)) - id++; - - // Static object - if (od.object_type == 0) - od.object_type = staticType; // Temporary. We'll make it 0 when we Save - - od.zone_id = zone->GetZoneID(); - od.zone_instance = zone->GetInstanceVersion(); - - o = new Object(id, od.object_type, icon, od, nullptr); - - // Add to our zone entity list and spawn immediately for all clients - entity_list.AddObject(o, true); - - // Bump player back to avoid getting stuck inside new object - - x2 = 10.0f * sin(c->GetHeading() / 256.0f * 3.14159265f); - y2 = 10.0f * cos(c->GetHeading() / 256.0f * 3.14159265f); - c->MovePC(c->GetX() - x2, c->GetY() - y2, c->GetZ(), c->GetHeading()); - - c->Message(Chat::White, "Spawning object with tentative id %u at location (%.1f, %.1f, %.1f heading %.1f). Use " - "'#object Save' to save to database when satisfied with placement.", - id, od.x, od.y, od.z, od.heading); - - // Temporary Static Object - if (od.object_type == staticType) - c->Message(Chat::White, "- Note: Static Object will act like a tradeskill container and will not reflect " - "size, solidtype, or incline values until you commit with '#object Save', after " - "which it will be unchangeable until you use '#object Edit' and zone back in."); - - return; - } - - if (strcasecmp(sep->arg[1], "edit") == 0) { - - if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) < 1)) { - c->Message(Chat::White, "Usage: #object Edit (ObjectID) [PropertyName] [NewValue]"); - c->Message(Chat::White, "- Static Object (Type 0) Properties: model, type, size, solidtype, incline"); - c->Message(Chat::White, "- Tradeskill Object (Type 2+) Properties: model, type, icon"); - - return; - } - - o = entity_list.FindObject(id); - - // Object already available in-zone? - if (o) { - // Yep, looks like we can make real-time changes. - if (sep->argnum < 4) { - // Or not. '#object Edit (ObjectID)' called without PropertyName and NewValue - c->Message(Chat::White, "Note: Object %u already unlocked and ready for changes", id); - return; - } - } else { - // Object not found in-zone in a modifiable form. Check for valid matching circumstances. - std::string query = StringFormat("SELECT zoneid, version, type FROM object WHERE id = %u", id); - auto results = content_db.QueryDatabase(query); - if (!results.Success() || results.RowCount() == 0) { - c->Message(Chat::White, "ERROR: Object %u not found", id); - return; - } - - auto row = results.begin(); - od.zone_id = atoi(row[0]); - od.zone_instance = atoi(row[1]); - od.object_type = atoi(row[2]); - uint32 objectsFound = 1; - - // Object not in this zone? - if (od.zone_id != zone->GetZoneID()) { - c->Message(Chat::White, "ERROR: Object %u not in this zone.", id); - return; - } - - // Object not in this instance? - if (od.zone_instance != zone->GetInstanceVersion()) { - c->Message(Chat::White, "ERROR: Object %u not part of this instance version.", id); - return; - } - - switch (od.object_type) { - case 0: // Static object needing unlocking - // Convert to tradeskill object temporarily for changes - query = StringFormat("UPDATE object SET type = %u WHERE id = %u", staticType, id); - - content_db.QueryDatabase(query); - - c->Message(Chat::White, "Static Object %u unlocked for editing. You must zone out and back in to " - "make your changes, then commit them with '#object Save'.", - id); - if (sep->argnum >= 4) - c->Message(Chat::White, "NOTE: The change you specified has not been applied, since the " - "static object had not been unlocked for editing yet."); - return; - - case OT_DROPPEDITEM: - c->Message(Chat::White, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, " - "which cannot be manipulated with #object. See the 'ground_spawns' table " - "in the database.", - id); - return; - - case staticType: - c->Message(Chat::White, "ERROR: Object %u has been unlocked for editing, but you must zone out " - "and back in for your client to refresh its object table before you can " - "make changes to it.", - id); - return; - - default: - // Unknown error preventing us from seeing the object in the zone. - c->Message(Chat::White, "ERROR: Unknown problem attempting to manipulate object %u", id); - return; - } - } - - // If we're here, we have a manipulable object ready for changes. - strlwr(sep->arg[3]); // Case insensitive PropertyName - strupr(sep->arg[4]); // In case it's model name, which should always be upper-case - - // Read current object info for reference - icon = o->GetIcon(); - o->GetObjectData(&od); - - // We'll be a little more picky with property names, to prevent errors. Check against the whole word. - if (strcmp(sep->arg[3], "model") == 0) { - - if ((sep->arg[4][0] < 'A') || (sep->arg[4][0] > 'Z')) { - c->Message(Chat::White, "ERROR: Model names must begin with a letter."); - return; - } - - strn0cpy(od.object_name, sep->arg[4], sizeof(od.object_name)); - - o->SetObjectData(&od); - - c->Message(Chat::White, "Object %u now being rendered with model '%s'", id, od.object_name); - } else if (strcmp(sep->arg[3], "type") == 0) { - if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { - c->Message(Chat::White, "ERROR: Invalid type number"); - return; - } - - od.object_type = atoi(sep->arg[4]); - - switch (od.object_type) { - case 0: - // Convert Static Object to temporary changeable type - od.object_type = staticType; - c->Message(Chat::White, "Note: Static Object will still act like tradeskill object and will not " - "reflect size, solidtype, or incline settings until committed to the " - "database with '#object Save', after which it will be unchangeable until " - "it is unlocked again with '#object Edit'."); - break; - - case OT_DROPPEDITEM: - c->Message(Chat::White, "ERROR: Object Type 1 is used for temporarily spawned ground spawns and " - "dropped items, which are not supported with #object. See the " - "'ground_spawns' table in the database."); - return; - - default: - c->Message(Chat::White, "Object %u changed to Tradeskill Object Type %u", id, od.object_type); - break; - } - - o->SetType(od.object_type); - } else if (strcmp(sep->arg[3], "size") == 0) { - if (od.object_type != staticType) { - c->Message( - 0, "ERROR: Object %u is not a Static Object and does not support the Size property", - id); - return; - } - - if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { - c->Message(Chat::White, "ERROR: Invalid size specified. Please enter a number."); - return; - } - - od.size = atoi(sep->arg[4]); - o->SetObjectData(&od); - - if (od.size == 0) // 0 == unspecified == 100% - od.size = 100; - - c->Message(Chat::White, "Static Object %u set to %u%% size. Size will take effect when you commit to the " - "database with '#object Save', after which the object will be unchangeable until " - "you unlock it again with '#object Edit' and zone out and back in.", - id, od.size); - } else if (strcmp(sep->arg[3], "solidtype") == 0) { - - if (od.object_type != staticType) { - c->Message(Chat::White, "ERROR: Object %u is not a Static Object and does not support the " - "SolidType property", - id); - return; - } - - if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { - c->Message(Chat::White, "ERROR: Invalid solidtype specified. Please enter a number."); - return; - } - - od.solidtype = atoi(sep->arg[4]); - o->SetObjectData(&od); - - c->Message(Chat::White, "Static Object %u set to SolidType %u. Change will take effect when you commit " - "to the database with '#object Save'. Support for this property is on a " - "per-model basis, mostly seen in smaller objects such as chests and tables.", - id, od.solidtype); - } else if (strcmp(sep->arg[3], "icon") == 0) { - - if ((od.object_type < 2) || (od.object_type == staticType)) { - c->Message(Chat::White, "ERROR: Object %u is not a Tradeskill Object and does not support the " - "Icon property", - id); - return; - } - - if ((icon = atoi(sep->arg[4])) == 0) { - c->Message(Chat::White, "ERROR: Invalid Icon specified. Please enter an icon number."); - return; - } - - o->SetIcon(icon); - c->Message(Chat::White, "Tradeskill Object %u icon set to %u", id, icon); - } else if (strcmp(sep->arg[3], "incline") == 0) { - if (od.object_type != staticType) { - c->Message( - 0, - "ERROR: Object %u is not a Static Object and does not support the Incline property", - id); - return; - } - - if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { - c->Message( - 0, - "ERROR: Invalid Incline specified. Please enter a number. Normal range is 0-512."); - return; - } - - od.unknown020 = atoi(sep->arg[4]); - o->SetObjectData(&od); - - c->Message(Chat::White, "Static Object %u set to %u incline. Incline will take effect when you commit to " - "the database with '#object Save', after which the object will be unchangeable " - "until you unlock it again with '#object Edit' and zone out and back in.", - id, od.unknown020); - } else { - c->Message(Chat::White, "ERROR: Unrecognized property name: %s", sep->arg[3]); - return; - } - - // Repop object to have it reflect the change. - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - - app = new EQApplicationPacket(); - o->CreateSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - return; - } - - if (strcasecmp(sep->arg[1], "move") == 0) { - - if ((sep->argnum < 2) || // Not enough arguments - ((id = atoi(sep->arg[2])) == 0) || // ID not specified - (((sep->arg[3][0] < '0') || (sep->arg[3][0] > '9')) && ((sep->arg[3][0] & 0xDF) != 'T') && - (sep->arg[3][0] != '-') && (sep->arg[3][0] != '.'))) { // Location argument not specified correctly - c->Message(Chat::White, "Usage: #object Move (ObjectID) ToMe|(x y z [h])"); - return; - } - - if (!(o = entity_list.FindObject(id))) { - std::string query = StringFormat("SELECT zoneid, version, type FROM object WHERE id = %u", id); - auto results = content_db.QueryDatabase(query); - if (!results.Success() || results.RowCount() == 0) { - c->Message(Chat::White, "ERROR: Object %u not found", id); - return; - } - - auto row = results.begin(); - od.zone_id = atoi(row[0]); - od.zone_instance = atoi(row[1]); - od.object_type = atoi(row[2]); - - if (od.zone_id != zone->GetZoneID()) { - c->Message(Chat::White, "ERROR: Object %u is not in this zone", id); - return; - } - - if (od.zone_instance != zone->GetInstanceVersion()) { - c->Message(Chat::White, "ERROR: Object %u is not in this instance version", id); - return; - } - - switch (od.object_type) { - case 0: - c->Message(Chat::White, "ERROR: Object %u is not yet unlocked for editing. Use '#object Edit' " - "then zone out and back in to move it.", - id); - return; - - case staticType: - c->Message(Chat::White, "ERROR: Object %u has been unlocked for editing, but you must zone out " - "and back in before your client sees the change and will allow you to " - "move it.", - id); - return; - - case 1: - c->Message(Chat::White, "ERROR: Object %u is a temporary spawned object and cannot be " - "manipulated with #object. See the 'ground_spawns' table in the " - "database.", - id); - return; - - default: - c->Message(Chat::White, "ERROR: Object %u not located in zone.", id); - return; - } - } - - // Move To Me - if ((sep->arg[3][0] & 0xDF) == 'T') { - od.x = c->GetX(); - od.y = c->GetY(); - od.z = c->GetZ() - - (c->GetSize() * - 0.625f); // Compensate for #loc bumping up Z coordinate by 62.5% of character's size. - - o->SetHeading(c->GetHeading()); - - // Bump player back to avoid getting stuck inside object - - x2 = 10.0f * std::sin(c->GetHeading() / 256.0f * 3.14159265f); - y2 = 10.0f * std::cos(c->GetHeading() / 256.0f * 3.14159265f); - c->MovePC(c->GetX() - x2, c->GetY() - y2, c->GetZ(), c->GetHeading()); - } // Move to x, y, z [h] - else { - od.x = atof(sep->arg[3]); - if (sep->argnum > 3) - od.y = atof(sep->arg[4]); - else - o->GetLocation(nullptr, &od.y, nullptr); - - if (sep->argnum > 4) - od.z = atof(sep->arg[5]); - else - o->GetLocation(nullptr, nullptr, &od.z); - - if (sep->argnum > 5) - o->SetHeading(atof(sep->arg[6])); - } - - o->SetLocation(od.x, od.y, od.z); - - // Despawn and respawn object to reflect change - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - - app = new EQApplicationPacket(); - o->CreateSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - return; - } - - if (strcasecmp(sep->arg[1], "rotate") == 0) { - // Insufficient or invalid arguments - if ((sep->argnum < 3) || ((id = atoi(sep->arg[2])) == 0)) { - c->Message(Chat::White, "Usage: #object Rotate (ObjectID) (Heading, 0-512)"); - return; - } - - if ((o = entity_list.FindObject(id)) == nullptr) { - c->Message(Chat::White, "ERROR: Object %u not found in zone, or is a static object not yet unlocked with " - "'#object Edit' for editing.", - id); - return; - } - - o->SetHeading(atof(sep->arg[3])); - - // Despawn and respawn object to reflect change - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - - app = new EQApplicationPacket(); - o->CreateSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - return; - } - - if (strcasecmp(sep->arg[1], "save") == 0) { - // Insufficient or invalid arguments - if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) == 0)) { - c->Message(Chat::White, "Usage: #object Save (ObjectID)"); - return; - } - - o = entity_list.FindObject(id); - - od.zone_id = 0; - od.zone_instance = 0; - od.object_type = 0; - - // If this ID isn't in the database yet, it's a new object - bNewObject = true; - std::string query = StringFormat("SELECT zoneid, version, type FROM object WHERE id = %u", id); - auto results = content_db.QueryDatabase(query); - if (results.Success() && results.RowCount() != 0) { - auto row = results.begin(); - od.zone_id = atoi(row[0]); - od.zone_instance = atoi(row[1]); - od.object_type = atoi(row[2]); - - // ID already in database. Not a new object. - bNewObject = false; - } - - if (!o) { - // Object not found in zone. Can't save an object we can't see. - - if (bNewObject) { - c->Message(Chat::White, "ERROR: Object %u not found", id); - return; - } - - if (od.zone_id != zone->GetZoneID()) { - c->Message(Chat::White, "ERROR: Wrong Object ID. %u is not part of this zone.", id); - return; - } - - if (od.zone_instance != zone->GetInstanceVersion()) { - c->Message(Chat::White, "ERROR: Wrong Object ID. %u is not part of this instance version.", id); - return; - } - - if (od.object_type == 0) { - c->Message(Chat::White, "ERROR: Static Object %u has already been committed. Use '#object Edit " - "%u' and zone out and back in to make changes.", - id, id); - return; - } - - if (od.object_type == 1) { - c->Message(Chat::White, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, " - "which is not supported with #object. See the 'ground_spawns' table in " - "the database.", - id); - return; - } - - c->Message(Chat::White, "ERROR: Object %u not found.", id); - return; - } - - // Oops! Another GM already saved an object with our id from another zone. - // We'll have to get a new one. - if ((od.zone_id > 0) && (od.zone_id != zone->GetZoneID())) - id = 0; - - // Oops! Another GM already saved an object with our id from another instance. - // We'll have to get a new one. - if ((id > 0) && (od.zone_instance != zone->GetInstanceVersion())) - id = 0; - - // If we're asking for a new ID, it's a new object. - bNewObject |= (id == 0); - - o->GetObjectData(&od); - od.object_type = o->GetType(); - icon = o->GetIcon(); - - // We're committing to the database now. Return temporary object type to actual. - if (od.object_type == staticType) - od.object_type = 0; - - if (!bNewObject) - query = StringFormat("UPDATE object SET zoneid = %u, version = %u, " - "xpos = %.1f, ypos=%.1f, zpos=%.1f, heading=%.1f, " - "objectname = '%s', type = %u, icon = %u, " - "unknown08 = %u, unknown10 = %u, unknown20 = %u " - "WHERE ID = %u", - zone->GetZoneID(), zone->GetInstanceVersion(), od.x, od.y, od.z, - od.heading, od.object_name, od.object_type, icon, od.size, - od.solidtype, od.unknown020, id); - else if (id == 0) - query = StringFormat("INSERT INTO object " - "(zoneid, version, xpos, ypos, zpos, heading, objectname, " - "type, icon, unknown08, unknown10, unknown20) " - "VALUES (%u, %u, %.1f, %.1f, %.1f, %.1f, '%s', %u, %u, %u, %u, %u)", - zone->GetZoneID(), zone->GetInstanceVersion(), od.x, od.y, od.z, - od.heading, od.object_name, od.object_type, icon, od.size, - od.solidtype, od.unknown020); - else - query = StringFormat("INSERT INTO object " - "(id, zoneid, version, xpos, ypos, zpos, heading, objectname, " - "type, icon, unknown08, unknown10, unknown20) " - "VALUES (%u, %u, %u, %.1f, %.1f, %.1f, %.1f, '%s', %u, %u, %u, %u, %u)", - id, zone->GetZoneID(), zone->GetInstanceVersion(), od.x, od.y, od.z, - od.heading, od.object_name, od.object_type, icon, od.size, - od.solidtype, od.unknown020); - - results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Database Error: %s", results.ErrorMessage().c_str()); - return; - } - - if (results.RowsAffected() == 0) { - // No change made, but no error message given - c->Message(Chat::White, "Database Error: Could not save change to Object %u", id); - return; - } - - if (bNewObject) { - if (newid == results.LastInsertedID()) { - c->Message(Chat::White, "Saved new Object %u to database", id); - return; - } - - c->Message(Chat::White, "Saved Object. NOTE: Database returned a new ID number for object: %u", newid); - id = newid; - return; - } - - c->Message(Chat::White, "Saved changes to Object %u", id); - newid = id; - - if (od.object_type == 0) { - // Static Object - Respawn as nonfunctional door - - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - - entity_list.RemoveObject(o->GetID()); - - auto door = DoorsRepository::NewEntity(); - - door.zone = zone->GetShortName(); - - door.id = 1000000000 + id; // Out of range of normal use for doors.id - door.doorid = -1; // Client doesn't care if these are all the same door_id - door.pos_x = od.x; - door.pos_y = od.y; - door.pos_z = od.z; - door.heading = od.heading; - - door.name = od.object_name; - - // Strip trailing "_ACTORDEF" if present. Client won't accept it for doors. - int pos = door.name.size() - strlen("_ACTORDEF"); - if (pos > 0 && door.name.compare(pos, std::string::npos, "_ACTORDEF") == 0) - { - door.name.erase(pos); - } - - door.dest_zone = "NONE"; - - if ((door.size = od.size) == 0) // unknown08 = optional size percentage - door.size = 100; - - door.opentype = od.solidtype; - - switch (door.opentype) // unknown10 = optional request_nonsolid (0 or 1 or experimental number) - { - case 0: - door.opentype = 31; - break; - - case 1: - door.opentype = 9; - break; - } - - door.incline = od.unknown020; // unknown20 = optional incline value - door.client_version_mask = 0xFFFFFFFF; - - Doors* doors = new Doors(door); - - entity_list.AddDoor(doors); - - app = new EQApplicationPacket(OP_SpawnDoor, sizeof(Door_Struct)); - ds = (Door_Struct *)app->pBuffer; - - memset(ds, 0, sizeof(Door_Struct)); - memcpy(ds->name, door.name.c_str(), 32); - ds->xPos = door.pos_x; - ds->yPos = door.pos_y; - ds->zPos = door.pos_z; - ds->heading = door.heading; - ds->incline = door.incline; - ds->size = door.size; - ds->doorId = door.doorid; - ds->opentype = door.opentype; - ds->unknown0052[9] = 1; // *ptr-1 and *ptr-3 from EntityList::MakeDoorSpawnPacket() - ds->unknown0052[11] = 1; - - entity_list.QueueClients(0, app); - safe_delete(app); - - c->Message(Chat::White, "NOTE: Object %u is now a static object, and is unchangeable. To make future " - "changes, use '#object Edit' to convert it to a changeable form, then zone out " - "and back in.", - id); - } - return; - } - - if (strcasecmp(sep->arg[1], "copy") == 0) { - // Insufficient or invalid arguments - if ((sep->argnum < 3) || - (((sep->arg[2][0] & 0xDF) != 'A') && ((sep->arg[2][0] < '0') || (sep->arg[2][0] > '9')))) { - c->Message(Chat::White, "Usage: #object Copy All|(ObjectID) (InstanceVersion)"); - c->Message(Chat::White, "- Note: Only objects saved in the database can be copied to another instance."); - return; - } - - od.zone_instance = atoi(sep->arg[3]); - - if (od.zone_instance == zone->GetInstanceVersion()) { - c->Message(Chat::White, "ERROR: Source and destination instance versions are the same."); - return; - } - - if ((sep->arg[2][0] & 0xDF) == 'A') { - // Copy All - - std::string query = - StringFormat("INSERT INTO object " - "(zoneid, version, xpos, ypos, zpos, heading, itemid, " - "objectname, type, icon, unknown08, unknown10, unknown20) " - "SELECT zoneid, %u, xpos, ypos, zpos, heading, itemid, " - "objectname, type, icon, unknown08, unknown10, unknown20 " - "FROM object WHERE zoneid = %u) AND version = %u", - od.zone_instance, zone->GetZoneID(), zone->GetInstanceVersion()); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Database Error: %s", results.ErrorMessage().c_str()); - return; - } - - c->Message(Chat::White, "Copied %u object%s into instance version %u", results.RowCount(), - (results.RowCount() == 1) ? "" : "s", od.zone_instance); - return; - } - - id = atoi(sep->arg[2]); - - std::string query = StringFormat("INSERT INTO object " - "(zoneid, version, xpos, ypos, zpos, heading, itemid, " - "objectname, type, icon, unknown08, unknown10, unknown20) " - "SELECT zoneid, %u, xpos, ypos, zpos, heading, itemid, " - "objectname, type, icon, unknown08, unknown10, unknown20 " - "FROM object WHERE id = %u AND zoneid = %u AND version = %u", - od.zone_instance, id, zone->GetZoneID(), zone->GetInstanceVersion()); - auto results = content_db.QueryDatabase(query); - if (results.Success() && results.RowsAffected() > 0) { - c->Message(Chat::White, "Copied Object %u into instance version %u", id, od.zone_instance); - return; - } - - // Couldn't copy the object. - - // got an error message - if (!results.Success()) { - c->Message(Chat::White, "Database Error: %s", results.ErrorMessage().c_str()); - return; - } - - // No database error returned. See if we can figure out why. - - query = StringFormat("SELECT zoneid, version FROM object WHERE id = %u", id); - results = content_db.QueryDatabase(query); - if (!results.Success()) - return; - - if (results.RowCount() == 0) { - c->Message(Chat::White, "ERROR: Object %u not found", id); - return; - } - - auto row = results.begin(); - // Wrong ZoneID? - if (atoi(row[0]) != zone->GetZoneID()) { - c->Message(Chat::White, "ERROR: Object %u is not part of this zone.", id); - return; - } - - // Wrong Instance Version? - if (atoi(row[1]) != zone->GetInstanceVersion()) { - c->Message(Chat::White, "ERROR: Object %u is not part of this instance version.", id); - return; - } - - // Well, NO clue at this point. Just let 'em know something screwed up. - c->Message(Chat::White, "ERROR: Unknown database error copying Object %u to instance version %u", id, - od.zone_instance); - return; - } - - if (strcasecmp(sep->arg[1], "delete") == 0) { - - if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) <= 0)) { - c->Message(Chat::White, "Usage: #object Delete (ObjectID) -- NOTE: Object deletions are permanent and " - "cannot be undone!"); - return; - } - - o = entity_list.FindObject(id); - - if (o) { - // Object found in zone. - - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(nullptr, app); - - entity_list.RemoveObject(o->GetID()); - - // Verifying ZoneID and Version in case someone else ended up adding an object with our ID - // from a different zone/version. Don't want to delete someone else's work. - std::string query = StringFormat("DELETE FROM object " - "WHERE id = %u AND zoneid = %u " - "AND version = %u LIMIT 1", - id, zone->GetZoneID(), zone->GetInstanceVersion()); - auto results = content_db.QueryDatabase(query); - - c->Message(Chat::White, "Object %u deleted", id); - return; - } - - // Object not found in zone. - std::string query = StringFormat("SELECT type FROM object " - "WHERE id = %u AND zoneid = %u " - "AND version = %u LIMIT 1", - id, zone->GetZoneID(), zone->GetInstanceVersion()); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) - return; - - if (results.RowCount() == 0) { - c->Message(Chat::White, "ERROR: Object %u not found in this zone or instance!", id); - return; - } - - auto row = results.begin(); - - switch (atoi(row[0])) { - case 0: // Static Object - query = StringFormat("DELETE FROM object WHERE id = %u " - "AND zoneid = %u AND version = %u LIMIT 1", - id, zone->GetZoneID(), zone->GetInstanceVersion()); - results = content_db.QueryDatabase(query); - - c->Message(Chat::White, "Object %u deleted. NOTE: This static object will remain for anyone currently in " - "the zone until they next zone out and in.", - id); - return; - - case 1: // Temporary Spawn - c->Message(Chat::White, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, which " - "is not supported with #object. See the 'ground_spawns' table in the database.", - id); - return; - } - - return; - } - - if (strcasecmp(sep->arg[1], "undo") == 0) { - // Insufficient or invalid arguments - if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) == 0)) { - c->Message(Chat::White, "Usage: #object Undo (ObjectID) -- Reload object from database, undoing any " - "changes you have made"); - return; - } - - o = entity_list.FindObject(id); - - if (!o) { - c->Message(Chat::White, "ERROR: Object %u not found in zone in a manipulable form. No changes to undo.", - id); - return; - } - - if (o->GetType() == OT_DROPPEDITEM) { - c->Message(Chat::White, "ERROR: Object %u is a temporary spawned item and cannot be manipulated with " - "#object. See the 'ground_spawns' table in the database.", - id); - return; - } - - // Despawn current item for reloading from database - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(0, app); - entity_list.RemoveObject(o->GetID()); - safe_delete(app); - - std::string query = StringFormat("SELECT xpos, ypos, zpos, " - "heading, objectname, type, icon, " - "unknown08, unknown10, unknown20 " - "FROM object WHERE id = %u", - id); - auto results = content_db.QueryDatabase(query); - if (!results.Success() || results.RowCount() == 0) { - c->Message(Chat::White, "Database Error: %s", results.ErrorMessage().c_str()); - return; - } - - memset(&od, 0, sizeof(od)); - - auto row = results.begin(); - - od.x = atof(row[0]); - od.y = atof(row[1]); - od.z = atof(row[2]); - od.heading = atof(row[3]); - strn0cpy(od.object_name, row[4], sizeof(od.object_name)); - od.object_type = atoi(row[5]); - icon = atoi(row[6]); - od.size = atoi(row[7]); - od.solidtype = atoi(row[8]); - od.unknown020 = atoi(row[9]); - - if (od.object_type == 0) - od.object_type = staticType; - - o = new Object(id, od.object_type, icon, od, nullptr); - entity_list.AddObject(o, true); - - c->Message(Chat::White, "Object %u reloaded from database.", id); - return; - } - - c->Message(Chat::White, usage_string); -} - -void command_showspellslist(Client *c, const Seperator *sep) -{ - Mob *target = c->GetTarget(); - if (!target || !target->IsNPC()) { - c->Message(Chat::White, "You must target an NPC to use this command."); - return; - } - target->CastToNPC()->AISpellsList(c); - return; -} - -void command_raidloot(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (!arguments) { - c->Message(Chat::White, "Usage: #raidloot [All|GroupLeader|RaidLeader|Selected]"); - return; - } - - auto client_raid = c->GetRaid(); - if (!client_raid) { - c->Message(Chat::White, "You must be in a Raid to use this command."); - return; - } - - if (!client_raid->IsLeader(c)) { - c->Message(Chat::White, "You must be the Raid Leader to use this command."); - return; - } - - std::string raid_loot_type = str_tolower(sep->arg[1]); - bool is_all = raid_loot_type.find("all") != std::string::npos; - bool is_group_leader = raid_loot_type.find("groupleader") != std::string::npos; - bool is_raid_leader = raid_loot_type.find("raidleader") != std::string::npos; - bool is_selected = raid_loot_type.find("selected") != std::string::npos; - if ( - !is_all && - !is_group_leader && - !is_raid_leader && - !is_selected - ) { - c->Message(Chat::White, "Usage: #raidloot [All|GroupLeader|RaidLeader|Selected]"); - return; - } - - std::map loot_types = { - { RaidLootTypes::All, "All" }, - { RaidLootTypes::GroupLeader, "GroupLeader" }, - { RaidLootTypes::RaidLeader, "RaidLeader" }, - { RaidLootTypes::Selected, "Selected" } - }; - - uint32 loot_type; - if (is_all) { - loot_type = RaidLootTypes::All; - } else if (is_group_leader) { - loot_type = RaidLootTypes::GroupLeader; - } else if (is_raid_leader) { - loot_type = RaidLootTypes::RaidLeader; - } else if (is_selected) { - loot_type = RaidLootTypes::Selected; - } - - c->Message( - Chat::White, - fmt::format( - "Loot type changed to {} ({}).", - loot_types[loot_type], - loot_type - ).c_str() - ); -} - -void command_emoteview(Client *c, const Seperator *sep) -{ - if(!c->GetTarget() || !c->GetTarget()->IsNPC()) - { - c->Message(Chat::White, "You must target a NPC to view their emotes."); - return; - } - - if(c->GetTarget() && c->GetTarget()->IsNPC()) - { - int count=0; - int emoteid = c->GetTarget()->CastToNPC()->GetEmoteID(); - - LinkedListIterator iterator(zone->NPCEmoteList); - iterator.Reset(); - while(iterator.MoreElements()) - { - NPC_Emote_Struct* nes = iterator.GetData(); - if(emoteid == nes->emoteid) - { - c->Message(Chat::White, "EmoteID: %i Event: %i Type: %i Text: %s", nes->emoteid, nes->event_, nes->type, nes->text); - count++; - } - iterator.Advance(); - } - if (count == 0) - c->Message(Chat::White, "No emotes found."); - else - c->Message(Chat::White, "%i emote(s) found", count); - } -} - -void command_emotesearch(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == 0) - c->Message(Chat::White, "Usage: #emotesearch [search string or emoteid]"); - else - { - const char *search_criteria=sep->argplus[1]; - int count=0; - - if (Seperator::IsNumber(search_criteria)) - { - uint16 emoteid = atoi(search_criteria); - LinkedListIterator iterator(zone->NPCEmoteList); - iterator.Reset(); - while(iterator.MoreElements()) - { - NPC_Emote_Struct* nes = iterator.GetData(); - if(emoteid == nes->emoteid) - { - c->Message(Chat::White, "EmoteID: %i Event: %i Type: %i Text: %s", nes->emoteid, nes->event_, nes->type, nes->text); - count++; - } - iterator.Advance(); - } - if (count == 0) - c->Message(Chat::White, "No emotes found."); - else - c->Message(Chat::White, "%i emote(s) found", count); - } - else - { - char sText[64]; - char sCriteria[515]; - strn0cpy(sCriteria, search_criteria, sizeof(sCriteria)); - strupr(sCriteria); - char* pdest; - - LinkedListIterator iterator(zone->NPCEmoteList); - iterator.Reset(); - while(iterator.MoreElements()) - { - NPC_Emote_Struct* nes = iterator.GetData(); - strn0cpy(sText, nes->text, sizeof(sText)); - strupr(sText); - pdest = strstr(sText, sCriteria); - if (pdest != nullptr) - { - c->Message(Chat::White, "EmoteID: %i Event: %i Type: %i Text: %s", nes->emoteid, nes->event_, nes->type, nes->text); - count++; - } - if (count == 50) - break; - - iterator.Advance(); - } - if (count == 50) - c->Message(Chat::White, "50 emotes shown...too many results."); - else - c->Message(Chat::White, "%i emote(s) found", count); - } - } -} - -void command_reloademote(Client *c, const Seperator *sep) -{ - zone->NPCEmoteList.Clear(); - zone->LoadNPCEmotes(&zone->NPCEmoteList); - c->Message(Chat::White, "NPC emotes reloaded."); -} - -void command_globalview(Client *c, const Seperator *sep) -{ - NPC * npcmob = nullptr; - - if(c->GetTarget() && c->GetTarget()->IsNPC()) - { - npcmob = c->GetTarget()->CastToNPC(); - QGlobalCache *npc_c = nullptr; - QGlobalCache *char_c = nullptr; - QGlobalCache *zone_c = nullptr; - - if(npcmob) - npc_c = npcmob->GetQGlobals(); - - char_c = c->GetQGlobals(); - zone_c = zone->GetQGlobals(); - - std::list globalMap; - uint32 ntype = 0; - - if(npcmob) - ntype = npcmob->GetNPCTypeID(); - - if(npc_c) - { - QGlobalCache::Combine(globalMap, npc_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); - } - - if(char_c) - { - QGlobalCache::Combine(globalMap, char_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); - } - - if(zone_c) - { - QGlobalCache::Combine(globalMap, zone_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); - } - - auto iter = globalMap.begin(); - uint32 gcount = 0; - - c->Message(Chat::White, "Name, Value"); - while(iter != globalMap.end()) - { - c->Message(Chat::White, "%s %s", (*iter).name.c_str(), (*iter).value.c_str()); - ++iter; - ++gcount; - } - c->Message(Chat::White, "%u globals loaded.", gcount); - } - else - { - QGlobalCache *char_c = nullptr; - QGlobalCache *zone_c = nullptr; - - char_c = c->GetQGlobals(); - zone_c = zone->GetQGlobals(); - - std::list globalMap; - uint32 ntype = 0; - - if(char_c) - { - QGlobalCache::Combine(globalMap, char_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); - } - - if(zone_c) - { - QGlobalCache::Combine(globalMap, zone_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); - } - - auto iter = globalMap.begin(); - uint32 gcount = 0; - - c->Message(Chat::White, "Name, Value"); - while(iter != globalMap.end()) - { - c->Message(Chat::White, "%s %s", (*iter).name.c_str(), (*iter).value.c_str()); - ++iter; - ++gcount; - } - c->Message(Chat::White, "%u globals loaded.", gcount); - } -} - -void command_distance(Client *c, const Seperator *sep) { - if (c->GetTarget()) { - Mob* target = c->GetTarget(); - if (c != target) { - c->Message( - Chat::White, - fmt::format( - "{} ({}) is {:.2f} units from you.", - target->GetCleanName(), - target->GetID(), - Distance( - c->GetPosition(), - target->GetPosition() - ) - ).c_str() - ); - } - } -} - -void command_door(Client *c, const Seperator *sep) { - DoorManipulation::CommandHandler(c, sep); -} - -void command_cvs(Client *c, const Seperator *sep) -{ - auto pack = new ServerPacket( - ServerOP_ClientVersionSummary, - sizeof(ServerRequestClientVersionSummary_Struct) - ); - auto srcvss = (ServerRequestClientVersionSummary_Struct*)pack->pBuffer; - strn0cpy(srcvss->Name, c->GetName(), sizeof(srcvss->Name)); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void command_max_all_skills(Client *c, const Seperator *sep) -{ - if(c) { - Client* client_target = (c->GetTarget() ? (c->GetTarget()->IsClient() ? c->GetTarget()->CastToClient() : c) : c); - auto Skills = EQ::skills::GetSkillTypeMap(); - for (auto& skills_iter : Skills) { - auto skill_id = skills_iter.first; - auto current_skill_value = ( - (EQ::skills::IsSpecializedSkill(skill_id)) ? - 50 : - content_db.GetSkillCap(client_target->GetClass(), skill_id, client_target->GetLevel()) - ); - client_target->SetSkill(skill_id, current_skill_value); - } - } -} - -void command_showbonusstats(Client *c, const Seperator *sep) -{ - if (c->GetTarget() == 0) - c->Message(Chat::White, "ERROR: No target!"); - else if (!c->GetTarget()->IsMob() && !c->GetTarget()->IsClient()) - c->Message(Chat::White, "ERROR: Target is not a Mob or Player!"); - else { - bool bAll = false; - if(sep->arg[1][0] == '\0' || strcasecmp(sep->arg[1], "all") == 0) - bAll = true; - if (bAll || (strcasecmp(sep->arg[1], "item")==0)) { - c->Message(Chat::White, "Target Item Bonuses:"); - c->Message(Chat::White, " Accuracy: %i%% Divine Save: %i%%", c->GetTarget()->GetItemBonuses().Accuracy, c->GetTarget()->GetItemBonuses().DivineSaveChance); - c->Message(Chat::White, " Flurry: %i%% HitChance: %i%%", c->GetTarget()->GetItemBonuses().FlurryChance, c->GetTarget()->GetItemBonuses().HitChance / 15); - } - if (bAll || (strcasecmp(sep->arg[1], "spell")==0)) { - c->Message(Chat::White, " Target Spell Bonuses:"); - c->Message(Chat::White, " Accuracy: %i%% Divine Save: %i%%", c->GetTarget()->GetSpellBonuses().Accuracy, c->GetTarget()->GetSpellBonuses().DivineSaveChance); - c->Message(Chat::White, " Flurry: %i%% HitChance: %i%% ", c->GetTarget()->GetSpellBonuses().FlurryChance, c->GetTarget()->GetSpellBonuses().HitChance / 15); - } - c->Message(Chat::White, " Effective Casting Level: %i", c->GetTarget()->GetCasterLevel(0)); - } -} - -void command_reloadallrules(Client *c, const Seperator *sep) -{ - if(c) - { - auto pack = new ServerPacket(ServerOP_ReloadRules, 0); - worldserver.SendPacket(pack); - c->Message(Chat::Red, "Successfully sent the packet to world to reload rules globally. (including world)"); - safe_delete(pack); - - } -} - -void command_reloadworldrules(Client *c, const Seperator *sep) -{ - if(c) - { - auto pack = new ServerPacket(ServerOP_ReloadRulesWorld, 0); - worldserver.SendPacket(pack); - c->Message(Chat::Red, "Successfully sent the packet to world to reload rules. (only world)"); - safe_delete(pack); - } -} - -void command_camerashake(Client *c, const Seperator *sep) -{ - if(c) - { - if(sep->arg[1][0] && sep->arg[2][0]) - { - auto pack = new ServerPacket(ServerOP_CameraShake, sizeof(ServerCameraShake_Struct)); - ServerCameraShake_Struct* scss = (ServerCameraShake_Struct*) pack->pBuffer; - scss->duration = atoi(sep->arg[1]); - scss->intensity = atoi(sep->arg[2]); - worldserver.SendPacket(pack); - c->Message(Chat::Red, "Successfully sent the packet to world! Shake it, world, shake it!"); - safe_delete(pack); - } - else { - c->Message(Chat::Red, "Usage -- #camerashake [duration], [intensity [1-10])"); - } - } - return; -} - -void command_disarmtrap(Client *c, const Seperator *sep) -{ - Mob *target = c->GetTarget(); - - if(!target) - { - c->Message(Chat::Red, "You must have a target."); - return; - } - - if(target->IsNPC()) - { - if (c->HasSkill(EQ::skills::SkillDisarmTraps)) - { - if(DistanceSquaredNoZ(c->GetPosition(), target->GetPosition()) > RuleI(Adventure, LDoNTrapDistanceUse)) - { - c->Message(Chat::Red, "%s is too far away.", target->GetCleanName()); - return; - } - c->HandleLDoNDisarm(target->CastToNPC(), c->GetSkill(EQ::skills::SkillDisarmTraps), LDoNTypeMechanical); - } - else - c->Message(Chat::Red, "You do not have the disarm trap skill."); - } -} - -void command_sensetrap(Client *c, const Seperator *sep) -{ - Mob * target = c->GetTarget(); - if(!target) - { - c->Message(Chat::Red, "You must have a target."); - return; - } - - if(target->IsNPC()) - { - if (c->HasSkill(EQ::skills::SkillSenseTraps)) - { - if(DistanceSquaredNoZ(c->GetPosition(), target->GetPosition()) > RuleI(Adventure, LDoNTrapDistanceUse)) - { - c->Message(Chat::Red, "%s is too far away.", target->GetCleanName()); - return; - } - c->HandleLDoNSenseTraps(target->CastToNPC(), c->GetSkill(EQ::skills::SkillSenseTraps), LDoNTypeMechanical); - } - else - c->Message(Chat::Red, "You do not have the sense traps skill."); - } -} - -void command_picklock(Client *c, const Seperator *sep) -{ - Mob * target = c->GetTarget(); - if(!target) - { - c->Message(Chat::Red, "You must have a target."); - return; - } - - if(target->IsNPC()) - { - if (c->HasSkill(EQ::skills::SkillPickLock)) - { - if(DistanceSquaredNoZ(c->GetPosition(), target->GetPosition()) > RuleI(Adventure, LDoNTrapDistanceUse)) - { - c->Message(Chat::Red, "%s is too far away.", target->GetCleanName()); - return; - } - c->HandleLDoNPickLock(target->CastToNPC(), c->GetSkill(EQ::skills::SkillPickLock), LDoNTypeMechanical); - } - else - c->Message(Chat::Red, "You do not have the pick locks skill."); - } -} - -void command_profanity(Client *c, const Seperator *sep) -{ - std::string arg1(sep->arg[1]); - - while (true) { - if (arg1.compare("list") == 0) { - // do nothing - } - else if (arg1.compare("clear") == 0) { - EQ::ProfanityManager::DeleteProfanityList(&database); - auto pack = new ServerPacket(ServerOP_RefreshCensorship); - worldserver.SendPacket(pack); - safe_delete(pack); - } - else if (arg1.compare("add") == 0) { - if (!EQ::ProfanityManager::AddProfanity(&database, sep->arg[2])) - c->Message(Chat::Red, "Could not add '%s' to the profanity list.", sep->arg[2]); - auto pack = new ServerPacket(ServerOP_RefreshCensorship); - worldserver.SendPacket(pack); - safe_delete(pack); - } - else if (arg1.compare("del") == 0) { - if (!EQ::ProfanityManager::RemoveProfanity(&database, sep->arg[2])) - c->Message(Chat::Red, "Could not delete '%s' from the profanity list.", sep->arg[2]); - auto pack = new ServerPacket(ServerOP_RefreshCensorship); - worldserver.SendPacket(pack); - safe_delete(pack); - } - else if (arg1.compare("reload") == 0) { - if (!EQ::ProfanityManager::UpdateProfanityList(&database)) - c->Message(Chat::Red, "Could not reload the profanity list."); - auto pack = new ServerPacket(ServerOP_RefreshCensorship); - worldserver.SendPacket(pack); - safe_delete(pack); - } - else { - break; - } - - std::string popup; - const auto &list = EQ::ProfanityManager::GetProfanityList(); - for (const auto &iter : list) { - popup.append(iter); - popup.append("
"); - } - if (list.empty()) - popup.append("** Censorship Inactive **
"); - else - popup.append("** End of List **
"); - - c->SendPopupToClient("Profanity List", popup.c_str()); - - return; - } - - c->Message(Chat::White, "Usage: #profanity [list] - shows profanity list"); - c->Message(Chat::White, "Usage: #profanity [clear] - deletes all entries"); - c->Message(Chat::White, "Usage: #profanity [add] [] - adds entry"); - c->Message(Chat::White, "Usage: #profanity [del] [] - deletes entry"); - c->Message(Chat::White, "Usage: #profanity [reload] - reloads profanity list"); -} - -void command_mysql(Client *c, const Seperator *sep) -{ - if(!sep->arg[1][0] || !sep->arg[2][0]) { - c->Message(Chat::White, "Usage: #mysql query \"Query here\""); - return; - } - - if (strcasecmp(sep->arg[1], "help") == 0) { - c->Message(Chat::White, "MYSQL In-Game CLI Interface:"); - c->Message(Chat::White, "Example: #mysql query \"Query goes here quoted\" -s -h"); - c->Message(Chat::White, "To use 'like \"%%something%%\" replace the %% with #"); - c->Message(Chat::White, "Example: #mysql query \"select * from table where name like \"#something#\""); - c->Message(Chat::White, "-s - Spaces select entries apart"); - c->Message(Chat::White, "-h - Colors every other select result"); - return; - } - - if (strcasecmp(sep->arg[1], "query") == 0) { - ///Parse switches here - int argnum = 3; - bool optionS = false; - bool optionH = false; - while(sep->arg[argnum] && strlen(sep->arg[argnum]) > 1){ - switch(sep->arg[argnum][1]){ - case 's': optionS = true; break; - case 'h': optionH = true; break; - default: - c->Message(Chat::Yellow, "%s, there is no option '%c'", c->GetName(), sep->arg[argnum][1]); - return; - } - ++argnum; - } - - int highlightTextIndex = 0; - std::string query(sep->arg[2]); - //swap # for % so like queries can work - std::replace(query.begin(), query.end(), '#', '%'); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - return; - } - - //Using sep->arg[2] again, replace # with %% so it doesn't screw up when sent through vsnprintf in Message - query = sep->arg[2]; - int pos = query.find('#'); - while(pos != std::string::npos) { - query.erase(pos,1); - query.insert(pos, "%%"); - pos = query.find('#'); - } - c->Message(Chat::Yellow, "---Running query: '%s'", query.c_str()); - - for (auto row = results.begin(); row != results.end(); ++row) { - std::stringstream lineText; - std::vector lineVec; - for(int i = 0; i < results.RowCount(); i++) { - //split lines that could overflow the buffer in Client::Message and get cut off - //This will crash MQ2 @ 4000 since their internal buffer is only 2048. - //Reducing it to 2000 fixes that but splits more results from tables with a lot of columns. - if(lineText.str().length() > 4000) { - lineVec.push_back(lineText.str()); - lineText.str(""); - } - lineText << results.FieldName(i) << ":" << "[" << (row[i] ? row[i] : "nullptr") << "] "; - } - - lineVec.push_back(lineText.str()); - - if(optionS) //This provides spacing for the space switch - c->Message(Chat::White, " "); - if(optionH) //This option will highlight every other row - highlightTextIndex = 1 - highlightTextIndex; - - for(int lineNum = 0; lineNum < lineVec.size(); ++lineNum) - c->Message(highlightTextIndex, lineVec[lineNum].c_str()); - } - } -} - -void command_xtargets(Client *c, const Seperator *sep) -{ - Client *t; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t = c->GetTarget()->CastToClient(); - else - t = c; - - if(sep->arg[1][0]) - { - uint8 NewMax = atoi(sep->arg[1]); - - if((NewMax < 5) || (NewMax > XTARGET_HARDCAP)) - { - c->Message(Chat::Red, "Number of XTargets must be between 5 and %i", XTARGET_HARDCAP); - return; - } - t->SetMaxXTargets(NewMax); - c->Message(Chat::White, "Max number of XTargets set to %i", NewMax); - } - else - t->ShowXTargets(c); -} - -void command_zopp(Client *c, const Seperator *sep) -{ // - Owner only command..non-targetable to eliminate malicious or mischievious activities. - if (!c) - return; - else if (sep->argnum < 3 || sep->argnum > 4) - c->Message(Chat::White, "Usage: #zopp [trade/summon] [slot id] [item id] [*charges]"); - else if (!strcasecmp(sep->arg[1], "trade") == 0 && !strcasecmp(sep->arg[1], "t") == 0 && !strcasecmp(sep->arg[1], "summon") == 0 && !strcasecmp(sep->arg[1], "s") == 0) - c->Message(Chat::White, "Usage: #zopp [trade/summon] [slot id] [item id] [*charges]"); - else if (!sep->IsNumber(2) || !sep->IsNumber(3) || (sep->argnum == 4 && !sep->IsNumber(4))) - c->Message(Chat::White, "Usage: #zopp [trade/summon] [slot id] [item id] [*charges]"); - else { - ItemPacketType packettype; - - if (strcasecmp(sep->arg[1], "trade") == 0 || strcasecmp(sep->arg[1], "t") == 0) { - packettype = ItemPacketTrade; - } - else { - packettype = ItemPacketLimbo; - } - - int16 slotid = atoi(sep->arg[2]); - uint32 itemid = atoi(sep->arg[3]); - int16 charges = sep->argnum == 4 ? atoi(sep->arg[4]) : 1; // defaults to 1 charge if not specified - - const EQ::ItemData* FakeItem = database.GetItem(itemid); - - if (!FakeItem) { - c->Message(Chat::Red, "Error: Item [%u] is not a valid item id.", itemid); - return; - } - - int16 item_status = 0; - const EQ::ItemData* item = database.GetItem(itemid); - if(item) { - item_status = static_cast(item->MinStatus); - } - if (item_status > c->Admin()) { - c->Message(Chat::Red, "Error: Insufficient status to use this command."); - return; - } - - if (charges < 0 || charges > FakeItem->StackSize) { - c->Message(Chat::Red, "Warning: The specified charge count does not meet expected criteria!"); - c->Message(Chat::White, "Processing request..results may cause unpredictable behavior."); - } - - EQ::ItemInstance* FakeItemInst = database.CreateItem(FakeItem, charges); - c->SendItemPacket(slotid, FakeItemInst, packettype); - c->Message(Chat::White, "Sending zephyr op packet to client - [%s] %s (%u) with %i %s to slot %i.", - packettype == ItemPacketTrade ? "Trade" : "Summon", FakeItem->Name, itemid, charges, - std::abs(charges == 1) ? "charge" : "charges", slotid); - safe_delete(FakeItemInst); - } -} - -void command_augmentitem(Client *c, const Seperator *sep) -{ - if (!c) - return; - - auto in_augment = new AugmentItem_Struct[sizeof(AugmentItem_Struct)]; - in_augment->container_slot = 1000; // - in_augment->augment_slot = -1; - if (c->GetTradeskillObject() != nullptr) - Object::HandleAugmentation(c, in_augment, c->GetTradeskillObject()); - safe_delete_array(in_augment); -} - -void command_questerrors(Client *c, const Seperator *sep) -{ - std::list err; - parse->GetErrors(err); - c->Message(Chat::White, "Current Quest Errors:"); - - auto iter = err.begin(); - int i = 0; - while(iter != err.end()) { - if(i >= 30) { - c->Message(Chat::White, "Maximum of 30 Errors shown..."); - break; - } - - c->Message(Chat::White, iter->c_str()); - ++i; - ++iter; - } -} - -void command_enablerecipe(Client *c, const Seperator *sep) -{ - uint32 recipe_id = 0; - bool success = false; - if (c) { - if (sep->argnum == 1) { - recipe_id = atoi(sep->arg[1]); - } - else { - c->Message(Chat::White, "Invalid number of arguments.\nUsage: #enablerecipe recipe_id"); - return; - } - if (recipe_id > 0) { - success = content_db.EnableRecipe(recipe_id); - if (success) { - c->Message(Chat::White, "Recipe enabled."); - } - else { - c->Message(Chat::White, "Recipe not enabled."); - } - } - else { - c->Message(Chat::White, "Invalid recipe id.\nUsage: #enablerecipe recipe_id"); - } - } -} - -void command_disablerecipe(Client *c, const Seperator *sep) -{ - uint32 recipe_id = 0; - bool success = false; - if (c) { - if (sep->argnum == 1) { - recipe_id = atoi(sep->arg[1]); - } - else { - c->Message(Chat::White, "Invalid number of arguments.\nUsage: #disablerecipe recipe_id"); - return; - } - if (recipe_id > 0) { - success = content_db.DisableRecipe(recipe_id); - if (success) { - c->Message(Chat::White, "Recipe disabled."); - } - else { - c->Message(Chat::White, "Recipe not disabled."); - } - } - else { - c->Message(Chat::White, "Invalid recipe id.\nUsage: #disablerecipe recipe_id"); - } - } -} - -void command_npctype_cache(Client *c, const Seperator *sep) -{ - if (sep->argnum > 0) { - for (int i = 0; i < sep->argnum; ++i) { - if (strcasecmp(sep->arg[i + 1], "all") == 0) { - c->Message(Chat::White, "Clearing all npc types from the cache."); - zone->ClearNPCTypeCache(-1); - } - else { - int id = atoi(sep->arg[i + 1]); - if (id > 0) { - c->Message(Chat::White, "Clearing npc type %d from the cache.", id); - zone->ClearNPCTypeCache(id); - return; - } - } - } - } - else { - c->Message(Chat::White, "Usage:"); - c->Message(Chat::White, "#npctype_cache [npctype_id] ..."); - c->Message(Chat::White, "#npctype_cache all"); - } -} - -void command_merchantopenshop(Client *c, const Seperator *sep) -{ - Mob *merchant = c->GetTarget(); - if (!merchant || merchant->GetClass() != MERCHANT) { - c->Message(Chat::White, "You must target a merchant to open their shop."); - return; - } - - merchant->CastToNPC()->MerchantOpenShop(); -} - -void command_merchantcloseshop(Client *c, const Seperator *sep) -{ - Mob *merchant = c->GetTarget(); - if (!merchant || merchant->GetClass() != MERCHANT) { - c->Message(Chat::White, "You must target a merchant to close their shop."); - return; - } - - merchant->CastToNPC()->MerchantCloseShop(); -} - -void command_shownumhits(Client *c, const Seperator *sep) -{ - c->ShowNumHits(); - return; -} - -void command_shownpcgloballoot(Client *c, const Seperator *sep) -{ - auto tar = c->GetTarget(); - - if (!tar || !tar->IsNPC()) { - c->Message(Chat::White, "You must target an NPC to use this command."); - return; - } - - auto npc = tar->CastToNPC(); - c->Message(Chat::White, "GlobalLoot for %s (%d)", npc->GetName(), npc->GetNPCTypeID()); - zone->ShowNPCGlobalLoot(c, npc); -} - -void command_tune(Client *c, const Seperator *sep) -{ - //Work in progress - Kayen - - if (sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { - c->Message(Chat::White, "Syntax: #tune [subcommand]."); - c->Message(Chat::White, "-- Tune System Commands --"); - c->Message(Chat::White, "-- Usage: Returns recommended combat statistical values based on a desired outcome through simulated combat."); - c->Message(Chat::White, "-- This commmand can answer the following difficult questions whening tunings NPCs and Players."); - c->Message(Chat::White, "-- Question: What is the average damage mitigation my AC provides against a specific targets attacks?"); - c->Message(Chat::White, "-- Question: What is amount of AC would I need to add to acheive a specific average damage mitigation agianst specific targets attacks?"); - c->Message(Chat::White, "-- Question: What is amount of AC would I need to add to my target to acheive a specific average damage mitigation from my attacks?"); - c->Message(Chat::White, "-- Question: What is my targets average AC damage mitigation based on my ATK stat?"); - c->Message(Chat::White, "-- Question: What is amount of ATK would I need to add to myself to acheive a specific average damage mitigation on my target?"); - c->Message(Chat::White, "-- Question: What is amount of ATK would I need to add to my target to acheive a specific average AC damage mitigation on myself?"); - c->Message(Chat::White, "-- Question: What is my hit chance against a target?"); - c->Message(Chat::White, "-- Question: What is the amount of avoidance I need to add to my target to achieve a specific hit chance?"); - c->Message(Chat::White, "-- Question: What is the amount of accuracy I need to add to my target to achieve a specific chance of hitting me?"); - c->Message(Chat::White, "-- Question: ... and many more..."); - c->Message(Chat::White, " "); - c->Message(Chat::White, "...#tune stats [A/D]"); - c->Message(Chat::White, "...#tune FindATK [A/D] [pct mitigation] [interval] [loop_max] [AC override] [Info Level]"); - c->Message(Chat::White, "...#tune FindAC [A/D] [pct mitigation] [interval] [loop_max] [ATK override] [Info Level] "); - c->Message(Chat::White, "...#tune FindAccuracy [A/D] [hit chance] [interval] [loop_max] [Avoidance override] [Info Level]"); - c->Message(Chat::White, "...#tune FindAvoidance [A/D] [hit chance] [interval] [loop_max] [Accuracy override] [Info Level] "); - c->Message(Chat::White, " "); - c->Message(Chat::White, "-- DETAILS AND EXAMPLES ON USAGE"); - c->Message(Chat::White, " "); - c->Message(Chat::White, "...Returns combat statistics, including AC mitigation pct, hit chance, and avoid melee chance for attacker and defender."); - c->Message(Chat::White, "...#tune stats [A/D]"); - c->Message(Chat::White, "..."); - c->Message(Chat::White, "...Returns recommended ATK adjustment (+/-) on ATTACKER that will result in a specific average AC mitigation pct on DEFENDER. "); - c->Message(Chat::White, "...#tune FindATK [A/D] [pct mitigation] [interval][loop_max][AC override][Info Level]"); - c->Message(Chat::White, "...Example: Find the amount of ATK stat I need to add to the targeted NPC so that it hits me for 50 pct damage on average."); - c->Message(Chat::White, "...Example: #tune FindATK D 50"); - c->Message(Chat::White, "..."); - c->Message(Chat::White, "...Returns recommended AC adjustment(+/-) on DEFENDER for a specific average AC mitigation pct from ATTACKER. "); - c->Message(Chat::White, "...#tune FindAC [A/D] [pct mitigation] [interval][loop_max][ATK override][Info Level] "); - c->Message(Chat::White, "...Example: Find the amount of AC stat I need to add to the targeted NPC so that I hit it for 70 pct damage on average."); - c->Message(Chat::White, "...Example: #tune FindAC D 70"); - c->Message(Chat::White, "..."); - c->Message(Chat::White, "...Returns recommended Accuracy adjustment (+/-) on ATTACKER that will result in a specific hit chance pct on DEFENDER. "); - c->Message(Chat::White, "...#tune FindAccuracy [A/D] [hit chance] [interval][loop_max][Avoidance override][Info Level]"); - c->Message(Chat::White, "...Example: Find the amount of Accuracy stat I need to add to the targeted NPC so that it has a 60 pct hit chance against me."); - c->Message(Chat::White, "...Example: #tune FindAccuracy D 60"); - c->Message(Chat::White, "..."); - c->Message(Chat::White, "...Returns recommended Avoidance adjustment (+/-) on DEFENDER for in a specific hit chance pct from ATTACKER. "); - c->Message(Chat::White, "...#tune FindAvoidance [A/D] [hit chance] [interval][loop_max][Accuracy override][Info Level] "); - c->Message(Chat::White, "...Example: Find the amount of Avoidance stat I need to add to the targeted NPC so that I have a 30 pct hit chance against it."); - c->Message(Chat::White, "...Example: #tune FindAvoidance D 30"); - c->Message(Chat::White, "... "); - c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); - c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); - c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); - c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); - - c->Message(Chat::White, " "); - - c->Message(Chat::White, "-- Warning: The calculations done in this process are intense and can potentially cause zone crashes depending on parameters set, use with caution!"); - c->Message(Chat::White, "-- Below are OPTIONAL parameters."); - c->Message(Chat::White, "-- Note: [interval] Determines how much the stat being checked increases/decreases till it finds the best result. Lower is more accurate. Default=10"); - c->Message(Chat::White, "-- Note: [loop_max] Determines how many iterations are done to increases/decreases the stat till it finds the best result. Higher is more accurate. Default=1000"); - c->Message(Chat::White, "-- Note: [Stat Override] Will override that stat on mob being checked with the specified value. Default=0"); - c->Message(Chat::White, "-- Example: If as the attacker you want to find the ATK value you would need to have agianst a target with 1000 AC to achieve an average AC mitigation of 50 pct."); - c->Message(Chat::White, "-- Example: #tune FindATK A 50 0 0 1000"); - c->Message(Chat::White, "-- Note: [Info Level] How much parsing detail is displayed[0 - 1]. Default: [0] "); - c->Message(Chat::White, " "); - - return; - } - /* - Category A: YOU are the attacker and your target is the defender - Category D: YOU are the defender and your target is the attacker - */ - - Mob* attacker = c; - Mob* defender = c->GetTarget(); - - if (!defender) - { - c->Message(Chat::White, "[#Tune] - Error no target selected. [#Tune help]"); - return; - } - - //Use if checkings on engaged targets. - Mob* ttarget = attacker->GetTarget(); - if (ttarget) { - defender = ttarget; - } - - if (!strcasecmp(sep->arg[1], "stats")) - { - - if (!strcasecmp(sep->arg[2], "A")) { - c->TuneGetStats(defender, attacker); - } - else if (!strcasecmp(sep->arg[2], "D")){ - c->TuneGetStats(attacker, defender); - } - else { - c->TuneGetStats(defender, attacker); - } - return; - } - - if (!strcasecmp(sep->arg[1], "FindATK")) - { - float pct_mitigation = atof(sep->arg[3]); - int interval = atoi(sep->arg[4]); - int max_loop = atoi(sep->arg[5]); - int ac_override = atoi(sep->arg[6]); - int info_level = atoi(sep->arg[7]); - - if (!pct_mitigation) - { - c->Message(Chat::White, "[#Tune] - Error must enter the desired percent mitigation on defender."); - c->Message(Chat::White, "...Returns recommended ATK adjustment (+/-) on ATTACKER that will result in a specific average AC mitigation pct on DEFENDER. "); - c->Message(Chat::White, "...#tune FindATK [A/D] [pct mitigation] [interval][loop_max][AC override][Info Level]"); - c->Message(Chat::White, "...Example: Find the amount of ATK stat I need to add to the targeted NPC so that it hits me for 50 pct damage on average."); - c->Message(Chat::White, "...Example: #tune FindATK D 50"); - return; - } - - if (!interval) { - interval = 10; - } - if (!max_loop) { - max_loop = 1000; - } - if (!ac_override) { - ac_override = 0; - } - if (!info_level) { - info_level = 0; - } - - if (!strcasecmp(sep->arg[2], "A")) { - c->TuneGetATKByPctMitigation(defender, attacker, pct_mitigation, interval, max_loop, ac_override, info_level); - } - else if (!strcasecmp(sep->arg[2], "D")) { - c->TuneGetATKByPctMitigation(attacker, defender, pct_mitigation, interval, max_loop, ac_override, info_level); - } - else { - c->Message(Chat::White, "#Tune - Error no category selcted. [#Tune help]"); - c->Message(Chat::White, "Usage #tune FindATK [A/B] [pct mitigation] [interval][loop_max][AC Overwride][Info Level] "); - c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); - c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); - c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); - c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); - c->Message(Chat::White, "... "); - c->Message(Chat::White, "...Example: Find the amount of ATK stat I need to add to the targeted NPC so that it hits me for 50 pct damage on average."); - c->Message(Chat::White, "...Example: #tune FindATK D 50"); - } - return; - } - - if (!strcasecmp(sep->arg[1], "FindAC")) - { - float pct_mitigation = atof(sep->arg[3]); - int interval = atoi(sep->arg[4]); - int max_loop = atoi(sep->arg[5]); - int atk_override = atoi(sep->arg[6]); - int info_level = atoi(sep->arg[7]); - - if (!pct_mitigation) - { - c->Message(Chat::White, "#Tune - Error must enter the desired percent mitigation on defender."); - c->Message(Chat::White, "...Returns recommended AC adjustment(+/-) on DEFENDER for a specific average AC mitigation pct from ATTACKER. "); - c->Message(Chat::White, "...#tune FindAC [A/D] [pct mitigation] [interval][loop_max][ATK override][Info Level] "); - c->Message(Chat::White, "...Example: Find the amount of AC stat I need to add to the targeted NPC so that I hit it for 70 pct damage on average."); - c->Message(Chat::White, "...Example: #tune FindAC D 70"); - return; - } - - if (!interval) { - interval = 10; - } - if (!max_loop) { - max_loop = 1000; - } - if (!atk_override) { - atk_override = 0; - } - if (!info_level) { - info_level = 0; - } - - if (!strcasecmp(sep->arg[2], "A")) { - c->TuneGetACByPctMitigation(defender, attacker, pct_mitigation, interval, max_loop, atk_override, info_level); - } - else if (!strcasecmp(sep->arg[2], "D")) { - c->TuneGetACByPctMitigation(attacker, defender, pct_mitigation, interval, max_loop, atk_override, info_level); - } - else { - c->Message(Chat::White, "#Tune - Error no category selcted. [#Tune help]"); - c->Message(Chat::White, "Usage #tune FindATK [A/B] [pct mitigation] [interval][loop_max][AC Overwride][Info Level] "); - c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); - c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); - c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); - c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); - c->Message(Chat::White, "... "); - c->Message(Chat::White, "...Example: Find the amount of AC stat I need to add to the targeted NPC so that I hit it for 70 pct damage on average."); - c->Message(Chat::White, "...Example: #tune FindAC D 70"); - } - - return; - } - - if (!strcasecmp(sep->arg[1], "FindAccuracy")) - { - float hit_chance = atof(sep->arg[3]); - int interval = atoi(sep->arg[4]); - int max_loop = atoi(sep->arg[5]); - int avoid_override = atoi(sep->arg[6]); - int info_level = atoi(sep->arg[7]); - - if (!hit_chance) - { - c->Message(Chat::White, "#Tune - Error must enter the desired hit chance on defender."); - c->Message(Chat::White, "...Returns recommended Accuracy adjustment (+/-) on ATTACKER that will result in a specific hit chance pct on DEFENDER. "); - c->Message(Chat::White, "...#tune FindAccuracy [A/D] [hit chance] [interval][loop_max][Avoidance override][Info Level]"); - c->Message(Chat::White, "...Example: Find the amount of Accuracy stat I need to add to the targeted NPC so that it has a 60 pct hit chance against me."); - c->Message(Chat::White, "...Example: #tune FindAccuracy D 60"); - return; - } - - if (!interval) { - interval = 10; - } - if (!max_loop) { - max_loop = 1000; - } - if (!avoid_override) { - avoid_override = 0; - } - if (!info_level) { - info_level = 0; - } - - if (!strcasecmp(sep->arg[2], "A")) - c->TuneGetAccuracyByHitChance(defender, attacker, hit_chance, interval, max_loop, avoid_override, info_level); - else if (!strcasecmp(sep->arg[2], "D")) - c->TuneGetAccuracyByHitChance(attacker, defender, hit_chance, interval, max_loop, avoid_override, info_level); - else { - c->Message(Chat::White, "#Tune - Error no category selcted. [#Tune help]"); - c->Message(Chat::White, "...#tune FindAccuracy [A/D] [hit chance] [interval][loop_max][Avoidance override][Info Level]"); - c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); - c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); - c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); - c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); - c->Message(Chat::White, "... "); - c->Message(Chat::White, "...Example: Find the amount of Accuracy stat I need to add to the targeted NPC so that it has a 60 pct hit chance against me."); - c->Message(Chat::White, "...Example: #tune FindAccuracy D 60"); - } - - return; - } - - if (!strcasecmp(sep->arg[1], "FindAvoidance")) - { - float hit_chance = atof(sep->arg[3]); - int interval = atoi(sep->arg[4]); - int max_loop = atoi(sep->arg[5]); - int acc_override = atoi(sep->arg[6]); - int info_level = atoi(sep->arg[7]); - - if (!hit_chance) - { - c->Message(Chat::White, "#Tune - Error must enter the desired hit chance on defender."); - c->Message(Chat::White, "...Returns recommended Avoidance adjustment (+/-) on DEFENDER for in a specific hit chance pct from ATTACKER. "); - c->Message(Chat::White, "...#tune FindAvoidance [A/D] [hit chance] [interval][loop_max][Accuracy override][Info Level] "); - c->Message(Chat::White, "...Example: Find the amount of Avoidance stat I need to add to the targeted NPC so that I have a 30 pct hit chance against it."); - c->Message(Chat::White, "...Example: #tune FindAvoidance D 30"); - return; - } - if (!interval) { - interval = 10; - } - if (!max_loop) { - max_loop = 1000; - } - if (!acc_override) { - acc_override = 0; - } - if (!info_level) { - info_level = 0; - } - - if (!strcasecmp(sep->arg[2], "A")) - c->TuneGetAvoidanceByHitChance(defender, attacker, hit_chance, interval, max_loop, acc_override, info_level); - else if (!strcasecmp(sep->arg[2], "D")) - c->TuneGetAvoidanceByHitChance(attacker, defender, hit_chance, interval, max_loop, acc_override, info_level); - else { - c->Message(Chat::White, "#Tune - Error no category selcted. [#Tune help]"); - c->Message(Chat::White, "...#tune FindAvoidance [A/D] [hit chance] [interval][loop_max][Accuracy override][Info Level] "); - c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); - c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); - c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); - c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); - c->Message(Chat::White, "... "); - c->Message(Chat::White, "...Example: Find the amount of Avoidance stat I need to add to the targeted NPC so that I have a 30 pct hit chance against it."); - c->Message(Chat::White, "...Example: #tune FindAvoidance D 30"); - } - - return; - } - - c->Message(Chat::White, "#Tune - Error no command [#Tune help]"); - return; -} - -void command_logs(Client *c, const Seperator *sep){ - int logs_set = 0; - if (sep->argnum > 0) { - /* #logs reload_all */ - if (strcasecmp(sep->arg[1], "reload_all") == 0){ - auto pack = new ServerPacket(ServerOP_ReloadLogs, 0); - worldserver.SendPacket(pack); - c->Message(Chat::Red, "Successfully sent the packet to world to reload log settings from the database for all zones"); - safe_delete(pack); - } - /* #logs list_settings */ - if (strcasecmp(sep->arg[1], "list_settings") == 0 || - (strcasecmp(sep->arg[1], "set") == 0 && strcasecmp(sep->arg[3], "") == 0)) { - c->Message(Chat::White, "[Category ID | console | file | gmsay | Category Description]"); - int redisplay_columns = 0; - for (int i = 0; i < Logs::LogCategory::MaxCategoryID; i++) { - if (redisplay_columns == 10) { - c->Message(Chat::White, "[Category ID | console | file | gmsay | Category Description]"); - redisplay_columns = 0; - } - c->Message( - 0, - StringFormat( - "--- %i | %u | %u | %u | %s", - i, - LogSys.log_settings[i].log_to_console, - LogSys.log_settings[i].log_to_file, - LogSys.log_settings[i].log_to_gmsay, - Logs::LogCategoryName[i] - ).c_str()); - redisplay_columns++; - } - } - /* #logs set */ - if (strcasecmp(sep->arg[1], "set") == 0){ - if (strcasecmp(sep->arg[2], "console") == 0){ - LogSys.log_settings[atoi(sep->arg[3])].log_to_console = atoi(sep->arg[4]); - logs_set = 1; - } - else if (strcasecmp(sep->arg[2], "file") == 0){ - LogSys.log_settings[atoi(sep->arg[3])].log_to_file = atoi(sep->arg[4]); - logs_set = 1; - } - else if (strcasecmp(sep->arg[2], "gmsay") == 0){ - LogSys.log_settings[atoi(sep->arg[3])].log_to_gmsay = atoi(sep->arg[4]); - logs_set = 1; - } - else{ - c->Message(Chat::White, "--- #logs set [console|file|gmsay] - Sets log settings during the lifetime of the zone"); - c->Message(Chat::White, "--- #logs set gmsay 20 1 - Would output Quest errors to gmsay"); - } - if (logs_set == 1){ - c->Message(Chat::Yellow, "Your Log Settings have been applied"); - c->Message(Chat::Yellow, "Output Method: %s :: Debug Level: %i - Category: %s", sep->arg[2], atoi(sep->arg[4]), Logs::LogCategoryName[atoi(sep->arg[3])]); - } - /* We use a general 'is_category_enabled' now, let's update when we update any output settings - This is used in hot places of code to check if its enabled in any way before triggering logs - */ - if (atoi(sep->arg[4]) > 0){ - LogSys.log_settings[atoi(sep->arg[3])].is_category_enabled = 1; - } - else{ - LogSys.log_settings[atoi(sep->arg[3])].is_category_enabled = 0; - } - } - } - else { - c->Message(Chat::White, "#logs usage:"); - c->Message(Chat::White, "--- #logs reload_all - Reload all settings in world and all zone processes with what is defined in the database"); - c->Message(Chat::White, "--- #logs list_settings - Shows current log settings and categories loaded into the current process' memory"); - c->Message(Chat::White, "--- #logs set [console|file|gmsay] - Sets log settings during the lifetime of the zone"); - } -} - -void command_resetaa_timer(Client *c, const Seperator *sep) { - Client *target = nullptr; - if(!c->GetTarget() || !c->GetTarget()->IsClient()) { - target = c; - } else { - target = c->GetTarget()->CastToClient(); - } - - if(sep->IsNumber(1)) - { - int timer_id = atoi(sep->arg[1]); - c->Message(Chat::White, "Reset of timer %i for %s", timer_id, c->GetName()); - c->ResetAlternateAdvancementTimer(timer_id); - } - else if(!strcasecmp(sep->arg[1], "all")) - { - c->Message(Chat::White, "Reset all timers for %s", c->GetName()); - c->ResetAlternateAdvancementTimers(); - } - else - { - c->Message(Chat::White, "usage: #resetaa_timer [all | timer_id]"); - } -} - -void command_resetdisc_timer(Client *c, const Seperator *sep) -{ - Client *target = c->GetTarget()->CastToClient(); - if (!c->GetTarget() || !c->GetTarget()->IsClient()) { - target = c; - } - - if (sep->IsNumber(1)) { - int timer_id = atoi(sep->arg[1]); - c->Message(Chat::White, "Reset of disc timer %i for %s", timer_id, c->GetName()); - c->ResetDisciplineTimer(timer_id); - } - else if (!strcasecmp(sep->arg[1], "all")) { - c->Message(Chat::White, "Reset all disc timers for %s", c->GetName()); - c->ResetAllDisciplineTimers(); - } - else { - c->Message(Chat::White, "usage: #resetdisc_timer [all | timer_id]"); - } -} - -void command_reloadaa(Client *c, const Seperator *sep) { - c->Message(Chat::White, "Reloading Alternate Advancement Data..."); - zone->LoadAlternateAdvancement(); - c->Message(Chat::White, "Alternate Advancement Data Reloaded"); - entity_list.SendAlternateAdvancementStats(); -} - -inline bool file_exists(const std::string& name) { - std::ifstream f(name.c_str()); - return f.good(); -} - void command_hotfix(Client *c, const Seperator *sep) { std::string hotfix; @@ -15398,870 +1070,6 @@ void command_apply_shared_memory(Client *c, const Seperator *sep) { worldserver.SendPacket(&pack); } -void command_reloadperlexportsettings(Client *c, const Seperator *sep) -{ - if (c) - { - auto pack = new ServerPacket(ServerOP_ReloadPerlExportSettings, 0); - worldserver.SendPacket(pack); - c->Message(Chat::Red, "Successfully sent the packet to world to reload Perl Export settings"); - safe_delete(pack); - - } -} - -void command_trapinfo(Client *c, const Seperator *sep) -{ - entity_list.GetTrapInfo(c); -} - -void command_reloadtraps(Client *c, const Seperator *sep) -{ - entity_list.UpdateAllTraps(true, true); - c->Message(Chat::Default, "Traps reloaded for %s.", zone->GetShortName()); -} - -void command_scale(Client *c, const Seperator *sep) -{ - if (sep->argnum == 0) { - c->Message(Chat::Yellow, "# Usage # "); - c->Message(Chat::Yellow, "#scale [static/dynamic] (With targeted NPC)"); - c->Message(Chat::Yellow, "#scale [npc_name_search] [static/dynamic] (To make zone-wide changes)"); - c->Message(Chat::Yellow, "#scale all [static/dynamic]"); - return; - } - - /** - * Targeted changes - */ - if (c->GetTarget() && c->GetTarget()->IsNPC() && sep->argnum < 2) { - NPC * npc = c->GetTarget()->CastToNPC(); - - bool apply_status = false; - if (strcasecmp(sep->arg[1], "dynamic") == 0) { - c->Message(Chat::Yellow, "Applying global base scaling to npc dynamically (All stats set to zeroes)..."); - apply_status = npc_scale_manager->ApplyGlobalBaseScalingToNPCDynamically(npc); - } - else if (strcasecmp(sep->arg[1], "static") == 0) { - c->Message(Chat::Yellow, "Applying global base scaling to npc statically (Copying base stats onto NPC)..."); - apply_status = npc_scale_manager->ApplyGlobalBaseScalingToNPCStatically(npc); - } - else { - return; - } - - if (apply_status) { - c->Message(Chat::Yellow, "Applied to NPC '%s' successfully!", npc->GetName()); - } - else { - c->Message(Chat::Yellow, "Failed to load scaling data from the database " - "for this npc / type, see 'NPCScaling' log for more info"); - } - } - else if (c->GetTarget() && sep->argnum < 2) { - c->Message(Chat::Yellow, "Target must be an npc!"); - } - - /** - * Zonewide - */ - if (sep->argnum > 1) { - - std::string scale_type; - if (strcasecmp(sep->arg[2], "dynamic") == 0) { - scale_type = "dynamic"; - } - else if (strcasecmp(sep->arg[2], "static") == 0) { - scale_type = "static"; - } - - if (scale_type.length() <= 0) { - c->Message(Chat::Yellow, "You must first set if you intend on using static versus dynamic for these changes"); - c->Message(Chat::Yellow, "#scale [npc_name_search] [static/dynamic]"); - c->Message(Chat::Yellow, "#scale all [static/dynamic]"); - return; - } - - std::string search_string = sep->arg[1]; - - auto &entity_list_search = entity_list.GetNPCList(); - - int found_count = 0; - for (auto &itr : entity_list_search) { - NPC *entity = itr.second; - - std::string entity_name = entity->GetName(); - - /** - * Filter by name - */ - if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos && strcasecmp(sep->arg[1], "all") != 0) { - continue; - } - - std::string status = "(Searching)"; - - if (strcasecmp(sep->arg[3], "apply") == 0) { - status = "(Applying)"; - - if (strcasecmp(sep->arg[2], "dynamic") == 0) { - npc_scale_manager->ApplyGlobalBaseScalingToNPCDynamically(entity); - } - if (strcasecmp(sep->arg[2], "static") == 0) { - npc_scale_manager->ApplyGlobalBaseScalingToNPCStatically(entity); - } - } - - c->Message( - 15, - "| ID %5d | %s | x %.0f | y %0.f | z %.0f | DBID %u %s", - entity->GetID(), - entity->GetName(), - entity->GetX(), - entity->GetY(), - entity->GetZ(), - entity->GetNPCTypeID(), - status.c_str() - ); - - found_count++; - } - - if (strcasecmp(sep->arg[3], "apply") == 0) { - c->Message(Chat::Yellow, "%s scaling applied against (%i) NPC's", sep->arg[2], found_count); - } - else { - - std::string saylink = StringFormat( - "#scale %s %s apply", - sep->arg[1], - sep->arg[2] - ); - - c->Message(Chat::Yellow, "Found (%i) NPC's that match this search...", found_count); - c->Message( - Chat::Yellow, "To apply these changes, click <%s> or type %s", - EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Apply").c_str(), - saylink.c_str() - ); - } - } -} - -void command_databuckets(Client *c, const Seperator *sep) - { - if (sep->arg[1][0] == 0) { - c->Message(Chat::Yellow, "Usage: #databuckets view (partial key)|(limit) OR #databuckets delete (key)"); - return; - } - if (strcasecmp(sep->arg[1], "view") == 0) { - - std::string key_filter; - uint8 limit = 50; - for (int i = 2; i < 4; i++) { - if (sep->arg[i][0] == '\0') - break; - if (strcasecmp(sep->arg[i], "limit") == 0) { - limit = (uint8)atoi(sep->arg[i + 1]); - continue; - } - } - if (sep->arg[2]) { - key_filter = str_tolower(sep->arg[2]); - } - std::string query = "SELECT `id`, `key`, `value`, `expires` FROM data_buckets"; - if (!key_filter.empty()) query += StringFormat(" WHERE `key` LIKE '%%%s%%'", key_filter.c_str()); - query += StringFormat(" LIMIT %u", limit); - auto results = database.QueryDatabase(query); - if (!results.Success()) - return; - if (results.RowCount() == 0) { - c->Message(Chat::Yellow, "No data_buckets found"); - return; - } - int _ctr = 0; - // put in window for easier readability in case want command line for something else - std::string window_title = "Data Buckets"; - std::string window_text = - "" - "" - "" - "" - "" - "" - ""; - for (auto row = results.begin(); row != results.end(); ++row) { - auto id = static_cast(atoi(row[0])); - std::string key = row[1]; - std::string value = row[2]; - std::string expires = row[3]; - window_text.append(StringFormat( - "" - "" - "" - "" - "" - "", - id, - expires.c_str(), - key.c_str(), - value.c_str() - )); - _ctr++; - std::string del_saylink = StringFormat("#databuckets delete %s", key.c_str()); - c->Message(Chat::White, "%s : %s", - EQ::SayLinkEngine::GenerateQuestSaylink(del_saylink, false, "Delete").c_str(), key.c_str(), " Value: ", value.c_str()); - } - window_text.append("
IDExpiresKeyValue
%u%s%s%s
"); - c->SendPopupToClient(window_title.c_str(), window_text.c_str()); - std::string response = _ctr > 0 ? StringFormat("Found %i matching data buckets", _ctr).c_str() : "No Databuckets found."; - c->Message(Chat::Yellow, response.c_str()); - } - else if (strcasecmp(sep->arg[1], "delete") == 0) - { - if (DataBucket::DeleteData(sep->argplus[2])) - c->Message(Chat::Yellow, "data bucket %s deleted.", sep->argplus[2]); - else - c->Message(Chat::Red, "An error occurred deleting data bucket %s", sep->argplus[2]); - return; - } -} - -void command_who(Client *c, const Seperator *sep) -{ - std::string query = - SQL ( - SELECT - character_data.account_id, - character_data.name, - character_data.zone_id, - character_data.zone_instance, - COALESCE( - ( - select - guilds.name - from - guilds - where - id = ( - ( - select - guild_id - from - guild_members - where - char_id = character_data.id - ) - ) - ), - "" - ) as guild_name, - character_data.level, - character_data.race, - character_data.class, - COALESCE( - ( - select - account.status - from - account - where - account.id = character_data.account_id - LIMIT - 1 - ), 0 - ) as account_status, - COALESCE( - ( - select - account.name - from - account - where - account.id = character_data.account_id - LIMIT - 1 - ), - 0 - ) as account_name, - COALESCE( - ( - select - account_ip.ip - from - account_ip - where - account_ip.accid = character_data.account_id - ORDER BY - account_ip.lastused DESC - LIMIT - 1 - ), - "" - ) as account_ip - FROM - character_data - WHERE - last_login > (UNIX_TIMESTAMP() - 600) - ORDER BY - character_data.name; - ) - ; - - auto results = database.QueryDatabase(query); - if (!results.Success()) - return; - - if (results.RowCount() == 0) { - c->Message(Chat::Yellow, "No results found"); - return; - } - - std::string search_string; - - if (sep->arg[1]) { - search_string = str_tolower(sep->arg[1]); - } - - int found_count = 0; - - c->Message(Chat::Magenta, "Players in EverQuest"); - c->Message(Chat::Magenta, "--------------------"); - - for (auto row = results.begin(); row != results.end(); ++row) { - auto account_id = static_cast(atoi(row[0])); - std::string player_name = row[1]; - auto zone_id = static_cast(atoi(row[2])); - std::string zone_short_name = ZoneName(zone_id); - auto zone_instance = static_cast(atoi(row[3])); - std::string guild_name = row[4]; - auto player_level = static_cast(atoi(row[5])); - auto player_race = static_cast(atoi(row[6])); - auto player_class = static_cast(atoi(row[7])); - auto account_status = static_cast(atoi(row[8])); - std::string account_name = row[9]; - std::string account_ip = row[10]; - std::string base_class_name = GetClassIDName(static_cast(player_class), 1); - std::string displayed_race_name = GetRaceIDName(static_cast(player_race)); - - if (search_string.length() > 0) { - bool found_search_term = - ( - str_tolower(player_name).find(search_string) != std::string::npos || - str_tolower(zone_short_name).find(search_string) != std::string::npos || - str_tolower(displayed_race_name).find(search_string) != std::string::npos || - str_tolower(base_class_name).find(search_string) != std::string::npos || - str_tolower(guild_name).find(search_string) != std::string::npos || - str_tolower(account_name).find(search_string) != std::string::npos || - str_tolower(account_ip).find(search_string) != std::string::npos - ); - - if (!found_search_term) { - continue; - } - } - - std::string displayed_guild_name; - if (guild_name.length() > 0) { - displayed_guild_name = EQ::SayLinkEngine::GenerateQuestSaylink( - StringFormat( - "#who \"%s\"", - guild_name.c_str()), - false, - StringFormat("<%s>", guild_name.c_str()) - ); - } - - std::string goto_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( - StringFormat("#goto %s", player_name.c_str()), false, "Goto" - ); - - std::string display_class_name = GetClassIDName(static_cast(player_class), static_cast(player_level)); - - c->Message( - 5, "%s[%u %s] %s (%s) %s ZONE: %s (%u) (%s) (%s) (%s)", - (account_status > 0 ? "* GM * " : ""), - player_level, - EQ::SayLinkEngine::GenerateQuestSaylink(StringFormat("#who %s", base_class_name.c_str()), false, display_class_name).c_str(), - player_name.c_str(), - EQ::SayLinkEngine::GenerateQuestSaylink(StringFormat("#who %s", displayed_race_name.c_str()), false, displayed_race_name).c_str(), - displayed_guild_name.c_str(), - EQ::SayLinkEngine::GenerateQuestSaylink(StringFormat("#who %s", zone_short_name.c_str()), false, zone_short_name).c_str(), - zone_instance, - goto_saylink.c_str(), - EQ::SayLinkEngine::GenerateQuestSaylink(StringFormat("#who %s", account_name.c_str()), false, account_name).c_str(), - EQ::SayLinkEngine::GenerateQuestSaylink(StringFormat("#who %s", account_ip.c_str()), false, account_ip).c_str() - ); - - found_count++; - } - - std::string message = ( - found_count > 0 ? - StringFormat("There is %i player(s) in EverQuest", found_count).c_str() : - "There are no players in EverQuest that match those who filters." - ); - - c->Message(Chat::Magenta, message.c_str()); -} - -void command_network(Client *c, const Seperator *sep) -{ - if (!strcasecmp(sep->arg[1], "getopt")) - { - auto eqsi = c->Connection(); - auto manager = eqsi->GetManager(); - auto opts = manager->GetOptions(); - - if (!strcasecmp(sep->arg[2], "all")) - { - c->Message(Chat::White, "max_packet_size: %llu", (uint64_t)opts.daybreak_options.max_packet_size); - c->Message(Chat::White, "max_connection_count: %llu", (uint64_t)opts.daybreak_options.max_connection_count); - c->Message(Chat::White, "keepalive_delay_ms: %llu", (uint64_t)opts.daybreak_options.keepalive_delay_ms); - c->Message(Chat::White, "resend_delay_factor: %.2f", opts.daybreak_options.resend_delay_factor); - c->Message(Chat::White, "resend_delay_ms: %llu", (uint64_t)opts.daybreak_options.resend_delay_ms); - c->Message(Chat::White, "resend_delay_min: %llu", (uint64_t)opts.daybreak_options.resend_delay_min); - c->Message(Chat::White, "resend_delay_max: %llu", (uint64_t)opts.daybreak_options.resend_delay_max); - c->Message(Chat::White, "connect_delay_ms: %llu", (uint64_t)opts.daybreak_options.connect_delay_ms); - c->Message(Chat::White, "connect_stale_ms: %llu", (uint64_t)opts.daybreak_options.connect_stale_ms); - c->Message(Chat::White, "stale_connection_ms: %llu", (uint64_t)opts.daybreak_options.stale_connection_ms); - c->Message(Chat::White, "crc_length: %llu", (uint64_t)opts.daybreak_options.crc_length); - c->Message(Chat::White, "hold_size: %llu", (uint64_t)opts.daybreak_options.hold_size); - c->Message(Chat::White, "hold_length_ms: %llu", (uint64_t)opts.daybreak_options.hold_length_ms); - c->Message(Chat::White, "simulated_in_packet_loss: %llu", (uint64_t)opts.daybreak_options.simulated_in_packet_loss); - c->Message(Chat::White, "simulated_out_packet_loss: %llu", (uint64_t)opts.daybreak_options.simulated_out_packet_loss); - c->Message(Chat::White, "tic_rate_hertz: %.2f", opts.daybreak_options.tic_rate_hertz); - c->Message(Chat::White, "resend_timeout: %llu", (uint64_t)opts.daybreak_options.resend_timeout); - c->Message(Chat::White, "connection_close_time: %llu", (uint64_t)opts.daybreak_options.connection_close_time); - c->Message(Chat::White, "encode_passes[0]: %llu", (uint64_t)opts.daybreak_options.encode_passes[0]); - c->Message(Chat::White, "encode_passes[1]: %llu", (uint64_t)opts.daybreak_options.encode_passes[1]); - c->Message(Chat::White, "port: %llu", (uint64_t)opts.daybreak_options.port); - } - else { - c->Message(Chat::White, "Unknown get option: %s", sep->arg[2]); - c->Message(Chat::White, "Available options:"); - //Todo the rest of these when im less lazy. - //c->Message(Chat::White, "max_packet_size"); - //c->Message(Chat::White, "max_connection_count"); - //c->Message(Chat::White, "keepalive_delay_ms"); - //c->Message(Chat::White, "resend_delay_factor"); - //c->Message(Chat::White, "resend_delay_ms"); - //c->Message(Chat::White, "resend_delay_min"); - //c->Message(Chat::White, "resend_delay_max"); - //c->Message(Chat::White, "connect_delay_ms"); - //c->Message(Chat::White, "connect_stale_ms"); - //c->Message(Chat::White, "stale_connection_ms"); - //c->Message(Chat::White, "crc_length"); - //c->Message(Chat::White, "hold_size"); - //c->Message(Chat::White, "hold_length_ms"); - //c->Message(Chat::White, "simulated_in_packet_loss"); - //c->Message(Chat::White, "simulated_out_packet_loss"); - //c->Message(Chat::White, "tic_rate_hertz"); - //c->Message(Chat::White, "resend_timeout"); - //c->Message(Chat::White, "connection_close_time"); - //c->Message(Chat::White, "encode_passes[0]"); - //c->Message(Chat::White, "encode_passes[1]"); - //c->Message(Chat::White, "port"); - c->Message(Chat::White, "all"); - } - } - else if (!strcasecmp(sep->arg[1], "setopt")) - { - auto eqsi = c->Connection(); - auto manager = eqsi->GetManager(); - auto opts = manager->GetOptions(); - - if (!strcasecmp(sep->arg[3], "")) - { - c->Message(Chat::White, "Missing value for set"); - return; - } - - std::string value = sep->arg[3]; - if (!strcasecmp(sep->arg[2], "max_connection_count")) - { - opts.daybreak_options.max_connection_count = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "keepalive_delay_ms")) - { - opts.daybreak_options.keepalive_delay_ms = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "resend_delay_factor")) - { - opts.daybreak_options.resend_delay_factor = std::stod(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "resend_delay_ms")) - { - opts.daybreak_options.resend_delay_ms = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "resend_delay_min")) - { - opts.daybreak_options.resend_delay_min = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "resend_delay_max")) - { - opts.daybreak_options.resend_delay_max = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "connect_delay_ms")) - { - opts.daybreak_options.connect_delay_ms = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "connect_stale_ms")) - { - opts.daybreak_options.connect_stale_ms = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "stale_connection_ms")) - { - opts.daybreak_options.stale_connection_ms = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "hold_size")) - { - opts.daybreak_options.hold_size = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "hold_length_ms")) - { - opts.daybreak_options.hold_length_ms = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "simulated_in_packet_loss")) - { - opts.daybreak_options.simulated_in_packet_loss = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "simulated_out_packet_loss")) - { - opts.daybreak_options.simulated_out_packet_loss = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "resend_timeout")) - { - opts.daybreak_options.resend_timeout = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "connection_close_time")) - { - opts.daybreak_options.connection_close_time = std::stoull(value); - manager->SetOptions(opts); - } - else { - c->Message(Chat::White, "Unknown set option: %s", sep->arg[2]); - c->Message(Chat::White, "Available options:"); - c->Message(Chat::White, "max_connection_count"); - c->Message(Chat::White, "keepalive_delay_ms"); - c->Message(Chat::White, "resend_delay_factor"); - c->Message(Chat::White, "resend_delay_ms"); - c->Message(Chat::White, "resend_delay_min"); - c->Message(Chat::White, "resend_delay_max"); - c->Message(Chat::White, "connect_delay_ms"); - c->Message(Chat::White, "connect_stale_ms"); - c->Message(Chat::White, "stale_connection_ms"); - c->Message(Chat::White, "hold_size"); - c->Message(Chat::White, "hold_length_ms"); - c->Message(Chat::White, "simulated_in_packet_loss"); - c->Message(Chat::White, "simulated_out_packet_loss"); - c->Message(Chat::White, "resend_timeout"); - c->Message(Chat::White, "connection_close_time"); - } - } - else { - c->Message(Chat::White, "Unknown command: %s", sep->arg[1]); - c->Message(Chat::White, "Network commands avail:"); - c->Message(Chat::White, "getopt optname - Retrieve the current option value set."); - c->Message(Chat::White, "setopt optname - Set the current option allowed."); - } -} - -void command_viewzoneloot(Client *c, const Seperator *sep) -{ - std::map zone_loot_list; - auto npc_list = entity_list.GetNPCList(); - uint32 loot_amount = 0, loot_id = 1, search_item_id = 0; - if (sep->argnum == 1 && sep->IsNumber(1)) { - search_item_id = atoi(sep->arg[1]); - } else if (sep->argnum == 1 && !sep->IsNumber(1)) { - c->Message( - Chat::Yellow, - "Usage: #viewzoneloot [item id]" - ); - return; - } - - for (auto npc_entity : npc_list) { - auto current_npc_item_list = npc_entity.second->GetItemList(); - zone_loot_list.insert({ npc_entity.second->GetID(), current_npc_item_list }); - } - - for (auto loot_item : zone_loot_list) { - uint32 current_entity_id = loot_item.first; - auto current_item_list = loot_item.second; - auto current_npc = entity_list.GetNPCByID(current_entity_id); - std::string npc_link; - if (current_npc) { - std::string npc_name = current_npc->GetCleanName(); - uint32 instance_id = zone->GetInstanceID(); - uint32 zone_id = zone->GetZoneID(); - std::string command_link = EQ::SayLinkEngine::GenerateQuestSaylink( - fmt::format( - "#{} {} {} {} {}", - (instance_id != 0 ? "zoneinstance" : "zone"), - (instance_id != 0 ? instance_id : zone_id), - current_npc->GetX(), - current_npc->GetY(), - current_npc->GetZ() - ), - false, - "Goto" - ); - npc_link = fmt::format( - " NPC: {} (ID {}) [{}]", - npc_name, - current_entity_id, - command_link - ); - } - - for (auto current_item : current_item_list) { - if (search_item_id == 0 || current_item->item_id == search_item_id) { - EQ::SayLinkEngine linker; - linker.SetLinkType(EQ::saylink::SayLinkLootItem); - linker.SetLootData(current_item); - c->Message( - Chat::White, - fmt::format( - "{}. {} ({}){}", - loot_id, - linker.GenerateLink(), - current_item->item_id, - npc_link - ).c_str() - ); - loot_id++; - loot_amount++; - } - } - } - - - if (search_item_id != 0) { - std::string drop_string = ( - loot_amount > 0 ? - fmt::format( - "dropping in {} {}", - loot_amount, - (loot_amount > 1 ? "places" : "place") - ) : - "not dropping" - ); - - c->Message( - Chat::White, - fmt::format( - "{} ({}) is {}.", - database.CreateItemLink(search_item_id), - search_item_id, - drop_string - ).c_str() - ); - } else { - std::string drop_string = ( - loot_amount > 0 ? - fmt::format( - "{} {} dropping", - (loot_amount > 1 ? "items" : "item"), - (loot_amount > 1 ? "are" : "is") - ) : - "items are dropping" - ); - - c->Message( - Chat::White, - fmt::format( - "{} {}.", - loot_amount, - drop_string - ).c_str() - ); - } -} - -void command_dye(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - - if (arguments == 0) { - c->Message(Chat::White, "Command Syntax: #dye help | #dye [slot] [red] [green] [blue] [use_tint]"); - return; - } - - uint8 slot = 0; - uint8 red = 255; - uint8 green = 255; - uint8 blue = 255; - uint8 use_tint = 255; - - std::vector dye_slots = { - "Helmet", - "Chest", - "Arms", - "Wrist", - "Hands", - "Legs", - "Feet" - }; - - if (arguments == 1 && !strcasecmp(sep->arg[1], "help")) { - int slot_id = 0; - std::vector slot_messages; - c->Message(Chat::White, "Command Syntax: #dye help | #dye [slot] [red] [green] [blue] [use_tint]"); - c->Message(Chat::White, "Red, Green, and Blue go from 0 to 255."); - - for (const auto& slot : dye_slots) { - slot_messages.push_back(fmt::format("({}) {}", slot_id, slot)); - slot_id++; - } - - c->Message( - Chat::White, - fmt::format( - "{} {}", - "Slots are as follows:", - implode(", ", slot_messages) - ).c_str() - ); - return; - } - - if (arguments >= 1 && sep->IsNumber(1)) { - slot = atoi(sep->arg[1]); - } - - if (arguments >= 2 && sep->IsNumber(2)) { - red = atoi(sep->arg[2]); - } - - if (arguments >= 3 && sep->IsNumber(3)) { - green = atoi(sep->arg[3]); - } - - if (arguments >= 4 && sep->IsNumber(4)) { - blue = atoi(sep->arg[4]); - } - - if (arguments >= 5 && sep->IsNumber(5)) { - use_tint = atoi(sep->arg[5]); - } - - if (RuleB(Command, DyeCommandRequiresDyes)) { - uint32 dye_item_id = 32557; - if (c->CountItem(dye_item_id) >= 1) { - c->RemoveItem(dye_item_id); - } else { - EQ::SayLinkEngine linker; - linker.SetLinkType(EQ::saylink::SayLinkItemData); - const EQ::ItemData *dye_item = database.GetItem(dye_item_id); - linker.SetItemData(dye_item); - c->Message(Chat::White, fmt::format("This command requires a {} to use.", linker.GenerateLink()).c_str()); - return; - } - } - - c->DyeArmorBySlot(slot, red, green, blue, use_tint); -} - -void command_findtask(Client *c, const Seperator *sep) -{ - if (RuleB(TaskSystem, EnableTaskSystem)) { - int arguments = sep->argnum; - - if (arguments == 0) { - c->Message(Chat::White, "Command Syntax: #findtask [search criteria]"); - return; - } - - if (sep->IsNumber(1)) { - auto task_id = std::stoul(sep->arg[1]); - auto task_name = task_manager->GetTaskName(task_id); - auto task_message = ( - !task_name.empty() ? - fmt::format( - "Task {}: {}", - task_id, - task_name - ).c_str() : - fmt::format( - "Task ID {} was not found.", - task_id - ).c_str() - ); - - c->Message( - Chat::White, - task_message - ); - } else { - std::string search_criteria = str_tolower(sep->argplus[1]); - if (!search_criteria.empty()) { - int found_count = 0; - for (uint32 task_id = 1; task_id <= MAXTASKS; task_id++) { - auto task_name = task_manager->GetTaskName(task_id); - std::string task_name_lower = str_tolower(task_name); - if (task_name_lower.find(search_criteria) == std::string::npos) { - continue; - } - - c->Message( - Chat::White, - fmt::format( - "Task {}: {}", - task_id, - task_name - ).c_str() - ); - found_count++; - - if (found_count == 20) { - break; - } - } - - if (found_count == 20) { - c->Message(Chat::White, "20 Tasks were found, max reached."); - } else { - auto task_message = ( - found_count > 0 ? - ( - found_count == 1 ? - "A Task was" : - fmt::format("{} Tasks were", found_count) - ) : - "No Tasks were" - ); - - c->Message( - Chat::White, - fmt::format( - "{} found.", - task_message - ).c_str() - ); - } - } - } - } else { - c->Message(Chat::White, "This command cannot be used while the Task system is disabled."); - } -} - void command_emptyinventory(Client *c, const Seperator *sep) { Client *target = c; diff --git a/zone/command.h b/zone/command.h index b0d5ccc40..4b5f99b2d 100644 --- a/zone/command.h +++ b/zone/command.h @@ -1,22 +1,3 @@ -/* 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 -*/ - - #ifndef COMMAND_H #define COMMAND_H @@ -25,18 +6,18 @@ class Seperator; #include "../common/types.h" -#define COMMAND_CHAR '#' +#define COMMAND_CHAR '#' -typedef void (*CmdFuncPtr)(Client *,const Seperator *); +typedef void (*CmdFuncPtr)(Client *, const Seperator *); typedef struct { - int access; - const char *desc; // description of command - CmdFuncPtr function; // null means perl function -} CommandRecord; + int access; + const char *desc; // description of command + CmdFuncPtr function; // null means perl function +} CommandRecord; -extern int (*command_dispatch)(Client *,char const*); -extern int commandcount; // number of commands loaded +extern int (*command_dispatch)(Client *, char const *); +extern int commandcount; // number of commands loaded // the command system: int command_init(void); @@ -59,7 +40,7 @@ void command_augmentitem(Client *c, const Seperator *sep); void command_ban(Client *c, const Seperator *sep); void command_beard(Client *c, const Seperator *sep); void command_beardcolor(Client *c, const Seperator *sep); -void command_bind(Client* c, const Seperator *sep); +void command_bind(Client *c, const Seperator *sep); void command_camerashake(Client *c, const Seperator *sep); void command_castspell(Client *c, const Seperator *sep); void command_chat(Client *c, const Seperator *sep); @@ -87,10 +68,10 @@ void command_doanim(Client *c, const Seperator *sep); void command_dye(Client *c, const Seperator *sep); void command_dz(Client *c, const Seperator *sep); void command_dzkickplayers(Client *c, const Seperator *sep); -void command_editmassrespawn(Client* c, const Seperator* sep); +void command_editmassrespawn(Client *c, const Seperator *sep); void command_emote(Client *c, const Seperator *sep); -void command_emotesearch(Client* c, const Seperator *sep); -void command_emoteview(Client* c, const Seperator *sep); +void command_emotesearch(Client *c, const Seperator *sep); +void command_emoteview(Client *c, const Seperator *sep); void command_emptyinventory(Client *c, const Seperator *sep); void command_enablerecipe(Client *c, const Seperator *sep); void command_endurance(Client *c, const Seperator *sep); @@ -121,14 +102,14 @@ void command_getvariable(Client *c, const Seperator *sep); void command_ginfo(Client *c, const Seperator *sep); void command_giveitem(Client *c, const Seperator *sep); void command_givemoney(Client *c, const Seperator *sep); -void command_globalview(Client* c, const Seperator *sep); +void command_globalview(Client *c, const Seperator *sep); void command_gm(Client *c, const Seperator *sep); void command_gmspeed(Client *c, const Seperator *sep); void command_gmzone(Client *c, const Seperator *sep); void command_goto(Client *c, const Seperator *sep); void command_grid(Client *c, const Seperator *sep); void command_guild(Client *c, const Seperator *sep); -bool helper_guild_edit(Client *c, uint32 dbid, uint32 eqid, uint8 rank, const char* what, const char* value); +bool helper_guild_edit(Client *c, uint32 dbid, uint32 eqid, uint8 rank, const char *what, const char *value); void command_guildapprove(Client *c, const Seperator *sep); void command_guildcreate(Client *c, const Seperator *sep); void command_guildlist(Client *c, const Seperator *sep); @@ -193,12 +174,14 @@ void command_npcspecialattk(Client *c, const Seperator *sep); void command_npcstats(Client *c, const Seperator *sep); void command_npctype_cache(Client *c, const Seperator *sep); void command_npctypespawn(Client *c, const Seperator *sep); -void command_nudge(Client* c, const Seperator* sep); +void command_nudge(Client *c, const Seperator *sep); void command_nukebuffs(Client *c, const Seperator *sep); void command_nukeitem(Client *c, const Seperator *sep); -void command_object(Client* c, const Seperator *sep); +void command_object(Client *c, const Seperator *sep); void command_oocmute(Client *c, const Seperator *sep); void command_opcode(Client *c, const Seperator *sep); +void command_bestz(Client *c, const Seperator *message); +void command_pf(Client *c, const Seperator *message); #ifdef PACKET_PROFILER void command_packetprofile(Client *c, const Seperator *sep); @@ -225,24 +208,24 @@ void command_pvp(Client *c, const Seperator *sep); void command_qglobal(Client *c, const Seperator *sep); void command_questerrors(Client *c, const Seperator *sep); void command_race(Client *c, const Seperator *sep); -void command_raidloot(Client* c, const Seperator *sep); +void command_raidloot(Client *c, const Seperator *sep); void command_randomfeatures(Client *c, const Seperator *sep); void command_refreshgroup(Client *c, const Seperator *sep); void command_reloadaa(Client *c, const Seperator *sep); void command_reloadallrules(Client *c, const Seperator *sep); -void command_reloademote(Client* c, const Seperator *sep); +void command_reloademote(Client *c, const Seperator *sep); void command_reloadlevelmods(Client *c, const Seperator *sep); void command_reloadmerchants(Client *c, const Seperator *sep); void command_reloadperlexportsettings(Client *c, const Seperator *sep); void command_reloadqst(Client *c, const Seperator *sep); void command_reloadstatic(Client *c, const Seperator *sep); void command_reloadtitles(Client *c, const Seperator *sep); -void command_reloadtraps(Client* c, const Seperator *sep); +void command_reloadtraps(Client *c, const Seperator *sep); void command_reloadworld(Client *c, const Seperator *sep); void command_reloadworldrules(Client *c, const Seperator *sep); void command_reloadzps(Client *c, const Seperator *sep); void command_repop(Client *c, const Seperator *sep); -void command_resetaa(Client* c,const Seperator *sep); +void command_resetaa(Client *c, const Seperator *sep); void command_resetaa_timer(Client *c, const Seperator *sep); void command_resetdisc_timer(Client *c, const Seperator *sep); void command_revoke(Client *c, const Seperator *sep); @@ -305,7 +288,7 @@ void command_timezone(Client *c, const Seperator *sep); void command_title(Client *c, const Seperator *sep); void command_titlesuffix(Client *c, const Seperator *sep); void command_traindisc(Client *c, const Seperator *sep); -void command_trapinfo(Client* c, const Seperator *sep); +void command_trapinfo(Client *c, const Seperator *sep); void command_tune(Client *c, const Seperator *sep); void command_ucs(Client *c, const Seperator *sep); void command_undye(Client *c, const Seperator *sep); diff --git a/zone/gm_commands/acceptrules.cpp b/zone/gm_commands/acceptrules.cpp new file mode 100755 index 000000000..a73e098a2 --- /dev/null +++ b/zone/gm_commands/acceptrules.cpp @@ -0,0 +1,11 @@ +#include "../client.h" + +void command_acceptrules(Client *c, const Seperator *sep) +{ + if (!database.GetAgreementFlag(c->AccountID())) { + database.SetAgreementFlag(c->AccountID()); + c->SendAppearancePacket(AT_Anim, ANIM_STAND); + c->Message(Chat::White, "It is recorded you have agreed to the rules."); + } +} + diff --git a/zone/gm_commands/advnpcspawn.cpp b/zone/gm_commands/advnpcspawn.cpp new file mode 100755 index 000000000..7545ded35 --- /dev/null +++ b/zone/gm_commands/advnpcspawn.cpp @@ -0,0 +1,534 @@ +#include "../client.h" +#include "../groups.h" + +void command_advnpcspawn(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments) { + c->Message( + Chat::White, + "Usage: #advnpcspawn addentry [Spawngroup ID] [NPC ID] [Spawn Chance] - Adds a new Spawngroup Entry" + ); + c->Message( + Chat::White, + "Usage: #advnpcspawn addspawn [Spawngroup ID] - Adds a new Spawngroup Entry from an existing Spawngroup" + ); + c->Message(Chat::White, "Usage: #advnpcspawn clearbox [Spawngroup ID] - Clears the roambox of a Spawngroup"); + c->Message(Chat::White, "Usage: #advnpcspawn deletespawn - Deletes a Spawngroup"); + c->Message( + Chat::White, + "Usage: #advnpcspawn editbox [Spawngroup ID] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Edit the roambox of a Spawngroup" + ); + c->Message( + Chat::White, + "Usage: #advnpcspawn editrespawn [Respawn Timer] [Variance] - Edit the Respawn Timer of a Spawngroup" + ); + c->Message( + Chat::White, + "Usage: #advnpcspawn makegroup [Spawn Group Name] [Spawn Limit] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Makes a new Spawngroup" + ); + c->Message(Chat::White, "Usage: #advnpcspawn makenpc - Makes a new NPC"); + c->Message(Chat::White, "Usage: #advnpcspawn movespawn - Moves a Spawngroup to your current location"); + c->Message(Chat::White, "Usage: #advnpcspawn setversion [Version] - Sets a Spawngroup's Version"); + return; + } + + std::string spawn_command = str_tolower(sep->arg[1]); + bool is_add_entry = spawn_command.find("addentry") != std::string::npos; + bool is_add_spawn = spawn_command.find("addspawn") != std::string::npos; + bool is_clear_box = spawn_command.find("clearbox") != std::string::npos; + bool is_delete_spawn = spawn_command.find("deletespawn") != std::string::npos; + bool is_edit_box = spawn_command.find("editgroup") != std::string::npos; + bool is_edit_respawn = spawn_command.find("editrespawn") != std::string::npos; + bool is_make_group = spawn_command.find("makegroup") != std::string::npos; + bool is_make_npc = spawn_command.find("makenpc") != std::string::npos; + bool is_move_spawn = spawn_command.find("movespawn") != std::string::npos; + bool is_set_version = spawn_command.find("setversion") != std::string::npos; + if ( + !is_add_entry && + !is_add_spawn && + !is_clear_box && + !is_delete_spawn && + !is_edit_box && + !is_edit_respawn && + !is_make_group && + !is_make_npc && + !is_move_spawn && + !is_set_version + ) { + c->Message( + Chat::White, + "Usage: #advnpcspawn addentry [Spawngroup ID] [NPC ID] [Spawn Chance] - Adds a new Spawngroup Entry" + ); + c->Message( + Chat::White, + "Usage: #advnpcspawn addspawn [Spawngroup ID] - Adds a new Spawngroup Entry from an existing Spawngroup" + ); + c->Message(Chat::White, "Usage: #advnpcspawn clearbox [Spawngroup ID] - Clears the roambox of a Spawngroup"); + c->Message(Chat::White, "Usage: #advnpcspawn deletespawn - Deletes a Spawngroup"); + c->Message( + Chat::White, + "Usage: #advnpcspawn editbox [Spawngroup ID] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Edit the roambox of a Spawngroup" + ); + c->Message( + Chat::White, + "Usage: #advnpcspawn editrespawn [Respawn Timer] [Variance] - Edit the Respawn Timer of a Spawngroup" + ); + c->Message( + Chat::White, + "Usage: #advnpcspawn makegroup [Spawn Group Name] [Spawn Limit] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Makes a new Spawngroup" + ); + c->Message(Chat::White, "Usage: #advnpcspawn makenpc - Makes a new NPC"); + c->Message(Chat::White, "Usage: #advnpcspawn movespawn - Moves a Spawngroup to your current location"); + c->Message(Chat::White, "Usage: #advnpcspawn setversion [Version] - Sets a Spawngroup's Version"); + return; + } + + + if (is_add_entry) { + if (arguments < 4) { + c->Message(Chat::White, "Usage: #advnpcspawn addentry [Spawngroup ID] [NPC ID] [Spawn Chance]"); + return; + } + + auto spawngroup_id = std::stoi(sep->arg[2]); + auto npc_id = std::stoi(sep->arg[2]); + auto spawn_chance = std::stoi(sep->arg[2]); + + std::string query = fmt::format( + SQL( + INSERT INTO spawnentry(spawngroupID, npcID, chance) + VALUES({}, {}, {}) + ), + spawngroup_id, + npc_id, + spawn_chance + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to add entry to Spawngroup."); + return; + } + + c->Message( + Chat::White, + fmt::format( + "{} ({}) added to Spawngroup {}, its spawn chance is {}%%.", + database.GetCleanNPCNameByID(npc_id), + npc_id, + spawngroup_id, + spawn_chance + ).c_str() + ); + return; + } + else if (is_add_spawn) { + content_db.NPCSpawnDB( + NPCSpawnTypes::AddSpawnFromSpawngroup, + zone->GetShortName(), + zone->GetInstanceVersion(), + c, + 0, + std::stoi(sep->arg[2]) + ); + c->Message( + Chat::White, + fmt::format( + "Spawn Added | Added spawn from Spawngroup ID {}.", + std::stoi(sep->arg[2]) + ).c_str() + ); + return; + } + else if (is_clear_box) { + if (arguments != 2 || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #advnpcspawn clearbox [Spawngroup ID]"); + return; + } + + std::string query = fmt::format( + "UPDATE spawngroup SET dist = 0, min_x = 0, max_x = 0, min_y = 0, max_y = 0, delay = 0 WHERE id = {}", + std::stoi(sep->arg[2]) + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to clear Spawngroup box."); + return; + } + + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Roambox Cleared | Delay: 0 Distance: 0.00", + std::stoi(sep->arg[2]) + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Roambox Cleared | Minimum X: 0.00 Maximum X: 0.00", + std::stoi(sep->arg[2]) + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Roambox Cleared | Minimum Y: 0.00 Maximum Y: 0.00", + std::stoi(sep->arg[2]) + ).c_str() + ); + return; + } + else if (is_delete_spawn) { + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } + + NPC *target = c->GetTarget()->CastToNPC(); + Spawn2 *spawn2 = target->respawn2; + if (!spawn2) { + c->Message(Chat::White, "Failed to delete spawn because NPC has no Spawn2."); + return; + } + + auto spawn2_id = spawn2->GetID(); + std::string query = fmt::format( + "DELETE FROM spawn2 WHERE id = {}", + spawn2_id + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to delete spawn."); + return; + } + + c->Message( + Chat::White, + fmt::format( + "Spawn2 {} Deleted | Name: {} ({})", + spawn2_id, + target->GetCleanName(), + target->GetID() + ).c_str() + ); + target->Depop(false); + return; + } + else if (is_edit_box) { + if ( + arguments != 8 || + !sep->IsNumber(3) || + !sep->IsNumber(4) || + !sep->IsNumber(5) || + !sep->IsNumber(6) || + !sep->IsNumber(7) || + !sep->IsNumber(8) + ) { + c->Message( + Chat::White, + "Usage: #advnpcspawn editbox [Spawngroup ID] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay]" + ); + return; + } + auto spawngroup_id = std::stoi(sep->arg[2]); + auto distance = std::stof(sep->arg[3]); + auto minimum_x = std::stof(sep->arg[4]); + auto maximum_x = std::stof(sep->arg[5]); + auto minimum_y = std::stof(sep->arg[6]); + auto maximum_y = std::stof(sep->arg[7]); + auto delay = std::stoi(sep->arg[8]); + + std::string query = fmt::format( + "UPDATE spawngroup SET dist = {:.2f}, min_x = {:.2f}, max_x = {:.2f}, max_y = {:.2f}, min_y = {:.2f}, delay = {} WHERE id = {}", + distance, + minimum_x, + maximum_x, + minimum_y, + maximum_y, + delay, + spawngroup_id + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to edit Spawngroup box."); + return; + } + + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Roambox Edited | Delay: {} Distance: {:.2f}", + spawngroup_id, + delay, + distance + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Roambox Edited | Minimum X: {:.2f} Maximum X: {:.2f}", + spawngroup_id, + minimum_x, + maximum_x + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Roambox Edited | Minimum Y: {:.2f} Maximum Y: {:.2f}", + spawngroup_id, + minimum_y, + maximum_y + ).c_str() + ); + return; + } + else if (is_edit_respawn) { + if (arguments < 2 || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #advnpcspawn editrespawn [Respawn Timer] [Variance]"); + return; + } + + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } + + NPC *target = c->GetTarget()->CastToNPC(); + Spawn2 *spawn2 = target->respawn2; + if (!spawn2) { + c->Message(Chat::White, "Failed to edit respawn because NPC has no Spawn2."); + return; + } + + auto spawn2_id = spawn2->GetID(); + uint32 respawn_timer = std::stoi(sep->arg[2]); + uint32 variance = ( + sep->IsNumber(3) ? + std::stoi(sep->arg[3]) : + spawn2->GetVariance() + ); + std::string query = fmt::format( + "UPDATE spawn2 SET respawntime = {}, variance = {} WHERE id = {}", + respawn_timer, + variance, + spawn2_id + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to edit respawn."); + return; + } + + c->Message( + Chat::White, + fmt::format( + "Spawn2 {} Respawn Modified | Name: {} ({}) Respawn Timer: {} Variance: {}", + spawn2_id, + target->GetCleanName(), + target->GetID(), + respawn_timer, + variance + ).c_str() + ); + spawn2->SetRespawnTimer(respawn_timer); + spawn2->SetVariance(variance); + return; + } + else if (is_make_group) { + if ( + arguments != 9 || + !sep->IsNumber(3) || + !sep->IsNumber(4) || + !sep->IsNumber(5) || + !sep->IsNumber(6) || + !sep->IsNumber(7) || + !sep->IsNumber(8) || + !sep->IsNumber(9) + ) { + c->Message( + Chat::White, + "Usage: #advncspawn makegroup [Spawn Group Name] [Spawn Limit] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay]" + ); + return; + } + std::string spawngroup_name = sep->arg[2]; + auto spawn_limit = std::stoi(sep->arg[3]); + auto distance = std::stof(sep->arg[4]); + auto minimum_x = std::stof(sep->arg[5]); + auto maximum_x = std::stof(sep->arg[6]); + auto minimum_y = std::stof(sep->arg[7]); + auto maximum_y = std::stof(sep->arg[8]); + auto delay = std::stoi(sep->arg[9]); + + std::string query = fmt::format( + "INSERT INTO spawngroup" + "(name, spawn_limit, dist, min_x, max_x, min_y, max_y, delay)" + "VALUES ('{}', {}, {:.2f}, {:.2f}, {:.2f}, {:.2f}, {:.2f}, {})", + spawngroup_name, + spawn_limit, + distance, + minimum_x, + maximum_x, + minimum_y, + maximum_y, + delay + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to make Spawngroup."); + return; + } + + auto spawngroup_id = results.LastInsertedID(); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Created | Name: {} Spawn Limit: {}", + spawngroup_id, + spawngroup_name, + spawn_limit + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Created | Delay: {} Distance: {:.2f}", + spawngroup_id, + delay, + distance + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Created | Minimum X: {:.2f} Maximum X: {:.2f}", + spawngroup_id, + minimum_x, + maximum_x + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Created | Minimum Y: {:.2f} Maximum Y: {:.2f}", + spawngroup_id, + minimum_y, + maximum_y + ).c_str() + ); + return; + } + else if (is_make_npc) { + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } + + NPC *target = c->GetTarget()->CastToNPC(); + content_db.NPCSpawnDB( + NPCSpawnTypes::CreateNewNPC, + zone->GetShortName(), + zone->GetInstanceVersion(), + c, + target + ); + return; + } + else if (is_move_spawn) { + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } + + NPC *target = c->GetTarget()->CastToNPC(); + Spawn2 *spawn2 = target->respawn2; + if (!spawn2) { + c->Message(Chat::White, "Failed to move spawn because NPC has no Spawn2."); + return; + } + + auto client_position = c->GetPosition(); + auto spawn2_id = spawn2->GetID(); + std::string query = fmt::format( + "UPDATE spawn2 SET x = {:.2f}, y = {:.2f}, z = {:.2f}, heading = {:.2f} WHERE id = {}", + client_position.x, + client_position.y, + client_position.z, + client_position.w, + spawn2_id + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to move spawn."); + return; + } + + c->Message( + Chat::White, + fmt::format( + "Spawn2 {} Moved | Name: {} ({})", + spawn2_id, + target->GetCleanName(), + target->GetID() + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawn2 {} Moved | XYZ: {}, {}, {} Heading: {}", + spawn2_id, + client_position.x, + client_position.y, + client_position.z, + client_position.w + ).c_str() + ); + target->GMMove( + client_position.x, + client_position.y, + client_position.z, + client_position.w + ); + return; + } + else if (is_set_version) { + if (arguments != 2 || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #advnpcspawn setversion [Version]"); + return; + } + + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } + + NPC *target = c->GetTarget()->CastToNPC(); + auto version = std::stoi(sep->arg[2]); + std::string query = fmt::format( + "UPDATE spawn2 SET version = {} WHERE spawngroupID = {}", + version, + target->GetSpawnGroupId() + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to set version."); + return; + } + + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Version Modified | Name: {} ({}) Version: {}", + target->GetSpawnGroupId(), + target->GetCleanName(), + target->GetID(), + version + ).c_str() + ); + target->Depop(false); + return; + } +} + diff --git a/zone/gm_commands/aggro.cpp b/zone/gm_commands/aggro.cpp new file mode 100755 index 000000000..9b8307cb2 --- /dev/null +++ b/zone/gm_commands/aggro.cpp @@ -0,0 +1,21 @@ +#include "../client.h" + +void command_aggro(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == nullptr || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "Error: you must have an NPC target."); + return; + } + float d = atof(sep->arg[1]); + if (d == 0.0f) { + c->Message(Chat::Red, "Error: distance argument required."); + return; + } + bool verbose = false; + if (sep->arg[2][0] == '-' && sep->arg[2][1] == 'v' && sep->arg[2][2] == '\0') { + verbose = true; + } + + entity_list.DescribeAggro(c, c->GetTarget()->CastToNPC(), d, verbose); +} + diff --git a/zone/gm_commands/aggrozone.cpp b/zone/gm_commands/aggrozone.cpp new file mode 100755 index 000000000..afdf4ad64 --- /dev/null +++ b/zone/gm_commands/aggrozone.cpp @@ -0,0 +1,19 @@ +#include "../client.h" + +void command_aggrozone(Client *c, const Seperator *sep) +{ + if (!c) { + return; + } + + Mob *m = c->CastToMob(); + + if (!m) { + return; + } + + uint32 hate = atoi(sep->arg[1]); //should default to 0 if we don't enter anything + entity_list.AggroZone(m, hate); + c->Message(Chat::White, "Train to you! Last chance to go invulnerable..."); +} + diff --git a/zone/gm_commands/ai.cpp b/zone/gm_commands/ai.cpp new file mode 100755 index 000000000..93f9a057f --- /dev/null +++ b/zone/gm_commands/ai.cpp @@ -0,0 +1,139 @@ +#include "../client.h" + +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"); + } +} + diff --git a/zone/gm_commands/appearance.cpp b/zone/gm_commands/appearance.cpp new file mode 100755 index 000000000..4f28fc50f --- /dev/null +++ b/zone/gm_commands/appearance.cpp @@ -0,0 +1,26 @@ +#include "../client.h" + +void command_appearance(Client *c, const Seperator *sep) +{ + Mob *t = c->CastToMob(); + + // sends any appearance packet + // Dev debug command, for appearance types + if (sep->arg[2][0] == 0) { + c->Message(Chat::White, "Usage: #appearance type value"); + } + else { + if ((c->GetTarget())) { + t = c->GetTarget(); + } + t->SendAppearancePacket(atoi(sep->arg[1]), atoi(sep->arg[2])); + c->Message( + Chat::White, + "Sending appearance packet: target=%s, type=%s, value=%s", + t->GetName(), + sep->arg[1], + sep->arg[2] + ); + } +} + diff --git a/zone/gm_commands/attack.cpp b/zone/gm_commands/attack.cpp new file mode 100755 index 000000000..c4a483aa9 --- /dev/null +++ b/zone/gm_commands/attack.cpp @@ -0,0 +1,18 @@ +#include "../client.h" + +void command_attack(Client *c, const Seperator *sep) +{ + if (c->GetTarget() && c->GetTarget()->IsNPC() && sep->arg[1] != 0) { + Mob *sictar = entity_list.GetMob(sep->argplus[1]); + if (sictar) { + c->GetTarget()->CastToNPC()->AddToHateList(sictar, 1, 0); + } + else { + c->Message(Chat::White, "Error: %s not found", sep->arg[1]); + } + } + else { + c->Message(Chat::White, "Usage: (needs NPC targeted) #attack targetname"); + } +} + diff --git a/zone/gm_commands/augmentitem.cpp b/zone/gm_commands/augmentitem.cpp new file mode 100755 index 000000000..81b5523aa --- /dev/null +++ b/zone/gm_commands/augmentitem.cpp @@ -0,0 +1,18 @@ +#include "../client.h" +#include "../object.h" + +void command_augmentitem(Client *c, const Seperator *sep) +{ + if (!c) { + return; + } + + auto in_augment = new AugmentItem_Struct[sizeof(AugmentItem_Struct)]; + in_augment->container_slot = 1000; // + in_augment->augment_slot = -1; + if (c->GetTradeskillObject() != nullptr) { + Object::HandleAugmentation(c, in_augment, c->GetTradeskillObject()); + } + safe_delete_array(in_augment); +} + diff --git a/zone/gm_commands/ban.cpp b/zone/gm_commands/ban.cpp new file mode 100755 index 000000000..3582aca19 --- /dev/null +++ b/zone/gm_commands/ban.cpp @@ -0,0 +1,72 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_ban(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0 || sep->arg[2][0] == 0) { + c->Message(Chat::White, "Usage: #ban "); + return; + } + + auto account_id = database.GetAccountIDByChar(sep->arg[1]); + + std::string message; + int i = 2; + while (1) { + if (sep->arg[i][0] == 0) { + break; + } + + if (message.length() > 0) { + message.push_back(' '); + } + + message += sep->arg[i]; + ++i; + } + + if (message.length() == 0) { + c->Message(Chat::White, "Usage: #ban "); + return; + } + + if (account_id == 0) { + c->Message(Chat::Red, "Character does not exist."); + return; + } + + std::string query = StringFormat( + "UPDATE account SET status = -2, ban_reason = '%s' " + "WHERE id = %i", EscapeString(message).c_str(), account_id + ); + auto results = database.QueryDatabase(query); + + c->Message( + Chat::Red, + "Account number %i with the character %s has been banned with message: \"%s\"", + account_id, + sep->arg[1], + message.c_str()); + + ServerPacket flagUpdatePack(ServerOP_FlagUpdate, 6); + *((uint32 *) &flagUpdatePack.pBuffer[0]) = account_id; + *((int16 *) &flagUpdatePack.pBuffer[4]) = -2; + worldserver.SendPacket(&flagUpdatePack); + + Client *client = nullptr; + client = entity_list.GetClientByName(sep->arg[1]); + if (client) { + client->WorldKick(); + return; + } + + ServerPacket kickPlayerPack(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); + ServerKickPlayer_Struct *skp = (ServerKickPlayer_Struct *) kickPlayerPack.pBuffer; + strcpy(skp->adminname, c->GetName()); + strcpy(skp->name, sep->arg[1]); + skp->adminrank = c->Admin(); + worldserver.SendPacket(&kickPlayerPack); +} + diff --git a/zone/gm_commands/beard.cpp b/zone/gm_commands/beard.cpp new file mode 100755 index 000000000..b43b7b9d4 --- /dev/null +++ b/zone/gm_commands/beard.cpp @@ -0,0 +1,37 @@ +#include "../client.h" + +void command_beard(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #beard [number of beard style]"); + } + 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 = atoi(sep->arg[1]); + 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 + ); + + c->Message(Chat::White, "Beard = %i", atoi(sep->arg[1])); + } +} + diff --git a/zone/gm_commands/beardcolor.cpp b/zone/gm_commands/beardcolor.cpp new file mode 100755 index 000000000..85eccf390 --- /dev/null +++ b/zone/gm_commands/beardcolor.cpp @@ -0,0 +1,37 @@ +#include "../client.h" + +void command_beardcolor(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #beardcolor [number of beard color]"); + } + 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 = atoi(sep->arg[1]); + 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 + ); + + c->Message(Chat::White, "Beard Color = %i", atoi(sep->arg[1])); + } +} + diff --git a/zone/gm_commands/bestz.cpp b/zone/gm_commands/bestz.cpp new file mode 100755 index 000000000..809e63ec1 --- /dev/null +++ b/zone/gm_commands/bestz.cpp @@ -0,0 +1,89 @@ +#include "../client.h" +#include "../water_map.h" + +void command_bestz(Client *c, const Seperator *sep) +{ + if (zone->zonemap == nullptr) { + c->Message(Chat::White, "Map not loaded for this zone"); + } + else { + glm::vec3 me; + me.x = c->GetX(); + me.y = c->GetY(); + me.z = c->GetZ() + (c->GetSize() == 0.0 ? 6 : c->GetSize()) * HEAD_POSITION; + glm::vec3 hit; + glm::vec3 bme(me); + bme.z -= 500; + + float best_z = zone->zonemap->FindBestZ(me, &hit); + + if (best_z != BEST_Z_INVALID) { + c->Message(Chat::White, "Z is %.3f at (%.3f, %.3f).", best_z, me.x, me.y); + } + else { + c->Message(Chat::White, "Found no Z."); + } + } + + if (zone->watermap == nullptr) { + c->Message(Chat::White, "Water Region Map not loaded for this zone"); + } + else { + WaterRegionType RegionType; + float z; + + if (c->GetTarget()) { + z = c->GetTarget()->GetZ(); + auto position = glm::vec3(c->GetTarget()->GetX(), c->GetTarget()->GetY(), z); + RegionType = zone->watermap->ReturnRegionType(position); + c->Message(Chat::White, "InWater returns %d", zone->watermap->InWater(position)); + c->Message(Chat::White, "InLava returns %d", zone->watermap->InLava(position)); + + } + else { + z = c->GetZ(); + auto position = glm::vec3(c->GetX(), c->GetY(), z); + RegionType = zone->watermap->ReturnRegionType(position); + c->Message(Chat::White, "InWater returns %d", zone->watermap->InWater(position)); + c->Message(Chat::White, "InLava returns %d", zone->watermap->InLava(position)); + + } + + switch (RegionType) { + case RegionTypeNormal: { + c->Message(Chat::White, "There is nothing special about the region you are in!"); + break; + } + case RegionTypeWater: { + c->Message(Chat::White, "You/your target are in Water."); + break; + } + case RegionTypeLava: { + c->Message(Chat::White, "You/your target are in Lava."); + break; + } + case RegionTypeVWater: { + c->Message(Chat::White, "You/your target are in VWater (Icy Water?)."); + break; + } + case RegionTypePVP: { + c->Message(Chat::White, "You/your target are in a pvp enabled area."); + break; + } + case RegionTypeSlime: { + c->Message(Chat::White, "You/your target are in slime."); + break; + } + case RegionTypeIce: { + c->Message(Chat::White, "You/your target are in ice."); + break; + } + default: + c->Message(Chat::White, "You/your target are in an unknown region type."); + } + } + + +} + + diff --git a/zone/gm_commands/bind.cpp b/zone/gm_commands/bind.cpp new file mode 100755 index 000000000..216985ae3 --- /dev/null +++ b/zone/gm_commands/bind.cpp @@ -0,0 +1,17 @@ +#include "../client.h" + +void command_bind(Client *c, const Seperator *sep) +{ + if (c->GetTarget() != 0) { + if (c->GetTarget()->IsClient()) { + c->GetTarget()->CastToClient()->SetBindPoint(); + } + else { + c->Message(Chat::White, "Error: target not a Player"); + } + } + else { + c->SetBindPoint(); + } +} + diff --git a/zone/gm_commands/camerashake.cpp b/zone/gm_commands/camerashake.cpp new file mode 100755 index 000000000..39a6ebcfd --- /dev/null +++ b/zone/gm_commands/camerashake.cpp @@ -0,0 +1,24 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_camerashake(Client *c, const Seperator *sep) +{ + if (c) { + if (sep->arg[1][0] && sep->arg[2][0]) { + auto pack = new ServerPacket(ServerOP_CameraShake, sizeof(ServerCameraShake_Struct)); + ServerCameraShake_Struct *scss = (ServerCameraShake_Struct *) pack->pBuffer; + scss->duration = atoi(sep->arg[1]); + scss->intensity = atoi(sep->arg[2]); + worldserver.SendPacket(pack); + c->Message(Chat::Red, "Successfully sent the packet to world! Shake it, world, shake it!"); + safe_delete(pack); + } + else { + c->Message(Chat::Red, "Usage -- #camerashake [duration], [intensity [1-10])"); + } + } + return; +} + diff --git a/zone/gm_commands/castspell.cpp b/zone/gm_commands/castspell.cpp new file mode 100755 index 000000000..b4410084d --- /dev/null +++ b/zone/gm_commands/castspell.cpp @@ -0,0 +1,77 @@ +#include "../client.h" + +void command_castspell(Client *c, const Seperator *sep) +{ + if (SPDAT_RECORDS <= 0) { + c->Message(Chat::White, "Spells not loaded."); + return; + } + + Mob *target = c; + if (c->GetTarget()) { + target = c->GetTarget(); + } + + if (!sep->IsNumber(1)) { + c->Message( + Chat::White, + "Usage: #castspell [Spell ID] [Instant (0 = False, 1 = True, Default is 1 if Unused)]" + ); + } + else { + uint16 spell_id = std::stoul(sep->arg[1]); + + if (CastRestrictedSpell(spell_id) && c->Admin() < commandCastSpecials) { + c->Message(Chat::Red, "Unable to cast spell."); + } + else if (spell_id >= SPDAT_RECORDS) { + c->Message(Chat::White, "Invalid Spell ID."); + } + else { + bool instant_cast = (c->Admin() >= commandInstacast ? true : false); + if (instant_cast && sep->IsNumber(2)) { + instant_cast = std::stoi(sep->arg[2]) ? true : false; + c->Message(Chat::White, fmt::format("{}", std::stoi(sep->arg[2])).c_str()); + } + + if (c->Admin() >= commandInstacast && instant_cast) { + c->SpellFinished( + spell_id, + target, + EQ::spells::CastingSlot::Item, + 0, + -1, + spells[spell_id].resist_difficulty + ); + } + else { + c->CastSpell(spell_id, target->GetID(), EQ::spells::CastingSlot::Item, spells[spell_id].cast_time); + } + + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "Cast {} ({}) on {}{}.", + GetSpellName(spell_id), + spell_id, + target->GetCleanName(), + instant_cast ? " instantly" : "" + ).c_str() + ); + } + else { + c->Message( + Chat::White, + fmt::format( + "Cast {} ({}) on yourself{}.", + GetSpellName(spell_id), + spell_id, + instant_cast ? " instantly" : "" + ).c_str() + ); + } + } + } +} + diff --git a/zone/gm_commands/chat.cpp b/zone/gm_commands/chat.cpp new file mode 100755 index 000000000..4b511ec84 --- /dev/null +++ b/zone/gm_commands/chat.cpp @@ -0,0 +1,15 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +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"); + } +} + diff --git a/zone/gm_commands/checklos.cpp b/zone/gm_commands/checklos.cpp new file mode 100755 index 000000000..fa54a56ef --- /dev/null +++ b/zone/gm_commands/checklos.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +void command_checklos(Client *c, const Seperator *sep) +{ + if (!c->GetTarget()) { + c->Message(Chat::White, "You must have a target to use this command."); + } + + bool has_los = c->CheckLosFN(c->GetTarget()); + c->Message( + Chat::White, + fmt::format( + "You {}have line of sight to {} ({}).", + has_los ? "" : "do not ", + c->GetTarget()->GetCleanName(), + c->GetTarget()->GetID() + ).c_str() + ); +} + diff --git a/zone/gm_commands/copycharacter.cpp b/zone/gm_commands/copycharacter.cpp new file mode 100755 index 000000000..136093005 --- /dev/null +++ b/zone/gm_commands/copycharacter.cpp @@ -0,0 +1,34 @@ +#include "../client.h" + +void command_copycharacter(Client *c, const Seperator *sep) +{ + if (sep->argnum < 3) { + c->Message( + Chat::White, + "Usage: [source_character_name] [destination_character_name] [destination_account_name]" + ); + return; + } + + std::string source_character_name = sep->arg[1]; + std::string destination_character_name = sep->arg[2]; + std::string destination_account_name = sep->arg[3]; + + bool result = database.CopyCharacter( + source_character_name, + destination_character_name, + destination_account_name + ); + + c->Message( + Chat::Yellow, + fmt::format( + "Character Copy [{}] to [{}] via account [{}] [{}]", + source_character_name, + destination_character_name, + destination_account_name, + result ? "Success" : "Failed" + ).c_str() + ); +} + diff --git a/zone/gm_commands/corpse.cpp b/zone/gm_commands/corpse.cpp new file mode 100755 index 000000000..739c3458a --- /dev/null +++ b/zone/gm_commands/corpse.cpp @@ -0,0 +1,168 @@ +#include "../client.h" +#include "../corpse.h" + +void command_corpse(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + + if (strcasecmp(sep->arg[1], "DeletePlayerCorpses") == 0 && c->Admin() >= commandEditPlayerCorpses) { + int32 tmp = entity_list.DeletePlayerCorpses(); + if (tmp >= 0) { + c->Message(Chat::White, "%i corpses deleted.", tmp); + } + else { + c->Message(Chat::White, "DeletePlayerCorpses Error #%i", tmp); + } + } + else if (strcasecmp(sep->arg[1], "delete") == 0) { + if (target == 0 || !target->IsCorpse()) { + c->Message(Chat::White, "Error: Target the corpse you wish to delete"); + } + else if (target->IsNPCCorpse()) { + + c->Message(Chat::White, "Depoping %s.", target->GetName()); + target->CastToCorpse()->Delete(); + } + else if (c->Admin() >= commandEditPlayerCorpses) { + c->Message(Chat::White, "Deleting %s.", target->GetName()); + target->CastToCorpse()->Delete(); + } + else { + c->Message(Chat::White, "Insufficient status to delete player corpse."); + } + } + else if (strcasecmp(sep->arg[1], "ListNPC") == 0) { + entity_list.ListNPCCorpses(c); + } + else if (strcasecmp(sep->arg[1], "ListPlayer") == 0) { + entity_list.ListPlayerCorpses(c); + } + else if (strcasecmp(sep->arg[1], "DeleteNPCCorpses") == 0) { + int32 tmp = entity_list.DeleteNPCCorpses(); + if (tmp >= 0) { + c->Message(Chat::White, "%d corpses deleted.", tmp); + } + else { + c->Message(Chat::White, "DeletePlayerCorpses Error #%d", tmp); + } + } + else if (strcasecmp(sep->arg[1], "charid") == 0 && c->Admin() >= commandEditPlayerCorpses) { + if (target == 0 || !target->IsPlayerCorpse()) { + c->Message(Chat::White, "Error: Target must be a player corpse."); + } + else if (!sep->IsNumber(2)) { + c->Message(Chat::White, "Error: charid must be a number."); + } + else { + c->Message( + Chat::White, + "Setting CharID=%u on PlayerCorpse '%s'", + target->CastToCorpse()->SetCharID(atoi(sep->arg[2])), + target->GetName()); + } + } + else if (strcasecmp(sep->arg[1], "ResetLooter") == 0) { + if (target == 0 || !target->IsCorpse()) { + c->Message(Chat::White, "Error: Target the corpse you wish to reset"); + } + else { + target->CastToCorpse()->ResetLooter(); + } + } + else if (strcasecmp(sep->arg[1], "RemoveCash") == 0) { + if (target == 0 || !target->IsCorpse()) { + c->Message(Chat::White, "Error: Target the corpse you wish to remove the cash from"); + } + else if (!target->IsPlayerCorpse() || c->Admin() >= commandEditPlayerCorpses) { + c->Message(Chat::White, "Removing Cash from %s.", target->GetName()); + target->CastToCorpse()->RemoveCash(); + } + else { + c->Message(Chat::White, "Insufficient status to modify player corpse."); + } + } + else if (strcasecmp(sep->arg[1], "InspectLoot") == 0) { + if (target == 0 || !target->IsCorpse()) { + c->Message(Chat::White, "Error: Target must be a corpse."); + } + else { + target->CastToCorpse()->QueryLoot(c); + } + } + else if (strcasecmp(sep->arg[1], "lock") == 0) { + if (target == 0 || !target->IsCorpse()) { + c->Message(Chat::White, "Error: Target must be a corpse."); + } + else { + target->CastToCorpse()->Lock(); + c->Message(Chat::White, "Locking %s...", target->GetName()); + } + } + else if (strcasecmp(sep->arg[1], "unlock") == 0) { + if (target == 0 || !target->IsCorpse()) { + c->Message(Chat::White, "Error: Target must be a corpse."); + } + else { + target->CastToCorpse()->UnLock(); + c->Message(Chat::White, "Unlocking %s...", target->GetName()); + } + } + else if (strcasecmp(sep->arg[1], "depop") == 0) { + if (target == 0 || !target->IsPlayerCorpse()) { + c->Message(Chat::White, "Error: Target must be a player corpse."); + } + else if (c->Admin() >= commandEditPlayerCorpses && target->IsPlayerCorpse()) { + c->Message(Chat::White, "Depoping %s.", target->GetName()); + target->CastToCorpse()->DepopPlayerCorpse(); + if (!sep->arg[2][0] || atoi(sep->arg[2]) != 0) { + target->CastToCorpse()->Bury(); + } + } + else { + c->Message(Chat::White, "Insufficient status to depop player corpse."); + } + } + else if (strcasecmp(sep->arg[1], "depopall") == 0) { + if (target == 0 || !target->IsClient()) { + c->Message(Chat::White, "Error: Target must be a player."); + } + else if (c->Admin() >= commandEditPlayerCorpses && target->IsClient()) { + c->Message(Chat::White, "Depoping %s\'s corpses.", target->GetName()); + target->CastToClient()->DepopAllCorpses(); + if (!sep->arg[2][0] || atoi(sep->arg[2]) != 0) { + target->CastToClient()->BuryPlayerCorpses(); + } + } + else { + c->Message(Chat::White, "Insufficient status to depop player corpse."); + } + + } + else if (strcasecmp(sep->arg[1], "moveallgraveyard") == 0) { + int count = entity_list.MovePlayerCorpsesToGraveyard(true); + c->Message(Chat::White, "Moved [%d] player corpse(s) to zone graveyard", count); + } + else if (sep->arg[1][0] == 0 || strcasecmp(sep->arg[1], "help") == 0) { + c->Message(Chat::White, "#Corpse Sub-Commands:"); + c->Message(Chat::White, " DeleteNPCCorpses"); + c->Message(Chat::White, " Delete - Delete targetted corpse"); + c->Message(Chat::White, " ListNPC"); + c->Message(Chat::White, " ListPlayer"); + c->Message(Chat::White, " Lock - GM locks the corpse - cannot be looted by non-GM"); + c->Message(Chat::White, " MoveAllGraveyard - move all player corpses to zone's graveyard or non-instance"); + c->Message(Chat::White, " UnLock"); + c->Message(Chat::White, " RemoveCash"); + c->Message(Chat::White, " InspectLoot"); + c->Message(Chat::White, " [to remove items from corpses, loot them]"); + c->Message(Chat::White, "Lead-GM status required to delete/modify player corpses"); + c->Message(Chat::White, " DeletePlayerCorpses"); + c->Message(Chat::White, " CharID [charid] - change player corpse's owner"); + c->Message(Chat::White, " Depop [bury] - Depops single target corpse."); + c->Message(Chat::White, " Depopall [bury] - Depops all target player's corpses."); + c->Message(Chat::White, "Set bury to 0 to skip burying the corpses."); + } + else { + c->Message(Chat::White, "Error, #corpse sub-command not found"); + } +} + diff --git a/zone/gm_commands/corpsefix.cpp b/zone/gm_commands/corpsefix.cpp new file mode 100755 index 000000000..0de35a8d3 --- /dev/null +++ b/zone/gm_commands/corpsefix.cpp @@ -0,0 +1,8 @@ +#include "../client.h" +#include "../corpse.h" + +void command_corpsefix(Client *c, const Seperator *sep) +{ + entity_list.CorpseFix(c); +} + diff --git a/zone/gm_commands/cvs.cpp b/zone/gm_commands/cvs.cpp new file mode 100755 index 000000000..1632c8663 --- /dev/null +++ b/zone/gm_commands/cvs.cpp @@ -0,0 +1,17 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_cvs(Client *c, const Seperator *sep) +{ + auto pack = new ServerPacket( + ServerOP_ClientVersionSummary, + sizeof(ServerRequestClientVersionSummary_Struct) + ); + auto srcvss = (ServerRequestClientVersionSummary_Struct *) pack->pBuffer; + strn0cpy(srcvss->Name, c->GetName(), sizeof(srcvss->Name)); + worldserver.SendPacket(pack); + safe_delete(pack); +} + diff --git a/zone/gm_commands/damage.cpp b/zone/gm_commands/damage.cpp new file mode 100755 index 000000000..80de8470e --- /dev/null +++ b/zone/gm_commands/damage.cpp @@ -0,0 +1,21 @@ +#include "../client.h" + +void command_damage(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0) { + c->Message(Chat::White, "Error: #Damage: No Target."); + } + else if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #damage x"); + } + else { + int32 nkdmg = atoi(sep->arg[1]); + if (nkdmg > 2100000000) { + c->Message(Chat::White, "Enter a value less then 2,100,000,000."); + } + else { + c->GetTarget()->Damage(c, nkdmg, SPELL_UNKNOWN, EQ::skills::SkillHandtoHand, false); + } + } +} + diff --git a/zone/gm_commands/databuckets.cpp b/zone/gm_commands/databuckets.cpp new file mode 100755 index 000000000..82df39342 --- /dev/null +++ b/zone/gm_commands/databuckets.cpp @@ -0,0 +1,92 @@ +#include "../client.h" +#include "../data_bucket.h" + +void command_databuckets(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message(Chat::Yellow, "Usage: #databuckets view (partial key)|(limit) OR #databuckets delete (key)"); + return; + } + if (strcasecmp(sep->arg[1], "view") == 0) { + + std::string key_filter; + uint8 limit = 50; + for (int i = 2; i < 4; i++) { + if (sep->arg[i][0] == '\0') { + break; + } + if (strcasecmp(sep->arg[i], "limit") == 0) { + limit = (uint8) atoi(sep->arg[i + 1]); + continue; + } + } + if (sep->arg[2]) { + key_filter = str_tolower(sep->arg[2]); + } + std::string query = "SELECT `id`, `key`, `value`, `expires` FROM data_buckets"; + if (!key_filter.empty()) { query += StringFormat(" WHERE `key` LIKE '%%%s%%'", key_filter.c_str()); } + query += StringFormat(" LIMIT %u", limit); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + return; + } + if (results.RowCount() == 0) { + c->Message(Chat::Yellow, "No data_buckets found"); + return; + } + int _ctr = 0; + // put in window for easier readability in case want command line for something else + std::string window_title = "Data Buckets"; + std::string window_text = + "" + "" + "" + "" + "" + "" + ""; + for (auto row = results.begin(); row != results.end(); ++row) { + auto id = static_cast(atoi(row[0])); + std::string key = row[1]; + std::string value = row[2]; + std::string expires = row[3]; + window_text.append( + StringFormat( + "" + "" + "" + "" + "" + "", + id, + expires.c_str(), + key.c_str(), + value.c_str() + )); + _ctr++; + std::string del_saylink = StringFormat("#databuckets delete %s", key.c_str()); + c->Message( + Chat::White, + "%s : %s", + EQ::SayLinkEngine::GenerateQuestSaylink(del_saylink, false, "Delete").c_str(), + key.c_str(), + " Value: ", + value.c_str()); + } + window_text.append("
IDExpiresKeyValue
%u%s%s%s
"); + c->SendPopupToClient(window_title.c_str(), window_text.c_str()); + std::string response = _ctr > 0 ? StringFormat("Found %i matching data buckets", _ctr).c_str() + : "No Databuckets found."; + c->Message(Chat::Yellow, response.c_str()); + } + else if (strcasecmp(sep->arg[1], "delete") == 0) { + if (DataBucket::DeleteData(sep->argplus[2])) { + c->Message(Chat::Yellow, "data bucket %s deleted.", sep->argplus[2]); + } + else { + c->Message(Chat::Red, "An error occurred deleting data bucket %s", sep->argplus[2]); + } + return; + } +} + diff --git a/zone/gm_commands/date.cpp b/zone/gm_commands/date.cpp new file mode 100755 index 000000000..3e8aa2455 --- /dev/null +++ b/zone/gm_commands/date.cpp @@ -0,0 +1,29 @@ +#include "../client.h" + +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); + } +} + diff --git a/zone/gm_commands/dbspawn2.cpp b/zone/gm_commands/dbspawn2.cpp new file mode 100755 index 000000000..2f31e2f19 --- /dev/null +++ b/zone/gm_commands/dbspawn2.cpp @@ -0,0 +1,31 @@ +#include "../client.h" + +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]"); + } +} + diff --git a/zone/gm_commands/delacct.cpp b/zone/gm_commands/delacct.cpp new file mode 100755 index 000000000..959fe7404 --- /dev/null +++ b/zone/gm_commands/delacct.cpp @@ -0,0 +1,21 @@ +#include "../client.h" + +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."); + } + } +} + diff --git a/zone/gm_commands/deletegraveyard.cpp b/zone/gm_commands/deletegraveyard.cpp new file mode 100755 index 000000000..43560eb6f --- /dev/null +++ b/zone/gm_commands/deletegraveyard.cpp @@ -0,0 +1,35 @@ +#include "../client.h" + +void command_deletegraveyard(Client *c, const Seperator *sep) +{ + uint32 zoneid = 0; + uint32 graveyard_id = 0; + + if (!sep->arg[1][0]) { + c->Message(Chat::White, "Usage: #deletegraveyard [zonename]"); + return; + } + + zoneid = ZoneID(sep->arg[1]); + graveyard_id = content_db.GetZoneGraveyardID(zoneid, 0); + + if (zoneid > 0 && graveyard_id > 0) { + if (content_db.DeleteGraveyard(zoneid, graveyard_id)) { + c->Message(Chat::White, "Successfuly deleted graveyard %u for zone %s.", graveyard_id, sep->arg[1]); + } + else { + c->Message(Chat::White, "Unable to delete graveyard %u for zone %s.", graveyard_id, sep->arg[1]); + } + } + else { + if (zoneid <= 0) { + c->Message(Chat::White, "Unable to retrieve a ZoneID for the zone: %s", sep->arg[1]); + } + else if (graveyard_id <= 0) { + c->Message(Chat::White, "Unable to retrieve a valid GraveyardID for the zone: %s", sep->arg[1]); + } + } + + return; +} + diff --git a/zone/gm_commands/delpetition.cpp b/zone/gm_commands/delpetition.cpp new file mode 100755 index 000000000..556e33866 --- /dev/null +++ b/zone/gm_commands/delpetition.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +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])); + +} + diff --git a/zone/gm_commands/depop.cpp b/zone/gm_commands/depop.cpp new file mode 100755 index 000000000..3a1693205 --- /dev/null +++ b/zone/gm_commands/depop.cpp @@ -0,0 +1,14 @@ +#include "../client.h" +#include "../corpse.h" + +void command_depop(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0 || !(c->GetTarget()->IsNPC() || c->GetTarget()->IsNPCCorpse())) { + c->Message(Chat::White, "You must have a NPC target for this command. (maybe you meant #depopzone?)"); + } + else { + c->Message(Chat::White, "Depoping '%s'.", c->GetTarget()->GetName()); + c->GetTarget()->Depop(); + } +} + diff --git a/zone/gm_commands/depopzone.cpp b/zone/gm_commands/depopzone.cpp new file mode 100755 index 000000000..518ef42db --- /dev/null +++ b/zone/gm_commands/depopzone.cpp @@ -0,0 +1,8 @@ +#include "../client.h" + +void command_depopzone(Client *c, const Seperator *sep) +{ + zone->Depop(); + c->Message(Chat::White, "Zone depoped."); +} + diff --git a/zone/gm_commands/details.cpp b/zone/gm_commands/details.cpp new file mode 100755 index 000000000..f8b5dee8f --- /dev/null +++ b/zone/gm_commands/details.cpp @@ -0,0 +1,37 @@ +#include "../client.h" + +void command_details(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #details [number of drakkin detail]"); + } + 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 = atoi(sep->arg[1]); + + target->SendIllusionPacket( + Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails + ); + + c->Message(Chat::White, "Details = %i", atoi(sep->arg[1])); + } +} + diff --git a/zone/gm_commands/devtools.cpp b/zone/gm_commands/devtools.cpp new file mode 100755 index 000000000..da46b4f77 --- /dev/null +++ b/zone/gm_commands/devtools.cpp @@ -0,0 +1,22 @@ +#include "../client.h" +#include "../data_bucket.h" + +void command_devtools(Client *c, const Seperator *sep) +{ + std::string dev_tools_key = StringFormat("%i-dev-tools-disabled", c->AccountID()); + + /** + * Handle window toggle + */ + if (strcasecmp(sep->arg[1], "disable") == 0) { + DataBucket::SetData(dev_tools_key, "true"); + c->SetDevToolsEnabled(false); + } + if (strcasecmp(sep->arg[1], "enable") == 0) { + DataBucket::DeleteData(dev_tools_key); + c->SetDevToolsEnabled(true); + } + + c->ShowDevToolsMenu(); +} + diff --git a/zone/gm_commands/disablerecipe.cpp b/zone/gm_commands/disablerecipe.cpp new file mode 100755 index 000000000..ca5c6e152 --- /dev/null +++ b/zone/gm_commands/disablerecipe.cpp @@ -0,0 +1,29 @@ +#include "../client.h" + +void command_disablerecipe(Client *c, const Seperator *sep) +{ + uint32 recipe_id = 0; + bool success = false; + if (c) { + if (sep->argnum == 1) { + recipe_id = atoi(sep->arg[1]); + } + else { + c->Message(Chat::White, "Invalid number of arguments.\nUsage: #disablerecipe recipe_id"); + return; + } + if (recipe_id > 0) { + success = content_db.DisableRecipe(recipe_id); + if (success) { + c->Message(Chat::White, "Recipe disabled."); + } + else { + c->Message(Chat::White, "Recipe not disabled."); + } + } + else { + c->Message(Chat::White, "Invalid recipe id.\nUsage: #disablerecipe recipe_id"); + } + } +} + diff --git a/zone/gm_commands/disarmtrap.cpp b/zone/gm_commands/disarmtrap.cpp new file mode 100755 index 000000000..84efe93cf --- /dev/null +++ b/zone/gm_commands/disarmtrap.cpp @@ -0,0 +1,25 @@ +#include "../client.h" + +void command_disarmtrap(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + + if (!target) { + c->Message(Chat::Red, "You must have a target."); + return; + } + + if (target->IsNPC()) { + if (c->HasSkill(EQ::skills::SkillDisarmTraps)) { + if (DistanceSquaredNoZ(c->GetPosition(), target->GetPosition()) > RuleI(Adventure, LDoNTrapDistanceUse)) { + c->Message(Chat::Red, "%s is too far away.", target->GetCleanName()); + return; + } + c->HandleLDoNDisarm(target->CastToNPC(), c->GetSkill(EQ::skills::SkillDisarmTraps), LDoNTypeMechanical); + } + else { + c->Message(Chat::Red, "You do not have the disarm trap skill."); + } + } +} + diff --git a/zone/gm_commands/distance.cpp b/zone/gm_commands/distance.cpp new file mode 100755 index 000000000..578f94bb5 --- /dev/null +++ b/zone/gm_commands/distance.cpp @@ -0,0 +1,23 @@ +#include "../client.h" + +void command_distance(Client *c, const Seperator *sep) +{ + if (c->GetTarget()) { + Mob *target = c->GetTarget(); + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) is {:.2f} units from you.", + target->GetCleanName(), + target->GetID(), + Distance( + c->GetPosition(), + target->GetPosition() + ) + ).c_str() + ); + } + } +} + diff --git a/zone/gm_commands/doanim.cpp b/zone/gm_commands/doanim.cpp new file mode 100755 index 000000000..d48725a97 --- /dev/null +++ b/zone/gm_commands/doanim.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +void command_doanim(Client *c, const Seperator *sep) +{ + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #DoAnim [number]"); + } + else if (c->Admin() >= commandDoAnimOthers) { + if (c->GetTarget() == 0) { + c->Message(Chat::White, "Error: You need a target."); + } + else { + c->GetTarget()->DoAnim(atoi(sep->arg[1]), atoi(sep->arg[2])); + } + } + else { + c->DoAnim(atoi(sep->arg[1]), atoi(sep->arg[2])); + } +} + diff --git a/zone/gm_commands/door.cpp b/zone/gm_commands/door.cpp new file mode 100755 index 000000000..5a92e5f62 --- /dev/null +++ b/zone/gm_commands/door.cpp @@ -0,0 +1,9 @@ +#include "../client.h" +#include "door_manipulation.h" +#include "../doors.h" + +void command_door(Client *c, const Seperator *sep) +{ + DoorManipulation::CommandHandler(c, sep); +} + diff --git a/zone/gm_commands/door_manipulation.cpp b/zone/gm_commands/door_manipulation.cpp index afb91b07a..a724fb61f 100644 --- a/zone/gm_commands/door_manipulation.cpp +++ b/zone/gm_commands/door_manipulation.cpp @@ -37,7 +37,10 @@ void DoorManipulation::CommandHandler(Client *c, const Seperator *sep) // option if (arg1.empty()) { DoorManipulation::CommandHeader(c); - c->Message(Chat::White, "#door create | Creates a door from a model. (Example IT78 creates a campfire)"); + c->Message( + Chat::White, + "#door create | Creates a door from a model. (Example IT78 creates a campfire)" + ); c->Message(Chat::White, "#door setinvertstate [0|1] | Sets selected door invert state"); c->Message(Chat::White, "#door setincline | Sets selected door incline"); c->Message(Chat::White, "#door opentype | Sets selected door opentype"); @@ -129,7 +132,7 @@ void DoorManipulation::CommandHandler(Client *c, const Seperator *sep) std::vector move_h_options_negative; std::vector set_size_options_positive; std::vector set_size_options_negative; - for (const auto &move_option : move_options) { + for (const auto &move_option : move_options) { if (move_option == move_x_action) { move_x_options_positive.emplace_back( EQ::SayLinkEngine::GenerateQuestSaylink( @@ -297,10 +300,10 @@ void DoorManipulation::CommandHandler(Client *c, const Seperator *sep) // we're passing a move action here if (!arg3.empty() && StringIsNumber(arg3)) { - float x_move = 0.0f; - float y_move = 0.0f; - float z_move = 0.0f; - float h_move = 0.0f; + float x_move = 0.0f; + float y_move = 0.0f; + float z_move = 0.0f; + float h_move = 0.0f; float set_size = 0.0f; if (arg2 == move_x_action) { diff --git a/zone/gm_commands/dye.cpp b/zone/gm_commands/dye.cpp new file mode 100755 index 000000000..ecc04f7ca --- /dev/null +++ b/zone/gm_commands/dye.cpp @@ -0,0 +1,87 @@ +#include "../client.h" + +void command_dye(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + + if (arguments == 0) { + c->Message(Chat::White, "Command Syntax: #dye help | #dye [slot] [red] [green] [blue] [use_tint]"); + return; + } + + uint8 slot = 0; + uint8 red = 255; + uint8 green = 255; + uint8 blue = 255; + uint8 use_tint = 255; + + std::vector dye_slots = { + "Helmet", + "Chest", + "Arms", + "Wrist", + "Hands", + "Legs", + "Feet" + }; + + if (arguments == 1 && !strcasecmp(sep->arg[1], "help")) { + int slot_id = 0; + std::vector slot_messages; + c->Message(Chat::White, "Command Syntax: #dye help | #dye [slot] [red] [green] [blue] [use_tint]"); + c->Message(Chat::White, "Red, Green, and Blue go from 0 to 255."); + + for (const auto &slot : dye_slots) { + slot_messages.push_back(fmt::format("({}) {}", slot_id, slot)); + slot_id++; + } + + c->Message( + Chat::White, + fmt::format( + "{} {}", + "Slots are as follows:", + implode(", ", slot_messages) + ).c_str() + ); + return; + } + + if (arguments >= 1 && sep->IsNumber(1)) { + slot = atoi(sep->arg[1]); + } + + if (arguments >= 2 && sep->IsNumber(2)) { + red = atoi(sep->arg[2]); + } + + if (arguments >= 3 && sep->IsNumber(3)) { + green = atoi(sep->arg[3]); + } + + if (arguments >= 4 && sep->IsNumber(4)) { + blue = atoi(sep->arg[4]); + } + + if (arguments >= 5 && sep->IsNumber(5)) { + use_tint = atoi(sep->arg[5]); + } + + if (RuleB(Command, DyeCommandRequiresDyes)) { + uint32 dye_item_id = 32557; + if (c->CountItem(dye_item_id) >= 1) { + c->RemoveItem(dye_item_id); + } + else { + EQ::SayLinkEngine linker; + linker.SetLinkType(EQ::saylink::SayLinkItemData); + const EQ::ItemData *dye_item = database.GetItem(dye_item_id); + linker.SetItemData(dye_item); + c->Message(Chat::White, fmt::format("This command requires a {} to use.", linker.GenerateLink()).c_str()); + return; + } + } + + c->DyeArmorBySlot(slot, red, green, blue, use_tint); +} + diff --git a/zone/gm_commands/dz.cpp b/zone/gm_commands/dz.cpp new file mode 100755 index 000000000..506a203fb --- /dev/null +++ b/zone/gm_commands/dz.cpp @@ -0,0 +1,244 @@ +#include "../client.h" +#include "../expedition.h" + +void command_dz(Client *c, const Seperator *sep) +{ + if (!c || !zone) { + return; + } + + if (strcasecmp(sep->arg[1], "cache") == 0) { + if (strcasecmp(sep->arg[2], "reload") == 0) { + DynamicZone::CacheAllFromDatabase(); + Expedition::CacheAllFromDatabase(); + c->Message( + Chat::White, fmt::format( + "Reloaded [{}] dynamic zone(s) and [{}] expedition(s) from database", + zone->dynamic_zone_cache.size(), zone->expedition_cache.size() + ).c_str()); + } + } + else if (strcasecmp(sep->arg[1], "expedition") == 0) { + if (strcasecmp(sep->arg[2], "list") == 0) { + std::vector expeditions; + for (const auto &expedition : zone->expedition_cache) { + expeditions.emplace_back(expedition.second.get()); + } + + std::sort( + expeditions.begin(), expeditions.end(), + [](const Expedition *lhs, const Expedition *rhs) { + return lhs->GetID() < rhs->GetID(); + } + ); + + c->Message(Chat::White, fmt::format("Total Active Expeditions: [{}]", expeditions.size()).c_str()); + for (const auto &expedition : expeditions) { + auto dz = expedition->GetDynamicZone(); + if (!dz) { + LogExpeditions("Expedition [{}] has an invalid dz [{}] in cache", + expedition->GetID(), + expedition->GetDynamicZoneID()); + continue; + } + + auto leader_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format( + "#goto {}", expedition->GetLeaderName()), false, expedition->GetLeaderName()); + auto zone_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format( + "#zoneinstance {}", dz->GetInstanceID()), false, "zone" + ); + + auto seconds = dz->GetSecondsRemaining(); + + c->Message( + Chat::White, fmt::format( + "expedition id: [{}] dz id: [{}] name: [{}] leader: [{}] {}: [{}]:[{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", + expedition->GetID(), + expedition->GetDynamicZoneID(), + expedition->GetName(), + leader_saylink, + zone_saylink, + ZoneName(dz->GetZoneID()), + dz->GetZoneID(), + dz->GetInstanceID(), + dz->GetZoneVersion(), + dz->GetMemberCount(), + seconds / 3600, // hours + (seconds / 60) % 60, // minutes + seconds % 60 // seconds + ).c_str()); + } + } + else if (strcasecmp(sep->arg[2], "reload") == 0) { + Expedition::CacheAllFromDatabase(); + c->Message( + Chat::White, fmt::format( + "Reloaded [{}] expeditions to cache from database.", zone->expedition_cache.size() + ).c_str()); + } + else if (strcasecmp(sep->arg[2], "destroy") == 0 && sep->IsNumber(3)) { + auto expedition_id = std::strtoul(sep->arg[3], nullptr, 10); + auto expedition = Expedition::FindCachedExpeditionByID(expedition_id); + if (expedition) { + c->Message( + Chat::White, fmt::format( + "Destroying expedition [{}] ({})", + expedition_id, expedition->GetName()).c_str()); + expedition->GetDynamicZone()->RemoveAllMembers(); + } + else { + c->Message(Chat::Red, fmt::format("Failed to destroy expedition [{}]", sep->arg[3]).c_str()); + } + } + else if (strcasecmp(sep->arg[2], "unlock") == 0 && sep->IsNumber(3)) { + auto expedition_id = std::strtoul(sep->arg[3], nullptr, 10); + auto expedition = Expedition::FindCachedExpeditionByID(expedition_id); + if (expedition) { + c->Message(Chat::White, fmt::format("Unlocking expedition [{}]", expedition_id).c_str()); + expedition->SetLocked(false, ExpeditionLockMessage::None, true); + } + else { + c->Message(Chat::Red, fmt::format("Failed to find expedition [{}]", sep->arg[3]).c_str()); + } + } + } + else if (strcasecmp(sep->arg[1], "list") == 0) { + c->Message( + Chat::White, + fmt::format("Total Dynamic Zones (cache): [{}]", zone->dynamic_zone_cache.size()).c_str()); + + std::vector dynamic_zones; + for (const auto &dz : zone->dynamic_zone_cache) { + dynamic_zones.emplace_back(dz.second.get()); + } + + std::sort( + dynamic_zones.begin(), dynamic_zones.end(), + [](const DynamicZone *lhs, const DynamicZone *rhs) { + return lhs->GetID() < rhs->GetID(); + } + ); + + for (const auto &dz : dynamic_zones) { + auto seconds = dz->GetSecondsRemaining(); + auto zone_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#zoneinstance {}", dz->GetInstanceID()), false, "zone" + ); + + std::string aligned_type = fmt::format( + "[{}]", + DynamicZone::GetDynamicZoneTypeName(static_cast(dz->GetType()))); + c->Message( + Chat::White, fmt::format( + "id: [{}] type: {:>10} {}: [{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", + dz->GetID(), + aligned_type, + zone_saylink, + dz->GetZoneID(), + dz->GetInstanceID(), + dz->GetZoneVersion(), + dz->GetMemberCount(), + seconds / 3600, // hours + (seconds / 60) % 60, // minutes + seconds % 60 // seconds + ).c_str()); + } + } + else if (strcasecmp(sep->arg[1], "listdb") == 0) { + auto dz_list = DynamicZonesRepository::AllDzInstancePlayerCounts(database); + c->Message(Chat::White, fmt::format("Total Dynamic Zones (database): [{}]", dz_list.size()).c_str()); + + auto now = std::chrono::system_clock::now(); + + for (const auto &dz : dz_list) { + auto expire_time = std::chrono::system_clock::from_time_t(dz.start_time + dz.duration); + auto remaining = std::chrono::duration_cast(expire_time - now); + auto seconds = std::max(0, static_cast(remaining.count())); + bool is_expired = now > expire_time; + + if (!is_expired || strcasecmp(sep->arg[2], "all") == 0) { + auto zone_saylink = is_expired ? "zone" : EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#zoneinstance {}", dz.instance), false, "zone" + ); + + c->Message( + Chat::White, fmt::format( + "id: [{}] type: [{}] {}: [{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", + dz.id, + DynamicZone::GetDynamicZoneTypeName(static_cast(dz.type)), + zone_saylink, + dz.zone, + dz.instance, + dz.version, + dz.member_count, + seconds / 3600, // hours + (seconds / 60) % 60, // minutes + seconds % 60 // seconds + ).c_str()); + } + } + } + else if (strcasecmp(sep->arg[1], "lockouts") == 0) { + if (strcasecmp(sep->arg[2], "remove") == 0 && sep->arg[3][0] != '\0') { + if (sep->arg[5][0] == '\0') { + c->Message( + Chat::White, fmt::format( + "Removing [{}] lockouts on [{}].", sep->arg[4][0] ? sep->arg[4] : "all", sep->arg[3] + ).c_str()); + } + else { + c->Message( + Chat::White, fmt::format( + "Removing [{}]:[{}] lockout on [{}].", sep->arg[4], sep->arg[5], sep->arg[3] + ).c_str()); + } + Expedition::RemoveLockoutsByCharacterName(sep->arg[3], sep->arg[4], sep->arg[5]); + } + } + else if (strcasecmp(sep->arg[1], "makeleader") == 0 && sep->IsNumber(2) && sep->arg[3][0] != '\0') { + auto expedition_id = std::strtoul(sep->arg[2], nullptr, 10); + auto expedition = Expedition::FindCachedExpeditionByID(expedition_id); + if (expedition) { + auto char_name = FormatName(sep->arg[3]); + c->Message( + Chat::White, + fmt::format("Setting expedition [{}] leader to [{}]", expedition_id, char_name).c_str()); + expedition->SendWorldMakeLeaderRequest(c->CharacterID(), char_name); + } + else { + c->Message(Chat::Red, fmt::format("Failed to find expedition [{}]", expedition_id).c_str()); + } + } + else { + c->Message(Chat::White, "#dz usage:"); + c->Message( + Chat::White, + "#dz cache reload - reload the current zone cache from db (also reloads expedition cache dependency)" + ); + c->Message(Chat::White, "#dz expedition list - list expeditions in current zone cache"); + c->Message(Chat::White, "#dz expedition reload - reload expedition zone cache from database"); + c->Message( + Chat::White, + "#dz expedition destroy - destroy expedition globally (must be in cache)" + ); + c->Message(Chat::White, "#dz expedition unlock - unlock expedition"); + c->Message(Chat::White, "#dz list - list all dynamic zone instances from current zone cache"); + c->Message( + Chat::White, + "#dz listdb [all] - list dynamic zone instances from database -- 'all' includes expired" + ); + c->Message(Chat::White, "#dz lockouts remove - delete all of character's expedition lockouts"); + c->Message( + Chat::White, + "#dz lockouts remove \"\" - delete lockouts by expedition" + ); + c->Message( + Chat::White, + "#dz lockouts remove \"\" \"\" - delete lockout by expedition event" + ); + c->Message(Chat::White, "#dz makeleader - set new expedition leader"); + } +} + diff --git a/zone/gm_commands/dzkickplayers.cpp b/zone/gm_commands/dzkickplayers.cpp new file mode 100755 index 000000000..149ccc0af --- /dev/null +++ b/zone/gm_commands/dzkickplayers.cpp @@ -0,0 +1,13 @@ +#include "../client.h" +#include "../expedition.h" + +void command_dzkickplayers(Client *c, const Seperator *sep) +{ + if (c) { + auto expedition = c->GetExpedition(); + if (expedition) { + expedition->DzKickPlayers(c); + } + } +} + diff --git a/zone/gm_commands/editmassrespawn.cpp b/zone/gm_commands/editmassrespawn.cpp new file mode 100755 index 000000000..41fcc6613 --- /dev/null +++ b/zone/gm_commands/editmassrespawn.cpp @@ -0,0 +1,140 @@ +#include "../client.h" + +void command_editmassrespawn(Client *c, const Seperator *sep) +{ + if (strcasecmp(sep->arg[1], "usage") == 0) { + c->Message(Chat::White, "#editmassrespawn [exact_match: =]npc_type_name new_respawn_seconds (apply)"); + return; + } + + std::string search_npc_type; + if (sep->arg[1]) { + search_npc_type = sep->arg[1]; + } + + int change_respawn_seconds = 0; + if (sep->arg[2] && sep->IsNumber(2)) { + change_respawn_seconds = atoi(sep->arg[2]); + } + + bool change_apply = false; + if (sep->arg[3] && strcasecmp(sep->arg[3], "apply") == 0) { + change_apply = true; + } + + std::string search_encapsulator = "%"; + if (search_npc_type[0] == '=') { + + search_npc_type = search_npc_type.substr(1); + search_encapsulator = ""; + } + + std::string query = fmt::format( + SQL( + SELECT npc_types.id, spawn2.spawngroupID, spawn2.id, npc_types.name, spawn2.respawntime + FROM spawn2 + INNER JOIN spawnentry ON spawn2.spawngroupID = spawnentry.spawngroupID + INNER JOIN npc_types ON spawnentry.npcID = npc_types.id + WHERE spawn2.zone LIKE '{}' + AND spawn2.version = '{}' + AND npc_types.name LIKE '{}{}{}' + ORDER BY npc_types.id, spawn2.spawngroupID, spawn2.id + ), + zone->GetShortName(), + zone->GetInstanceVersion(), + search_encapsulator, + search_npc_type, + search_encapsulator + ); + + std::string status = "(Searching)"; + if (change_apply) { + status = "(Applying)"; + } + + int results_count = 0; + + auto results = content_db.QueryDatabase(query); + if (results.Success() && results.RowCount()) { + + results_count = results.RowCount(); + + for (auto row : results) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC (npcid:{}) (sgid:{}) (s2id:{}) [{}] Respawn: Current [{}] New [{}] {}", + row[0], + row[1], + row[2], + row[3], + row[4], + change_respawn_seconds, + status + ).c_str() + ); + } + + c->Message(Chat::Yellow, "Found (%i) NPC's that match this search...", results_count); + + if (change_respawn_seconds > 0) { + + if (change_apply) { + + results = content_db.QueryDatabase( + fmt::format( + SQL( + UPDATE spawn2 + SET respawntime = '{}' + WHERE id IN( + SELECT spawn2.id + FROM spawn2 + INNER JOIN spawnentry ON spawn2.spawngroupID = spawnentry.spawngroupID + INNER JOIN npc_types ON spawnentry.npcID = npc_types.id + WHERE spawn2.zone LIKE '{}' + AND spawn2.version = '{}' + AND npc_types.name LIKE '{}{}{}' + ) + ), + change_respawn_seconds, + zone->GetShortName(), + zone->GetInstanceVersion(), + search_encapsulator, + search_npc_type, + search_encapsulator + ) + ); + + if (results.Success()) { + + c->Message(Chat::Yellow, "Changes applied to (%i) NPC 'Spawn2' entries", results_count); + zone->Repop(); + } + else { + + c->Message(Chat::Yellow, "Found (0) NPC's that match this search..."); + } + } + else { + + std::string saylink = fmt::format( + "#editmassrespawn {}{} {} apply", + (search_encapsulator.empty() ? "=" : ""), + search_npc_type, + change_respawn_seconds + ); + + c->Message( + Chat::Yellow, "To apply these changes, click <%s> or type [%s]", + EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Apply").c_str(), + saylink.c_str() + ); + } + } + } + else { + + c->Message(Chat::Yellow, "Found (0) NPC's that match this search..."); + } +} + diff --git a/zone/gm_commands/emote.cpp b/zone/gm_commands/emote.cpp new file mode 100755 index 000000000..6deda54b5 --- /dev/null +++ b/zone/gm_commands/emote.cpp @@ -0,0 +1,45 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +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")) { + 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] + ); + } + } +} + diff --git a/zone/gm_commands/emotesearch.cpp b/zone/gm_commands/emotesearch.cpp new file mode 100755 index 000000000..40d5bfac6 --- /dev/null +++ b/zone/gm_commands/emotesearch.cpp @@ -0,0 +1,78 @@ +#include "../client.h" + +void command_emotesearch(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #emotesearch [search string or emoteid]"); + } + else { + const char *search_criteria = sep->argplus[1]; + int count = 0; + + if (Seperator::IsNumber(search_criteria)) { + uint16 emoteid = atoi(search_criteria); + LinkedListIterator iterator(zone->NPCEmoteList); + iterator.Reset(); + while (iterator.MoreElements()) { + NPC_Emote_Struct *nes = iterator.GetData(); + if (emoteid == nes->emoteid) { + c->Message( + Chat::White, + "EmoteID: %i Event: %i Type: %i Text: %s", + nes->emoteid, + nes->event_, + nes->type, + nes->text + ); + count++; + } + iterator.Advance(); + } + if (count == 0) { + c->Message(Chat::White, "No emotes found."); + } + else { + c->Message(Chat::White, "%i emote(s) found", count); + } + } + else { + char sText[64]; + char sCriteria[515]; + strn0cpy(sCriteria, search_criteria, sizeof(sCriteria)); + strupr(sCriteria); + char *pdest; + + LinkedListIterator iterator(zone->NPCEmoteList); + iterator.Reset(); + while (iterator.MoreElements()) { + NPC_Emote_Struct *nes = iterator.GetData(); + strn0cpy(sText, nes->text, sizeof(sText)); + strupr(sText); + pdest = strstr(sText, sCriteria); + if (pdest != nullptr) { + c->Message( + Chat::White, + "EmoteID: %i Event: %i Type: %i Text: %s", + nes->emoteid, + nes->event_, + nes->type, + nes->text + ); + count++; + } + if (count == 50) { + break; + } + + iterator.Advance(); + } + if (count == 50) { + c->Message(Chat::White, "50 emotes shown...too many results."); + } + else { + c->Message(Chat::White, "%i emote(s) found", count); + } + } + } +} + diff --git a/zone/gm_commands/emoteview.cpp b/zone/gm_commands/emoteview.cpp new file mode 100755 index 000000000..afbec21aa --- /dev/null +++ b/zone/gm_commands/emoteview.cpp @@ -0,0 +1,39 @@ +#include "../client.h" + +void command_emoteview(Client *c, const Seperator *sep) +{ + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target a NPC to view their emotes."); + return; + } + + if (c->GetTarget() && c->GetTarget()->IsNPC()) { + int count = 0; + int emoteid = c->GetTarget()->CastToNPC()->GetEmoteID(); + + LinkedListIterator iterator(zone->NPCEmoteList); + iterator.Reset(); + while (iterator.MoreElements()) { + NPC_Emote_Struct *nes = iterator.GetData(); + if (emoteid == nes->emoteid) { + c->Message( + Chat::White, + "EmoteID: %i Event: %i Type: %i Text: %s", + nes->emoteid, + nes->event_, + nes->type, + nes->text + ); + count++; + } + iterator.Advance(); + } + if (count == 0) { + c->Message(Chat::White, "No emotes found."); + } + else { + c->Message(Chat::White, "%i emote(s) found", count); + } + } +} + diff --git a/zone/gm_commands/enablerecipe.cpp b/zone/gm_commands/enablerecipe.cpp new file mode 100755 index 000000000..acfa1ff93 --- /dev/null +++ b/zone/gm_commands/enablerecipe.cpp @@ -0,0 +1,29 @@ +#include "../client.h" + +void command_enablerecipe(Client *c, const Seperator *sep) +{ + uint32 recipe_id = 0; + bool success = false; + if (c) { + if (sep->argnum == 1) { + recipe_id = atoi(sep->arg[1]); + } + else { + c->Message(Chat::White, "Invalid number of arguments.\nUsage: #enablerecipe recipe_id"); + return; + } + if (recipe_id > 0) { + success = content_db.EnableRecipe(recipe_id); + if (success) { + c->Message(Chat::White, "Recipe enabled."); + } + else { + c->Message(Chat::White, "Recipe not enabled."); + } + } + else { + c->Message(Chat::White, "Invalid recipe id.\nUsage: #enablerecipe recipe_id"); + } + } +} + diff --git a/zone/gm_commands/endurance.cpp b/zone/gm_commands/endurance.cpp new file mode 100755 index 000000000..26132c913 --- /dev/null +++ b/zone/gm_commands/endurance.cpp @@ -0,0 +1,27 @@ +#include "../client.h" + +void command_endurance(Client *c, const Seperator *sep) +{ + auto target = c->GetTarget() ? c->GetTarget() : c; + if (target->IsClient()) { + target->CastToClient()->SetEndurance(target->CastToClient()->GetMaxEndurance()); + } + else { + target->SetEndurance(target->GetMaxEndurance()); + } + + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "Set {} ({}) to full Endurance.", + target->GetCleanName(), + target->GetID() + ).c_str() + ); + } + else { + c->Message(Chat::White, "Restored your Endurance to full."); + } +} + diff --git a/zone/gm_commands/equipitem.cpp b/zone/gm_commands/equipitem.cpp new file mode 100755 index 000000000..7ffe8ad84 --- /dev/null +++ b/zone/gm_commands/equipitem.cpp @@ -0,0 +1,82 @@ +#include "../client.h" + +void command_equipitem(Client *c, const Seperator *sep) +{ + uint32 slot_id = atoi(sep->arg[1]); + if (sep->IsNumber(1) && (slot_id >= EQ::invslot::EQUIPMENT_BEGIN && slot_id <= EQ::invslot::EQUIPMENT_END)) { + const EQ::ItemInstance *from_inst = c->GetInv().GetItem(EQ::invslot::slotCursor); + const EQ::ItemInstance *to_inst = c->GetInv().GetItem(slot_id); // added (desync issue when forcing stack to stack) + bool partialmove = false; + int16 movecount; + + if (from_inst && from_inst->IsClassCommon()) { + auto outapp = new EQApplicationPacket(OP_MoveItem, sizeof(MoveItem_Struct)); + MoveItem_Struct *mi = (MoveItem_Struct *) outapp->pBuffer; + mi->from_slot = EQ::invslot::slotCursor; + mi->to_slot = slot_id; + // mi->number_in_stack = from_inst->GetCharges(); // replaced with con check for stacking + + // crude stackable check to only 'move' the difference count on client instead of entire stack when applicable + if (to_inst && to_inst->IsStackable() && + (to_inst->GetItem()->ID == from_inst->GetItem()->ID) && + (to_inst->GetCharges() < to_inst->GetItem()->StackSize) && + (from_inst->GetCharges() > to_inst->GetItem()->StackSize - to_inst->GetCharges())) { + movecount = to_inst->GetItem()->StackSize - to_inst->GetCharges(); + mi->number_in_stack = (uint32) movecount; + partialmove = true; + } + else { + mi->number_in_stack = from_inst->GetCharges(); + } + + // Save move changes + // Added conditional check to packet send..would have sent change even on a swap failure..whoops! + + if (partialmove) { // remove this con check if someone can figure out removing charges from cursor stack issue below + // mi->number_in_stack is always from_inst->GetCharges() when partialmove is false + c->Message(Chat::Red, "Error: Partial stack added to existing stack exceeds allowable stacksize"); + safe_delete(outapp); + return; + } + else if (c->SwapItem(mi)) { + c->FastQueuePacket(&outapp); + + // if the below code is still needed..just send an an item trade packet to each slot..it should overwrite the client instance + + // below code has proper logic, but client does not like to have cursor charges changed + // (we could delete the cursor item and resend, but issues would arise if there are queued items) + //if (partialmove) { + // EQApplicationPacket* outapp2 = new EQApplicationPacket(OP_DeleteItem, sizeof(DeleteItem_Struct)); + // DeleteItem_Struct* di = (DeleteItem_Struct*)outapp2->pBuffer; + // di->from_slot = SLOT_CURSOR; + // di->to_slot = 0xFFFFFFFF; + // di->number_in_stack = 0xFFFFFFFF; + + // c->Message(Chat::White, "Deleting %i charges from stack", movecount); // debug line..delete + + // for (int16 deletecount=0; deletecount < movecount; deletecount++) + // have to use 'movecount' because mi->number_in_stack is 'ENCODED' at this point (i.e., 99 charges returns 22...) + // c->QueuePacket(outapp2); + + // safe_delete(outapp2); + //} + } + else { + c->Message(Chat::Red, "Error: Unable to equip current item"); + } + safe_delete(outapp); + + // also send out a wear change packet? + } + else if (from_inst == nullptr) { + c->Message(Chat::Red, "Error: There is no item on your cursor"); + } + else { + c->Message(Chat::Red, "Error: Item on your cursor cannot be equipped"); + } + } + else { + c->Message(Chat::White, "Usage: #equipitem slotid[0-21] - equips the item on your cursor to the position"); + } +} + diff --git a/zone/gm_commands/face.cpp b/zone/gm_commands/face.cpp new file mode 100755 index 000000000..274d5a300 --- /dev/null +++ b/zone/gm_commands/face.cpp @@ -0,0 +1,37 @@ +#include "../client.h" + +void command_face(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #face [number of face]"); + } + 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 = atoi(sep->arg[1]); + 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 + ); + + c->Message(Chat::White, "Face = %i", atoi(sep->arg[1])); + } +} + diff --git a/zone/gm_commands/faction.cpp b/zone/gm_commands/faction.cpp new file mode 100755 index 000000000..3a704c81a --- /dev/null +++ b/zone/gm_commands/faction.cpp @@ -0,0 +1,174 @@ +#include "../client.h" + +void command_faction(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments) { + c->Message( + Chat::White, + "Usage: #faction review [Search Criteria | All] - Review Targeted Player's Faction Hits" + ); + c->Message( + Chat::White, + "Usage: #faction reset [Faction ID] - Reset Targeted Player's Faction to Base Faction Value" + ); + c->Message(Chat::White, "Usage: #faction view - Displays Target NPC's Primary Faction"); + return; + } + + std::string faction_filter; + if (sep->arg[2]) { + faction_filter = str_tolower(sep->arg[2]); + } + + if (!strcasecmp(sep->arg[1], "review")) { + if (!(c->GetTarget() && c->GetTarget()->IsClient())) { + c->Message(Chat::Red, "Player Target Required for faction review"); + return; + } + + Client *target = c->GetTarget()->CastToClient(); + uint32 character_id = target->CharacterID(); + std::string query; + if (!strcasecmp(faction_filter.c_str(), "all")) { + query = fmt::format( + "SELECT id, `name`, current_value FROM faction_list INNER JOIN faction_values ON faction_list.id = faction_values.faction_id WHERE char_id = {}", + character_id + ); + } + else { + query = fmt::format( + "SELECT id, `name`, current_value FROM faction_list INNER JOIN faction_values ON faction_list.id = faction_values.faction_id WHERE `name` like '%{}%' and char_id = {}", + faction_filter.c_str(), + character_id + ); + } + + auto results = content_db.QueryDatabase(query); + if (!results.Success() || !results.RowCount()) { + c->Message(Chat::Yellow, "No faction hits found. All are at base level."); + return; + } + + uint32 found_count = 0; + for (auto row : results) { + uint32 faction_number = (found_count + 1); + auto faction_id = std::stoul(row[0]); + std::string faction_name = row[1]; + std::string faction_value = row[2]; + std::string reset_link = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#faction reset {}", faction_id), + false, + "Reset" + ); + + c->Message( + Chat::White, + fmt::format( + "Faction {} | Name: {} ({}) Value: {} [{}]", + faction_number, + faction_name, + faction_id, + faction_value, + reset_link + ).c_str() + ); + found_count++; + } + + auto faction_message = ( + found_count > 0 ? + ( + found_count == 1 ? + "A Faction was" : + fmt::format("{} Factions were", found_count) + ) : + "No Factions were" + ); + c->Message( + Chat::White, + fmt::format( + "{} found.", + faction_message + ).c_str() + ); + } + else if (!strcasecmp(sep->arg[1], "reset")) { + if (strlen(faction_filter.c_str()) > 0) { + if (c->GetTarget() && c->GetTarget()->IsClient()) { + Client *target = c->GetTarget()->CastToClient(); + if ( + ( + !c->GetFeigned() && + c->GetAggroCount() == 0 + ) || + ( + !target->GetFeigned() && + target->GetAggroCount() == 0 + ) + ) { + uint32 character_id = target->CharacterID(); + uint32 faction_id = std::stoul(faction_filter.c_str()); + if (target->ReloadCharacterFaction(target, faction_id, character_id)) { + c->Message( + Chat::White, + fmt::format( + "Faction Reset | {} ({}) was reset for {}.", + content_db.GetFactionName(faction_id), + faction_id, + target->GetCleanName() + ).c_str() + ); + } + else { + c->Message( + Chat::White, + fmt::format( + "Faction Reset Failed | {} ({}) was unable to be reset for {}.", + content_db.GetFactionName(faction_id), + faction_id, + target->GetCleanName() + ).c_str() + ); + } + } + else { + c->Message( + Chat::White, + "You cannot reset factions while you or your target is in combat or feigned." + ); + return; + } + } + else { + c->Message(Chat::White, "You must target a PC for this command."); + return; + } + } + else { + c->Message( + Chat::White, + "Usage: #faction reset [Faction ID] - Reset Targeted Player's Faction to Base Faction Value" + ); + } + } + else if (!strcasecmp(sep->arg[1], "view")) { + if (c->GetTarget() && c->GetTarget()->IsNPC()) { + Mob *target = c->GetTarget(); + uint32 npc_id = target->GetNPCTypeID(); + uint32 npc_faction_id = target->CastToNPC()->GetPrimaryFaction(); + std::string npc_name = target->GetCleanName(); + c->Message( + Chat::White, + fmt::format( + "{} ({}) has a Primary Faction of {} ({}).", + npc_name, + npc_id, + content_db.GetFactionName(npc_faction_id), + npc_faction_id + ).c_str() + ); + } + } +} + diff --git a/zone/gm_commands/findclass.cpp b/zone/gm_commands/findclass.cpp new file mode 100755 index 000000000..a220f5ded --- /dev/null +++ b/zone/gm_commands/findclass.cpp @@ -0,0 +1,84 @@ +#include "../client.h" + +void command_findclass(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + + if (arguments == 0) { + c->Message(Chat::White, "Command Syntax: #findclass [search criteria]"); + return; + } + + if (sep->IsNumber(1)) { + int class_id = std::stoi(sep->arg[1]); + if (class_id >= WARRIOR && class_id <= MERCERNARY_MASTER) { + std::string class_name = GetClassIDName(class_id); + c->Message( + Chat::White, + fmt::format( + "Class {}: {}", + class_id, + class_name + ).c_str() + ); + } + else { + c->Message( + Chat::White, + fmt::format( + "Class ID {} was not found.", + class_id + ).c_str() + ); + } + } + else { + std::string search_criteria = str_tolower(sep->argplus[1]); + int found_count = 0; + for (int class_id = WARRIOR; class_id <= MERCERNARY_MASTER; class_id++) { + std::string class_name = GetClassIDName(class_id); + std::string class_name_lower = str_tolower(class_name); + if (search_criteria.length() > 0 && class_name_lower.find(search_criteria) == std::string::npos) { + continue; + } + + c->Message( + Chat::White, + fmt::format( + "Class {}: {}", + class_id, + class_name + ).c_str() + ); + found_count++; + + if (found_count == 20) { + break; + } + } + + if (found_count == 20) { + c->Message(Chat::White, "20 Classes found... max reached."); + } + else { + auto class_message = ( + found_count > 0 ? + ( + found_count == 1 ? + "A Class was" : + fmt::format("{} Classes were", found_count) + ) : + "No Classes were" + ); + + c->Message( + Chat::White, + fmt::format( + "{} found.", + class_message + ).c_str() + ); + } + } +} + diff --git a/zone/gm_commands/findfaction.cpp b/zone/gm_commands/findfaction.cpp new file mode 100755 index 000000000..29b72f441 --- /dev/null +++ b/zone/gm_commands/findfaction.cpp @@ -0,0 +1,89 @@ +#include "../client.h" + +void command_findfaction(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + + if (arguments == 0) { + c->Message(Chat::White, "Command Syntax: #findfaction [search criteria]"); + return; + } + + if (sep->IsNumber(1)) { + int faction_id = std::stoi(sep->arg[1]); + auto faction_name = content_db.GetFactionName(faction_id); + if (!faction_name.empty()) { + c->Message( + Chat::White, + fmt::format( + "Faction {}: {}", + faction_id, + faction_name + ).c_str() + ); + } + else { + c->Message( + Chat::White, + fmt::format( + "Faction ID {} was not found.", + faction_id + ).c_str() + ); + } + } + else { + std::string search_criteria = str_tolower(sep->argplus[1]); + int found_count = 0; + int max_faction_id = content_db.GetMaxFaction(); + for (int faction_id = 0; faction_id < max_faction_id; faction_id++) { + std::string faction_name = content_db.GetFactionName(faction_id); + std::string faction_name_lower = str_tolower(faction_name); + if (faction_name.empty()) { + continue; + } + + if (faction_name.find(search_criteria) == std::string::npos) { + continue; + } + + c->Message( + Chat::White, + fmt::format( + "Faction {}: {}", + faction_id, + faction_name + ).c_str() + ); + found_count++; + + if (found_count == 20) { + break; + } + } + + if (found_count == 20) { + c->Message(Chat::White, "20 Factions found... max reached."); + } + else { + auto faction_message = ( + found_count > 0 ? + ( + found_count == 1 ? + "A Faction was" : + fmt::format("{} Factions were", found_count) + ) : + "No Factions were" + ); + + c->Message( + Chat::White, + fmt::format( + "{} found.", + faction_message + ).c_str() + ); + } + } +} + diff --git a/zone/gm_commands/findnpctype.cpp b/zone/gm_commands/findnpctype.cpp new file mode 100755 index 000000000..991bd8f18 --- /dev/null +++ b/zone/gm_commands/findnpctype.cpp @@ -0,0 +1,77 @@ +#include "../client.h" + +void command_findnpctype(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #findnpctype [Search Criteria]"); + return; + } + + std::string query; + std::string search_criteria = sep->arg[1]; + if (sep->IsNumber(1)) { + query = fmt::format( + "SELECT id, name FROM npc_types WHERE id = {}", + search_criteria + ); + } + else { + query = fmt::format( + "SELECT id, name FROM npc_types WHERE name LIKE '%%{}%%'", + search_criteria + ); + } + + auto results = content_db.QueryDatabase(query); + if (!results.Success() || !results.RowCount()) { + c->Message( + Chat::White, + fmt::format( + "No matches found for '{}'.", + search_criteria + ).c_str() + ); + return; + } + + int found_count = 0; + + for (auto row : results) { + int found_number = (found_count + 1); + if (found_count == 20) { + break; + } + + c->Message( + Chat::White, + fmt::format( + "NPC {} | {} ({})", + found_number, + row[1], + row[0] + ).c_str() + ); + found_count++; + } + + if (found_count == 20) { + c->Message(Chat::White, "20 NPCs were found, max reached."); + } + else { + auto npc_message = ( + found_count == 1 ? + "An NPC was" : + fmt::format("{} NPCs were", found_count) + ); + + c->Message( + Chat::White, + fmt::format( + "{} found.", + npc_message + ).c_str() + ); + } +} + diff --git a/zone/gm_commands/findrace.cpp b/zone/gm_commands/findrace.cpp new file mode 100755 index 000000000..413687160 --- /dev/null +++ b/zone/gm_commands/findrace.cpp @@ -0,0 +1,84 @@ +#include "../client.h" + +void command_findrace(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + + if (arguments == 0) { + c->Message(Chat::White, "Command Syntax: #findrace [search criteria]"); + return; + } + + if (sep->IsNumber(1)) { + int race_id = std::stoi(sep->arg[1]); + std::string race_name = GetRaceIDName(race_id); + if (race_id >= RACE_HUMAN_1 && race_id <= RACE_PEGASUS_732) { + c->Message( + Chat::White, + fmt::format( + "Race {}: {}", + race_id, + race_name + ).c_str() + ); + } + else { + c->Message( + Chat::White, + fmt::format( + "Race ID {} was not found.", + race_id + ).c_str() + ); + } + } + else { + std::string search_criteria = str_tolower(sep->argplus[1]); + int found_count = 0; + for (int race_id = RACE_HUMAN_1; race_id <= RACE_PEGASUS_732; race_id++) { + std::string race_name = GetRaceIDName(race_id); + std::string race_name_lower = str_tolower(race_name); + if (search_criteria.length() > 0 && race_name_lower.find(search_criteria) == std::string::npos) { + continue; + } + + c->Message( + Chat::White, + fmt::format( + "Race {}: {}", + race_id, + race_name + ).c_str() + ); + found_count++; + + if (found_count == 20) { + break; + } + } + + if (found_count == 20) { + c->Message(Chat::White, "20 Races found... max reached."); + } + else { + auto race_message = ( + found_count > 0 ? + ( + found_count == 1 ? + "A Race was" : + fmt::format("{} Races were", found_count) + ) : + "No Races were" + ); + + c->Message( + Chat::White, + fmt::format( + "{} found.", + race_message + ).c_str() + ); + } + } +} + diff --git a/zone/gm_commands/findskill.cpp b/zone/gm_commands/findskill.cpp new file mode 100755 index 000000000..b26ad3ab1 --- /dev/null +++ b/zone/gm_commands/findskill.cpp @@ -0,0 +1,90 @@ +#include "../client.h" + +void command_findskill(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + + if (arguments == 0) { + c->Message(Chat::White, "Command Syntax: #findskill [search criteria]"); + return; + } + + std::map skills = EQ::skills::GetSkillTypeMap(); + if (sep->IsNumber(1)) { + int skill_id = std::stoi(sep->arg[1]); + if (skill_id >= EQ::skills::Skill1HBlunt && skill_id < EQ::skills::SkillCount) { + for (auto skill : skills) { + if (skill_id == skill.first) { + c->Message( + Chat::White, + fmt::format( + "Skill {}: {}", + skill.first, + skill.second + ).c_str() + ); + break; + } + } + } + else { + c->Message( + Chat::White, + fmt::format( + "Skill ID {} was not found.", + skill_id + ).c_str() + ); + } + } + else { + std::string search_criteria = str_tolower(sep->argplus[1]); + if (!search_criteria.empty()) { + int found_count = 0; + for (auto skill : skills) { + std::string skill_name_lower = str_tolower(skill.second); + if (skill_name_lower.find(search_criteria) == std::string::npos) { + continue; + } + + c->Message( + Chat::White, + fmt::format( + "Skill {}: {}", + skill.first, + skill.second + ).c_str() + ); + found_count++; + + if (found_count == 20) { + break; + } + } + + if (found_count == 20) { + c->Message(Chat::White, "20 Skills were found, max reached."); + } + else { + auto skill_message = ( + found_count > 0 ? + ( + found_count == 1 ? + "A Skill was" : + fmt::format("{} Skills were", found_count) + ) : + "No Skills were" + ); + + c->Message( + Chat::White, + fmt::format( + "{} found.", + skill_message + ).c_str() + ); + } + } + } +} + diff --git a/zone/gm_commands/findspell.cpp b/zone/gm_commands/findspell.cpp new file mode 100755 index 000000000..b17febac4 --- /dev/null +++ b/zone/gm_commands/findspell.cpp @@ -0,0 +1,129 @@ +#include "../client.h" + +void command_findspell(Client *c, const Seperator *sep) +{ + if (SPDAT_RECORDS <= 0) { + c->Message(Chat::White, "Spells not loaded"); + return; + } + + int arguments = sep->argnum; + + if (arguments == 0) { + c->Message(Chat::White, "Command Syntax: #findspell [search criteria]"); + return; + } + + if (sep->IsNumber(1)) { + int spell_id = std::stoi(sep->arg[1]); + if (!IsValidSpell(spell_id)) { + c->Message( + Chat::White, + fmt::format( + "Spell ID {} was not found.", + spell_id + ).c_str() + ); + } + else { + c->Message( + Chat::White, + fmt::format( + "Spell {}: {}", + spell_id, + spells[spell_id].name + ).c_str() + ); + } + } + else { + std::string search_criteria = str_tolower(sep->argplus[1]); + int found_count = 0; + for (int spell_id = 0; spell_id < SPDAT_RECORDS; spell_id++) { + auto current_spell = spells[spell_id]; + if (current_spell.name[0] != 0) { + std::string spell_name = current_spell.name; + std::string spell_name_lower = str_tolower(spell_name); + if (search_criteria.length() > 0 && spell_name_lower.find(search_criteria) == std::string::npos) { + continue; + } + + c->Message( + Chat::White, + fmt::format( + "Spell {}: {}", + spell_id, + spell_name + ).c_str() + ); + found_count++; + + if (found_count == 20) { + break; + } + } + } + + if (found_count == 20) { + c->Message(Chat::White, "20 Spells found... max reached."); + } + else { + auto spell_message = ( + found_count > 0 ? + ( + found_count == 1 ? + "A Spell was" : + fmt::format("{} Spells were", found_count) + ) : + "No Spells were" + ); + + c->Message( + Chat::White, + fmt::format( + "{} found.", + spell_message + ).c_str() + ); + } + } +} + +inline bool CastRestrictedSpell(int spellid) +{ + switch (spellid) { + case SPELL_TOUCH_OF_VINITRAS: + case SPELL_DESPERATE_HOPE: + case SPELL_CHARM: + case SPELL_METAMORPHOSIS65: + case SPELL_JT_BUFF: + case SPELL_CAN_O_WHOOP_ASS: + case SPELL_PHOENIX_CHARM: + case SPELL_CAZIC_TOUCH: + case SPELL_AVATAR_KNOCKBACK: + case SPELL_SHAPECHANGE65: + case SPELL_SUNSET_HOME1218: + case SPELL_SUNSET_HOME819: + case SPELL_SHAPECHANGE75: + case SPELL_SHAPECHANGE80: + case SPELL_SHAPECHANGE85: + case SPELL_SHAPECHANGE90: + case SPELL_SHAPECHANGE95: + case SPELL_SHAPECHANGE100: + case SPELL_SHAPECHANGE25: + case SPELL_SHAPECHANGE30: + case SPELL_SHAPECHANGE35: + case SPELL_SHAPECHANGE40: + case SPELL_SHAPECHANGE45: + case SPELL_SHAPECHANGE50: + case SPELL_NPC_AEGOLISM: + case SPELL_SHAPECHANGE55: + case SPELL_SHAPECHANGE60: + case SPELL_COMMAND_OF_DRUZZIL: + case SPELL_SHAPECHANGE70: + return true; + default: + return false; + } +} + diff --git a/zone/gm_commands/findtask.cpp b/zone/gm_commands/findtask.cpp new file mode 100755 index 000000000..5edf0dcde --- /dev/null +++ b/zone/gm_commands/findtask.cpp @@ -0,0 +1,89 @@ +#include "../client.h" + +void command_findtask(Client *c, const Seperator *sep) +{ + if (RuleB(TaskSystem, EnableTaskSystem)) { + int arguments = sep->argnum; + + if (arguments == 0) { + c->Message(Chat::White, "Command Syntax: #findtask [search criteria]"); + return; + } + + if (sep->IsNumber(1)) { + auto task_id = std::stoul(sep->arg[1]); + auto task_name = task_manager->GetTaskName(task_id); + auto task_message = ( + !task_name.empty() ? + fmt::format( + "Task {}: {}", + task_id, + task_name + ).c_str() : + fmt::format( + "Task ID {} was not found.", + task_id + ).c_str() + ); + + c->Message( + Chat::White, + task_message + ); + } + else { + std::string search_criteria = str_tolower(sep->argplus[1]); + if (!search_criteria.empty()) { + int found_count = 0; + for (uint32 task_id = 1; task_id <= MAXTASKS; task_id++) { + auto task_name = task_manager->GetTaskName(task_id); + std::string task_name_lower = str_tolower(task_name); + if (task_name_lower.find(search_criteria) == std::string::npos) { + continue; + } + + c->Message( + Chat::White, + fmt::format( + "Task {}: {}", + task_id, + task_name + ).c_str() + ); + found_count++; + + if (found_count == 20) { + break; + } + } + + if (found_count == 20) { + c->Message(Chat::White, "20 Tasks were found, max reached."); + } + else { + auto task_message = ( + found_count > 0 ? + ( + found_count == 1 ? + "A Task was" : + fmt::format("{} Tasks were", found_count) + ) : + "No Tasks were" + ); + + c->Message( + Chat::White, + fmt::format( + "{} found.", + task_message + ).c_str() + ); + } + } + } + } + else { + c->Message(Chat::White, "This command cannot be used while the Task system is disabled."); + } +} + diff --git a/zone/gm_commands/findzone.cpp b/zone/gm_commands/findzone.cpp new file mode 100755 index 000000000..53dacc52d --- /dev/null +++ b/zone/gm_commands/findzone.cpp @@ -0,0 +1,95 @@ +#include "../client.h" + +void command_findzone(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #findzone [search criteria]"); + c->Message(Chat::White, "Usage: #findzone expansion [expansion number]"); + return; + } + + std::string query; + int id = atoi((const char *) sep->arg[1]); + + std::string arg1 = sep->arg[1]; + + if (arg1 == "expansion") { + query = fmt::format( + "SELECT zoneidnumber, short_name, long_name, version FROM zone WHERE expansion = {}", + sep->arg[2] + ); + } + else { + + /** + * If id evaluates to 0, then search as if user entered a string + */ + if (id == 0) { + query = fmt::format( + "SELECT zoneidnumber, short_name, long_name, version FROM zone WHERE long_name LIKE '%{}%' OR `short_name` LIKE '%{}%'", + EscapeString(sep->arg[1]), + EscapeString(sep->arg[1]) + ); + } + else { + query = fmt::format( + "SELECT zoneidnumber, short_name, long_name, version FROM zone WHERE zoneidnumber = {}", + id + ); + } + } + + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Error querying database."); + c->Message(Chat::White, query.c_str()); + return; + } + + int count = 0; + const int maxrows = 100; + + for (auto row = results.begin(); row != results.end(); ++row) { + std::string zone_id = row[0]; + std::string short_name = row[1]; + std::string long_name = row[2]; + int version = atoi(row[3]); + + if (++count > maxrows) { + c->Message(Chat::White, "%i zones shown. Too many results.", maxrows); + break; + } + + std::string command_zone = EQ::SayLinkEngine::GenerateQuestSaylink("#zone " + short_name, false, "zone"); + std::string command_gmzone = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#gmzone {} {}", short_name, version), + false, + "gmzone" + ); + + c->Message( + Chat::White, + fmt::format( + "[{}] [{}] [{}] ID ({}) Version ({}) [{}]", + (version == 0 ? command_zone : "zone"), + command_gmzone, + short_name, + zone_id, + version, + long_name + ).c_str() + ); + } + + if (count <= maxrows) { + c->Message( + Chat::White, + "Query complete. %i rows shown. %s", + count, + (arg1 == "expansion" ? "(expansion search)" : "")); + } + else if (count == 0) { + c->Message(Chat::White, "No matches found for %s.", sep->arg[1]); + } +} + diff --git a/zone/gm_commands/fixmob.cpp b/zone/gm_commands/fixmob.cpp new file mode 100755 index 000000000..00e2edf11 --- /dev/null +++ b/zone/gm_commands/fixmob.cpp @@ -0,0 +1,250 @@ +#include "../client.h" + +void command_fixmob(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + const char *Usage = "Usage: #fixmob [race|gender|texture|helm|face|hair|haircolor|beard|beardcolor|heritage|tattoo|detail] [next|prev]"; + + if (!sep->arg[1]) { + c->Message(Chat::White, Usage); + } + else if (!target) { + c->Message(Chat::White, "Error: this command requires a target"); + } + else { + + uint32 Adjustment = 1; // Previous or Next + char codeMove = 0; + + if (sep->arg[2]) { + char *command2 = sep->arg[2]; + codeMove = (command2[0] | 0x20); // First character, lower-cased + if (codeMove == 'n') { + Adjustment = 1; + } + else if (codeMove == 'p') { + Adjustment = -1; + } + } + + 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(); + + const char *ChangeType = nullptr; // If it's still nullptr after processing, they didn't send a valid command + uint32 ChangeSetting; + char *command = sep->arg[1]; + + if (strcasecmp(command, "race") == 0) { + if (Race == 1 && codeMove == 'p') { + Race = RuleI(NPC, MaxRaceID); + } + else if (Race >= RuleI(NPC, MaxRaceID) && codeMove != 'p') { + Race = 1; + } + else { + Race += Adjustment; + } + ChangeType = "Race"; + ChangeSetting = Race; + } + else if (strcasecmp(command, "gender") == 0) { + if (Gender == 0 && codeMove == 'p') { + Gender = 2; + } + else if (Gender >= 2 && codeMove != 'p') { + Gender = 0; + } + else { + Gender += Adjustment; + } + ChangeType = "Gender"; + ChangeSetting = Gender; + } + else if (strcasecmp(command, "texture") == 0) { + Texture = target->GetTexture(); + + if (Texture == 0 && codeMove == 'p') { + Texture = 25; + } + else if (Texture >= 25 && codeMove != 'p') { + Texture = 0; + } + else { + Texture += Adjustment; + } + ChangeType = "Texture"; + ChangeSetting = Texture; + } + else if (strcasecmp(command, "helm") == 0) { + HelmTexture = target->GetHelmTexture(); + if (HelmTexture == 0 && codeMove == 'p') { + HelmTexture = 25; + } + else if (HelmTexture >= 25 && codeMove != 'p') { + HelmTexture = 0; + } + else { + HelmTexture += Adjustment; + } + ChangeType = "HelmTexture"; + ChangeSetting = HelmTexture; + } + else if (strcasecmp(command, "face") == 0) { + if (LuclinFace == 0 && codeMove == 'p') { + LuclinFace = 87; + } + else if (LuclinFace >= 87 && codeMove != 'p') { + LuclinFace = 0; + } + else { + LuclinFace += Adjustment; + } + ChangeType = "LuclinFace"; + ChangeSetting = LuclinFace; + } + else if (strcasecmp(command, "hair") == 0) { + if (HairStyle == 0 && codeMove == 'p') { + HairStyle = 8; + } + else if (HairStyle >= 8 && codeMove != 'p') { + HairStyle = 0; + } + else { + HairStyle += Adjustment; + } + ChangeType = "HairStyle"; + ChangeSetting = HairStyle; + } + else if (strcasecmp(command, "haircolor") == 0) { + if (HairColor == 0 && codeMove == 'p') { + HairColor = 24; + } + else if (HairColor >= 24 && codeMove != 'p') { + HairColor = 0; + } + else { + HairColor += Adjustment; + } + ChangeType = "HairColor"; + ChangeSetting = HairColor; + } + else if (strcasecmp(command, "beard") == 0) { + if (Beard == 0 && codeMove == 'p') { + Beard = 11; + } + else if (Beard >= 11 && codeMove != 'p') { + Beard = 0; + } + else { + Beard += Adjustment; + } + ChangeType = "Beard"; + ChangeSetting = Beard; + } + else if (strcasecmp(command, "beardcolor") == 0) { + if (BeardColor == 0 && codeMove == 'p') { + BeardColor = 24; + } + else if (BeardColor >= 24 && codeMove != 'p') { + BeardColor = 0; + } + else { + BeardColor += Adjustment; + } + ChangeType = "BeardColor"; + ChangeSetting = BeardColor; + } + else if (strcasecmp(command, "heritage") == 0) { + if (DrakkinHeritage == 0 && codeMove == 'p') { + DrakkinHeritage = 6; + } + else if (DrakkinHeritage >= 6 && codeMove != 'p') { + DrakkinHeritage = 0; + } + else { + DrakkinHeritage += Adjustment; + } + ChangeType = "DrakkinHeritage"; + ChangeSetting = DrakkinHeritage; + } + else if (strcasecmp(command, "tattoo") == 0) { + if (DrakkinTattoo == 0 && codeMove == 'p') { + DrakkinTattoo = 8; + } + else if (DrakkinTattoo >= 8 && codeMove != 'p') { + DrakkinTattoo = 0; + } + else { + DrakkinTattoo += Adjustment; + } + ChangeType = "DrakkinTattoo"; + ChangeSetting = DrakkinTattoo; + } + else if (strcasecmp(command, "detail") == 0) { + if (DrakkinDetails == 0 && codeMove == 'p') { + DrakkinDetails = 7; + } + else if (DrakkinDetails >= 7 && codeMove != 'p') { + DrakkinDetails = 0; + } + else { + DrakkinDetails += Adjustment; + } + ChangeType = "DrakkinDetails"; + ChangeSetting = DrakkinDetails; + } + + // Hack to fix some races that base features from face + switch (Race) { + case 2: // Barbarian + if (LuclinFace > 10) { + LuclinFace -= ((DrakkinTattoo - 1) * 10); + } + LuclinFace += (DrakkinTattoo * 10); + break; + case 3: // Erudite + if (LuclinFace > 10) { + LuclinFace -= ((HairStyle - 1) * 10); + } + LuclinFace += (HairStyle * 10); + break; + case 5: // HighElf + case 6: // DarkElf + case 7: // HalfElf + if (LuclinFace > 10) { + LuclinFace -= ((Beard - 1) * 10); + } + LuclinFace += (Beard * 10); + break; + default: + break; + } + + + if (ChangeType == nullptr) { + c->Message(Chat::White, Usage); + } + else { + target->SendIllusionPacket( + Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails + ); + + c->Message(Chat::White, "%s=%i", ChangeType, ChangeSetting); + } + } +} + diff --git a/zone/gm_commands/flag.cpp b/zone/gm_commands/flag.cpp new file mode 100755 index 000000000..8f02c8624 --- /dev/null +++ b/zone/gm_commands/flag.cpp @@ -0,0 +1,53 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_flag(Client *c, const Seperator *sep) +{ + if (sep->arg[2][0] == 0) { + if (!c->GetTarget() || (c->GetTarget() && c->GetTarget() == c)) { + c->UpdateAdmin(); + c->Message(Chat::White, "Refreshed your admin flag from DB."); + } + else if (c->GetTarget() && c->GetTarget() != c && c->GetTarget()->IsClient()) { + c->GetTarget()->CastToClient()->UpdateAdmin(); + c->Message(Chat::White, "%s's admin flag has been refreshed.", c->GetTarget()->GetName()); + c->GetTarget()->Message(Chat::White, "%s refreshed your admin flag.", c->GetName()); + } + } + else if (!sep->IsNumber(1) || atoi(sep->arg[1]) < -2 || atoi(sep->arg[1]) > 255 || strlen(sep->arg[2]) == 0) { + c->Message(Chat::White, "Usage: #flag [status] [acctname]"); + } + + else if (c->Admin() < commandChangeFlags) { + //this check makes banning players by less than this level + //impossible, but i'll leave it in anyways + c->Message(Chat::White, "You may only refresh your own flag, doing so now."); + c->UpdateAdmin(); + } + else { + if (atoi(sep->arg[1]) > c->Admin()) { + c->Message(Chat::White, "You cannot set people's status to higher than your own"); + } + else if (atoi(sep->arg[1]) < 0 && c->Admin() < commandBanPlayers) { + c->Message(Chat::White, "You have too low of status to suspend/ban"); + } + else if (!database.SetAccountStatus(sep->argplus[2], atoi(sep->arg[1]))) { + c->Message(Chat::White, "Unable to set GM Flag."); + } + else { + c->Message(Chat::White, "Set GM Flag on account."); + + std::string user; + std::string loginserver; + ParseAccountString(sep->argplus[2], user, loginserver); + + ServerPacket pack(ServerOP_FlagUpdate, 6); + *((uint32 *) pack.pBuffer) = database.GetAccountIDByName(user.c_str(), loginserver.c_str()); + *((int16 *) &pack.pBuffer[4]) = atoi(sep->arg[1]); + worldserver.SendPacket(&pack); + } + } +} + diff --git a/zone/gm_commands/flagedit.cpp b/zone/gm_commands/flagedit.cpp new file mode 100755 index 000000000..7e0bff517 --- /dev/null +++ b/zone/gm_commands/flagedit.cpp @@ -0,0 +1,157 @@ +#include "../client.h" + +void command_flagedit(Client *c, const Seperator *sep) +{ + //super-command for editing zone flags + if (sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { + c->Message(Chat::White, "Syntax: #flagedit [lockzone|unlockzone|listzones|give|take]."); + c->Message( + Chat::White, + "...lockzone [zone id/short] [flag name] - Set the specified flag name on the zone, locking the zone" + ); + c->Message(Chat::White, "...unlockzone [zone id/short] - Removes the flag requirement from the specified zone"); + c->Message(Chat::White, "...listzones - List all zones which require a flag, and their flag's name"); + c->Message(Chat::White, "...give [zone id/short] - Give your target the zone flag for the specified zone."); + c->Message( + Chat::White, + "...take [zone id/short] - Take the zone flag for the specified zone away from your target" + ); + c->Message(Chat::White, "...Note: use #flags to view flags on a person"); + return; + } + + if (!strcasecmp(sep->arg[1], "lockzone")) { + uint32 zoneid = 0; + if (sep->arg[2][0] != '\0') { + zoneid = atoi(sep->arg[2]); + if (zoneid < 1) { + zoneid = ZoneID(sep->arg[2]); + } + } + if (zoneid < 1) { + c->Message(Chat::Red, "zone required. see help."); + return; + } + + char flag_name[128]; + if (sep->argplus[3][0] == '\0') { + c->Message(Chat::Red, "flag name required. see help."); + return; + } + database.DoEscapeString(flag_name, sep->argplus[3], 64); + flag_name[127] = '\0'; + + std::string query = StringFormat( + "UPDATE zone SET flag_needed = '%s' " + "WHERE zoneidnumber = %d AND version = %d", + flag_name, zoneid, zone->GetInstanceVersion()); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::Red, "Error updating zone: %s", results.ErrorMessage().c_str()); + return; + } + + c->Message(Chat::Yellow, "Success! Zone %s now requires a flag, named %s", ZoneName(zoneid), flag_name); + return; + } + + if (!strcasecmp(sep->arg[1], "unlockzone")) { + uint32 zoneid = 0; + if (sep->arg[2][0] != '\0') { + zoneid = atoi(sep->arg[2]); + if (zoneid < 1) { + zoneid = ZoneID(sep->arg[2]); + } + } + + if (zoneid < 1) { + c->Message(Chat::Red, "zone required. see help."); + return; + } + + std::string query = StringFormat( + "UPDATE zone SET flag_needed = '' " + "WHERE zoneidnumber = %d AND version = %d", + zoneid, zone->GetInstanceVersion()); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::Yellow, "Error updating zone: %s", results.ErrorMessage().c_str()); + return; + } + + c->Message(Chat::Yellow, "Success! Zone %s no longer requires a flag.", ZoneName(zoneid)); + return; + } + + if (!strcasecmp(sep->arg[1], "listzones")) { + std::string query = "SELECT zoneidnumber, short_name, long_name, version, flag_needed " + "FROM zone WHERE flag_needed != ''"; + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + return; + } + + c->Message(Chat::White, "Zones which require flags:"); + for (auto row = results.begin(); row != results.end(); ++row) + c->Message( + Chat::White, + "Zone %s (%s,%s) version %s requires key %s", + row[2], + row[0], + row[1], + row[3], + row[4] + ); + + return; + } + + if (!strcasecmp(sep->arg[1], "give")) { + uint32 zoneid = 0; + if (sep->arg[2][0] != '\0') { + zoneid = atoi(sep->arg[2]); + if (zoneid < 1) { + zoneid = ZoneID(sep->arg[2]); + } + } + if (zoneid < 1) { + c->Message(Chat::Red, "zone required. see help."); + return; + } + + Mob *t = c->GetTarget(); + if (t == nullptr || !t->IsClient()) { + c->Message(Chat::Red, "client target required"); + return; + } + + t->CastToClient()->SetZoneFlag(zoneid); + return; + } + + if (!strcasecmp(sep->arg[1], "give")) { + uint32 zoneid = 0; + if (sep->arg[2][0] != '\0') { + zoneid = atoi(sep->arg[2]); + if (zoneid < 1) { + zoneid = ZoneID(sep->arg[2]); + } + } + if (zoneid < 1) { + c->Message(Chat::Red, "zone required. see help."); + return; + } + + Mob *t = c->GetTarget(); + if (t == nullptr || !t->IsClient()) { + c->Message(Chat::Red, "client target required"); + return; + } + + t->CastToClient()->ClearZoneFlag(zoneid); + return; + } + + c->Message(Chat::Yellow, "Invalid action specified. use '#flagedit help' for help"); +} + diff --git a/zone/gm_commands/flags.cpp b/zone/gm_commands/flags.cpp new file mode 100755 index 000000000..c2e3d1f2d --- /dev/null +++ b/zone/gm_commands/flags.cpp @@ -0,0 +1,16 @@ +#include "../client.h" + +void command_flags(Client *c, const Seperator *sep) +{ + Client *t = c; + + if (c->Admin() >= minStatusToSeeOthersZoneFlags) { + Mob *tgt = c->GetTarget(); + if (tgt != nullptr && tgt->IsClient()) { + t = tgt->CastToClient(); + } + } + + t->SendZoneFlagInfo(c); +} + diff --git a/zone/gm_commands/flymode.cpp b/zone/gm_commands/flymode.cpp new file mode 100755 index 000000000..905241b50 --- /dev/null +++ b/zone/gm_commands/flymode.cpp @@ -0,0 +1,40 @@ +#include "../client.h" + +void command_flymode(Client *c, const Seperator *sep) +{ + Mob *t = c; + + if (strlen(sep->arg[1]) == 1 && sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 5) { + if (c->GetTarget()) { + t = c->GetTarget(); + } + + int fm = atoi(sep->arg[1]); + + t->SetFlyMode(static_cast(fm)); + t->SendAppearancePacket(AT_Levitate, fm); + if (sep->arg[1][0] == '0') { + c->Message(Chat::White, "Setting %s to Grounded", t->GetName()); + } + else if (sep->arg[1][0] == '1') { + c->Message(Chat::White, "Setting %s to Flying", t->GetName()); + } + else if (sep->arg[1][0] == '2') { + c->Message(Chat::White, "Setting %s to Levitating", t->GetName()); + } + else if (sep->arg[1][0] == '3') { + c->Message(Chat::White, "Setting %s to In Water", t->GetName()); + } + else if (sep->arg[1][0] == '4') { + c->Message(Chat::White, "Setting %s to Floating(Boat)", t->GetName()); + } + else if (sep->arg[1][0] == '5') { + c->Message(Chat::White, "Setting %s to Levitating While Running", t->GetName()); + } + } + else { + c->Message(Chat::White, "#flymode [0/1/2/3/4/5]"); + } +} + + diff --git a/zone/gm_commands/fov.cpp b/zone/gm_commands/fov.cpp new file mode 100755 index 000000000..50447a326 --- /dev/null +++ b/zone/gm_commands/fov.cpp @@ -0,0 +1,42 @@ +#include "../client.h" + +void command_fov(Client *c, const Seperator *sep) +{ + if (c->GetTarget()) { + auto target = c->GetTarget(); + std::string behind_message = ( + c->BehindMob( + target, + c->GetX(), + c->GetY() + ) ? + "behind" : + "not behind" + ); + std::string gender_message = ( + target->GetGender() == MALE ? + "he" : + ( + target->GetGender() == FEMALE ? + "she" : + "it" + ) + ); + + c->Message( + Chat::White, + fmt::format( + "You are {} {} ({}), {} has a heading of {}.", + behind_message, + target->GetCleanName(), + target->GetID(), + gender_message, + target->GetHeading() + ).c_str() + ); + } + else { + c->Message(Chat::White, "You must have a target to use this command."); + } +} + diff --git a/zone/gm_commands/freeze.cpp b/zone/gm_commands/freeze.cpp new file mode 100755 index 000000000..8cce36ba2 --- /dev/null +++ b/zone/gm_commands/freeze.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_freeze(Client *c, const Seperator *sep) +{ + if (c->GetTarget() != 0) { + c->GetTarget()->SendAppearancePacket(AT_Anim, ANIM_FREEZE); + } + else { + c->Message(Chat::White, "ERROR: Freeze requires a target."); + } +} + diff --git a/zone/gm_commands/gassign.cpp b/zone/gm_commands/gassign.cpp new file mode 100755 index 000000000..1a830b2fd --- /dev/null +++ b/zone/gm_commands/gassign.cpp @@ -0,0 +1,14 @@ +#include "../client.h" + +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!"); + } +} + diff --git a/zone/gm_commands/gearup.cpp b/zone/gm_commands/gearup.cpp new file mode 100755 index 000000000..e02ba600d --- /dev/null +++ b/zone/gm_commands/gearup.cpp @@ -0,0 +1,181 @@ +#include "../client.h" +#include "../../common/http/httplib.h" +#include "../../common/content/world_content_service.h" + +void command_gearup(Client *c, const Seperator *sep) +{ + std::string tool_table_name = "tool_gearup_armor_sets"; + if (!database.DoesTableExist(tool_table_name)) { + c->Message( + Chat::Yellow, + fmt::format( + "Table [{}] does not exist. Downloading from Github and installing...", + tool_table_name + ).c_str() + ); + + // http get request + httplib::Client cli("https://raw.githubusercontent.com"); + cli.set_connection_timeout(0, 15000000); // 15 sec + cli.set_read_timeout(15, 0); // 15 seconds + cli.set_write_timeout(15, 0); // 15 seconds + + int sourced_queries = 0; + std::string url = "/EQEmu/Server/master/utils/sql/git/optional/2020_07_20_tool_gearup_armor_sets.sql"; + + if (auto res = cli.Get(url.c_str())) { + if (res->status == 200) { + for (auto &s: SplitString(res->body, ';')) { + if (!trim(s).empty()) { + auto results = database.QueryDatabase(s); + if (!results.ErrorMessage().empty()) { + c->Message( + Chat::Yellow, + fmt::format( + "Error sourcing SQL [{}]", results.ErrorMessage() + ).c_str() + ); + return; + } + sourced_queries++; + } + } + } + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Error retrieving URL [{}]", + url + ).c_str() + ); + } + + c->Message( + Chat::Yellow, + fmt::format( + "Table [{}] installed. Sourced [{}] queries", + tool_table_name, sourced_queries + ).c_str() + ); + } + + std::string expansion_arg = sep->arg[1]; + std::string expansion_filter; + if (expansion_arg.length() > 0) { + expansion_filter = fmt::format("and `expansion` = {}", expansion_arg); + } + + auto results = database.QueryDatabase( + fmt::format( + SQL ( + select + item_id, + slot + from + {} + where + `class` = {} + and `level` = {} + {} + order by score desc, expansion desc + ), + tool_table_name, + c->GetClass(), + c->GetLevel(), + expansion_filter + ) + ); + + int items_equipped = 0; + int items_already_have = 0; + std::set equipped; + + for (auto row = results.begin(); row != results.end(); ++row) { + int item_id = atoi(row[0]); + int slot_id = atoi(row[1]); + + if (equipped.find(slot_id) != equipped.end()) { + if (slot_id == EQ::invslot::slotEar1) { + slot_id = EQ::invslot::slotEar2; + } + if (slot_id == EQ::invslot::slotFinger1) { + slot_id = EQ::invslot::slotFinger2; + } + if (slot_id == EQ::invslot::slotWrist1) { + slot_id = EQ::invslot::slotWrist2; + } + } + + if (equipped.find(slot_id) == equipped.end()) { + const EQ::ItemData *item = database.GetItem(item_id); + bool has_item = (c->GetInv().HasItem(item_id, 1, invWhereWorn) != INVALID_INDEX); + bool can_wear_item = !c->CheckLoreConflict(item) && !has_item; + if (!can_wear_item) { + items_already_have++; + } + + if (c->CastToMob()->CanClassEquipItem(item_id) && can_wear_item) { + equipped.insert(slot_id); + c->SummonItem( + item_id, + 0, 0, 0, 0, 0, 0, 0, 0, + slot_id + ); + items_equipped++; + } + } + } + + c->Message( + Chat::White, + fmt::format( + "Equipped items [{}] already had [{}] items equipped", + items_equipped, + items_already_have + ).c_str() + ); + + if (expansion_arg.empty()) { + results = database.QueryDatabase( + fmt::format( + SQL ( + select + expansion + from + {} + where + class = {} + and level = {} + group by + expansion; + ), + tool_table_name, + c->GetClass(), + c->GetLevel() + ) + ); + + c->Message(Chat::White, "Choose armor from a specific era"); + std::string message; + for (auto row = results.begin(); row != results.end(); ++row) { + int expansion = atoi(row[0]); + message += "[" + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#gearup {}", expansion), + false, + Expansion::ExpansionName[expansion] + ) + "] "; + + if (message.length() > 2000) { + c->Message(Chat::White, message.c_str()); + message = ""; + } + } + if (message.length() > 0) { + c->Message(Chat::White, message.c_str()); + } + } + +} + diff --git a/zone/gm_commands/gender.cpp b/zone/gm_commands/gender.cpp new file mode 100755 index 000000000..0051ddd43 --- /dev/null +++ b/zone/gm_commands/gender.cpp @@ -0,0 +1,17 @@ +#include "../client.h" + +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]"); + } +} + diff --git a/zone/gm_commands/getplayerburiedcorpsecount.cpp b/zone/gm_commands/getplayerburiedcorpsecount.cpp new file mode 100755 index 000000000..3be9b205c --- /dev/null +++ b/zone/gm_commands/getplayerburiedcorpsecount.cpp @@ -0,0 +1,27 @@ +#include "../client.h" +#include "../corpse.h" + +void command_getplayerburiedcorpsecount(Client *c, const Seperator *sep) +{ + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + t = c->GetTarget()->CastToClient(); + } + else { + c->Message(Chat::White, "You must first select a target!"); + return; + } + + uint32 CorpseCount = database.GetCharacterBuriedCorpseCount(t->CharacterID()); + + if (CorpseCount > 0) { + c->Message(Chat::White, "Your target has a total of %u buried corpses.", CorpseCount); + } + else { + c->Message(Chat::White, "Your target doesn't have any buried corpses."); + } + + return; +} + diff --git a/zone/gm_commands/getvariable.cpp b/zone/gm_commands/getvariable.cpp new file mode 100755 index 000000000..23c90219e --- /dev/null +++ b/zone/gm_commands/getvariable.cpp @@ -0,0 +1,13 @@ +#include "../client.h" + +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]); + } +} + diff --git a/zone/gm_commands/ginfo.cpp b/zone/gm_commands/ginfo.cpp new file mode 100755 index 000000000..ddec8ba24 --- /dev/null +++ b/zone/gm_commands/ginfo.cpp @@ -0,0 +1,52 @@ +#include "../client.h" +#include "../groups.h" + +void command_ginfo(Client *c, const Seperator *sep) +{ + Client *t; + + if (c->GetTarget() && c->GetTarget()->IsClient()) { + t = c->GetTarget()->CastToClient(); + } + else { + t = c; + } + + Group *g = t->GetGroup(); + if (!g) { + c->Message(Chat::White, "This client is not in a group"); + return; + } + + c->Message( + Chat::White, + "Player: %s is in Group #%lu: with %i members", + t->GetName(), + (unsigned long) g->GetID(), + g->GroupCount()); + + uint32 r; + for (r = 0; r < MAX_GROUP_MEMBERS; r++) { + if (g->members[r] == nullptr) { + if (g->membername[r][0] == '\0') { + continue; + } + c->Message( + Chat::White, "...Zoned Member: %s, Roles: %s %s %s", g->membername[r], + (g->MemberRoles[r] & RoleAssist) ? "Assist" : "", + (g->MemberRoles[r] & RoleTank) ? "Tank" : "", + (g->MemberRoles[r] & RolePuller) ? "Puller" : "" + ); + } + else { + c->Message( + Chat::White, "...In-Zone Member: %s (0x%x) Roles: %s %s %s", g->membername[r], g->members[r], + (g->MemberRoles[r] & RoleAssist) ? "Assist" : "", + (g->MemberRoles[r] & RoleTank) ? "Tank" : "", + (g->MemberRoles[r] & RolePuller) ? "Puller" : "" + ); + + } + } +} + diff --git a/zone/gm_commands/giveitem.cpp b/zone/gm_commands/giveitem.cpp new file mode 100755 index 000000000..17458182b --- /dev/null +++ b/zone/gm_commands/giveitem.cpp @@ -0,0 +1,111 @@ +#include "../client.h" + +void command_giveitem(Client *c, const Seperator *sep) +{ + uint32 item_id = 0; + int16 charges = -1; + uint32 augment_one = 0; + uint32 augment_two = 0; + uint32 augment_three = 0; + uint32 augment_four = 0; + uint32 augment_five = 0; + uint32 augment_six = 0; + int arguments = sep->argnum; + std::string cmd_msg = sep->msg; + size_t link_open = cmd_msg.find('\x12'); + size_t link_close = cmd_msg.find_last_of('\x12'); + if (c->GetTarget()) { + if (!c->GetTarget()->IsClient()) { + c->Message(Chat::Red, "You can only give items to players with this command."); + return; + } + + if (link_open != link_close && (cmd_msg.length() - link_open) > EQ::constants::SAY_LINK_BODY_SIZE) { + EQ::SayLinkBody_Struct link_body; + EQ::saylink::DegenerateLinkBody( + link_body, + cmd_msg.substr(link_open + 1, EQ::constants::SAY_LINK_BODY_SIZE)); + item_id = link_body.item_id; + augment_one = link_body.augment_1; + augment_two = link_body.augment_2; + augment_three = link_body.augment_3; + augment_four = link_body.augment_4; + augment_five = link_body.augment_5; + augment_six = link_body.augment_6; + } + else if (sep->IsNumber(1)) { + item_id = atoi(sep->arg[1]); + } + else if (!sep->IsNumber(1)) { + c->Message( + Chat::Red, + "Usage: #giveitem [item id | link] [charges] [augment_one_id] [augment_two_id] [augment_three_id] [augment_four_id] [augment_five_id] [augment_six_id] (Charges are optional.)" + ); + return; + } + + Client *client_target = c->GetTarget()->CastToClient(); + uint8 item_status = 0; + uint8 current_status = c->Admin(); + const EQ::ItemData *item = database.GetItem(item_id); + if (item) { + item_status = item->MinStatus; + } + + if (item_status > current_status) { + c->Message( + Chat::White, + fmt::format( + "Insufficient status to summon this item, current status is {}, required status is {}.", + current_status, + item_status + ).c_str() + ); + return; + } + + if (arguments >= 2 && sep->IsNumber(2)) { + charges = atoi(sep->arg[2]); + } + + if (arguments >= 3 && sep->IsNumber(3)) { + augment_one = atoi(sep->arg[3]); + } + + if (arguments >= 4 && sep->IsNumber(4)) { + augment_two = atoi(sep->arg[4]); + } + + if (arguments >= 5 && sep->IsNumber(5)) { + augment_three = atoi(sep->arg[5]); + } + + if (arguments >= 6 && sep->IsNumber(6)) { + augment_four = atoi(sep->arg[6]); + } + + if (arguments >= 7 && sep->IsNumber(7)) { + augment_five = atoi(sep->arg[7]); + } + + if (arguments == 8 && sep->IsNumber(8)) { + augment_six = atoi(sep->arg[8]); + } + + client_target->SummonItem( + item_id, + charges, + augment_one, + augment_two, + augment_three, + augment_four, + augment_five, + augment_six + ); + } + else { + c->Message(Chat::Red, "You must target a client to give the item to."); + return; + } +} + diff --git a/zone/gm_commands/givemoney.cpp b/zone/gm_commands/givemoney.cpp new file mode 100755 index 000000000..e5b2c3167 --- /dev/null +++ b/zone/gm_commands/givemoney.cpp @@ -0,0 +1,33 @@ +#include "../client.h" + +void command_givemoney(Client *c, const Seperator *sep) +{ + if (!sep->IsNumber(1)) { //as long as the first one is a number, we'll just let atoi convert the rest to 0 or a number + c->Message(Chat::Red, "Usage: #Usage: #givemoney [pp] [gp] [sp] [cp]"); + } + else if (c->GetTarget() == nullptr) { + c->Message(Chat::Red, "You must target a player to give money to."); + } + else if (!c->GetTarget()->IsClient()) { + c->Message(Chat::Red, "You can only give money to players with this command."); + } + else { + //TODO: update this to the client, otherwise the client doesn't show any weight change until you zone, move an item, etc + c->GetTarget()->CastToClient()->AddMoneyToPP( + atoi(sep->arg[4]), + atoi(sep->arg[3]), + atoi(sep->arg[2]), + atoi(sep->arg[1]), + true + ); + c->Message( + Chat::White, + "Added %i Platinum, %i Gold, %i Silver, and %i Copper to %s's inventory.", + atoi(sep->arg[1]), + atoi(sep->arg[2]), + atoi(sep->arg[3]), + atoi(sep->arg[4]), + c->GetTarget()->GetName()); + } +} + diff --git a/zone/gm_commands/globalview.cpp b/zone/gm_commands/globalview.cpp new file mode 100755 index 000000000..948ecd353 --- /dev/null +++ b/zone/gm_commands/globalview.cpp @@ -0,0 +1,80 @@ +#include "../client.h" + +void command_globalview(Client *c, const Seperator *sep) +{ + NPC *npcmob = nullptr; + + if (c->GetTarget() && c->GetTarget()->IsNPC()) { + npcmob = c->GetTarget()->CastToNPC(); + QGlobalCache *npc_c = nullptr; + QGlobalCache *char_c = nullptr; + QGlobalCache *zone_c = nullptr; + + if (npcmob) { + npc_c = npcmob->GetQGlobals(); + } + + char_c = c->GetQGlobals(); + zone_c = zone->GetQGlobals(); + + std::list globalMap; + uint32 ntype = 0; + + if (npcmob) { + ntype = npcmob->GetNPCTypeID(); + } + + if (npc_c) { + QGlobalCache::Combine(globalMap, npc_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); + } + + if (char_c) { + QGlobalCache::Combine(globalMap, char_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); + } + + if (zone_c) { + QGlobalCache::Combine(globalMap, zone_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); + } + + auto iter = globalMap.begin(); + uint32 gcount = 0; + + c->Message(Chat::White, "Name, Value"); + while (iter != globalMap.end()) { + c->Message(Chat::White, "%s %s", (*iter).name.c_str(), (*iter).value.c_str()); + ++iter; + ++gcount; + } + c->Message(Chat::White, "%u globals loaded.", gcount); + } + else { + QGlobalCache *char_c = nullptr; + QGlobalCache *zone_c = nullptr; + + char_c = c->GetQGlobals(); + zone_c = zone->GetQGlobals(); + + std::list globalMap; + uint32 ntype = 0; + + if (char_c) { + QGlobalCache::Combine(globalMap, char_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); + } + + if (zone_c) { + QGlobalCache::Combine(globalMap, zone_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); + } + + auto iter = globalMap.begin(); + uint32 gcount = 0; + + c->Message(Chat::White, "Name, Value"); + while (iter != globalMap.end()) { + c->Message(Chat::White, "%s %s", (*iter).name.c_str(), (*iter).value.c_str()); + ++iter; + ++gcount; + } + c->Message(Chat::White, "%u globals loaded.", gcount); + } +} + diff --git a/zone/gm_commands/gm.cpp b/zone/gm_commands/gm.cpp new file mode 100755 index 000000000..2920c5026 --- /dev/null +++ b/zone/gm_commands/gm.cpp @@ -0,0 +1,27 @@ +#include "../client.h" + +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 diff --git a/zone/gm_commands/gmspeed.cpp b/zone/gm_commands/gmspeed.cpp new file mode 100755 index 000000000..424c68ef5 --- /dev/null +++ b/zone/gm_commands/gmspeed.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +void command_gmspeed(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) { + database.SetGMSpeed(t->AccountID(), state ? 1 : 0); + c->Message(Chat::White, "Turning GMSpeed %s for %s (zone to take effect)", state ? "On" : "Off", t->GetName()); + } + else { + c->Message(Chat::White, "Usage: #gmspeed [on/off]"); + } +} + diff --git a/zone/gm_commands/gmzone.cpp b/zone/gm_commands/gmzone.cpp new file mode 100755 index 000000000..074ae7c7d --- /dev/null +++ b/zone/gm_commands/gmzone.cpp @@ -0,0 +1,88 @@ +#include "../client.h" +#include "../data_bucket.h" + +void command_gmzone(Client *c, const Seperator *sep) +{ + if (!sep->arg[1]) { + c->Message(Chat::White, "Usage"); + c->Message(Chat::White, "-------"); + c->Message(Chat::White, "#gmzone [zone_short_name] [zone_version=0]"); + return; + } + + std::string zone_short_name_string = sep->arg[1]; + const char *zone_short_name = sep->arg[1]; + auto zone_version = static_cast(sep->arg[2] ? atoi(sep->arg[2]) : 0); + std::string identifier = "gmzone"; + uint32 zone_id = ZoneID(zone_short_name); + uint32 duration = 100000000; + uint16 instance_id = 0; + + if (zone_id == 0) { + c->Message(Chat::Red, "Invalid zone specified"); + return; + } + + if (sep->arg[3] && sep->arg[3][0]) { + identifier = sep->arg[3]; + } + + std::string bucket_key = StringFormat( + "%s-%s-%u-instance", + zone_short_name, + identifier.c_str(), + zone_version + ); + std::string existing_zone_instance = DataBucket::GetData(bucket_key); + + if (existing_zone_instance.length() > 0) { + instance_id = std::stoi(existing_zone_instance); + + c->Message(Chat::Yellow, "Found already created instance (%s) (%u)", zone_short_name, instance_id); + } + + if (instance_id == 0) { + if (!database.GetUnusedInstanceID(instance_id)) { + c->Message(Chat::Red, "Server was unable to find a free instance id."); + return; + } + + if (!database.CreateInstance(instance_id, zone_id, zone_version, duration)) { + c->Message(Chat::Red, "Server was unable to create a new instance."); + return; + } + + c->Message( + Chat::Yellow, + "New private GM instance %s was created with id %lu.", + zone_short_name, + (unsigned long) instance_id + ); + DataBucket::SetData(bucket_key, std::to_string(instance_id)); + } + + if (instance_id > 0) { + float target_x = -1, target_y = -1, target_z = -1, target_heading = -1; + int16 min_status = AccountStatus::Player; + uint8 min_level = 0; + + if (!content_db.GetSafePoints( + zone_short_name, + zone_version, + &target_x, + &target_y, + &target_z, + &target_heading, + &min_status, + &min_level + )) { + c->Message(Chat::Red, "Failed to find safe coordinates for specified zone"); + } + + c->Message(Chat::Yellow, "Zoning to private GM instance (%s) (%u)", zone_short_name, instance_id); + + c->AssignToInstance(instance_id); + c->MovePC(zone_id, instance_id, target_x, target_y, target_z, target_heading, 1); + } +} + diff --git a/zone/gm_commands/goto.cpp b/zone/gm_commands/goto.cpp new file mode 100755 index 000000000..cdc8da2e2 --- /dev/null +++ b/zone/gm_commands/goto.cpp @@ -0,0 +1,63 @@ +#include "../client.h" + +void command_goto(Client *c, const Seperator *sep) +{ + std::string arg1 = sep->arg[1]; + + bool goto_via_target_no_args = sep->arg[1][0] == '\0' && c->GetTarget(); + bool goto_via_player_name = !sep->IsNumber(1) && !arg1.empty(); + bool goto_via_x_y_z = sep->IsNumber(1) && sep->IsNumber(2) && sep->IsNumber(3); + + if (goto_via_target_no_args) { + c->MovePC( + zone->GetZoneID(), + zone->GetInstanceID(), + c->GetTarget()->GetX(), + c->GetTarget()->GetY(), + c->GetTarget()->GetZ(), + c->GetTarget()->GetHeading() + ); + } + else if (goto_via_player_name) { + + /** + * Find them in zone first + */ + const char *player_name = sep->arg[1]; + std::string player_name_string = sep->arg[1]; + Client *client = entity_list.GetClientByName(player_name); + if (client) { + c->MovePC( + zone->GetZoneID(), + zone->GetInstanceID(), + client->GetX(), + client->GetY(), + client->GetZ(), + client->GetHeading() + ); + + c->Message(Chat::Yellow, "Goto player '%s' same zone", player_name_string.c_str()); + } + else if (c->GotoPlayer(player_name_string)) { + c->Message(Chat::Yellow, "Goto player '%s' different zone", player_name_string.c_str()); + } + else { + c->Message(Chat::Yellow, "Player '%s' not found", player_name_string.c_str()); + } + } + else if (goto_via_x_y_z) { + c->MovePC( + zone->GetZoneID(), + zone->GetInstanceID(), + atof(sep->arg[1]), + atof(sep->arg[2]), + atof(sep->arg[3]), + (sep->arg[4] ? atof(sep->arg[4]) : c->GetHeading()) + ); + } + else { + c->Message(Chat::White, "Usage: #goto [x y z] [h]"); + c->Message(Chat::White, "Usage: #goto [player_name]"); + } +} + diff --git a/zone/gm_commands/grid.cpp b/zone/gm_commands/grid.cpp new file mode 100755 index 000000000..3ff62539c --- /dev/null +++ b/zone/gm_commands/grid.cpp @@ -0,0 +1,143 @@ +#include "../client.h" + +void command_grid(Client *c, const Seperator *sep) +{ + auto command_type = sep->arg[1]; + auto zone_id = zone->GetZoneID(); + if (strcasecmp("max", command_type) == 0) { + c->Message( + Chat::White, + fmt::format( + "Highest grid ID in this zone is {}.", + content_db.GetHighestGrid(zone_id) + ).c_str() + ); + } + else if (strcasecmp("add", command_type) == 0) { + auto grid_id = atoi(sep->arg[2]); + auto wander_type = atoi(sep->arg[3]); + auto pause_type = atoi(sep->arg[4]); + if (!content_db.GridExistsInZone(zone_id, grid_id)) { + content_db.ModifyGrid(c, false, grid_id, wander_type, pause_type, zone_id); + c->Message( + Chat::White, + fmt::format( + "Grid {} added to zone ID {} with wander type {} and pause type {}.", + grid_id, + zone_id, + wander_type, + pause_type + ).c_str() + ); + } + else { + c->Message( + Chat::White, + fmt::format( + "Grid {} already exists in zone ID {}.", + grid_id, + zone_id + ).c_str() + ); + return; + } + } + else if (strcasecmp("show", command_type) == 0) { + Mob *target = c->GetTarget(); + if (!target || !target->IsNPC()) { + c->Message(Chat::White, "You need to target an NPC!"); + return; + } + + auto grid_id = target->CastToNPC()->GetGrid(); + std::string query = fmt::format( + "SELECT `x`, `y`, `z`, `heading`, `number` " + "FROM `grid_entries` " + "WHERE `zoneid` = {} AND `gridid` = {} " + "ORDER BY `number`", + zone_id, + grid_id + ); + + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Error querying database."); + c->Message(Chat::White, query.c_str()); + } + + if (results.RowCount() == 0) { + c->Message(Chat::White, "No grid found."); + return; + } + + // Depop any node npc's already spawned + entity_list.DespawnGridNodes(grid_id); + + // Spawn grid nodes + std::map, int32> zoffset; + for (auto row : results) { + glm::vec4 node_position = glm::vec4(atof(row[0]), atof(row[1]), atof(row[2]), atof(row[3])); + std::vector node_loc{ + node_position.x, + node_position.y, + node_position.z + }; + + // If we already have a node at this location, set the z offset + // higher from the existing one so we can see it. Adjust so if + // there is another at the same spot we adjust again. + auto search = zoffset.find(node_loc); + if (search != zoffset.end()) { + search->second = search->second + 3; + } + else { + zoffset[node_loc] = 0.0; + } + + node_position.z += zoffset[node_loc]; + NPC::SpawnGridNodeNPC(node_position, grid_id, atoi(row[4]), zoffset[node_loc]); + } + c->Message( + Chat::White, + fmt::format( + "Spawning nodes for grid {}.", + grid_id + ).c_str() + ); + } + else if (strcasecmp("hide", command_type) == 0) { + Mob *target = c->GetTarget(); + if (!target || !target->IsNPC()) { + c->Message(Chat::White, "You need to target an NPC!"); + return; + } + + auto grid_id = target->CastToNPC()->GetGrid(); + entity_list.DespawnGridNodes(grid_id); + c->Message( + Chat::White, + fmt::format( + "Depawning nodes for grid {}.", + grid_id + ).c_str() + ); + } + else if (strcasecmp("delete", command_type) == 0) { + auto grid_id = atoi(sep->arg[2]); + content_db.ModifyGrid(c, true, grid_id, 0, 0, zone_id); + c->Message( + Chat::White, + fmt::format( + "Grid {} deleted from zone ID {}.", + grid_id, + zone_id + ).c_str() + ); + } + else { + c->Message(Chat::White, "Usage: #grid [add|delete] [grid_id] [wander_type] [pause_type]"); + c->Message(Chat::White, "Usage: #grid [max] - displays the highest grid ID used in this zone (for add)"); + c->Message(Chat::White, "Usage: #grid [show] - displays wp nodes as boxes"); + } +} + diff --git a/zone/gm_commands/guild.cpp b/zone/gm_commands/guild.cpp new file mode 100755 index 000000000..e76f83965 --- /dev/null +++ b/zone/gm_commands/guild.cpp @@ -0,0 +1,444 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +#include "../guild_mgr.h" +#include "../doors.h" + +void command_guild(Client *c, const Seperator *sep) +{ + int admin = c->Admin(); + Mob *target = c->GetTarget(); + + if (strcasecmp(sep->arg[1], "help") == 0) { + c->Message(Chat::White, "GM Guild commands:"); + c->Message(Chat::White, " #guild list - lists all guilds on the server"); + c->Message(Chat::White, " #guild create {guildleader charname or CharID} guildname"); + c->Message(Chat::White, " #guild delete guildID"); + c->Message(Chat::White, " #guild rename guildID newname"); + c->Message(Chat::White, " #guild set charname guildID (0=no guild)"); + c->Message(Chat::White, " #guild setrank charname rank"); + c->Message(Chat::White, " #guild setleader guildID {guildleader charname or CharID}"); + } + else if (strcasecmp(sep->arg[1], "status") == 0 || strcasecmp(sep->arg[1], "stat") == 0) { + Client *client = 0; + if (sep->arg[2][0] != 0) { + client = entity_list.GetClientByName(sep->argplus[2]); + } + else if (target != 0 && target->IsClient()) { + client = target->CastToClient(); + } + if (client == 0) { + c->Message(Chat::White, "You must target someone or specify a character name"); + } + else if ((client->Admin() >= minStatusToEditOtherGuilds && admin < minStatusToEditOtherGuilds) && + client->GuildID() != c->GuildID()) { // no peeping for GMs, make sure tell message stays the same + c->Message(Chat::White, "You must target someone or specify a character name."); + } + else { + if (client->IsInAGuild()) { + c->Message(Chat::White, "%s is not in a guild.", client->GetName()); + } + else if (guild_mgr.IsGuildLeader(client->GuildID(), client->CharacterID())) { + c->Message( + Chat::White, + "%s is the leader of <%s> rank: %s", + client->GetName(), + guild_mgr.GetGuildName(client->GuildID()), + guild_mgr.GetRankName(client->GuildID(), client->GuildRank())); + } + else { + c->Message( + Chat::White, + "%s is a member of <%s> rank: %s", + client->GetName(), + guild_mgr.GetGuildName(client->GuildID()), + guild_mgr.GetRankName(client->GuildID(), client->GuildRank())); + } + } + } + else if (strcasecmp(sep->arg[1], "info") == 0) { + if (sep->arg[2][0] == 0 && c->IsInAGuild()) { + if (admin >= minStatusToEditOtherGuilds) { + c->Message(Chat::White, "Usage: #guildinfo guild_id"); + } + else { + c->Message(Chat::White, "You're not in a guild"); + } + } + else { + uint32 tmp = GUILD_NONE; + if (sep->arg[2][0] == 0) { + tmp = c->GuildID(); + } + else if (admin >= minStatusToEditOtherGuilds) { + tmp = atoi(sep->arg[2]); + } + + if (tmp != GUILD_NONE) { + guild_mgr.DescribeGuild(c, tmp); + } + } + } + else if (strcasecmp(sep->arg[1], "set") == 0) { + if (!sep->IsNumber(3)) { + c->Message(Chat::White, "Usage: #guild set charname guildgbid (0 = clear guildtag)"); + } + else { + uint32 guild_id = atoi(sep->arg[3]); + + if (guild_id == 0) { + guild_id = GUILD_NONE; + } + else if (!guild_mgr.GuildExists(guild_id)) { + c->Message(Chat::Red, "Guild %d does not exist.", guild_id); + return; + } + + uint32 charid = database.GetCharacterID(sep->arg[2]); + if (charid == 0) { + c->Message(Chat::Red, "Unable to find character '%s'", charid); + return; + } + + //we could do the checking we need for guild_mgr.CheckGMStatus, but im lazy right now + if (admin < minStatusToEditOtherGuilds) { + c->Message(Chat::Red, "Access denied."); + return; + } + + if (guild_id == GUILD_NONE) { + LogGuilds("[{}]: Removing [{}] ([{}]) from guild with GM command", c->GetName(), sep->arg[2], charid); + } + else { + LogGuilds("[{}]: Putting [{}] ([{}]) into guild [{}] ([{}]) with GM command", + c->GetName(), + sep->arg[2], + charid, + guild_mgr.GetGuildName(guild_id), + guild_id); + } + + if (!guild_mgr.SetGuild(charid, guild_id, GUILD_MEMBER)) { + c->Message(Chat::Red, "Error putting '%s' into guild %d", sep->arg[2], guild_id); + } + else { + c->Message(Chat::White, "%s has been put into guild %d", sep->arg[2], guild_id); + } + } + } + /*else if (strcasecmp(sep->arg[1], "setdoor") == 0 && admin >= minStatusToEditOtherGuilds) { + + if (!sep->IsNumber(2)) + c->Message(Chat::White, "Usage: #guild setdoor guildEQid (0 = delete guilddoor)"); + else { + // guild doors + if((!guilds[atoi(sep->arg[2])].databaseID) && (atoi(sep->arg[2])!=0) ) + { + + c->Message(Chat::White, "These is no guild with this guildEQid"); + } + else { + c->SetIsSettingGuildDoor(true); + c->Message(Chat::White, "Click on a door you want to become a guilddoor"); + c->SetSetGuildDoorID(atoi(sep->arg[2])); + } + } + }*/ + else if (strcasecmp(sep->arg[1], "setrank") == 0) { + int rank = atoi(sep->arg[3]); + if (!sep->IsNumber(3)) { + c->Message(Chat::White, "Usage: #guild setrank charname rank"); + } + else if (rank < 0 || rank > GUILD_MAX_RANK) { + c->Message(Chat::White, "Error: invalid rank #."); + } + else { + uint32 charid = database.GetCharacterID(sep->arg[2]); + if (charid == 0) { + c->Message(Chat::Red, "Unable to find character '%s'", charid); + return; + } + + //we could do the checking we need for guild_mgr.CheckGMStatus, but im lazy right now + if (admin < minStatusToEditOtherGuilds) { + c->Message(Chat::Red, "Access denied."); + return; + } + + LogGuilds("[{}]: Setting [{}] ([{}])'s guild rank to [{}] with GM command", + c->GetName(), + sep->arg[2], + charid, + rank); + + if (!guild_mgr.SetGuildRank(charid, rank)) { + c->Message(Chat::Red, "Error while setting rank %d on '%s'.", rank, sep->arg[2]); + } + else { + c->Message(Chat::White, "%s has been set to rank %d", sep->arg[2], rank); + } + } + } + else if (strcasecmp(sep->arg[1], "create") == 0) { + if (sep->arg[3][0] == 0) { + c->Message(Chat::White, "Usage: #guild create {guildleader charname or CharID} guild name"); + } + else if (!worldserver.Connected()) { + c->Message(Chat::White, "Error: World server dirconnected"); + } + else { + uint32 leader = 0; + if (sep->IsNumber(2)) { + leader = atoi(sep->arg[2]); + } + else if ((leader = database.GetCharacterID(sep->arg[2])) != 0) { + //got it from the db.. + } + else { + c->Message(Chat::Red, "Unable to find char '%s'", sep->arg[2]); + return; + } + if (leader == 0) { + c->Message(Chat::White, "Guild leader not found."); + return; + } + + uint32 tmp = guild_mgr.FindGuildByLeader(leader); + if (tmp != GUILD_NONE) { + c->Message( + Chat::White, + "Error: %s already is the leader of DB# %i '%s'.", + sep->arg[2], + tmp, + guild_mgr.GetGuildName(tmp)); + } + else { + + if (admin < minStatusToEditOtherGuilds) { + c->Message(Chat::Red, "Access denied."); + return; + } + + uint32 id = guild_mgr.CreateGuild(sep->argplus[3], leader); + + LogGuilds("[{}]: Creating guild [{}] with leader [{}] with GM command. It was given id [{}]", + c->GetName(), + sep->argplus[3], + leader, + (unsigned long) id); + + if (id == GUILD_NONE) { + c->Message(Chat::White, "Guild creation failed."); + } + else { + c->Message(Chat::White, "Guild created: Leader: %i, number %i: %s", leader, id, sep->argplus[3]); + + if (!guild_mgr.SetGuild(leader, id, GUILD_LEADER)) { + c->Message( + Chat::White, + "Unable to set guild leader's guild in the database. Your going to have to run #guild set" + ); + } + } + + } + } + } + else if (strcasecmp(sep->arg[1], "delete") == 0) { + if (!sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #guild delete guildID"); + } + else if (!worldserver.Connected()) { + c->Message(Chat::White, "Error: World server dirconnected"); + } + else { + uint32 id = atoi(sep->arg[2]); + + if (!guild_mgr.GuildExists(id)) { + c->Message(Chat::White, "Guild %d does not exist!", id); + return; + } + + if (admin < minStatusToEditOtherGuilds) { + //this person is not allowed to just edit any guild, check this guild's min status. + if (c->GuildID() != id) { + c->Message(Chat::Red, "Access denied to edit other people's guilds"); + return; + } + else if (!guild_mgr.CheckGMStatus(id, admin)) { + c->Message(Chat::Red, "Access denied to edit your guild with GM commands."); + return; + } + } + + LogGuilds("[{}]: Deleting guild [{}] ([{}]) with GM command", c->GetName(), + guild_mgr.GetGuildName(id), id); + + if (!guild_mgr.DeleteGuild(id)) { + c->Message(Chat::White, "Guild delete failed."); + } + else { + c->Message(Chat::White, "Guild %d deleted.", id); + } + } + } + else if (strcasecmp(sep->arg[1], "rename") == 0) { + if ((!sep->IsNumber(2)) || sep->arg[3][0] == 0) { + c->Message(Chat::White, "Usage: #guild rename guildID newname"); + } + else if (!worldserver.Connected()) { + c->Message(Chat::White, "Error: World server dirconnected"); + } + else { + uint32 id = atoi(sep->arg[2]); + + if (!guild_mgr.GuildExists(id)) { + c->Message(Chat::White, "Guild %d does not exist!", id); + return; + } + + if (admin < minStatusToEditOtherGuilds) { + //this person is not allowed to just edit any guild, check this guild's min status. + if (c->GuildID() != id) { + c->Message(Chat::Red, "Access denied to edit other people's guilds"); + return; + } + else if (!guild_mgr.CheckGMStatus(id, admin)) { + c->Message(Chat::Red, "Access denied to edit your guild with GM commands."); + return; + } + } + + LogGuilds("[{}]: Renaming guild [{}] ([{}]) to [{}] with GM command", c->GetName(), + guild_mgr.GetGuildName(id), id, sep->argplus[3]); + + if (!guild_mgr.RenameGuild(id, sep->argplus[3])) { + c->Message(Chat::White, "Guild rename failed."); + } + else { + c->Message(Chat::White, "Guild %d renamed to %s", id, sep->argplus[3]); + } + } + } + else if (strcasecmp(sep->arg[1], "setleader") == 0) { + if (sep->arg[3][0] == 0 || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #guild setleader guild_id {guildleader charname or CharID}"); + } + else if (!worldserver.Connected()) { + c->Message(Chat::White, "Error: World server dirconnected"); + } + else { + uint32 leader = 0; + if (sep->IsNumber(2)) { + leader = atoi(sep->arg[2]); + } + else if ((leader = database.GetCharacterID(sep->arg[2])) != 0) { + //got it from the db.. + } + else { + c->Message(Chat::Red, "Unable to find char '%s'", sep->arg[2]); + return; + } + + uint32 tmpdb = guild_mgr.FindGuildByLeader(leader); + if (leader == 0) { + c->Message(Chat::White, "New leader not found."); + } + else if (tmpdb != 0) { + c->Message(Chat::White, "Error: %s already is the leader of guild # %i", sep->arg[2], tmpdb); + } + else { + uint32 id = atoi(sep->arg[2]); + + if (!guild_mgr.GuildExists(id)) { + c->Message(Chat::White, "Guild %d does not exist!", id); + return; + } + + if (admin < minStatusToEditOtherGuilds) { + //this person is not allowed to just edit any guild, check this guild's min status. + if (c->GuildID() != id) { + c->Message(Chat::Red, "Access denied to edit other people's guilds"); + return; + } + else if (!guild_mgr.CheckGMStatus(id, admin)) { + c->Message(Chat::Red, "Access denied to edit your guild with GM commands."); + return; + } + } + + LogGuilds("[{}]: Setting leader of guild [{}] ([{}]) to [{}] with GM command", c->GetName(), + guild_mgr.GetGuildName(id), id, leader); + + if (!guild_mgr.SetGuildLeader(id, leader)) { + c->Message(Chat::White, "Guild leader change failed."); + } + else { + c->Message(Chat::White, "Guild leader changed: guild # %d, Leader: %s", id, sep->argplus[3]); + } + } + } + } + else if (strcasecmp(sep->arg[1], "list") == 0) { + if (admin < minStatusToEditOtherGuilds) { + c->Message(Chat::Red, "Access denied."); + return; + } + guild_mgr.ListGuilds(c); + } + else { + c->Message(Chat::White, "Unknown guild command, try #guild help"); + } +} +/* +bool helper_guild_edit(Client *c, uint32 dbid, uint32 eqid, uint8 rank, const char* what, const char* value) { + struct GuildRankLevel_Struct grl; + strcpy(grl.rankname, guild_mgr.GetRankName(eqid, rank)); + grl.demote = guilds[eqid].rank[rank].demote; + grl.heargu = guilds[eqid].rank[rank].heargu; + grl.invite = guilds[eqid].rank[rank].invite; + grl.motd = guilds[eqid].rank[rank].motd; + grl.promote = guilds[eqid].rank[rank].promote; + grl.remove = guilds[eqid].rank[rank].remove; + grl.speakgu = guilds[eqid].rank[rank].speakgu; + grl.warpeace = guilds[eqid].rank[rank].warpeace; + + if (strcasecmp(what, "title") == 0) { + if (strlen(value) > 100) + c->Message(Chat::White, "Error: Title has a maxium length of 100 characters."); + else + strcpy(grl.rankname, value); + } + else if (rank == 0) + c->Message(Chat::White, "Error: Rank 0's permissions can not be changed."); + else { + if (!(strlen(value) == 1 && (value[0] == '0' || value[0] == '1'))) + + return false; + if (strcasecmp(what, "demote") == 0) + grl.demote = (value[0] == '1'); + else if (strcasecmp(what, "heargu") == 0) + grl.heargu = (value[0] == '1'); + else if (strcasecmp(what, "invite") == 0) + grl.invite = (value[0] == '1'); + else if (strcasecmp(what, "motd") == 0) + grl.motd = (value[0] == '1'); + else if (strcasecmp(what, "promote") == 0) + grl.promote = (value[0] == '1'); + else if (strcasecmp(what, "remove") == 0) + + grl.remove = (value[0] == '1'); + else if (strcasecmp(what, "speakgu") == 0) + grl.speakgu = (value[0] == '1'); + else if (strcasecmp(what, "warpeace") == 0) + grl.warpeace = (value[0] == '1'); + else + c->Message(Chat::White, "Error: Permission name not recognized."); + } + if (!database.EditGuild(dbid, rank, &grl)) + c->Message(Chat::White, "Error: database.EditGuild() failed"); + return true; +}*/ + diff --git a/zone/gm_commands/guildapprove.cpp b/zone/gm_commands/guildapprove.cpp new file mode 100755 index 000000000..5c7805224 --- /dev/null +++ b/zone/gm_commands/guildapprove.cpp @@ -0,0 +1,8 @@ +#include "../client.h" +#include "../guild_mgr.h" + +void command_guildapprove(Client *c, const Seperator *sep) +{ + guild_mgr.AddMemberApproval(atoi(sep->arg[1]), c); +} + diff --git a/zone/gm_commands/guildcreate.cpp b/zone/gm_commands/guildcreate.cpp new file mode 100755 index 000000000..48e90d3ef --- /dev/null +++ b/zone/gm_commands/guildcreate.cpp @@ -0,0 +1,13 @@ +#include "../client.h" +#include "../guild_mgr.h" + +void command_guildcreate(Client *c, const Seperator *sep) +{ + if (strlen(sep->argplus[1]) > 4 && strlen(sep->argplus[1]) < 16) { + guild_mgr.AddGuildApproval(sep->argplus[1], c); + } + else { + c->Message(Chat::White, "Guild name must be more than 4 characters and less than 16."); + } +} + diff --git a/zone/gm_commands/guildlist.cpp b/zone/gm_commands/guildlist.cpp new file mode 100755 index 000000000..3ec40ef02 --- /dev/null +++ b/zone/gm_commands/guildlist.cpp @@ -0,0 +1,14 @@ +#include "../client.h" +#include "../guild_mgr.h" + +void command_guildlist(Client *c, const Seperator *sep) +{ + GuildApproval *tmp = guild_mgr.FindGuildByIDApproval(atoi(sep->arg[1])); + if (tmp) { + tmp->ApprovedMembers(c); + } + else { + c->Message(Chat::White, "Could not find reference id."); + } +} + diff --git a/zone/gm_commands/hair.cpp b/zone/gm_commands/hair.cpp new file mode 100755 index 000000000..56f80aaed --- /dev/null +++ b/zone/gm_commands/hair.cpp @@ -0,0 +1,37 @@ +#include "../client.h" + +void command_hair(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #hair [number of hair style]"); + } + 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 = atoi(sep->arg[1]); + 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 + ); + + c->Message(Chat::White, "Hair = %i", atoi(sep->arg[1])); + } +} + diff --git a/zone/gm_commands/haircolor.cpp b/zone/gm_commands/haircolor.cpp new file mode 100755 index 000000000..2767808ac --- /dev/null +++ b/zone/gm_commands/haircolor.cpp @@ -0,0 +1,37 @@ +#include "../client.h" + +void command_haircolor(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #haircolor [number of hair color]"); + } + 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 = atoi(sep->arg[1]); + 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 + ); + + c->Message(Chat::White, "Hair Color = %i", atoi(sep->arg[1])); + } +} + diff --git a/zone/gm_commands/haste.cpp b/zone/gm_commands/haste.cpp new file mode 100755 index 000000000..bf5be4d26 --- /dev/null +++ b/zone/gm_commands/haste.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +void command_haste(Client *c, const Seperator *sep) +{ + // #haste command to set client attack speed. Takes a percentage (100 = twice normal attack speed) + if (sep->arg[1][0] != 0) { + uint16 Haste = atoi(sep->arg[1]); + if (Haste > 85) { + Haste = 85; + } + c->SetExtraHaste(Haste); + // SetAttackTimer must be called to make this take effect, so player needs to change + // the primary weapon. + c->Message(Chat::White, "Haste set to %d%% - Need to re-equip primary weapon before it takes effect", Haste); + } + else { + c->Message(Chat::White, "Usage: #haste [percentage]"); + } +} + diff --git a/zone/gm_commands/hatelist.cpp b/zone/gm_commands/hatelist.cpp new file mode 100755 index 000000000..13006d47b --- /dev/null +++ b/zone/gm_commands/hatelist.cpp @@ -0,0 +1,15 @@ +#include "../client.h" + +void command_hatelist(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (target == nullptr) { + c->Message(Chat::White, "Error: you must have a target."); + return; + } + + c->Message(Chat::White, "Display hate list for %s..", target->GetName()); + target->PrintHateListToClient(c); +} + + diff --git a/zone/gm_commands/heal.cpp b/zone/gm_commands/heal.cpp new file mode 100755 index 000000000..f309b30e3 --- /dev/null +++ b/zone/gm_commands/heal.cpp @@ -0,0 +1,21 @@ +#include "../client.h" + +void command_heal(Client *c, const Seperator *sep) +{ + auto target = c->GetTarget() ? c->GetTarget() : c; + target->Heal(); + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "Healed {} ({}) to full.", + target->GetCleanName(), + target->GetID() + ).c_str() + ); + } + else { + c->Message(Chat::White, "Healed yourself to full."); + } +} + diff --git a/zone/gm_commands/helm.cpp b/zone/gm_commands/helm.cpp new file mode 100755 index 000000000..8d0a36407 --- /dev/null +++ b/zone/gm_commands/helm.cpp @@ -0,0 +1,37 @@ +#include "../client.h" + +void command_helm(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #helm [number of helm texture]"); + } + 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 = atoi(sep->arg[1]); + 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 + ); + + c->Message(Chat::White, "Helm = %i", atoi(sep->arg[1])); + } +} + diff --git a/zone/gm_commands/heritage.cpp b/zone/gm_commands/heritage.cpp new file mode 100755 index 000000000..0f4e1d9e9 --- /dev/null +++ b/zone/gm_commands/heritage.cpp @@ -0,0 +1,37 @@ +#include "../client.h" + +void command_heritage(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #heritage [number of Drakkin heritage]"); + } + 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 = atoi(sep->arg[1]); + 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 + ); + + c->Message(Chat::White, "Heritage = %i", atoi(sep->arg[1])); + } +} + diff --git a/zone/gm_commands/heromodel.cpp b/zone/gm_commands/heromodel.cpp new file mode 100755 index 000000000..33ea72c2c --- /dev/null +++ b/zone/gm_commands/heromodel.cpp @@ -0,0 +1,35 @@ +#include "../client.h" + +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."); + } + } + } +} + diff --git a/zone/gm_commands/hideme.cpp b/zone/gm_commands/hideme.cpp new file mode 100755 index 000000000..d936a7fdc --- /dev/null +++ b/zone/gm_commands/hideme.cpp @@ -0,0 +1,16 @@ +#include "../client.h" +#include "../string_ids.h" + +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()); + } +} + diff --git a/zone/gm_commands/hp.cpp b/zone/gm_commands/hp.cpp new file mode 100755 index 000000000..178ef9890 --- /dev/null +++ b/zone/gm_commands/hp.cpp @@ -0,0 +1,8 @@ +#include "../client.h" + +void command_hp(Client *c, const Seperator *sep) +{ + c->SendHPUpdate(); + c->CheckManaEndUpdate(); +} + diff --git a/zone/gm_commands/incstat.cpp b/zone/gm_commands/incstat.cpp new file mode 100755 index 000000000..6a09c1785 --- /dev/null +++ b/zone/gm_commands/incstat.cpp @@ -0,0 +1,18 @@ +#include "../client.h" + +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"); + } +} + diff --git a/zone/gm_commands/instance.cpp b/zone/gm_commands/instance.cpp new file mode 100755 index 000000000..27c1171d7 --- /dev/null +++ b/zone/gm_commands/instance.cpp @@ -0,0 +1,188 @@ +#include "../client.h" + +void command_instance(Client *c, const Seperator *sep) +{ + if (!c) { + return; + } + + //options: + //help + //create [zone_id] [version] + //destroy [instance_id] + //add [instance_id] [player_name] + //remove [instance_id] [player_name] + //list [player_name] + + if (strcasecmp(sep->arg[1], "help") == 0) { + c->Message(Chat::White, "#instance usage:"); + c->Message( + Chat::White, "#instance create zone_id version duration - Creates an instance of version 'version' in the " + "zone with id matching zone_id, will last for duration seconds." + ); + c->Message(Chat::White, "#instance destroy instance_id - Destroys the instance with id matching instance_id."); + c->Message( + Chat::White, "#instance add instance_id player_name - adds the player 'player_name' to the instance " + "with id matching instance_id." + ); + c->Message( + Chat::White, "#instance remove instance_id player_name - removes the player 'player_name' from the " + "instance with id matching instance_id." + ); + c->Message(Chat::White, "#instance list player_name - lists all the instances 'player_name' is apart of."); + return; + } + else if (strcasecmp(sep->arg[1], "create") == 0) { + if (!sep->IsNumber(3) || !sep->IsNumber(4)) { + c->Message( + Chat::White, + "#instance create zone_id version duration - Creates an instance of version 'version' in the " + "zone with id matching zone_id, will last for duration seconds." + ); + return; + } + + const char *zn = nullptr; + uint32 zone_id = 0; + + if (sep->IsNumber(2)) { + zone_id = atoi(sep->arg[2]); + } + else { + zone_id = ZoneID(sep->arg[2]); + } + + uint32 version = atoi(sep->arg[3]); + uint32 duration = atoi(sep->arg[4]); + zn = ZoneName(zone_id); + + if (!zn) { + c->Message(Chat::White, "Zone with id %lu was not found by the server.", (unsigned long) zone_id); + return; + } + + uint16 id = 0; + if (!database.GetUnusedInstanceID(id)) { + c->Message(Chat::White, "Server was unable to find a free instance id."); + return; + } + + if (!database.CreateInstance(id, zone_id, version, duration)) { + c->Message(Chat::White, "Server was unable to create a new instance."); + return; + } + + c->Message(Chat::White, "New instance %s was created with id %lu.", zn, (unsigned long) id); + } + else if (strcasecmp(sep->arg[1], "destroy") == 0) { + if (!sep->IsNumber(2)) { + c->Message( + Chat::White, + "#instance destroy instance_id - Destroys the instance with id matching instance_id." + ); + return; + } + + uint16 id = atoi(sep->arg[2]); + database.DeleteInstance(id); + c->Message(Chat::White, "Destroyed instance with id %lu.", (unsigned long) id); + } + else if (strcasecmp(sep->arg[1], "add") == 0) { + if (!sep->IsNumber(2)) { + c->Message( + Chat::White, "#instance add instance_id player_name - adds the player 'player_name' to the instance " + "with id matching instance_id." + ); + return; + } + + uint16 id = atoi(sep->arg[2]); + uint32 charid = database.GetCharacterID(sep->arg[3]); + + if (id <= 0 || charid <= 0) { + c->Message(Chat::White, "Must enter a valid instance id and player name."); + return; + } + + if (!database.CheckInstanceExists(id)) { + c->Message(Chat::White, "Instance does not exist."); + return; + } + + uint32 zone_id = database.ZoneIDFromInstanceID(id); + uint32 version = database.VersionFromInstanceID(id); + uint32 cur_id = database.GetInstanceID(zone_id, charid, version); + if (cur_id == 0) { + if (database.AddClientToInstance(id, charid)) { + c->Message(Chat::White, "Added client to instance."); + } + else { + c->Message(Chat::White, "Failed to add client to instance."); + } + } + else { + c->Message( + Chat::White, + "Client was already saved to %u which has uses the same zone and version as that instance.", + cur_id + ); + } + } + else if (strcasecmp(sep->arg[1], "remove") == 0) { + if (!sep->IsNumber(2)) { + c->Message( + Chat::White, "#instance remove instance_id player_name - removes the player 'player_name' from the " + "instance with id matching instance_id." + ); + return; + } + + uint16 id = atoi(sep->arg[2]); + uint32 charid = database.GetCharacterID(sep->arg[3]); + + if (id <= 0 || charid <= 0) { + c->Message(Chat::White, "Must enter a valid instance id and player name."); + } + + if (database.RemoveClientFromInstance(id, charid)) { + c->Message(Chat::White, "Removed client from instance."); + } + else { + c->Message(Chat::White, "Failed to remove client from instance."); + } + } + else if (strcasecmp(sep->arg[1], "list") == 0) { + uint32 charid = database.GetCharacterID(sep->arg[2]); + if (charid <= 0) { + if (c->GetTarget() == nullptr || (c->GetTarget() && !c->GetTarget()->IsClient())) { + c->Message(Chat::White, "Character not found."); + return; + } + else { + charid = c->GetTarget()->CastToClient()->CharacterID(); + } + } + + database.ListAllInstances(c, charid); + } + else { + c->Message(Chat::White, "Invalid Argument."); + c->Message(Chat::White, "#instance usage:"); + c->Message( + Chat::White, "#instance create zone_id version duration - Creates an instance of version 'version' in the " + "zone with id matching zone_id, will last for duration seconds." + ); + c->Message(Chat::White, "#instance destroy instance_id - Destroys the instance with id matching instance_id."); + c->Message( + Chat::White, "#instance add instance_id player_name - adds the player 'player_name' to the instance " + "with id matching instance_id." + ); + c->Message( + Chat::White, "#instance remove instance_id player_name - removes the player 'player_name' from the " + "instance with id matching instance_id." + ); + c->Message(Chat::White, "#instance list player_name - lists all the instances 'player_name' is apart of."); + return; + } +} + diff --git a/zone/gm_commands/interrogateinv.cpp b/zone/gm_commands/interrogateinv.cpp new file mode 100755 index 000000000..58451e05c --- /dev/null +++ b/zone/gm_commands/interrogateinv.cpp @@ -0,0 +1,76 @@ +#include "../client.h" + +void command_interrogateinv(Client *c, const Seperator *sep) +{ + // 'command_interrogateinv' is an in-memory inventory interrogation tool only. + // + // it does not verify against actual database entries..but, the output can be + // used to verify that something has been corrupted in a player's inventory. + // any error condition should be assumed that the item in question will be + // lost when the player logs out or zones (or incurrs any action that will + // consume the Client-Inventory object instance in question.) + // + // any item instances located at a greater depth than a reported error should + // be treated as an error themselves regardless of whether they report as the + // same or not. + + if (strcasecmp(sep->arg[1], "help") == 0) { + if (c->Admin() < commandInterrogateInv) { + c->Message(Chat::White, "Usage: #interrogateinv"); + c->Message(Chat::White, " Displays your inventory's current in-memory nested storage references"); + } + else { + c->Message(Chat::White, "Usage: #interrogateinv [log] [silent]"); + c->Message( + Chat::White, + " Displays your or your Player target inventory's current in-memory nested storage references" + ); + c->Message(Chat::White, " [log] - Logs interrogation to file"); + c->Message(Chat::White, " [silent] - Omits the in-game message portion of the interrogation"); + } + return; + } + + Client *target = nullptr; + std::map instmap; + bool log = false; + bool silent = false; + bool error = false; + bool allowtrip = false; + + if (c->Admin() < commandInterrogateInv) { + if (c->GetInterrogateInvState()) { + c->Message(Chat::Red, "The last use of #interrogateinv on this inventory instance discovered an error..."); + c->Message(Chat::Red, "Logging out, zoning or re-arranging items at this point will result in item loss!"); + return; + } + target = c; + allowtrip = true; + } + else { + if (c->GetTarget() == nullptr) { + target = c; + } + else if (c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + else { + c->Message(Chat::Default, "Use of this command is limited to Client entities"); + return; + } + + if (strcasecmp(sep->arg[1], "log") == 0) { + log = true; + } + if (strcasecmp(sep->arg[2], "silent") == 0) { + silent = true; + } + } + + bool success = target->InterrogateInventory(c, log, silent, allowtrip, error); + + if (!success) { + c->Message(Chat::Red, "An unknown error occurred while processing Client::InterrogateInventory()"); + } +} + diff --git a/zone/gm_commands/interrupt.cpp b/zone/gm_commands/interrupt.cpp new file mode 100755 index 000000000..1126a850c --- /dev/null +++ b/zone/gm_commands/interrupt.cpp @@ -0,0 +1,16 @@ +#include "../client.h" + +void command_interrupt(Client *c, const Seperator *sep) +{ + uint16 ci_message = 0x01b7, ci_color = 0x0121; + + if (sep->arg[1][0]) { + ci_message = atoi(sep->arg[1]); + } + if (sep->arg[2][0]) { + ci_color = atoi(sep->arg[2]); + } + + c->InterruptSpell(ci_message, ci_color); +} + diff --git a/zone/gm_commands/invsnapshot.cpp b/zone/gm_commands/invsnapshot.cpp new file mode 100755 index 000000000..6e818524a --- /dev/null +++ b/zone/gm_commands/invsnapshot.cpp @@ -0,0 +1,418 @@ +#include "../client.h" + +void command_invsnapshot(Client *c, const Seperator *sep) +{ + if (!c) { + return; + } + + if (sep->argnum == 0 || strcmp(sep->arg[1], "help") == 0) { + std::string window_title = "Inventory Snapshot Argument Help Menu"; + + std::string window_text = + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ""; + + if (c->Admin() >= commandInvSnapshot) { + window_text.append( + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ); + } + + window_text.append( + "
Usage:#invsnapshot arguments
(required optional)
helpthis menu
capturetakes snapshot of character inventory
gcountreturns global snapshot count
gclear
now
delete all snapshots - rule
delete all snapshots - now
countreturns character snapshot count
clear
now
delete character snapshots - rule
delete character snapshots - now
list
count
lists entry ids for current character
limits to count
parsetstmpdisplays slots and items in snapshot
comparetstmpcompares inventory against snapshot
restoretstmprestores slots and items in snapshot
" + ); + + c->SendPopupToClient(window_title.c_str(), window_text.c_str()); + + return; + } + + if (c->Admin() >= commandInvSnapshot) { // global arguments + + if (strcmp(sep->arg[1], "gcount") == 0) { + auto is_count = database.CountInvSnapshots(); + c->Message( + Chat::White, + "There %s %i inventory snapshot%s.", + (is_count == 1 ? "is" : "are"), + is_count, + (is_count == 1 ? "" : "s")); + + return; + } + + if (strcmp(sep->arg[1], "gclear") == 0) { + if (strcmp(sep->arg[2], "now") == 0) { + database.ClearInvSnapshots(true); + c->Message(Chat::White, "Inventory snapshots cleared using current time."); + } + else { + database.ClearInvSnapshots(); + c->Message( + Chat::White, "Inventory snapshots cleared using RuleI(Character, InvSnapshotHistoryD) (%i day%s).", + RuleI(Character, InvSnapshotHistoryD), (RuleI(Character, InvSnapshotHistoryD) == 1 ? "" : "s")); + } + + return; + } + } + + if (!c->GetTarget() || !c->GetTarget()->IsClient()) { + c->Message(Chat::White, "Target must be a client."); + return; + } + + auto tc = (Client *) c->GetTarget(); + + if (strcmp(sep->arg[1], "capture") == 0) { + if (database.SaveCharacterInvSnapshot(tc->CharacterID())) { + tc->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinIntervalM)); + c->Message( + Chat::White, + "Successful inventory snapshot taken of %s - setting next interval for %i minute%s.", + tc->GetName(), + RuleI(Character, InvSnapshotMinIntervalM), + (RuleI(Character, InvSnapshotMinIntervalM) == 1 ? "" : "s")); + } + else { + tc->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinRetryM)); + c->Message( + Chat::White, + "Failed to take inventory snapshot of %s - retrying in %i minute%s.", + tc->GetName(), + RuleI(Character, InvSnapshotMinRetryM), + (RuleI(Character, InvSnapshotMinRetryM) == 1 ? "" : "s")); + } + + return; + } + + if (c->Admin() >= commandInvSnapshot) { + if (strcmp(sep->arg[1], "count") == 0) { + auto is_count = database.CountCharacterInvSnapshots(tc->CharacterID()); + c->Message( + Chat::White, + "%s (id: %u) has %i inventory snapshot%s.", + tc->GetName(), + tc->CharacterID(), + is_count, + (is_count == 1 ? "" : "s")); + + return; + } + + if (strcmp(sep->arg[1], "clear") == 0) { + if (strcmp(sep->arg[2], "now") == 0) { + database.ClearCharacterInvSnapshots(tc->CharacterID(), true); + c->Message( + Chat::White, + "%s\'s (id: %u) inventory snapshots cleared using current time.", + tc->GetName(), + tc->CharacterID()); + } + else { + database.ClearCharacterInvSnapshots(tc->CharacterID()); + c->Message( + Chat::White, + "%s\'s (id: %u) inventory snapshots cleared using RuleI(Character, InvSnapshotHistoryD) (%i day%s).", + tc->GetName(), + tc->CharacterID(), + RuleI(Character, InvSnapshotHistoryD), + (RuleI(Character, InvSnapshotHistoryD) == 1 ? "" : "s")); + } + + return; + } + + if (strcmp(sep->arg[1], "list") == 0) { + std::list> is_list; + database.ListCharacterInvSnapshots(tc->CharacterID(), is_list); + + if (is_list.empty()) { + c->Message(Chat::White, "No inventory snapshots for %s (id: %u)", tc->GetName(), tc->CharacterID()); + return; + } + + auto list_count = 0; + if (sep->IsNumber(2)) { + list_count = atoi(sep->arg[2]); + } + if (list_count < 1 || list_count > is_list.size()) { + list_count = is_list.size(); + } + + std::string window_title = StringFormat("Snapshots for %s", tc->GetName()); + + std::string window_text = + "" + "" + "" + "" + ""; + + for (auto iter : is_list) { + if (!list_count) { + break; + } + + window_text.append( + StringFormat( + "" + "" + "" + "", + iter.first, + iter.second + )); + + --list_count; + } + + window_text.append( + "
TimestampEntry Count
%u%i
" + ); + + c->SendPopupToClient(window_title.c_str(), window_text.c_str()); + + return; + } + + if (strcmp(sep->arg[1], "parse") == 0) { + if (!sep->IsNumber(2)) { + c->Message(Chat::White, "A timestamp is required to use this option."); + return; + } + + uint32 timestamp = atoul(sep->arg[2]); + + if (!database.ValidateCharacterInvSnapshotTimestamp(tc->CharacterID(), timestamp)) { + c->Message( + Chat::White, + "No inventory snapshots for %s (id: %u) exist at %u.", + tc->GetName(), + tc->CharacterID(), + timestamp + ); + return; + } + + std::list> parse_list; + database.ParseCharacterInvSnapshot(tc->CharacterID(), timestamp, parse_list); + + std::string window_title = StringFormat("Snapshot Parse for %s @ %u", tc->GetName(), timestamp); + + std::string window_text = "Slot: ItemID - Description
"; + + for (auto iter : parse_list) { + auto item_data = database.GetItem(iter.second); + std::string window_line = StringFormat( + "%i: %u - %s
", + iter.first, + iter.second, + (item_data ? item_data->Name : "[error]")); + + if (window_text.length() + window_line.length() < 4095) { + window_text.append(window_line); + } + else { + c->Message(Chat::White, "Too many snapshot entries to list..."); + break; + } + } + + c->SendPopupToClient(window_title.c_str(), window_text.c_str()); + + return; + } + + if (strcmp(sep->arg[1], "compare") == 0) { + if (!sep->IsNumber(2)) { + c->Message(Chat::White, "A timestamp is required to use this option."); + return; + } + + uint32 timestamp = atoul(sep->arg[2]); + + if (!database.ValidateCharacterInvSnapshotTimestamp(tc->CharacterID(), timestamp)) { + c->Message( + Chat::White, + "No inventory snapshots for %s (id: %u) exist at %u.", + tc->GetName(), + tc->CharacterID(), + timestamp + ); + return; + } + + std::list> inv_compare_list; + database.DivergeCharacterInventoryFromInvSnapshot(tc->CharacterID(), timestamp, inv_compare_list); + + std::list> iss_compare_list; + database.DivergeCharacterInvSnapshotFromInventory(tc->CharacterID(), timestamp, iss_compare_list); + + std::string window_title = StringFormat("Snapshot Comparison for %s @ %u", tc->GetName(), timestamp); + + std::string window_text = "Slot: (action) Snapshot -> Inventory
"; + + auto inv_iter = inv_compare_list.begin(); + auto iss_iter = iss_compare_list.begin(); + + while (true) { + std::string window_line; + + if (inv_iter == inv_compare_list.end() && iss_iter == iss_compare_list.end()) { + break; + } + else if (inv_iter != inv_compare_list.end() && iss_iter == iss_compare_list.end()) { + window_line = StringFormat("%i: (delete) [empty] -> %u
", inv_iter->first, inv_iter->second); + ++inv_iter; + } + else if (inv_iter == inv_compare_list.end() && iss_iter != iss_compare_list.end()) { + window_line = StringFormat("%i: (insert) %u -> [empty]
", iss_iter->first, iss_iter->second); + ++iss_iter; + } + else { + if (inv_iter->first < iss_iter->first) { + window_line = StringFormat( + "%i: (delete) [empty] -> %u
", + inv_iter->first, + inv_iter->second + ); + ++inv_iter; + } + else if (inv_iter->first > iss_iter->first) { + window_line = StringFormat( + "%i: (insert) %u -> [empty]
", + iss_iter->first, + iss_iter->second + ); + ++iss_iter; + } + else { + window_line = StringFormat( + "%i: (replace) %u -> %u
", + iss_iter->first, + iss_iter->second, + inv_iter->second + ); + ++inv_iter; + ++iss_iter; + } + } + + if (window_text.length() + window_line.length() < 4095) { + window_text.append(window_line); + } + else { + c->Message(Chat::White, "Too many comparison entries to list..."); + break; + } + } + + c->SendPopupToClient(window_title.c_str(), window_text.c_str()); + + return; + } + + if (strcmp(sep->arg[1], "restore") == 0) { + if (!sep->IsNumber(2)) { + c->Message(Chat::White, "A timestamp is required to use this option."); + return; + } + + uint32 timestamp = atoul(sep->arg[2]); + + if (!database.ValidateCharacterInvSnapshotTimestamp(tc->CharacterID(), timestamp)) { + c->Message( + Chat::White, + "No inventory snapshots for %s (id: %u) exist at %u.", + tc->GetName(), + tc->CharacterID(), + timestamp + ); + return; + } + + if (database.SaveCharacterInvSnapshot(tc->CharacterID())) { + tc->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinIntervalM)); + } + else { + c->Message( + Chat::Red, "Failed to take pre-restore inventory snapshot of %s (id: %u).", + tc->GetName(), tc->CharacterID()); + return; + } + + if (database.RestoreCharacterInvSnapshot(tc->CharacterID(), timestamp)) { + // cannot delete all valid item slots from client..so, we worldkick + tc->WorldKick(); // self restores update before the 'kick' is processed + + c->Message( + Chat::White, "Successfully applied snapshot %u to %s's (id: %u) inventory.", + timestamp, tc->GetName(), tc->CharacterID()); + } + else { + c->Message( + Chat::Red, "Failed to apply snapshot %u to %s's (id: %u) inventory.", + timestamp, tc->GetName(), tc->CharacterID()); + } + + return; + } + } +} + diff --git a/zone/gm_commands/invul.cpp b/zone/gm_commands/invul.cpp new file mode 100755 index 000000000..4af42ceaf --- /dev/null +++ b/zone/gm_commands/invul.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +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]"); + } +} + diff --git a/zone/gm_commands/ipban.cpp b/zone/gm_commands/ipban.cpp new file mode 100755 index 000000000..5dd63a0fb --- /dev/null +++ b/zone/gm_commands/ipban.cpp @@ -0,0 +1,21 @@ +#include "../client.h" + +void command_ipban(Client *c, const Seperator *sep) +{ + if (sep->arg[1] == 0) { + c->Message(Chat::White, "Usage: #ipban [xxx.xxx.xxx.xxx]"); + } + else { + if (database.AddBannedIP(sep->arg[1], c->GetName())) { + c->Message( + Chat::White, + "%s has been successfully added to the banned_ips table by %s", + sep->arg[1], + c->GetName()); + } + else { + c->Message(Chat::White, "IPBan Failed (IP address is possibly already in the table?)"); + } + } +} + diff --git a/zone/gm_commands/iplookup.cpp b/zone/gm_commands/iplookup.cpp new file mode 100755 index 000000000..1595a5ee6 --- /dev/null +++ b/zone/gm_commands/iplookup.cpp @@ -0,0 +1,23 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +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); +} + diff --git a/zone/gm_commands/iteminfo.cpp b/zone/gm_commands/iteminfo.cpp new file mode 100755 index 000000000..683c23075 --- /dev/null +++ b/zone/gm_commands/iteminfo.cpp @@ -0,0 +1,94 @@ +#include "../client.h" +#include "../groups.h" + +void command_iteminfo(Client *c, const Seperator *sep) +{ + auto inst = c->GetInv()[EQ::invslot::slotCursor]; + if (!inst) { + c->Message(Chat::Red, "Error: You need an item on your cursor for this command"); + return; + } + auto item = inst->GetItem(); + if (!item) { + LogInventory("([{}]) Command #iteminfo processed an item with no data pointer"); + c->Message(Chat::Red, "Error: This item has no data reference"); + return; + } + + EQ::SayLinkEngine linker; + linker.SetLinkType(EQ::saylink::SayLinkItemInst); + linker.SetItemInst(inst); + + c->Message(Chat::White, "*** Item Info for [%s] ***", linker.GenerateLink().c_str()); + c->Message(Chat::White, ">> ID: %u, ItemUseType: %u, ItemClassType: %u", item->ID, item->ItemType, item->ItemClass); + c->Message(Chat::White, ">> IDFile: '%s', IconID: %u", item->IDFile, item->Icon); + c->Message( + Chat::White, + ">> Size: %u, Weight: %u, Price: %u, LDoNPrice: %u", + item->Size, + item->Weight, + item->Price, + item->LDoNPrice + ); + c->Message( + Chat::White, + ">> Material: 0x%02X, Color: 0x%08X, Tint: 0x%08X, Light: 0x%02X", + item->Material, + item->Color, + inst->GetColor(), + item->Light + ); + c->Message( + Chat::White, + ">> IsLore: %s, LoreGroup: %u, Lore: '%s'", + (item->LoreFlag ? "TRUE" : "FALSE"), + item->LoreGroup, + item->Lore + ); + c->Message( + Chat::White, ">> NoDrop: %u, NoRent: %u, NoPet: %u, NoTransfer: %u, FVNoDrop: %u", + item->NoDrop, item->NoRent, (uint8) item->NoPet, (uint8) item->NoTransfer, item->FVNoDrop + ); + + if (item->IsClassBook()) { + c->Message(Chat::White, "*** This item is a Book (filename:'%s') ***", item->Filename); + } + else if (item->IsClassBag()) { + c->Message(Chat::White, "*** This item is a Container (%u slots) ***", item->BagSlots); + } + else { + c->Message(Chat::White, "*** This item is Common ***"); + c->Message(Chat::White, ">> Classes: %u, Races: %u, Slots: %u", item->Classes, item->Races, item->Slots); + c->Message( + Chat::White, + ">> ReqSkill: %u, ReqLevel: %u, RecLevel: %u", + item->RecSkill, + item->ReqLevel, + item->RecLevel + ); + c->Message(Chat::White, ">> SkillModType: %u, SkillModValue: %i", item->SkillModType, item->SkillModValue); + c->Message( + Chat::White, ">> BaneRaceType: %u, BaneRaceDamage: %u, BaneBodyType: %u, BaneBodyDamage: %i", + item->BaneDmgRace, item->BaneDmgRaceAmt, item->BaneDmgBody, item->BaneDmgAmt + ); + c->Message( + Chat::White, + ">> Magic: %s, SpellID: %i, ProcLevel: %u, Charges: %u, MaxCharges: %u", + (item->Magic ? "TRUE" : "FALSE"), + item->Click.Effect, + item->Click.Level, + inst->GetCharges(), + item->MaxCharges + ); + c->Message( + Chat::White, + ">> EffectType: 0x%02X, CastTime: %.2f", + (uint8) item->Click.Type, + ((double) item->CastTime / 1000)); + } + + if (c->Admin() >= AccountStatus::GMMgmt) { + c->Message(Chat::White, ">> MinStatus: %u", item->MinStatus); + } +} + diff --git a/zone/gm_commands/itemsearch.cpp b/zone/gm_commands/itemsearch.cpp new file mode 100755 index 000000000..4617e2397 --- /dev/null +++ b/zone/gm_commands/itemsearch.cpp @@ -0,0 +1,125 @@ +#include "../client.h" + +void command_itemsearch(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #itemsearch [search string]"); + } + else { + const char *search_criteria = sep->argplus[1]; + + const EQ::ItemData *item = nullptr; + EQ::SayLinkEngine linker; + linker.SetLinkType(EQ::saylink::SayLinkItemData); + + if (Seperator::IsNumber(search_criteria)) { + item = database.GetItem(atoi(search_criteria)); + if (item) { + linker.SetItemData(item); + std::string item_id = std::to_string(item->ID); + std::string saylink_commands = + "[" + + EQ::SayLinkEngine::GenerateQuestSaylink( + "#si " + item_id, + false, + "X" + ) + + "] "; + + if (item->Stackable && item->StackSize > 1) { + std::string stack_size = std::to_string(item->StackSize); + saylink_commands += + "[" + + EQ::SayLinkEngine::GenerateQuestSaylink( + "#si " + item_id + " " + stack_size, + false, + stack_size + ) + + "]"; + } + + c->Message( + Chat::White, + fmt::format( + " Summon {} [{}] [{}]", + saylink_commands, + linker.GenerateLink(), + item->ID + ).c_str() + ); + } + else { + c->Message( + Chat::White, + fmt::format( + "Item {} not found", + search_criteria + ).c_str() + ); + } + + return; + } + + int count = 0; + char sName[64]; + char sCriteria[255]; + strn0cpy(sCriteria, search_criteria, sizeof(sCriteria)); + strupr(sCriteria); + char *pdest; + uint32 it = 0; + while ((item = database.IterateItems(&it))) { + strn0cpy(sName, item->Name, sizeof(sName)); + strupr(sName); + pdest = strstr(sName, sCriteria); + if (pdest != nullptr) { + linker.SetItemData(item); + std::string item_id = std::to_string(item->ID); + std::string saylink_commands = + "[" + + EQ::SayLinkEngine::GenerateQuestSaylink( + "#si " + item_id, + false, + "X" + ) + + "] "; + if (item->Stackable && item->StackSize > 1) { + std::string stack_size = std::to_string(item->StackSize); + saylink_commands += + "[" + + EQ::SayLinkEngine::GenerateQuestSaylink( + "#si " + item_id + " " + stack_size, + false, + stack_size + ) + + "]"; + } + + c->Message( + Chat::White, + fmt::format( + " Summon {} [{}] [{}]", + saylink_commands, + linker.GenerateLink(), + item->ID + ).c_str() + ); + + ++count; + } + + if (count == 50) { + break; + } + } + + if (count == 50) { + c->Message(Chat::White, "50 items shown...too many results."); + } + else { + c->Message(Chat::White, "%i items found", count); + } + + } +} + diff --git a/zone/gm_commands/kick.cpp b/zone/gm_commands/kick.cpp new file mode 100755 index 000000000..a6a4644c1 --- /dev/null +++ b/zone/gm_commands/kick.cpp @@ -0,0 +1,36 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_kick(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #kick [charname]"); + } + else { + Client *client = entity_list.GetClientByName(sep->arg[1]); + if (client != 0) { + if (client->Admin() <= c->Admin()) { + client->Message(Chat::White, "You have been kicked by %s", c->GetName()); + auto outapp = new EQApplicationPacket(OP_GMKick, 0); + client->QueuePacket(outapp); + client->Kick("Ordered kicked by command"); + c->Message(Chat::White, "Kick: local: kicking %s", sep->arg[1]); + } + } + else if (!worldserver.Connected()) { + c->Message(Chat::White, "Error: World server disconnected"); + } + else { + auto pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); + ServerKickPlayer_Struct *skp = (ServerKickPlayer_Struct *) pack->pBuffer; + strcpy(skp->adminname, c->GetName()); + strcpy(skp->name, sep->arg[1]); + skp->adminrank = c->Admin(); + worldserver.SendPacket(pack); + safe_delete(pack); + } + } +} + diff --git a/zone/gm_commands/kill.cpp b/zone/gm_commands/kill.cpp new file mode 100755 index 000000000..d54245662 --- /dev/null +++ b/zone/gm_commands/kill.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_kill(Client *c, const Seperator *sep) +{ + if (!c->GetTarget()) { + c->Message(Chat::White, "Error: #Kill: No target."); + } + else if (!c->GetTarget()->IsClient() || c->GetTarget()->CastToClient()->Admin() <= c->Admin()) { + c->GetTarget()->Kill(); + } +} + diff --git a/zone/gm_commands/killallnpcs.cpp b/zone/gm_commands/killallnpcs.cpp new file mode 100755 index 000000000..c298f2abb --- /dev/null +++ b/zone/gm_commands/killallnpcs.cpp @@ -0,0 +1,45 @@ +#include "../client.h" + +void command_killallnpcs(Client *c, const Seperator *sep) +{ + std::string search_string; + if (sep->arg[1]) { + search_string = sep->arg[1]; + } + + int count = 0; + for (auto &itr : entity_list.GetMobList()) { + Mob *entity = itr.second; + if (!entity->IsNPC()) { + continue; + } + + std::string entity_name = entity->GetName(); + + /** + * Filter by name + */ + if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { + continue; + } + + bool is_not_attackable = + ( + entity->IsInvisible() || + !entity->IsAttackAllowed(c) || + entity->GetRace() == 127 || + entity->GetRace() == 240 + ); + + if (is_not_attackable) { + continue; + } + + entity->Damage(c, 1000000000, 0, EQ::skills::SkillDragonPunch); + + count++; + } + + c->Message(Chat::Yellow, "Killed (%i) npc(s)", count); +} + diff --git a/zone/gm_commands/lastname.cpp b/zone/gm_commands/lastname.cpp new file mode 100755 index 000000000..09bed4771 --- /dev/null +++ b/zone/gm_commands/lastname.cpp @@ -0,0 +1,19 @@ +#include "../client.h" + +void command_lastname(Client *c, const Seperator *sep) +{ + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient()) { + t = c->GetTarget()->CastToClient(); + } + LogInfo("#lastname request from [{}] for [{}]", c->GetName(), t->GetName()); + + if (strlen(sep->arg[1]) <= 70) { + t->ChangeLastName(sep->arg[1]); + } + else { + c->Message(Chat::White, "Usage: #lastname where is less than 70 chars long"); + } +} + diff --git a/zone/gm_commands/list.cpp b/zone/gm_commands/list.cpp new file mode 100755 index 000000000..a38b3ddc3 --- /dev/null +++ b/zone/gm_commands/list.cpp @@ -0,0 +1,265 @@ +#include "../client.h" +#include "../corpse.h" +#include "../object.h" +#include "../doors.h" + +void command_list(Client *c, const Seperator *sep) +{ + std::string search_type; + if (strcasecmp(sep->arg[1], "npcs") == 0) { + search_type = "npcs"; + } + + if (strcasecmp(sep->arg[1], "players") == 0) { + search_type = "players"; + } + + if (strcasecmp(sep->arg[1], "corpses") == 0) { + search_type = "corpses"; + } + + if (strcasecmp(sep->arg[1], "doors") == 0) { + search_type = "doors"; + } + + if (strcasecmp(sep->arg[1], "objects") == 0) { + search_type = "objects"; + } + + if (search_type.length() > 0) { + + int entity_count = 0; + int found_count = 0; + + std::string search_string; + + if (sep->arg[2]) { + search_string = sep->arg[2]; + } + + /** + * NPC + */ + if (search_type.find("npcs") != std::string::npos) { + auto &entity_list_search = entity_list.GetMobList(); + + for (auto &itr : entity_list_search) { + Mob *entity = itr.second; + if (!entity->IsNPC()) { + continue; + } + + entity_count++; + + std::string entity_name = entity->GetName(); + + /** + * Filter by name + */ + if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { + continue; + } + + std::string saylink = StringFormat( + "#goto %.0f %0.f %.0f", + entity->GetX(), + entity->GetY(), + entity->GetZ() + (entity->IsBoat() ? 50 : 0)); + + c->Message( + 0, + "| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f", + EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), + entity->GetID(), + entity->GetName(), + entity->GetX(), + entity->GetY(), + entity->GetZ() + ); + + found_count++; + } + } + + /** + * Client + */ + if (search_type.find("players") != std::string::npos) { + auto &entity_list_search = entity_list.GetClientList(); + + for (auto &itr : entity_list_search) { + Client *entity = itr.second; + + entity_count++; + + std::string entity_name = entity->GetName(); + + /** + * Filter by name + */ + if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { + continue; + } + + std::string saylink = StringFormat( + "#goto %.0f %0.f %.0f", + entity->GetX(), + entity->GetY(), + entity->GetZ()); + + c->Message( + 0, + "| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f", + EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), + entity->GetID(), + entity->GetName(), + entity->GetX(), + entity->GetY(), + entity->GetZ() + ); + + found_count++; + } + } + + /** + * Corpse + */ + if (search_type.find("corpses") != std::string::npos) { + auto &entity_list_search = entity_list.GetCorpseList(); + + for (auto &itr : entity_list_search) { + Corpse *entity = itr.second; + + entity_count++; + + std::string entity_name = entity->GetName(); + + /** + * Filter by name + */ + if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { + continue; + } + + std::string saylink = StringFormat( + "#goto %.0f %0.f %.0f", + entity->GetX(), + entity->GetY(), + entity->GetZ()); + + c->Message( + 0, + "| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f", + EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), + entity->GetID(), + entity->GetName(), + entity->GetX(), + entity->GetY(), + entity->GetZ() + ); + + found_count++; + } + } + + /** + * Doors + */ + if (search_type.find("doors") != std::string::npos) { + auto &entity_list_search = entity_list.GetDoorsList(); + + for (auto &itr : entity_list_search) { + Doors *entity = itr.second; + + entity_count++; + + std::string entity_name = entity->GetDoorName(); + + /** + * Filter by name + */ + if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { + continue; + } + + std::string saylink = StringFormat( + "#goto %.0f %0.f %.0f", + entity->GetX(), + entity->GetY(), + entity->GetZ()); + + c->Message( + 0, + "| %s | Entity ID %5d | Door ID %i | %s | x %.0f | y %0.f | z %.0f", + EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), + entity->GetID(), + entity->GetDoorID(), + entity->GetDoorName(), + entity->GetX(), + entity->GetY(), + entity->GetZ() + ); + + found_count++; + } + } + + /** + * Objects + */ + if (search_type.find("objects") != std::string::npos) { + auto &entity_list_search = entity_list.GetObjectList(); + + for (auto &itr : entity_list_search) { + Object *entity = itr.second; + + entity_count++; + + std::string entity_name = entity->GetModelName(); + + /** + * Filter by name + */ + if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { + continue; + } + + std::string saylink = StringFormat( + "#goto %.0f %0.f %.0f", + entity->GetX(), + entity->GetY(), + entity->GetZ()); + + c->Message( + 0, + "| %s | Entity ID %5d | Object DBID %i | %s | x %.0f | y %0.f | z %.0f", + EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), + entity->GetID(), + entity->GetDBID(), + entity->GetModelName(), + entity->GetX(), + entity->GetY(), + entity->GetZ() + ); + + found_count++; + } + } + + if (found_count) { + c->Message( + 0, "Found (%i) of type (%s) in zone (%i) total", + found_count, + search_type.c_str(), + entity_count + ); + } + } + else { + c->Message(Chat::White, "Usage of #list"); + c->Message(Chat::White, "- #list [npcs|players|corpses|doors|objects] [search]"); + c->Message(Chat::White, "- Example: #list npcs (Blank for all)"); + } +} + diff --git a/zone/gm_commands/listpetition.cpp b/zone/gm_commands/listpetition.cpp new file mode 100755 index 000000000..110809deb --- /dev/null +++ b/zone/gm_commands/listpetition.cpp @@ -0,0 +1,22 @@ +#include "../client.h" + +void command_listpetition(Client *c, const Seperator *sep) +{ + std::string query = "SELECT petid, charname, accountname FROM petitions ORDER BY petid"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + return; + } + + LogInfo("Petition list requested by [{}]", c->GetName()); + + if (results.RowCount() == 0) { + return; + } + + c->Message(Chat::Red, " ID : Character Name , Account Name"); + + for (auto row = results.begin(); row != results.end(); ++row) + c->Message(Chat::Yellow, " %s: %s , %s ", row[0], row[1], row[2]); +} + diff --git a/zone/gm_commands/loc.cpp b/zone/gm_commands/loc.cpp new file mode 100755 index 000000000..79d734711 --- /dev/null +++ b/zone/gm_commands/loc.cpp @@ -0,0 +1,32 @@ +#include "../client.h" + +void command_loc(Client *c, const Seperator *sep) +{ + Mob *target = c; + if (c->GetTarget()) { + target = c->GetTarget(); + } + + auto target_position = target->GetPosition(); + + c->Message( + Chat::White, + fmt::format( + "{} Location | XYZ: {:.2f}, {:.2f}, {:.2f} Heading: {:.2f}", + ( + c == target ? + "Your" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + target_position.x, + target_position.y, + target_position.z, + target_position.w + ).c_str() + ); +} + diff --git a/zone/gm_commands/lock.cpp b/zone/gm_commands/lock.cpp new file mode 100755 index 000000000..1fda53841 --- /dev/null +++ b/zone/gm_commands/lock.cpp @@ -0,0 +1,15 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_lock(Client *c, const Seperator *sep) +{ + auto outpack = new ServerPacket(ServerOP_Lock, sizeof(ServerLock_Struct)); + ServerLock_Struct *lss = (ServerLock_Struct *) outpack->pBuffer; + strcpy(lss->myname, c->GetName()); + lss->mode = 1; + worldserver.SendPacket(outpack); + safe_delete(outpack); +} + diff --git a/zone/gm_commands/logcommand.cpp b/zone/gm_commands/logcommand.cpp new file mode 100755 index 000000000..3b85e2ff3 --- /dev/null +++ b/zone/gm_commands/logcommand.cpp @@ -0,0 +1,85 @@ +#include "../client.h" + +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 >= AccountStatus::GMLeadAdmin && + admin < AccountStatus::GMMgmt + ) { + continueevents = true; + } + break; + } + case 8: { // log only GM + if ( + admin >= AccountStatus::GMAdmin && + admin < AccountStatus::GMLeadAdmin + ) { + continueevents = true; + } + break; + } + case 1: { + if (admin >= AccountStatus::GMMgmt) { + continueevents = true; + } + break; + } + case 2: { + if (admin >= AccountStatus::GMLeadAdmin) { + continueevents = true; + } + break; + } + case 3: { + if (admin >= AccountStatus::GMAdmin) { + continueevents = true; + } + break; + } + case 4: { + if (admin >= AccountStatus::QuestTroupe) { + continueevents = true; + } + break; + } + case 5: { + if (admin >= AccountStatus::ApprenticeGuide) { + continueevents = true; + } + break; + } + case 6: { + if (admin >= AccountStatus::Steward) { + 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 + */ diff --git a/zone/gm_commands/logs.cpp b/zone/gm_commands/logs.cpp new file mode 100755 index 000000000..16ba3439a --- /dev/null +++ b/zone/gm_commands/logs.cpp @@ -0,0 +1,101 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_logs(Client *c, const Seperator *sep) +{ + int logs_set = 0; + if (sep->argnum > 0) { + /* #logs reload_all */ + if (strcasecmp(sep->arg[1], "reload_all") == 0) { + auto pack = new ServerPacket(ServerOP_ReloadLogs, 0); + worldserver.SendPacket(pack); + c->Message( + Chat::Red, + "Successfully sent the packet to world to reload log settings from the database for all zones" + ); + safe_delete(pack); + } + /* #logs list_settings */ + if (strcasecmp(sep->arg[1], "list_settings") == 0 || + (strcasecmp(sep->arg[1], "set") == 0 && strcasecmp(sep->arg[3], "") == 0)) { + c->Message(Chat::White, "[Category ID | console | file | gmsay | Category Description]"); + int redisplay_columns = 0; + for (int i = 0; i < Logs::LogCategory::MaxCategoryID; i++) { + if (redisplay_columns == 10) { + c->Message(Chat::White, "[Category ID | console | file | gmsay | Category Description]"); + redisplay_columns = 0; + } + c->Message( + 0, + StringFormat( + "--- %i | %u | %u | %u | %s", + i, + LogSys.log_settings[i].log_to_console, + LogSys.log_settings[i].log_to_file, + LogSys.log_settings[i].log_to_gmsay, + Logs::LogCategoryName[i] + ).c_str()); + redisplay_columns++; + } + } + /* #logs set */ + if (strcasecmp(sep->arg[1], "set") == 0) { + if (strcasecmp(sep->arg[2], "console") == 0) { + LogSys.log_settings[atoi(sep->arg[3])].log_to_console = atoi(sep->arg[4]); + logs_set = 1; + } + else if (strcasecmp(sep->arg[2], "file") == 0) { + LogSys.log_settings[atoi(sep->arg[3])].log_to_file = atoi(sep->arg[4]); + logs_set = 1; + } + else if (strcasecmp(sep->arg[2], "gmsay") == 0) { + LogSys.log_settings[atoi(sep->arg[3])].log_to_gmsay = atoi(sep->arg[4]); + logs_set = 1; + } + else { + c->Message( + Chat::White, + "--- #logs set [console|file|gmsay] - Sets log settings during the lifetime of the zone" + ); + c->Message(Chat::White, "--- #logs set gmsay 20 1 - Would output Quest errors to gmsay"); + } + if (logs_set == 1) { + c->Message(Chat::Yellow, "Your Log Settings have been applied"); + c->Message( + Chat::Yellow, + "Output Method: %s :: Debug Level: %i - Category: %s", + sep->arg[2], + atoi(sep->arg[4]), + Logs::LogCategoryName[atoi(sep->arg[3])] + ); + } + /* We use a general 'is_category_enabled' now, let's update when we update any output settings + This is used in hot places of code to check if its enabled in any way before triggering logs + */ + if (atoi(sep->arg[4]) > 0) { + LogSys.log_settings[atoi(sep->arg[3])].is_category_enabled = 1; + } + else { + LogSys.log_settings[atoi(sep->arg[3])].is_category_enabled = 0; + } + } + } + else { + c->Message(Chat::White, "#logs usage:"); + c->Message( + Chat::White, + "--- #logs reload_all - Reload all settings in world and all zone processes with what is defined in the database" + ); + c->Message( + Chat::White, + "--- #logs list_settings - Shows current log settings and categories loaded into the current process' memory" + ); + c->Message( + Chat::White, + "--- #logs set [console|file|gmsay] - Sets log settings during the lifetime of the zone" + ); + } +} + diff --git a/zone/gm_commands/makepet.cpp b/zone/gm_commands/makepet.cpp new file mode 100755 index 000000000..d83411404 --- /dev/null +++ b/zone/gm_commands/makepet.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +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]); + } +} + diff --git a/zone/gm_commands/mana.cpp b/zone/gm_commands/mana.cpp new file mode 100755 index 000000000..33fe4e388 --- /dev/null +++ b/zone/gm_commands/mana.cpp @@ -0,0 +1,27 @@ +#include "../client.h" + +void command_mana(Client *c, const Seperator *sep) +{ + auto target = c->GetTarget() ? c->GetTarget() : c; + if (target->IsClient()) { + target->CastToClient()->SetMana(target->CastToClient()->CalcMaxMana()); + } + else { + target->SetMana(target->CalcMaxMana()); + } + + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "Set {} ({}) to full Mana.", + target->GetCleanName(), + target->GetID() + ).c_str() + ); + } + else { + c->Message(Chat::White, "Restored your Mana to full."); + } +} + diff --git a/zone/gm_commands/max_all_skills.cpp b/zone/gm_commands/max_all_skills.cpp new file mode 100755 index 000000000..6c63c60ca --- /dev/null +++ b/zone/gm_commands/max_all_skills.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +void command_max_all_skills(Client *c, const Seperator *sep) +{ + if (c) { + Client *client_target = (c->GetTarget() ? (c->GetTarget()->IsClient() ? c->GetTarget()->CastToClient() : c) + : c); + auto Skills = EQ::skills::GetSkillTypeMap(); + for (auto &skills_iter : Skills) { + auto skill_id = skills_iter.first; + auto current_skill_value = ( + (EQ::skills::IsSpecializedSkill(skill_id)) ? + 50 : + content_db.GetSkillCap(client_target->GetClass(), skill_id, client_target->GetLevel()) + ); + client_target->SetSkill(skill_id, current_skill_value); + } + } +} + diff --git a/zone/gm_commands/memspell.cpp b/zone/gm_commands/memspell.cpp new file mode 100755 index 000000000..5511dae39 --- /dev/null +++ b/zone/gm_commands/memspell.cpp @@ -0,0 +1,22 @@ +#include "../client.h" + +void command_memspell(Client *c, const Seperator *sep) +{ + uint32 slot; + uint16 spell_id; + + if (!(sep->IsNumber(1) && sep->IsNumber(2))) { + c->Message(Chat::White, "Usage: #MemSpell slotid spellid"); + } + else { + slot = atoi(sep->arg[1]) - 1; + spell_id = atoi(sep->arg[2]); + if (slot > EQ::spells::SPELL_GEM_COUNT || spell_id >= SPDAT_RECORDS) { + c->Message(Chat::White, "Error: #MemSpell: Arguement out of range"); + } + else { + c->MemSpell(spell_id, slot); + c->Message(Chat::White, "Spell slot changed, have fun!"); + } + } +} diff --git a/zone/gm_commands/merchantcloseshop.cpp b/zone/gm_commands/merchantcloseshop.cpp new file mode 100755 index 000000000..571d80d46 --- /dev/null +++ b/zone/gm_commands/merchantcloseshop.cpp @@ -0,0 +1,13 @@ +#include "../client.h" + +void command_merchantcloseshop(Client *c, const Seperator *sep) +{ + Mob *merchant = c->GetTarget(); + if (!merchant || merchant->GetClass() != MERCHANT) { + c->Message(Chat::White, "You must target a merchant to close their shop."); + return; + } + + merchant->CastToNPC()->MerchantCloseShop(); +} + diff --git a/zone/gm_commands/merchantopenshop.cpp b/zone/gm_commands/merchantopenshop.cpp new file mode 100755 index 000000000..cd05465ba --- /dev/null +++ b/zone/gm_commands/merchantopenshop.cpp @@ -0,0 +1,13 @@ +#include "../client.h" + +void command_merchantopenshop(Client *c, const Seperator *sep) +{ + Mob *merchant = c->GetTarget(); + if (!merchant || merchant->GetClass() != MERCHANT) { + c->Message(Chat::White, "You must target a merchant to open their shop."); + return; + } + + merchant->CastToNPC()->MerchantOpenShop(); +} + diff --git a/zone/gm_commands/modifynpcstat.cpp b/zone/gm_commands/modifynpcstat.cpp new file mode 100755 index 000000000..5cde6e8f3 --- /dev/null +++ b/zone/gm_commands/modifynpcstat.cpp @@ -0,0 +1,30 @@ +#include "../client.h" + +void command_modifynpcstat(Client *c, const Seperator *sep) +{ + if (!c) { + return; + } + + if (sep->arg[1][0] == '\0') { + c->Message(Chat::White, "usage #modifynpcstat arg value"); + c->Message( + Chat::White, + "Args: ac, str, sta, agi, dex, wis, _int, cha, max_hp, mr, fr, cr, pr, dr, runspeed, special_attacks, " + "attack_speed, atk, accuracy, trackable, min_hit, max_hit, see_invis_undead, see_hide, see_improved_hide, " + "hp_regen, mana_regen, aggro, assist, slow_mitigation, loottable_id, healscale, spellscale" + ); + return; + } + + if (!c->GetTarget()) { + return; + } + + if (!c->GetTarget()->IsNPC()) { + return; + } + + c->GetTarget()->CastToNPC()->ModifyNPCStat(sep->arg[1], sep->arg[2]); +} + diff --git a/zone/gm_commands/motd.cpp b/zone/gm_commands/motd.cpp new file mode 100755 index 000000000..fdfba84b4 --- /dev/null +++ b/zone/gm_commands/motd.cpp @@ -0,0 +1,15 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_motd(Client *c, const Seperator *sep) +{ + auto outpack = new ServerPacket(ServerOP_Motd, sizeof(ServerMotd_Struct)); + ServerMotd_Struct *mss = (ServerMotd_Struct *) outpack->pBuffer; + strn0cpy(mss->myname, c->GetName(), 64); + strn0cpy(mss->motd, sep->argplus[1], 512); + worldserver.SendPacket(outpack); + safe_delete(outpack); +} + diff --git a/zone/gm_commands/movechar.cpp b/zone/gm_commands/movechar.cpp new file mode 100755 index 000000000..1fc3c7f27 --- /dev/null +++ b/zone/gm_commands/movechar.cpp @@ -0,0 +1,32 @@ +#include "../client.h" + +void command_movechar(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0 || sep->arg[2][0] == 0) { + c->Message(Chat::White, "Usage: #movechar [charactername] [zonename]"); + } + else if (c->Admin() < commandMovecharToSpecials && strcasecmp(sep->arg[2], "cshome") == 0 || + strcasecmp(sep->arg[2], "load") == 0 || strcasecmp(sep->arg[2], "load2") == 0) { + c->Message(Chat::White, "Invalid zone name"); + } + else { + uint32 tmp = database.GetAccountIDByChar(sep->arg[1]); + if (tmp) { + if (c->Admin() >= commandMovecharSelfOnly || tmp == c->AccountID()) { + if (!database.MoveCharacterToZone((char *) sep->arg[1], ZoneID(sep->arg[2]))) { + c->Message(Chat::White, "Character Move Failed!"); + } + else { + c->Message(Chat::White, "Character has been moved."); + } + } + else { + c->Message(Chat::Red, "You cannot move characters that are not on your account."); + } + } + else { + c->Message(Chat::White, "Character Does Not Exist"); + } + } +} + diff --git a/zone/gm_commands/movement.cpp b/zone/gm_commands/movement.cpp new file mode 100755 index 000000000..f934f900c --- /dev/null +++ b/zone/gm_commands/movement.cpp @@ -0,0 +1,76 @@ +#include "../client.h" +#include "../mob_movement_manager.h" + +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"); + } +} + diff --git a/zone/gm_commands/myskills.cpp b/zone/gm_commands/myskills.cpp new file mode 100755 index 000000000..d781f50ca --- /dev/null +++ b/zone/gm_commands/myskills.cpp @@ -0,0 +1,7 @@ +#include "../client.h" + +void command_myskills(Client *c, const Seperator *sep) +{ + c->ShowSkillsWindow(); +} + diff --git a/zone/gm_commands/mysql.cpp b/zone/gm_commands/mysql.cpp new file mode 100755 index 000000000..67fc0f0df --- /dev/null +++ b/zone/gm_commands/mysql.cpp @@ -0,0 +1,87 @@ +#include "../client.h" + +void command_mysql(Client *c, const Seperator *sep) +{ + if (!sep->arg[1][0] || !sep->arg[2][0]) { + c->Message(Chat::White, "Usage: #mysql query \"Query here\""); + return; + } + + if (strcasecmp(sep->arg[1], "help") == 0) { + c->Message(Chat::White, "MYSQL In-Game CLI Interface:"); + c->Message(Chat::White, "Example: #mysql query \"Query goes here quoted\" -s -h"); + c->Message(Chat::White, "To use 'like \"%%something%%\" replace the %% with #"); + c->Message(Chat::White, "Example: #mysql query \"select * from table where name like \"#something#\""); + c->Message(Chat::White, "-s - Spaces select entries apart"); + c->Message(Chat::White, "-h - Colors every other select result"); + return; + } + + if (strcasecmp(sep->arg[1], "query") == 0) { + ///Parse switches here + int argnum = 3; + bool optionS = false; + bool optionH = false; + while (sep->arg[argnum] && strlen(sep->arg[argnum]) > 1) { + switch (sep->arg[argnum][1]) { + case 's': + optionS = true; + break; + case 'h': + optionH = true; + break; + default: + c->Message(Chat::Yellow, "%s, there is no option '%c'", c->GetName(), sep->arg[argnum][1]); + return; + } + ++argnum; + } + + int highlightTextIndex = 0; + std::string query(sep->arg[2]); + //swap # for % so like queries can work + std::replace(query.begin(), query.end(), '#', '%'); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + return; + } + + //Using sep->arg[2] again, replace # with %% so it doesn't screw up when sent through vsnprintf in Message + query = sep->arg[2]; + int pos = query.find('#'); + while (pos != std::string::npos) { + query.erase(pos, 1); + query.insert(pos, "%%"); + pos = query.find('#'); + } + c->Message(Chat::Yellow, "---Running query: '%s'", query.c_str()); + + for (auto row = results.begin(); row != results.end(); ++row) { + std::stringstream lineText; + std::vector lineVec; + for (int i = 0; i < results.RowCount(); i++) { + //split lines that could overflow the buffer in Client::Message and get cut off + //This will crash MQ2 @ 4000 since their internal buffer is only 2048. + //Reducing it to 2000 fixes that but splits more results from tables with a lot of columns. + if (lineText.str().length() > 4000) { + lineVec.push_back(lineText.str()); + lineText.str(""); + } + lineText << results.FieldName(i) << ":" << "[" << (row[i] ? row[i] : "nullptr") << "] "; + } + + lineVec.push_back(lineText.str()); + + if (optionS) { //This provides spacing for the space switch + c->Message(Chat::White, " "); + } + if (optionH) { //This option will highlight every other row + highlightTextIndex = 1 - highlightTextIndex; + } + + for (int lineNum = 0; lineNum < lineVec.size(); ++lineNum) + c->Message(highlightTextIndex, lineVec[lineNum].c_str()); + } + } +} + diff --git a/zone/gm_commands/mystats.cpp b/zone/gm_commands/mystats.cpp new file mode 100755 index 000000000..e5a781d8a --- /dev/null +++ b/zone/gm_commands/mystats.cpp @@ -0,0 +1,17 @@ +#include "../client.h" + +void command_mystats(Client *c, const Seperator *sep) +{ + if (c->GetTarget() && c->GetPet()) { + if (c->GetTarget()->IsPet() && c->GetTarget() == c->GetPet()) { + c->GetTarget()->ShowStats(c); + } + else { + c->ShowStats(c); + } + } + else { + c->ShowStats(c); + } +} + diff --git a/zone/gm_commands/name.cpp b/zone/gm_commands/name.cpp new file mode 100755 index 000000000..fbe60f752 --- /dev/null +++ b/zone/gm_commands/name.cpp @@ -0,0 +1,30 @@ +#include "../client.h" + +void command_name(Client *c, const Seperator *sep) +{ + Client *target; + + if ((strlen(sep->arg[1]) == 0) || (!(c->GetTarget() && c->GetTarget()->IsClient()))) { + c->Message(Chat::White, "Usage: #name newname (requires player target)"); + } + else { + target = c->GetTarget()->CastToClient(); + char *oldname = strdup(target->GetName()); + if (target->ChangeFirstName(sep->arg[1], c->GetName())) { + c->Message(Chat::White, "Successfully renamed %s to %s", oldname, sep->arg[1]); + // until we get the name packet working right this will work + c->Message(Chat::White, "Sending player to char select."); + target->Kick("Name was changed"); + } + else { + c->Message( + Chat::Red, + "ERROR: Unable to rename %s. Check that the new name '%s' isn't already taken.", + oldname, + sep->arg[2] + ); + } + free(oldname); + } +} + diff --git a/zone/gm_commands/netstats.cpp b/zone/gm_commands/netstats.cpp new file mode 100755 index 000000000..6bd56f4fa --- /dev/null +++ b/zone/gm_commands/netstats.cpp @@ -0,0 +1,141 @@ +#include "../client.h" + +void command_netstats(Client *c, const Seperator *sep) +{ + if (c) { + auto client = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + client = c->GetTarget()->CastToClient(); + } + + if (strcasecmp(sep->arg[1], "reset") == 0) { + auto connection = c->Connection(); + c->Message(Chat::White, "Resetting client stats (packet loss will not read correctly after reset)."); + connection->ResetStats(); + return; + } + + auto connection = c->Connection(); + auto opts = connection->GetManager()->GetOptions(); + auto eqs_stats = connection->GetStats(); + auto &stats = eqs_stats.DaybreakStats; + auto now = EQ::Net::Clock::now(); + auto sec_since_stats_reset = std::chrono::duration_cast>( + now - stats.created + ).count(); + + c->Message(Chat::White, "Netstats:"); + c->Message(Chat::White, "--------------------------------------------------------------------"); + c->Message( + Chat::White, + "Sent Bytes: %u (%.2f/sec)", + stats.sent_bytes, + stats.sent_bytes / sec_since_stats_reset + ); + c->Message( + Chat::White, + "Recv Bytes: %u (%.2f/sec)", + stats.recv_bytes, + stats.recv_bytes / sec_since_stats_reset + ); + c->Message( + Chat::White, "Bytes Before Encode (Sent): %u, Compression Rate: %.2f%%", stats.bytes_before_encode, + static_cast(stats.bytes_before_encode - stats.sent_bytes) / + static_cast(stats.bytes_before_encode) * 100.0 + ); + c->Message( + Chat::White, "Bytes After Decode (Recv): %u, Compression Rate: %.2f%%", stats.bytes_after_decode, + static_cast(stats.bytes_after_decode - stats.recv_bytes) / + static_cast(stats.bytes_after_decode) * 100.0 + ); + c->Message(Chat::White, "Min Ping: %u", stats.min_ping); + c->Message(Chat::White, "Max Ping: %u", stats.max_ping); + c->Message(Chat::White, "Last Ping: %u", stats.last_ping); + c->Message(Chat::White, "Average Ping: %u", stats.avg_ping); + c->Message(Chat::White, "--------------------------------------------------------------------"); + c->Message( + Chat::White, + "(Realtime) Recv Packets: %u (%.2f/sec)", + stats.recv_packets, + stats.recv_packets / sec_since_stats_reset + ); + c->Message( + Chat::White, + "(Realtime) Sent Packets: %u (%.2f/sec)", + stats.sent_packets, + stats.sent_packets / sec_since_stats_reset + ); + c->Message(Chat::White, "(Sync) Recv Packets: %u", stats.sync_recv_packets); + c->Message(Chat::White, "(Sync) Sent Packets: %u", stats.sync_sent_packets); + c->Message(Chat::White, "(Sync) Remote Recv Packets: %u", stats.sync_remote_recv_packets); + c->Message(Chat::White, "(Sync) Remote Sent Packets: %u", stats.sync_remote_sent_packets); + c->Message( + Chat::White, + "Packet Loss In: %.2f%%", + 100.0 * (1.0 - static_cast(stats.sync_recv_packets) / + static_cast(stats.sync_remote_sent_packets))); + c->Message( + Chat::White, + "Packet Loss Out: %.2f%%", + 100.0 * (1.0 - static_cast(stats.sync_remote_recv_packets) / + static_cast(stats.sync_sent_packets))); + c->Message(Chat::White, "--------------------------------------------------------------------"); + c->Message( + Chat::White, + "Resent Packets: %u (%.2f/sec)", + stats.resent_packets, + stats.resent_packets / sec_since_stats_reset + ); + c->Message( + Chat::White, + "Resent Fragments: %u (%.2f/sec)", + stats.resent_fragments, + stats.resent_fragments / sec_since_stats_reset + ); + c->Message( + Chat::White, + "Resent Non-Fragments: %u (%.2f/sec)", + stats.resent_full, + stats.resent_full / sec_since_stats_reset + ); + c->Message( + Chat::White, + "Dropped Datarate Packets: %u (%.2f/sec)", + stats.dropped_datarate_packets, + stats.dropped_datarate_packets / sec_since_stats_reset + ); + + if (opts.daybreak_options.outgoing_data_rate > 0.0) { + c->Message( + Chat::White, + "Outgoing Link Saturation %.2f%% (%.2fkb/sec)", + 100.0 * (1.0 - ((opts.daybreak_options.outgoing_data_rate - stats.datarate_remaining) / + opts.daybreak_options.outgoing_data_rate)), + opts.daybreak_options.outgoing_data_rate + ); + } + + if (strcasecmp(sep->arg[1], "full") == 0) { + c->Message(Chat::White, "--------------------------------------------------------------------"); + c->Message(Chat::White, "Sent Packet Types"); + for (auto i = 0; i < _maxEmuOpcode; ++i) { + auto cnt = eqs_stats.SentCount[i]; + if (cnt > 0) { + c->Message(Chat::White, "%s: %u (%.2f / sec)", OpcodeNames[i], cnt, cnt / sec_since_stats_reset); + } + } + + c->Message(Chat::White, "--------------------------------------------------------------------"); + c->Message(Chat::White, "Recv Packet Types"); + for (auto i = 0; i < _maxEmuOpcode; ++i) { + auto cnt = eqs_stats.RecvCount[i]; + if (cnt > 0) { + c->Message(Chat::White, "%s: %u (%.2f / sec)", OpcodeNames[i], cnt, cnt / sec_since_stats_reset); + } + } + } + + c->Message(Chat::White, "--------------------------------------------------------------------"); + } +} + diff --git a/zone/gm_commands/network.cpp b/zone/gm_commands/network.cpp new file mode 100755 index 000000000..1c35d28ec --- /dev/null +++ b/zone/gm_commands/network.cpp @@ -0,0 +1,175 @@ +#include "../client.h" + +void command_network(Client *c, const Seperator *sep) +{ + if (!strcasecmp(sep->arg[1], "getopt")) { + auto eqsi = c->Connection(); + auto manager = eqsi->GetManager(); + auto opts = manager->GetOptions(); + + if (!strcasecmp(sep->arg[2], "all")) { + c->Message(Chat::White, "max_packet_size: %llu", (uint64_t) opts.daybreak_options.max_packet_size); + c->Message( + Chat::White, + "max_connection_count: %llu", + (uint64_t) opts.daybreak_options.max_connection_count + ); + c->Message(Chat::White, "keepalive_delay_ms: %llu", (uint64_t) opts.daybreak_options.keepalive_delay_ms); + c->Message(Chat::White, "resend_delay_factor: %.2f", opts.daybreak_options.resend_delay_factor); + c->Message(Chat::White, "resend_delay_ms: %llu", (uint64_t) opts.daybreak_options.resend_delay_ms); + c->Message(Chat::White, "resend_delay_min: %llu", (uint64_t) opts.daybreak_options.resend_delay_min); + c->Message(Chat::White, "resend_delay_max: %llu", (uint64_t) opts.daybreak_options.resend_delay_max); + c->Message(Chat::White, "connect_delay_ms: %llu", (uint64_t) opts.daybreak_options.connect_delay_ms); + c->Message(Chat::White, "connect_stale_ms: %llu", (uint64_t) opts.daybreak_options.connect_stale_ms); + c->Message(Chat::White, "stale_connection_ms: %llu", (uint64_t) opts.daybreak_options.stale_connection_ms); + c->Message(Chat::White, "crc_length: %llu", (uint64_t) opts.daybreak_options.crc_length); + c->Message(Chat::White, "hold_size: %llu", (uint64_t) opts.daybreak_options.hold_size); + c->Message(Chat::White, "hold_length_ms: %llu", (uint64_t) opts.daybreak_options.hold_length_ms); + c->Message( + Chat::White, + "simulated_in_packet_loss: %llu", + (uint64_t) opts.daybreak_options.simulated_in_packet_loss + ); + c->Message( + Chat::White, + "simulated_out_packet_loss: %llu", + (uint64_t) opts.daybreak_options.simulated_out_packet_loss + ); + c->Message(Chat::White, "tic_rate_hertz: %.2f", opts.daybreak_options.tic_rate_hertz); + c->Message(Chat::White, "resend_timeout: %llu", (uint64_t) opts.daybreak_options.resend_timeout); + c->Message( + Chat::White, + "connection_close_time: %llu", + (uint64_t) opts.daybreak_options.connection_close_time + ); + c->Message(Chat::White, "encode_passes[0]: %llu", (uint64_t) opts.daybreak_options.encode_passes[0]); + c->Message(Chat::White, "encode_passes[1]: %llu", (uint64_t) opts.daybreak_options.encode_passes[1]); + c->Message(Chat::White, "port: %llu", (uint64_t) opts.daybreak_options.port); + } + else { + c->Message(Chat::White, "Unknown get option: %s", sep->arg[2]); + c->Message(Chat::White, "Available options:"); + //Todo the rest of these when im less lazy. + //c->Message(Chat::White, "max_packet_size"); + //c->Message(Chat::White, "max_connection_count"); + //c->Message(Chat::White, "keepalive_delay_ms"); + //c->Message(Chat::White, "resend_delay_factor"); + //c->Message(Chat::White, "resend_delay_ms"); + //c->Message(Chat::White, "resend_delay_min"); + //c->Message(Chat::White, "resend_delay_max"); + //c->Message(Chat::White, "connect_delay_ms"); + //c->Message(Chat::White, "connect_stale_ms"); + //c->Message(Chat::White, "stale_connection_ms"); + //c->Message(Chat::White, "crc_length"); + //c->Message(Chat::White, "hold_size"); + //c->Message(Chat::White, "hold_length_ms"); + //c->Message(Chat::White, "simulated_in_packet_loss"); + //c->Message(Chat::White, "simulated_out_packet_loss"); + //c->Message(Chat::White, "tic_rate_hertz"); + //c->Message(Chat::White, "resend_timeout"); + //c->Message(Chat::White, "connection_close_time"); + //c->Message(Chat::White, "encode_passes[0]"); + //c->Message(Chat::White, "encode_passes[1]"); + //c->Message(Chat::White, "port"); + c->Message(Chat::White, "all"); + } + } + else if (!strcasecmp(sep->arg[1], "setopt")) { + auto eqsi = c->Connection(); + auto manager = eqsi->GetManager(); + auto opts = manager->GetOptions(); + + if (!strcasecmp(sep->arg[3], "")) { + c->Message(Chat::White, "Missing value for set"); + return; + } + + std::string value = sep->arg[3]; + if (!strcasecmp(sep->arg[2], "max_connection_count")) { + opts.daybreak_options.max_connection_count = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "keepalive_delay_ms")) { + opts.daybreak_options.keepalive_delay_ms = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "resend_delay_factor")) { + opts.daybreak_options.resend_delay_factor = std::stod(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "resend_delay_ms")) { + opts.daybreak_options.resend_delay_ms = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "resend_delay_min")) { + opts.daybreak_options.resend_delay_min = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "resend_delay_max")) { + opts.daybreak_options.resend_delay_max = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "connect_delay_ms")) { + opts.daybreak_options.connect_delay_ms = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "connect_stale_ms")) { + opts.daybreak_options.connect_stale_ms = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "stale_connection_ms")) { + opts.daybreak_options.stale_connection_ms = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "hold_size")) { + opts.daybreak_options.hold_size = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "hold_length_ms")) { + opts.daybreak_options.hold_length_ms = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "simulated_in_packet_loss")) { + opts.daybreak_options.simulated_in_packet_loss = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "simulated_out_packet_loss")) { + opts.daybreak_options.simulated_out_packet_loss = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "resend_timeout")) { + opts.daybreak_options.resend_timeout = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "connection_close_time")) { + opts.daybreak_options.connection_close_time = std::stoull(value); + manager->SetOptions(opts); + } + else { + c->Message(Chat::White, "Unknown set option: %s", sep->arg[2]); + c->Message(Chat::White, "Available options:"); + c->Message(Chat::White, "max_connection_count"); + c->Message(Chat::White, "keepalive_delay_ms"); + c->Message(Chat::White, "resend_delay_factor"); + c->Message(Chat::White, "resend_delay_ms"); + c->Message(Chat::White, "resend_delay_min"); + c->Message(Chat::White, "resend_delay_max"); + c->Message(Chat::White, "connect_delay_ms"); + c->Message(Chat::White, "connect_stale_ms"); + c->Message(Chat::White, "stale_connection_ms"); + c->Message(Chat::White, "hold_size"); + c->Message(Chat::White, "hold_length_ms"); + c->Message(Chat::White, "simulated_in_packet_loss"); + c->Message(Chat::White, "simulated_out_packet_loss"); + c->Message(Chat::White, "resend_timeout"); + c->Message(Chat::White, "connection_close_time"); + } + } + else { + c->Message(Chat::White, "Unknown command: %s", sep->arg[1]); + c->Message(Chat::White, "Network commands avail:"); + c->Message(Chat::White, "getopt optname - Retrieve the current option value set."); + c->Message(Chat::White, "setopt optname - Set the current option allowed."); + } +} + diff --git a/zone/gm_commands/npccast.cpp b/zone/gm_commands/npccast.cpp new file mode 100755 index 000000000..4abef226f --- /dev/null +++ b/zone/gm_commands/npccast.cpp @@ -0,0 +1,94 @@ +#include "../client.h" + +void command_npccast(Client *c, const Seperator *sep) +{ + if (c->GetTarget() && c->GetTarget()->IsNPC()) { + NPC *target = c->GetTarget()->CastToNPC(); + if (!sep->IsNumber(1) && sep->arg[1] && sep->IsNumber(2)) { + const char *entity_name = sep->arg[1] ? sep->arg[1] : 0; + auto spell_id = sep->arg[2] ? std::stoul(sep->arg[2]) : 0; + Mob *spell_target = entity_list.GetMob(entity_name); + if (spell_target && IsValidSpell(spell_id) && spell_id < SPDAT_RECORDS) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) casting {} ({}) on {} ({}).", + target->GetCleanName(), + target->GetID(), + GetSpellName(static_cast(spell_id)), + spell_id, + spell_target->GetCleanName(), + spell_target->GetID() + ).c_str() + ); + + target->CastSpell(spell_id, spell_target->GetID()); + } + else { + if (!spell_target) { + c->Message( + Chat::White, + fmt::format( + "Entity {} was not found", + entity_name + ).c_str() + ); + } + else if (!spell_id || !IsValidSpell(spell_id)) { + c->Message( + Chat::White, + fmt::format( + "Spell ID {} was not found", + spell_id + ).c_str() + ); + } + } + } + else if (sep->IsNumber(1) && sep->IsNumber(2)) { + uint16 entity_id = sep->arg[1] ? std::stoul(sep->arg[1]) : 0; + auto spell_id = sep->arg[2] ? std::stoul(sep->arg[2]) : 0; + Mob *spell_target = entity_list.GetMob(entity_id); + if (spell_target && IsValidSpell(spell_id) && spell_id < SPDAT_RECORDS) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) casting {} ({}) on {} ({}).", + target->GetCleanName(), + target->GetID(), + GetSpellName(static_cast(spell_id)), + spell_id, + spell_target->GetCleanName(), + spell_target->GetID() + ).c_str() + ); + + target->CastSpell(spell_id, spell_target->GetID()); + } + else { + if (!spell_target) { + c->Message( + Chat::White, + fmt::format( + "Entity ID {} was not found", + entity_id + ).c_str() + ); + } + else if (!spell_id || !IsValidSpell(spell_id)) { + c->Message( + Chat::White, + fmt::format( + "Spell ID {} was not found", + spell_id + ).c_str() + ); + } + } + } + } + else { + c->Message(Chat::White, "You must target an NPC to use this command."); + } +} + diff --git a/zone/gm_commands/npcedit.cpp b/zone/gm_commands/npcedit.cpp new file mode 100755 index 000000000..332b9efa0 --- /dev/null +++ b/zone/gm_commands/npcedit.cpp @@ -0,0 +1,1408 @@ +#include "../client.h" +#include "../groups.h" +#include "../mob_movement_manager.h" +#include "../raids.h" +#include "../raids.h" + +void command_npcedit(Client *c, const Seperator *sep) +{ + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "Error: Must have NPC targeted"); + return; + } + + if (strcasecmp(sep->arg[1], "help") == 0) { + + c->Message(Chat::White, "Help File for #npcedit. Syntax for commands are:"); + c->Message(Chat::White, "#npcedit name - Sets an NPC's Name"); + c->Message(Chat::White, "#npcedit lastname - Sets an NPC's Lastname"); + c->Message(Chat::White, "#npcedit level - Sets an NPC's Level"); + c->Message(Chat::White, "#npcedit race - Sets an NPC's Race"); + c->Message(Chat::White, "#npcedit class - Sets an NPC's Class"); + c->Message(Chat::White, "#npcedit bodytype - Sets an NPC's Bodytype"); + c->Message(Chat::White, "#npcedit hp - Sets an NPC's Hitpoints"); + c->Message(Chat::White, "#npcedit mana - Sets an NPC's Mana"); + c->Message(Chat::White, "#npcedit gender - Sets an NPC's Gender"); + c->Message(Chat::White, "#npcedit texture - Sets an NPC's Texture"); + c->Message(Chat::White, "#npcedit helmtexture - Sets an NPC's Helmet Texture"); + c->Message(Chat::White, "#npcedit herosforgemodel - Sets an NPC's Hero's Forge Model"); + c->Message(Chat::White, "#npcedit size - Sets an NPC's Size"); + c->Message(Chat::White, "#npcedit hpregen - Sets an NPC's Hitpoints Regeneration Rate Per Tick"); + c->Message(Chat::White, "#npcedit manaregen - Sets an NPC's Mana Regeneration Rate Per Tick"); + c->Message(Chat::White, "#npcedit loottable - Sets an NPC's Loottable ID"); + c->Message(Chat::White, "#npcedit merchantid - Sets an NPC's Merchant ID"); + c->Message(Chat::White, "#npcedit alt_currency_id - Sets an NPC's Alternate Currency ID"); + c->Message(Chat::White, "#npcedit spell - Sets an NPC's Spells List ID"); + c->Message(Chat::White, "#npcedit npc_spells_effects_id - Sets an NPC's Spell Effects ID"); + c->Message(Chat::White, "#npcedit faction - Sets an NPC's Faction ID"); + c->Message(Chat::White, "#npcedit adventure_template_id - Sets an NPC's Adventure Template ID"); + c->Message(Chat::White, "#npcedit trap_template - Sets an NPC's Trap Template ID"); + c->Message(Chat::White, "#npcedit damage [minimum] [maximum] - Sets an NPC's Damage"); + c->Message(Chat::White, "#npcedit attackcount - Sets an NPC's Attack Count"); + c->Message(Chat::White, "#npcedit special_attacks - Sets an NPC's Special Attacks"); + c->Message(Chat::White, "#npcedit special_abilities - Sets an NPC's Special Abilities"); + c->Message(Chat::White, "#npcedit aggroradius - Sets an NPC's Aggro Radius"); + c->Message(Chat::White, "#npcedit assistradius - Sets an NPC's Assist Radius"); + c->Message(Chat::White, "#npcedit featuresave - Saves an NPC's current facial features to the database"); + c->Message(Chat::White, "#npcedit armortint_id - Sets an NPC's Armor Tint ID"); + c->Message(Chat::White, "#npcedit color [red] [green] [blue] - Sets an NPC's Red, Green, and Blue armor tint"); + c->Message(Chat::White, "#npcedit ammoidfile - Sets an NPC's Ammo ID File"); + c->Message( + Chat::White, + "#npcedit weapon [primary_model] [secondary_model] - Sets an NPC's Primary and Secondary Weapon Model" + ); + c->Message(Chat::White, "#npcedit meleetype [primary_type] [secondary_type] - Sets an NPC's Melee Types"); + c->Message(Chat::White, "#npcedit rangedtype - Sets an NPC's Ranged Type"); + c->Message(Chat::White, "#npcedit runspeed - Sets an NPC's Run Speed"); + c->Message(Chat::White, "#npcedit mr - Sets an NPC's Magic Resistance"); + c->Message(Chat::White, "#npcedit pr - Sets an NPC's Poison Resistance"); + c->Message(Chat::White, "#npcedit dr - Sets an NPC's Disease Resistance"); + c->Message(Chat::White, "#npcedit fr - Sets an NPC's Fire Resistance"); + c->Message(Chat::White, "#npcedit cr - Sets an NPC's Cold Resistance"); + c->Message(Chat::White, "#npcedit corrup - Sets an NPC's Corruption Resistance"); + c->Message(Chat::White, "#npcedit phr - Sets and NPC's Physical Resistance"); + c->Message( + Chat::White, + "#npcedit seeinvis - Sets an NPC's See Invisible Flag [0 = Cannot See Invisible, 1 = Can See Invisible]" + ); + c->Message( + Chat::White, + "#npcedit seeinvisundead - Sets an NPC's See Invisible vs. Undead Flag [0 = Cannot See Invisible vs. Undead, 1 = Can See Invisible vs. Undead]" + ); + c->Message( + Chat::White, + "#npcedit qglobal - Sets an NPC's Quest Global Flag [0 = Quest Globals Off, 1 = Quest Globals On]" + ); + c->Message(Chat::White, "#npcedit ac - Sets an NPC's Armor Class"); + c->Message( + Chat::White, + "#npcedit npcaggro - Sets an NPC's NPC Aggro Flag [0 = Aggro NPCs Off, 1 = Aggro NPCs On]" + ); + c->Message(Chat::White, "#npcedit spawn_limit - Sets an NPC's Spawn Limit Counter"); + c->Message(Chat::White, "#npcedit attackspeed - Sets an NPC's Attack Speed Modifier"); + c->Message(Chat::White, "#npcedit attackdelay - Sets an NPC's Attack Delay"); + c->Message(Chat::White, "#npcedit findable - Sets an NPC's Findable Flag [0 = Not Findable, 1 = Findable]"); + c->Message(Chat::White, "#npcedit str - Sets an NPC's Strength"); + c->Message(Chat::White, "#npcedit sta - Sets an NPC's Stamina"); + c->Message(Chat::White, "#npcedit dex - Sets an NPC's Dexterity"); + c->Message(Chat::White, "#npcedit agi - Sets an NPC's Agility"); + c->Message(Chat::White, "#npcedit int - Sets an NPC's Intelligence"); + c->Message(Chat::White, "#npcedit wis - Sets an NPC's Wisdom"); + c->Message(Chat::White, "#npcedit cha - Sets an NPC's Charisma"); + c->Message( + Chat::White, + "#npcedit seehide - Sets an NPC's See Hide Flag [0 = Cannot See Hide, 1 = Can See Hide]" + ); + c->Message( + Chat::White, + "#npcedit seeimprovedhide - Sets an NPC's See Improved Hide Flag [0 = Cannot See Improved Hide, 1 = Can See Improved Hide]" + ); + c->Message(Chat::White, "#npcedit trackable - Sets an NPC's Trackable Flag [0 = Not Trackable, 1 = Trackable]"); + c->Message(Chat::White, "#npcedit atk - Sets an NPC's Attack"); + c->Message(Chat::White, "#npcedit accuracy - Sets an NPC's Accuracy"); + c->Message(Chat::White, "#npcedit avoidance - Sets an NPC's Avoidance"); + c->Message(Chat::White, "#npcedit slow_mitigation - Sets an NPC's Slow Mitigation"); + c->Message(Chat::White, "#npcedit version - Sets an NPC's Version"); + c->Message(Chat::White, "#npcedit maxlevel - Sets an NPC's Maximum Level"); + c->Message(Chat::White, "#npcedit scalerate - Sets an NPC's Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]"); + c->Message( + Chat::White, + "#npcedit spellscale - Sets an NPC's Spell Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]" + ); + c->Message( + Chat::White, + "#npcedit healscale - Sets an NPC's Heal Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]" + ); + c->Message( + Chat::White, + "#npcedit no_target - Sets an NPC's No Target Hotkey Flag [0 = Not Targetable with Target Hotkey, 1 = Targetable with Target Hotkey]" + ); + c->Message( + Chat::White, + "#npcedit raidtarget - Sets an NPC's Raid Target Flag [0 = Not a Raid Target, 1 = Raid Target]" + ); + c->Message(Chat::White, "#npcedit armtexture - Sets an NPC's Arm Texture"); + c->Message(Chat::White, "#npcedit bracertexture - Sets an NPC's Bracer Texture"); + c->Message(Chat::White, "#npcedit handtexture - Sets an NPC's Hand Texture"); + c->Message(Chat::White, "#npcedit legtexture - Sets an NPC's Leg Texture"); + c->Message(Chat::White, "#npcedit feettexture - Sets an NPC's Feet Texture"); + c->Message(Chat::White, "#npcedit walkspeed - Sets an NPC's Walk Speed"); + c->Message(Chat::White, "#npcedit show_name - Sets an NPC's Show Name Flag [0 = Hidden, 1 = Shown]"); + c->Message( + Chat::White, + "#npcedit untargetable - Sets an NPC's Untargetable Flag [0 = Targetable, 1 = Untargetable]" + ); + c->Message(Chat::White, "#npcedit charm_ac - Sets an NPC's Armor Class while Charmed"); + c->Message(Chat::White, "#npcedit charm_min_dmg - Sets an NPC's Minimum Damage while Charmed"); + c->Message(Chat::White, "#npcedit charm_max_dmg - Sets an NPC's Max Damage while Charmed"); + c->Message(Chat::White, "#npcedit charm_attack_delay - Sets an NPC's Attack Delay while Charmed"); + c->Message(Chat::White, "#npcedit charm_accuracy_rating - Sets an NPC's Accuracy Rating while Charmed"); + c->Message(Chat::White, "#npcedit charm_avoidance_rating - Sets an NPC's Avoidance Rating while Charmed"); + c->Message(Chat::White, "#npcedit charm_atk - Sets an NPC's Attack while Charmed"); + c->Message( + Chat::White, + "#npcedit skip_global_loot - Sets an NPC's Skip Global Loot Flag [0 = Don't Skip, 1 = Skip" + ); + c->Message( + Chat::White, + "#npcedit rarespawn - Sets an NPC's Rare Spawn Flag [0 = Not a Rare Spawn, 1 = Rare Spawn]" + ); + c->Message( + Chat::White, + "#npcedit stuck_behavior - Sets an NPC's Stuck Behavior [0 = Run to Target, 1 = Warp to Target, 2 = Take No Action, 3 = Evade Combat]" + ); + c->Message( + Chat::White, + "#npcedit flymode - Sets an NPC's flymode [0 = Ground, 1 = Flying, 2 = Levitating, 3 = Water, 4 = Floating, 5 = Levitating While Running]" + ); + c->Message( + Chat::White, + "#npcedit always_aggro - Sets an NPC's Always Aggro Flag [0 = Does not Always Aggro, 1 = Always Aggro]" + ); + c->Message( + Chat::White, + "#npcedit exp_mod - Sets an NPC's Experience Modifier [50 = 50%, 100 = 100%, 200 = 200%]" + ); + c->Message(Chat::White, "#npcedit setanimation - Sets an NPC's Animation on Spawn (Stored in spawn2 table)"); + c->Message( + Chat::White, + "#npcedit respawntime - Sets an NPC's Respawn Timer in Seconds (Stored in spawn2 table)" + ); + } + + uint32 npc_id = c->GetTarget()->CastToNPC()->GetNPCTypeID(); + if (strcasecmp(sep->arg[1], "name") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has the name '{}'.", npc_id, sep->arg[2]).c_str()); + std::string query = fmt::format("UPDATE npc_types SET name = '{}' WHERE id = {}", sep->arg[2], npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "lastname") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has the lastname '{}'.", npc_id, sep->arg[2]).c_str()); + std::string query = fmt::format("UPDATE npc_types SET lastname = '{}' WHERE id = {}", sep->arg[2], npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "level") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now level {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET level = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "race") == 0) { + auto race_id = atoi(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now a {} ({}).", npc_id, GetRaceIDName(race_id), race_id).c_str()); + std::string query = fmt::format("UPDATE npc_types SET race = {} WHERE id = {}", race_id, npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "class") == 0) { + auto class_id = atoi(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now a {} ({}).", npc_id, GetClassIDName(class_id), class_id).c_str()); + std::string query = fmt::format("UPDATE npc_types SET class = {} WHERE id = {}", class_id, npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "bodytype") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Bodytype {} .", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET bodytype = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "hp") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Health.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET hp = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "mana") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Mana.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET mana = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "gender") == 0) { + auto gender_id = atoi(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now a {} ({}).", npc_id, gender_id, GetGenderName(gender_id)).c_str()); + std::string query = fmt::format("UPDATE npc_types SET gender = {} WHERE id = {}", gender_id, npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "texture") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET texture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "helmtexture") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Helmet Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET helmtexture = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "herosforgemodel") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Hero's Forge Model {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET herosforgemodel = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "size") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now Size {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET size = {:.2f} WHERE id = {}", atof(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "hpregen") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now regenerates {} Health per Tick.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET hp_regen_rate = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "manaregen") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now regenerates {} Mana per Tick.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET mana_regen_rate = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "loottable") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using loottable ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET loottable_id = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "merchantid") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using merchant ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET merchant_id = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "alt_currency_id") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Alternate Currency ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET alt_currency_id = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "spell") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Spell List ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET npc_spells_id = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "npc_spells_effects_id") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using NPC Spells Effects ID {}.", npc_id, sep->arg[2]).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET npc_spells_effects_id = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "faction") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Faction ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET npc_faction_id = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "adventure_template_id") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Adventure Template ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET adventure_template_id = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "trap_template") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Trap Template ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET trap_template = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "damage") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} now hits from {} to {} damage.", + npc_id, + atoi(sep->arg[2]), + atoi(sep->arg[3])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET mindmg = {}, maxdmg = {} WHERE id = {}", + atoi(sep->arg[2]), + atoi(sep->arg[3]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "attackcount") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has an Attack Count of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET attack_count = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "special_attacks") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} is now using the following Special Attacks '{}'.", + npc_id, + sep->arg[2] + ).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET npcspecialattks = '{}' WHERE id = {}", + sep->arg[2], + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "special_abilities") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} is now using the following Special Abilities '{}'.", + npc_id, + sep->arg[2] + ).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET special_abilities = '{}' WHERE id = {}", + sep->arg[2], + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "aggroradius") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has an Aggro Radius of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET aggroradius = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "assistradius") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has an Assist Radius of {}", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET assistradius = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "featuresave") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} saved with all current facial feature settings.", npc_id).c_str()); + Mob *target = c->GetTarget(); + std::string query = fmt::format( + "UPDATE npc_types " + "SET luclin_haircolor = {}, luclin_beardcolor = {}, " + "luclin_hairstyle = {}, luclin_beard = {}, " + "face = {}, drakkin_heritage = {}, " + "drakkin_tattoo = {}, drakkin_details = {} " + "WHERE id = {}", + target->GetHairColor(), target->GetBeardColor(), + target->GetHairStyle(), target->GetBeard(), + target->GetLuclinFace(), target->GetDrakkinHeritage(), + target->GetDrakkinTattoo(), target->GetDrakkinDetails(), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "armortint_id") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Armor Tint ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET armortint_id = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "color") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} now has {} Red, {} Green, and {} Blue tinting on their armor.", + npc_id, + atoi(sep->arg[2]), + atoi(sep->arg[3]), + atoi(sep->arg[4])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET armortint_red = {}, armortint_green = {}, armortint_blue = {} WHERE id = {}", + atoi(sep->arg[2]), + atoi(sep->arg[3]), + atoi(sep->arg[4]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "ammoidfile") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Ammo ID File {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET ammo_idfile = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "weapon") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} will have Model {} set to their Primary and Model {} set to their Secondary on repop.", + npc_id, + atoi(sep->arg[2]), + atoi(sep->arg[3])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET d_melee_texture1 = {}, d_melee_texture2 = {} WHERE id = {}", + atoi(sep->arg[2]), + atoi(sep->arg[3]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "meleetype") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} now has a Primary Melee Type of {} and a Secondary Melee Type of {}.", + npc_id, + atoi(sep->arg[2]), + atoi(sep->arg[3])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET prim_melee_type = {}, sec_melee_type = {} WHERE id = {}", + atoi(sep->arg[2]), + atoi(sep->arg[3]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "rangedtype") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Ranged Type of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET ranged_type = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "runspeed") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now runs at {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET runspeed = {:.2f} WHERE id = {}", + atof(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "mr") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Magic Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET MR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "pr") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Poison Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET PR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "dr") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Disease Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET DR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "fr") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Fire Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET FR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "cr") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Cold Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET CR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "corrup") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Corruption Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET corrup = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "phr") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Physical Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET PhR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "seeinvis") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} can {} See Invisible.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET see_invis = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "seeinvisundead") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} can {} See Invisible vs. Undead.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET see_invis_undead = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "qglobal") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} can {} use Quest Globals.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET qglobal = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "ac") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Armor Class.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET ac = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "npcaggro") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} will {} aggro other NPCs that have a hostile faction.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET npc_aggro = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "spawn_limit") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Spawn Limit of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET spawn_limit = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "attackspeed") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has an Attack Speed of {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET attack_speed = {:.2f} WHERE id = {}", + atof(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "attackdelay") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has an Attack Delay of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET attack_delay = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "findable") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} is {} Findable.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET findable = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "str") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Strength.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET STR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "sta") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Stamina.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET STA = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "agi") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Agility.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET AGI = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "dex") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Dexterity.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET DEX = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "int") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Intelligence.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET _INT = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "wis") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Magic Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET WIS = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "cha") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Charisma.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET CHA = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "seehide") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} can {} See Hide.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET see_hide = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "seeimprovedhide") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} can {} See Improved Hide.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET see_improved_hide = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "trackable") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} is {} Trackable.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET trackable = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "atk") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Attack.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET atk = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "accuracy") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Accuracy.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET accuracy = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "avoidance") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Avoidance.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET avoidance = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "slow_mitigation") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has {} Slow Mitigation.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET slow_mitigation = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "version") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Version {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET version = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "maxlevel") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Maximum Level of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET maxlevel = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "scalerate") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Scaling Rate of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET scalerate = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "spellscale") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Spell Scaling Rate of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET spellscale = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "healscale") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Heal Scaling Rate of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET healscale = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "no_target") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} is {} Targetable with Target Hotkey.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET no_target_hotkey = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "raidtarget") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} is {} designated as a Raid Target.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET raid_target = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "armtexture") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Arm Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET armtexture = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "bracertexture") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Bracer Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET bracertexture = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "handtexture") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Hand Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET handtexture = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "legtexture") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Leg Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET legtexture = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "feettexture") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Feet Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET feettexture = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "walkspeed") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now walks at {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET walkspeed = {:.2f} WHERE id = {}", + atof(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "show_name") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} will {} show their name.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET show_name = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "untargetable") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} will {} be untargetable.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET untargetable = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "charm_ac") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has {} Armor Class while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET charm_ac = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "charm_min_dmg") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} now does {} Minimum Damage while Charmed.", + npc_id, + atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET charm_min_dmg = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "charm_max_dmg") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} now does {} Maximum Damage while Charmed.", + npc_id, + atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET charm_max_dmg = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "charm_attack_delay") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has {} Attack Delay while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET charm_attack_delay = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "charm_accuracy_rating") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} now has {} Accuracy Rating while Charmed.", + npc_id, + atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET charm_accuracy_rating = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "charm_avoidance_rating") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} now has {} Avoidance Rating while Charmed.", + npc_id, + atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET charm_avoidance_rating = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "charm_atk") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has {} Attack while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET charm_atk = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "skip_global_loot") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} will {} skip Global Loot.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET skip_global_loot = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "rarespawn") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} is {} designated as a Rare Spawn.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET rare_spawn = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "stuck_behavior") == 0) { + auto behavior_id = atoi(sep->arg[2]); + std::string behavior_name = "Unknown"; + if (behavior_id == MobStuckBehavior::RunToTarget) { + behavior_name = "Run To Target"; + } + else if (behavior_id == MobStuckBehavior::WarpToTarget) { + behavior_name = "Warp To Target"; + } + else if (behavior_id == MobStuckBehavior::TakeNoAction) { + behavior_name = "Take No Action"; + } + else if (behavior_id == MobStuckBehavior::EvadeCombat) { + behavior_name = "Evade Combat"; + } + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} is now using Stuck Behavior {} ({}).", + npc_id, + behavior_name, + behavior_id + ).c_str()); + std::string query = fmt::format("UPDATE npc_types SET stuck_behavior = {} WHERE id = {}", behavior_id, npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "flymode") == 0) { + auto flymode_id = atoi(sep->arg[2]); + std::string flymode_name = "Unknown"; + if (flymode_id == GravityBehavior::Ground) { + flymode_name = "Ground"; + } + else if (flymode_id == GravityBehavior::Flying) { + flymode_name = "Flying"; + } + else if (flymode_id == GravityBehavior::Levitating) { + flymode_name = "Levitating"; + } + else if (flymode_id == GravityBehavior::Water) { + flymode_name = "Water"; + } + else if (flymode_id == GravityBehavior::Floating) { + flymode_name = "Floating"; + } + else if (flymode_id == GravityBehavior::LevitateWhileRunning) { + flymode_name = "Levitating While Running"; + } + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Fly Mode {} ({}).", npc_id, flymode_name, flymode_id).c_str()); + std::string query = fmt::format("UPDATE npc_types SET flymode = {} WHERE id = {}", flymode_id, npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "always_aggro") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} will {} Always Aggro.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET always_aggro = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "exp_mod") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has an Experience Modifier of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET exp_mod = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "setanimation") == 0) { + int animation = 0; + std::string animation_name = "Unknown"; + if (sep->arg[2] && atoi(sep->arg[2]) <= 4) { + if (strcasecmp(sep->arg[2], "stand") == 0 || atoi(sep->arg[2]) == 0) { // Stand + animation = 0; + animation_name = "Standing"; + } + else if (strcasecmp(sep->arg[2], "sit") == 0 || atoi(sep->arg[2]) == 1) { // Sit + animation = 1; + animation_name = "Sitting"; + } + else if (strcasecmp(sep->arg[2], "crouch") == 0 || atoi(sep->arg[2]) == 2) { // Crouch + animation = 2; + animation_name = "Crouching"; + } + else if (strcasecmp(sep->arg[2], "dead") == 0 || atoi(sep->arg[2]) == 3) { // Dead + animation = 3; + animation_name = "Dead"; + } + else if (strcasecmp(sep->arg[2], "loot") == 0 || atoi(sep->arg[2]) == 4) { // Looting Animation + animation = 4; + animation_name = "Looting"; + } + } + else { + c->Message( + Chat::White, + "You must specify an Animation (0 = Stand, 1 = Sit, 2 = Crouch, 3 = Dead, 4 = Loot)" + ); + c->Message(Chat::White, "Example: #npcedit setanimation sit"); + c->Message(Chat::White, "Example: #npcedit setanimation 0"); + return; + } + + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} now has their Spawn Animation set to {} ({}) on Spawn Group ID {}.", + npc_id, + animation_name, + animation, + c->GetTarget()->CastToNPC()->GetSpawnGroupId() + ).c_str() + ); + std::string query = fmt::format( + "UPDATE spawn2 SET animation = {} WHERE spawngroupID = {}", + animation, + c->GetTarget()->CastToNPC()->GetSpawnGroupId() + ); + content_db.QueryDatabase(query); + + c->GetTarget()->SetAppearance(EmuAppearance(animation)); + return; + } + + if (strcasecmp(sep->arg[1], "respawntime") == 0) { + if (sep->arg[2][0] && sep->IsNumber(sep->arg[2]) && atoi(sep->arg[2]) > 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} now has a Respawn Timer of {} Seconds on Spawn Group ID {}.", + npc_id, + atoi(sep->arg[2]), + c->GetTarget()->CastToNPC()->GetSpawnGroupId()).c_str()); + std::string query = fmt::format( + "UPDATE spawn2 SET respawntime = {} WHERE spawngroupID = {} AND version = {}", + atoi(sep->arg[2]), + c->GetTarget()->CastToNPC()->GetSpawnGroupId(), + zone->GetInstanceVersion()); + content_db.QueryDatabase(query); + return; + } + } + + if ((sep->arg[1][0] == 0 || strcasecmp(sep->arg[1], "*") == 0) || + ((c->GetTarget() == 0) || (c->GetTarget()->IsClient()))) { + c->Message(Chat::White, "Type #npcedit help for more info"); + } + +} + diff --git a/zone/gm_commands/npceditmass.cpp b/zone/gm_commands/npceditmass.cpp new file mode 100755 index 000000000..8dcb2c338 --- /dev/null +++ b/zone/gm_commands/npceditmass.cpp @@ -0,0 +1,194 @@ +#include "../client.h" + +void command_npceditmass(Client *c, const Seperator *sep) +{ + if (strcasecmp(sep->arg[1], "usage") == 0) { + c->Message( + Chat::White, + "#npceditmass search_column [exact_match: =]search_value change_column change_value (apply)" + ); + return; + } + + std::string query = SQL( + SELECT + COLUMN_NAME + FROM + INFORMATION_SCHEMA.COLUMNS + WHERE + table_name = 'npc_types' + AND + COLUMN_NAME != 'id' + ); + + std::string search_column, search_value, change_column, change_value; + if (sep->arg[1]) { + search_column = sep->arg[1]; + } + if (sep->arg[2]) { + search_value = sep->arg[2]; + } + if (sep->arg[3]) { + change_column = sep->arg[3]; + } + if (sep->arg[4]) { + change_value = sep->arg[4]; + } + + bool valid_change_column = false; + bool valid_search_column = false; + auto results = content_db.QueryDatabase(query); + + std::vector possible_column_options; + + for (auto row = results.begin(); row != results.end(); ++row) { + if (row[0] == change_column) { + valid_change_column = true; + } + if (row[0] == search_column) { + valid_search_column = true; + } + + possible_column_options.push_back(row[0]); + } + + std::string options_glue = ", "; + + if (!valid_search_column) { + c->Message(Chat::Red, "You must specify a valid search column. [%s] is not valid", search_column.c_str()); + c->Message(Chat::Yellow, "Possible columns [%s]", implode(options_glue, possible_column_options).c_str()); + return; + } + + if (!valid_change_column) { + c->Message(Chat::Red, "You must specify a valid change column. [%s] is not valid", change_column.c_str()); + c->Message(Chat::Yellow, "Possible columns [%s]", implode(options_glue, possible_column_options).c_str()); + return; + } + + if (!valid_search_column || !valid_change_column) { + c->Message(Chat::Red, "One requested column is invalid"); + return; + } + + query = fmt::format( + SQL( + select + id, + name, + { 0 }, + { 1 } + from + npc_types + where + id IN( + select + spawnentry.npcID + from + spawnentry + join spawn2 on spawn2.spawngroupID = spawnentry.spawngroupID + where + spawn2.zone = '{2}' and spawn2.version = {3} + ) + ), + search_column, + change_column, + zone->GetShortName(), + zone->GetInstanceVersion() + ); + + std::string status = "(Searching)"; + + if (strcasecmp(sep->arg[5], "apply") == 0) { + status = "(Applying)"; + } + + std::vector npc_ids; + + bool exact_match = false; + if (search_value[0] == '=') { + exact_match = true; + search_value = search_value.substr(1); + } + + int found_count = 0; + results = content_db.QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + + std::string npc_id = row[0]; + std::string npc_name = row[1]; + std::string search_column_value = str_tolower(row[2]); + std::string change_column_current_value = row[3]; + + if (exact_match) { + if (search_column_value.compare(search_value) != 0) { + continue; + } + } + else { + if (search_column_value.find(search_value) == std::string::npos) { + continue; + } + } + + c->Message( + Chat::Yellow, + fmt::format( + "NPC ({0}) [{1}] ({2}) [{3}] Current ({4}) [{5}] New [{6}] {7}", + npc_id, + npc_name, + search_column, + search_column_value, + change_column, + change_column_current_value, + change_value, + status + ).c_str() + ); + + npc_ids.push_back(npc_id); + + found_count++; + } + + std::string saylink = fmt::format( + "#npceditmass {} {}{} {} {} apply", + search_column, + (exact_match ? "=" : ""), + search_value, + change_column, + change_value + ); + + if (strcasecmp(sep->arg[5], "apply") == 0) { + std::string npc_ids_string = implode(",", npc_ids); + if (npc_ids_string.empty()) { + c->Message(Chat::Red, "Error: Ran into an unknown error compiling NPC IDs"); + return; + } + + content_db.QueryDatabase( + fmt::format( + "UPDATE `npc_types` SET {} = '{}' WHERE id IN ({})", + change_column, + change_value, + npc_ids_string + ) + ); + + c->Message(Chat::Yellow, "Changes applied to (%i) NPC's", found_count); + zone->Repop(); + } + else { + c->Message(Chat::Yellow, "Found (%i) NPC's that match this search...", found_count); + + if (found_count > 0) { + c->Message( + Chat::Yellow, "To apply these changes, click <%s> or type [%s]", + EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Apply").c_str(), + saylink.c_str() + ); + } + } +} + diff --git a/zone/gm_commands/npcemote.cpp b/zone/gm_commands/npcemote.cpp new file mode 100755 index 000000000..c4ba83388 --- /dev/null +++ b/zone/gm_commands/npcemote.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_npcemote(Client *c, const Seperator *sep) +{ + if (c->GetTarget() && c->GetTarget()->IsNPC() && sep->arg[1][0]) { + c->GetTarget()->Emote(sep->argplus[1]); + } + else { + c->Message(Chat::White, "Usage: #npcemote message (requires NPC target"); + } +} + diff --git a/zone/gm_commands/npcloot.cpp b/zone/gm_commands/npcloot.cpp new file mode 100755 index 000000000..6ebe557ac --- /dev/null +++ b/zone/gm_commands/npcloot.cpp @@ -0,0 +1,104 @@ +#include "../client.h" +#include "../corpse.h" + +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]"); + } +} + diff --git a/zone/gm_commands/npcsay.cpp b/zone/gm_commands/npcsay.cpp new file mode 100755 index 000000000..96d79232f --- /dev/null +++ b/zone/gm_commands/npcsay.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_npcsay(Client *c, const Seperator *sep) +{ + if (c->GetTarget() && c->GetTarget()->IsNPC() && sep->arg[1][0]) { + c->GetTarget()->Say(sep->argplus[1]); + } + else { + c->Message(Chat::White, "Usage: #npcsay message (requires NPC target"); + } +} + diff --git a/zone/gm_commands/npcshout.cpp b/zone/gm_commands/npcshout.cpp new file mode 100755 index 000000000..f04d74a9f --- /dev/null +++ b/zone/gm_commands/npcshout.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_npcshout(Client *c, const Seperator *sep) +{ + if (c->GetTarget() && c->GetTarget()->IsNPC() && sep->arg[1][0]) { + c->GetTarget()->Shout(sep->argplus[1]); + } + else { + c->Message(Chat::White, "Usage: #npcshout message (requires NPC target"); + } +} + diff --git a/zone/gm_commands/npcspawn.cpp b/zone/gm_commands/npcspawn.cpp new file mode 100755 index 000000000..dd88bbece --- /dev/null +++ b/zone/gm_commands/npcspawn.cpp @@ -0,0 +1,97 @@ +#include "../client.h" + +void command_npcspawn(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Command Syntax: #npcspawn [Add|Create|Delete|Remove|Update]"); + return; + } + + if (!(c->GetTarget() && c->GetTarget()->IsNPC())) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } + + NPC *target = c->GetTarget()->CastToNPC(); + std::string spawn_type = str_tolower(sep->arg[1]); + uint32 extra = 0; + bool is_add = spawn_type.find("add") != std::string::npos; + bool is_create = spawn_type.find("create") != std::string::npos; + bool is_delete = spawn_type.find("delete") != std::string::npos; + bool is_remove = spawn_type.find("remove") != std::string::npos; + bool is_update = spawn_type.find("update") != std::string::npos; + if (!is_add && !is_create && !is_delete && !is_remove && !is_update) { + c->Message(Chat::White, "Command Syntax: #npcspawn [Add|Create|Delete|Remove|Update]"); + return; + } + + if (is_add || is_create) { + extra = ( + sep->IsNumber(2) ? + ( + is_add ? + std::stoi(sep->arg[2]) : + 1 + ) : ( + is_add ? + 1200 : + 0 + ) + ); // Default to 1200 for Add, 0 for Create if not set + content_db.NPCSpawnDB( + is_add ? NPCSpawnTypes::AddNewSpawngroup : NPCSpawnTypes::CreateNewSpawn, + zone->GetShortName(), + zone->GetInstanceVersion(), + c, + target, + extra + ); + c->Message( + Chat::White, + fmt::format( + "Spawn {} | Name: {} ({})", + is_add ? "Added" : "Created", + target->GetCleanName(), + target->GetID() + ).c_str() + ); + } + else if (is_delete || is_remove || is_update) { + uint8 spawn_update_type = ( + is_delete ? + NPCSpawnTypes::DeleteSpawn : + ( + is_remove ? + NPCSpawnTypes::RemoveSpawn : + NPCSpawnTypes::UpdateAppearance + ) + ); + std::string spawn_message = ( + is_delete ? + "Deleted" : + ( + is_remove ? + "Removed" : + "Updated" + ) + ); + content_db.NPCSpawnDB( + spawn_update_type, + zone->GetShortName(), + zone->GetInstanceVersion(), + c, + target + ); + c->Message( + Chat::White, + fmt::format( + "Spawn {} | Name: {} ({})", + spawn_message, + target->GetCleanName(), + target->GetID() + ).c_str() + ); + } +} + diff --git a/zone/gm_commands/npcspecialattk.cpp b/zone/gm_commands/npcspecialattk.cpp new file mode 100755 index 000000000..4d2a63dc8 --- /dev/null +++ b/zone/gm_commands/npcspecialattk.cpp @@ -0,0 +1,16 @@ +#include "../client.h" + +void command_npcspecialattk(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0 || c->GetTarget()->IsClient() || strlen(sep->arg[1]) <= 0 || strlen(sep->arg[2]) <= 0) { + c->Message( + Chat::White, + "Usage: #npcspecialattk *flagchar* *permtag* (Flags are E(nrage) F(lurry) R(ampage) S(ummon), permtag is 1 = True, 0 = False)." + ); + } + else { + c->GetTarget()->CastToNPC()->NPCSpecialAttacks(sep->arg[1], atoi(sep->arg[2])); + c->Message(Chat::White, "NPC Special Attack set."); + } +} + diff --git a/zone/gm_commands/npcstats.cpp b/zone/gm_commands/npcstats.cpp new file mode 100755 index 000000000..9bc05e766 --- /dev/null +++ b/zone/gm_commands/npcstats.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +void command_npcstats(Client *c, const Seperator *sep) +{ + if (c->GetTarget() && c->GetTarget()->IsNPC()) { + NPC *target = c->GetTarget()->CastToNPC(); + + // Stats + target->ShowStats(c); + + // Loot Data + if (target->GetLoottableID()) { + target->QueryLoot(c); + } + } + else { + c->Message(Chat::White, "You must target an NPC to use this command."); + } +} + diff --git a/zone/gm_commands/npctype_cache.cpp b/zone/gm_commands/npctype_cache.cpp new file mode 100755 index 000000000..27cc45429 --- /dev/null +++ b/zone/gm_commands/npctype_cache.cpp @@ -0,0 +1,27 @@ +#include "../client.h" + +void command_npctype_cache(Client *c, const Seperator *sep) +{ + if (sep->argnum > 0) { + for (int i = 0; i < sep->argnum; ++i) { + if (strcasecmp(sep->arg[i + 1], "all") == 0) { + c->Message(Chat::White, "Clearing all npc types from the cache."); + zone->ClearNPCTypeCache(-1); + } + else { + int id = atoi(sep->arg[i + 1]); + if (id > 0) { + c->Message(Chat::White, "Clearing npc type %d from the cache.", id); + zone->ClearNPCTypeCache(id); + return; + } + } + } + } + else { + c->Message(Chat::White, "Usage:"); + c->Message(Chat::White, "#npctype_cache [npctype_id] ..."); + c->Message(Chat::White, "#npctype_cache all"); + } +} + diff --git a/zone/gm_commands/npctypespawn.cpp b/zone/gm_commands/npctypespawn.cpp new file mode 100755 index 000000000..18c757aa6 --- /dev/null +++ b/zone/gm_commands/npctypespawn.cpp @@ -0,0 +1,29 @@ +#include "../client.h" + +void command_npctypespawn(Client *c, const Seperator *sep) +{ + if (sep->IsNumber(1)) { + const NPCType *tmp = 0; + if ((tmp = content_db.LoadNPCTypesData(atoi(sep->arg[1])))) { + //tmp->fixedZ = 1; + auto npc = new NPC(tmp, 0, c->GetPosition(), GravityBehavior::Water); + if (npc && sep->IsNumber(2)) { + npc->SetNPCFactionID(atoi(sep->arg[2])); + } + + npc->AddLootTable(); + if (npc->DropsGlobalLoot()) { + npc->CheckGlobalLootTables(); + } + entity_list.AddNPC(npc); + } + else { + c->Message(Chat::White, "NPC Type %i not found", atoi(sep->arg[1])); + } + } + else { + c->Message(Chat::White, "Usage: #npctypespawn npctypeid factionid"); + } + +} + diff --git a/zone/gm_commands/nudge.cpp b/zone/gm_commands/nudge.cpp new file mode 100755 index 000000000..e8f4bfaa0 --- /dev/null +++ b/zone/gm_commands/nudge.cpp @@ -0,0 +1,77 @@ +#include "../client.h" + +void command_nudge(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #nudge [x=f] [y=f] [z=f] [h=f] (partial/mixed arguments allowed)"); + } + else { + + auto target = c->GetTarget(); + if (!target) { + + c->Message(Chat::Yellow, "This command requires a target."); + return; + } + if (target->IsMoving()) { + + c->Message(Chat::Yellow, "This command requires a stationary target."); + return; + } + + glm::vec4 position_offset(0.0f, 0.0f, 0.0f, 0.0f); + for (auto index = 1; index <= 4; ++index) { + + if (!sep->arg[index]) { + continue; + } + + Seperator argsep(sep->arg[index], '='); + if (!argsep.arg[1][0]) { + continue; + } + + switch (argsep.arg[0][0]) { + case 'x': + position_offset.x = atof(argsep.arg[1]); + break; + case 'y': + position_offset.y = atof(argsep.arg[1]); + break; + case 'z': + position_offset.z = atof(argsep.arg[1]); + break; + case 'h': + position_offset.w = atof(argsep.arg[1]); + break; + default: + break; + } + } + + const auto ¤t_position = target->GetPosition(); + glm::vec4 new_position( + (current_position.x + position_offset.x), + (current_position.y + position_offset.y), + (current_position.z + position_offset.z), + (current_position.w + position_offset.w) + ); + + target->GMMove(new_position.x, new_position.y, new_position.z, new_position.w); + + c->Message( + Chat::White, + "Nudging '%s' to {%1.3f, %1.3f, %1.3f, %1.2f} (adjustment: {%1.3f, %1.3f, %1.3f, %1.2f})", + target->GetName(), + new_position.x, + new_position.y, + new_position.z, + new_position.w, + position_offset.x, + position_offset.y, + position_offset.z, + position_offset.w + ); + } +} + diff --git a/zone/gm_commands/nukebuffs.cpp b/zone/gm_commands/nukebuffs.cpp new file mode 100755 index 000000000..8d1267b3c --- /dev/null +++ b/zone/gm_commands/nukebuffs.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_nukebuffs(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0) { + c->BuffFadeAll(); + } + else { + c->GetTarget()->BuffFadeAll(); + } +} + diff --git a/zone/gm_commands/nukeitem.cpp b/zone/gm_commands/nukeitem.cpp new file mode 100755 index 000000000..91e219b4d --- /dev/null +++ b/zone/gm_commands/nukeitem.cpp @@ -0,0 +1,16 @@ +#include "../client.h" + +void command_nukeitem(Client *c, const Seperator *sep) +{ + int numitems, itemid; + + if (c->GetTarget() && c->GetTarget()->IsClient() && (sep->IsNumber(1) || sep->IsHexNumber(1))) { + itemid = sep->IsNumber(1) ? atoi(sep->arg[1]) : hextoi(sep->arg[1]); + numitems = c->GetTarget()->CastToClient()->NukeItem(itemid); + c->Message(Chat::White, " %u items deleted", numitems); + } + else { + c->Message(Chat::White, "Usage: (targted) #nukeitem itemnum - removes the item from the player's inventory"); + } +} + diff --git a/zone/gm_commands/object.cpp b/zone/gm_commands/object.cpp new file mode 100755 index 000000000..9c233cd80 --- /dev/null +++ b/zone/gm_commands/object.cpp @@ -0,0 +1,1263 @@ +#include "../client.h" +#include "../object.h" +#include "../doors.h" + +void command_object(Client *c, const Seperator *sep) +{ + if (!c) { + return; + } // Crash Suppressant: No client. How did we get here? + + // Save it here. We sometimes have need to refer to it in multiple places. + const char *usage_string = "Usage: #object List|Add|Edit|Move|Rotate|Save|Copy|Delete|Undo"; + + if ((!sep) || (sep->argnum == 0)) { + c->Message(Chat::White, usage_string); + return; + } + + Object *o = nullptr; + Object_Struct od; + Door_Struct *ds; + uint32 id = 0; + uint32 itemid = 0; + uint32 icon = 0; + uint32 instance = 0; + uint32 newid = 0; + uint16 radius; + EQApplicationPacket *app; + + bool bNewObject = false; + + float x2; + float y2; + + // Temporary object type for static objects to allow manipulation + // NOTE: Zone::LoadZoneObjects() currently loads this as an uint8, so max value is 255! + static const uint32 staticType = 255; + + // Case insensitive commands (List == list == LIST) + strlwr(sep->arg[1]); + + if (strcasecmp(sep->arg[1], "list") == 0) { + // Insufficient or invalid args + if ((sep->argnum < 2) || (sep->arg[2][0] < '0') || + ((sep->arg[2][0] > '9') && ((sep->arg[2][0] & 0xDF) != 'A'))) { + c->Message(Chat::White, "Usage: #object List All|(radius)"); + return; + } + + if ((sep->arg[2][0] & 0xDF) == 'A') { + radius = 0; // List All + } + else if ((radius = atoi(sep->arg[2])) <= 0) { + radius = 500; + } // Invalid radius. Default to 500 units. + + if (radius == 0) + c->Message(Chat::White, "Objects within this zone:"); + else + c->Message(Chat::White, "Objects within %u units of your current location:", radius); + + std::string query; + if (radius) + query = StringFormat( + "SELECT id, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20 " + "FROM object WHERE zoneid = %u AND version = %u " + "AND (xpos BETWEEN %.1f AND %.1f) " + "AND (ypos BETWEEN %.1f AND %.1f) " + "AND (zpos BETWEEN %.1f AND %.1f) " + "ORDER BY id", + zone->GetZoneID(), zone->GetInstanceVersion(), + c->GetX() - radius, // Yes, we're actually using a bounding box instead of a radius. + c->GetX() + radius, // Much less processing power used this way. + c->GetY() - radius, c->GetY() + radius, c->GetZ() - radius, c->GetZ() + radius + ); + else + query = StringFormat( + "SELECT id, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20 " + "FROM object WHERE zoneid = %u AND version = %u " + "ORDER BY id", + zone->GetZoneID(), zone->GetInstanceVersion()); + + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Error in objects query"); + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) { + id = atoi(row[0]); + od.x = atof(row[1]); + od.y = atof(row[2]); + od.z = atof(row[3]); + od.heading = atof(row[4]); + itemid = atoi(row[5]); + strn0cpy(od.object_name, row[6], sizeof(od.object_name)); + od.object_name[sizeof(od.object_name) - 1] = + '\0'; // Required if strlen(row[col++]) exactly == sizeof(object_name) + + od.object_type = atoi(row[7]); + icon = atoi(row[8]); + od.size = atoi(row[9]); + od.solidtype = atoi(row[10]); + od.unknown020 = atoi(row[11]); + + switch (od.object_type) { + case 0: // Static Object + case staticType: // Static Object unlocked for changes + if (od.size == 0) // Unknown08 field is optional Size parameter for static objects + od.size = 100; // Static object default Size is 100% + + c->Message( + Chat::White, "- STATIC Object (%s): id %u, x %.1f, y %.1f, z %.1f, h %.1f, model %s, " + "size %u, solidtype %u, incline %u", + (od.object_type == 0) ? "locked" : "unlocked", id, od.x, od.y, od.z, + od.heading, od.object_name, od.size, od.solidtype, od.unknown020 + ); + break; + + case OT_DROPPEDITEM: // Ground Spawn + c->Message( + Chat::White, "- TEMPORARY Object: id %u, x %.1f, y %.1f, z %.1f, h %.1f, itemid %u, " + "model %s, icon %u", + id, od.x, od.y, od.z, od.heading, itemid, od.object_name, icon + ); + break; + + default: // All others == Tradeskill Objects + c->Message( + Chat::White, "- TRADESKILL Object: id %u, x %.1f, y %.1f, z %.1f, h %.1f, model %s, " + "type %u, icon %u", + id, od.x, od.y, od.z, od.heading, od.object_name, od.object_type, icon + ); + break; + } + } + + c->Message(Chat::White, "%u object%s found", results.RowCount(), (results.RowCount() == 1) ? "" : "s"); + return; + } + + if (strcasecmp(sep->arg[1], "add") == 0) { + // Insufficient or invalid arguments + if ((sep->argnum < 3) || + ((sep->arg[3][0] == '\0') && (sep->arg[4][0] < '0') && (sep->arg[4][0] > '9'))) { + c->Message( + Chat::White, "Usage: (Static Object): #object Add [ObjectID] 0 Model [SizePercent] " + "[SolidType] [Incline]" + ); + c->Message(Chat::White, "Usage: (Tradeskill Object): #object Add [ObjectID] TypeNum Model Icon"); + c->Message( + Chat::White, "- Notes: Model must start with a letter, max length 16. SolidTypes = 0 (Solid), " + "1 (Sometimes Non-Solid)" + ); + return; + } + + int col; + + if (sep->argnum > 3) { // Model name in arg3? + if ((sep->arg[3][0] <= '9') && (sep->arg[3][0] >= '0')) { + // Nope, user must have specified ObjectID. Extract it. + id = atoi(sep->arg[2]); + col = 1; // Bump all other arguments one to the right. Model is in arg4. + } + else { + // Yep, arg3 is non-numeric, ObjectID must be omitted and model must be arg3 + id = 0; + col = 0; + } + } + else { + // Nope, only 3 args. Object ID must be omitted and arg3 must be model. + id = 0; + col = 0; + } + + memset(&od, 0, sizeof(od)); + + od.object_type = atoi(sep->arg[2 + col]); + + switch (od.object_type) { + case 0: // Static Object + if ((sep->argnum - col) > 3) { + od.size = atoi(sep->arg[4 + col]); // Size specified + + if ((sep->argnum - col) > 4) { + od.solidtype = atoi(sep->arg[5 + col]); // SolidType specified + + if ((sep->argnum - col) > 5) { + od.unknown020 = atoi(sep->arg[6 + col]); + } // Incline specified + } + } + break; + + case 1: // Ground Spawn + c->Message( + Chat::White, "ERROR: Object Type 1 is used for temporarily spawned ground spawns and dropped " + "items, which are not supported with #object. See the 'ground_spawns' table in " + "the database." + ); + return; + + default: // Everything else == Tradeskill Object + icon = ((sep->argnum - col) > 3) ? atoi(sep->arg[4 + col]) : 0; + + if (icon == 0) { + c->Message(Chat::White, "ERROR: Required property 'Icon' not specified for Tradeskill Object"); + return; + } + + break; + } + + od.x = c->GetX(); + od.y = c->GetY(); + od.z = c->GetZ() - (c->GetSize() * 0.625f); + od.heading = c->GetHeading(); + + std::string query; + if (id) { + // ID specified. Verify that it doesn't already exist. + query = StringFormat("SELECT COUNT(*) FROM object WHERE ID = %u", id); + auto results = content_db.QueryDatabase(query); + if (results.Success() && results.RowCount() != 0) { + auto row = results.begin(); + if (atoi(row[0]) > 0) { // Yep, in database already. + id = 0; + } + } + + // Not in database. Already spawned, just not saved? + // Yep, already spawned. + if (id && entity_list.FindObject(id)) { + id = 0; + } + + if (id == 0) { + c->Message(Chat::White, "ERROR: An object already exists with the id %u", atoi(sep->arg[2])); + return; + } + } + + int objectsFound = 0; + // Verify no other objects already in this spot (accidental double-click of Hotkey?) + query = StringFormat( + "SELECT COUNT(*) FROM object WHERE zoneid = %u " + "AND version=%u AND (xpos BETWEEN %.1f AND %.1f) " + "AND (ypos BETWEEN %.1f AND %.1f) " + "AND (zpos BETWEEN %.1f AND %.1f)", + zone->GetZoneID(), zone->GetInstanceVersion(), od.x - 0.2f, + od.x + 0.2f, // Yes, we're actually using a bounding box instead of a radius. + od.y - 0.2f, od.y + 0.2f, // Much less processing power used this way. + od.z - 0.2f, od.z + 0.2f + ); // It's pretty forgiving, though, allowing for close-proximity objects + + auto results = content_db.QueryDatabase(query); + if (results.Success() && results.RowCount() != 0) { + auto row = results.begin(); + objectsFound = atoi(row[0]); // Number of nearby objects from database + } + + // No objects found in database too close. How about spawned but not yet saved? + if (objectsFound == 0 && entity_list.FindNearbyObject(od.x, od.y, od.z, 0.2f)) { + objectsFound = 1; + } + + if (objectsFound) { + c->Message(Chat::White, "ERROR: Object already at this location."); + return; + } + + // Strip any single quotes from objectname (SQL injection FTL!) + strn0cpy(od.object_name, sep->arg[3 + col], sizeof(od.object_name)); + + uint32 len = strlen(od.object_name); + for (col = 0; col < (uint32) len; col++) { + if (od.object_name[col] != '\'') { + continue; + } + + // Uh oh, 1337 h4x0r monkeying around! Strip that apostrophe! + memcpy(&od.object_name[col], &od.object_name[col + 1], len - col); + len--; + col--; + } + + strupr(od.object_name); // Model names are always upper-case. + + if ((od.object_name[0] < 'A') || (od.object_name[0] > 'Z')) { + c->Message(Chat::White, "ERROR: Model name must start with a letter."); + return; + } + + if (id == 0) { + // No ID specified. Get a best-guess next number from the database + // If there's a problem retrieving an ID from the database, it'll end up being object # 1. No + // biggie. + + query = "SELECT MAX(id) FROM object"; + results = content_db.QueryDatabase(query); + if (results.Success() && results.RowCount() != 0) { + auto row = results.begin(); + id = atoi(row[0]); + } + + id++; + } + + // Make sure not to overwrite already-spawned objects that haven't been saved yet. + while (o = entity_list.FindObject(id)) + id++; + + // Static object + if (od.object_type == 0) { + od.object_type = staticType; + } // Temporary. We'll make it 0 when we Save + + od.zone_id = zone->GetZoneID(); + od.zone_instance = zone->GetInstanceVersion(); + + o = new Object(id, od.object_type, icon, od, nullptr); + + // Add to our zone entity list and spawn immediately for all clients + entity_list.AddObject(o, true); + + // Bump player back to avoid getting stuck inside new object + + x2 = 10.0f * sin(c->GetHeading() / 256.0f * 3.14159265f); + y2 = 10.0f * cos(c->GetHeading() / 256.0f * 3.14159265f); + c->MovePC(c->GetX() - x2, c->GetY() - y2, c->GetZ(), c->GetHeading()); + + c->Message( + Chat::White, "Spawning object with tentative id %u at location (%.1f, %.1f, %.1f heading %.1f). Use " + "'#object Save' to save to database when satisfied with placement.", + id, od.x, od.y, od.z, od.heading + ); + + // Temporary Static Object + if (od.object_type == staticType) + c->Message( + Chat::White, "- Note: Static Object will act like a tradeskill container and will not reflect " + "size, solidtype, or incline values until you commit with '#object Save', after " + "which it will be unchangeable until you use '#object Edit' and zone back in." + ); + + return; + } + + if (strcasecmp(sep->arg[1], "edit") == 0) { + + if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) < 1)) { + c->Message(Chat::White, "Usage: #object Edit (ObjectID) [PropertyName] [NewValue]"); + c->Message(Chat::White, "- Static Object (Type 0) Properties: model, type, size, solidtype, incline"); + c->Message(Chat::White, "- Tradeskill Object (Type 2+) Properties: model, type, icon"); + + return; + } + + o = entity_list.FindObject(id); + + // Object already available in-zone? + if (o) { + // Yep, looks like we can make real-time changes. + if (sep->argnum < 4) { + // Or not. '#object Edit (ObjectID)' called without PropertyName and NewValue + c->Message(Chat::White, "Note: Object %u already unlocked and ready for changes", id); + return; + } + } + else { + // Object not found in-zone in a modifiable form. Check for valid matching circumstances. + std::string query = StringFormat("SELECT zoneid, version, type FROM object WHERE id = %u", id); + auto results = content_db.QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) { + c->Message(Chat::White, "ERROR: Object %u not found", id); + return; + } + + auto row = results.begin(); + od.zone_id = atoi(row[0]); + od.zone_instance = atoi(row[1]); + od.object_type = atoi(row[2]); + uint32 objectsFound = 1; + + // Object not in this zone? + if (od.zone_id != zone->GetZoneID()) { + c->Message(Chat::White, "ERROR: Object %u not in this zone.", id); + return; + } + + // Object not in this instance? + if (od.zone_instance != zone->GetInstanceVersion()) { + c->Message(Chat::White, "ERROR: Object %u not part of this instance version.", id); + return; + } + + switch (od.object_type) { + case 0: // Static object needing unlocking + // Convert to tradeskill object temporarily for changes + query = StringFormat("UPDATE object SET type = %u WHERE id = %u", staticType, id); + + content_db.QueryDatabase(query); + + c->Message( + Chat::White, "Static Object %u unlocked for editing. You must zone out and back in to " + "make your changes, then commit them with '#object Save'.", + id + ); + if (sep->argnum >= 4) { + c->Message( + Chat::White, "NOTE: The change you specified has not been applied, since the " + "static object had not been unlocked for editing yet." + ); + } + return; + + case OT_DROPPEDITEM: + c->Message( + Chat::White, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, " + "which cannot be manipulated with #object. See the 'ground_spawns' table " + "in the database.", + id + ); + return; + + case staticType: + c->Message( + Chat::White, "ERROR: Object %u has been unlocked for editing, but you must zone out " + "and back in for your client to refresh its object table before you can " + "make changes to it.", + id + ); + return; + + default: + // Unknown error preventing us from seeing the object in the zone. + c->Message(Chat::White, "ERROR: Unknown problem attempting to manipulate object %u", id); + return; + } + } + + // If we're here, we have a manipulable object ready for changes. + strlwr(sep->arg[3]); // Case insensitive PropertyName + strupr(sep->arg[4]); // In case it's model name, which should always be upper-case + + // Read current object info for reference + icon = o->GetIcon(); + o->GetObjectData(&od); + + // We'll be a little more picky with property names, to prevent errors. Check against the whole word. + if (strcmp(sep->arg[3], "model") == 0) { + + if ((sep->arg[4][0] < 'A') || (sep->arg[4][0] > 'Z')) { + c->Message(Chat::White, "ERROR: Model names must begin with a letter."); + return; + } + + strn0cpy(od.object_name, sep->arg[4], sizeof(od.object_name)); + + o->SetObjectData(&od); + + c->Message(Chat::White, "Object %u now being rendered with model '%s'", id, od.object_name); + } + else if (strcmp(sep->arg[3], "type") == 0) { + if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { + c->Message(Chat::White, "ERROR: Invalid type number"); + return; + } + + od.object_type = atoi(sep->arg[4]); + + switch (od.object_type) { + case 0: + // Convert Static Object to temporary changeable type + od.object_type = staticType; + c->Message( + Chat::White, "Note: Static Object will still act like tradeskill object and will not " + "reflect size, solidtype, or incline settings until committed to the " + "database with '#object Save', after which it will be unchangeable until " + "it is unlocked again with '#object Edit'." + ); + break; + + case OT_DROPPEDITEM: + c->Message( + Chat::White, "ERROR: Object Type 1 is used for temporarily spawned ground spawns and " + "dropped items, which are not supported with #object. See the " + "'ground_spawns' table in the database." + ); + return; + + default: + c->Message(Chat::White, "Object %u changed to Tradeskill Object Type %u", id, od.object_type); + break; + } + + o->SetType(od.object_type); + } + else if (strcmp(sep->arg[3], "size") == 0) { + if (od.object_type != staticType) { + c->Message( + 0, "ERROR: Object %u is not a Static Object and does not support the Size property", + id + ); + return; + } + + if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { + c->Message(Chat::White, "ERROR: Invalid size specified. Please enter a number."); + return; + } + + od.size = atoi(sep->arg[4]); + o->SetObjectData(&od); + + if (od.size == 0) { // 0 == unspecified == 100% + od.size = 100; + } + + c->Message( + Chat::White, "Static Object %u set to %u%% size. Size will take effect when you commit to the " + "database with '#object Save', after which the object will be unchangeable until " + "you unlock it again with '#object Edit' and zone out and back in.", + id, od.size + ); + } + else if (strcmp(sep->arg[3], "solidtype") == 0) { + + if (od.object_type != staticType) { + c->Message( + Chat::White, "ERROR: Object %u is not a Static Object and does not support the " + "SolidType property", + id + ); + return; + } + + if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { + c->Message(Chat::White, "ERROR: Invalid solidtype specified. Please enter a number."); + return; + } + + od.solidtype = atoi(sep->arg[4]); + o->SetObjectData(&od); + + c->Message( + Chat::White, "Static Object %u set to SolidType %u. Change will take effect when you commit " + "to the database with '#object Save'. Support for this property is on a " + "per-model basis, mostly seen in smaller objects such as chests and tables.", + id, od.solidtype + ); + } + else if (strcmp(sep->arg[3], "icon") == 0) { + + if ((od.object_type < 2) || (od.object_type == staticType)) { + c->Message( + Chat::White, "ERROR: Object %u is not a Tradeskill Object and does not support the " + "Icon property", + id + ); + return; + } + + if ((icon = atoi(sep->arg[4])) == 0) { + c->Message(Chat::White, "ERROR: Invalid Icon specified. Please enter an icon number."); + return; + } + + o->SetIcon(icon); + c->Message(Chat::White, "Tradeskill Object %u icon set to %u", id, icon); + } + else if (strcmp(sep->arg[3], "incline") == 0) { + if (od.object_type != staticType) { + c->Message( + 0, + "ERROR: Object %u is not a Static Object and does not support the Incline property", + id + ); + return; + } + + if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { + c->Message( + 0, + "ERROR: Invalid Incline specified. Please enter a number. Normal range is 0-512." + ); + return; + } + + od.unknown020 = atoi(sep->arg[4]); + o->SetObjectData(&od); + + c->Message( + Chat::White, "Static Object %u set to %u incline. Incline will take effect when you commit to " + "the database with '#object Save', after which the object will be unchangeable " + "until you unlock it again with '#object Edit' and zone out and back in.", + id, od.unknown020 + ); + } + else { + c->Message(Chat::White, "ERROR: Unrecognized property name: %s", sep->arg[3]); + return; + } + + // Repop object to have it reflect the change. + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + + app = new EQApplicationPacket(); + o->CreateSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + return; + } + + if (strcasecmp(sep->arg[1], "move") == 0) { + + if ((sep->argnum < 2) || // Not enough arguments + ((id = atoi(sep->arg[2])) == 0) || // ID not specified + (((sep->arg[3][0] < '0') || (sep->arg[3][0] > '9')) && ((sep->arg[3][0] & 0xDF) != 'T') && + (sep->arg[3][0] != '-') && (sep->arg[3][0] != '.'))) { // Location argument not specified correctly + c->Message(Chat::White, "Usage: #object Move (ObjectID) ToMe|(x y z [h])"); + return; + } + + if (!(o = entity_list.FindObject(id))) { + std::string query = StringFormat("SELECT zoneid, version, type FROM object WHERE id = %u", id); + auto results = content_db.QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) { + c->Message(Chat::White, "ERROR: Object %u not found", id); + return; + } + + auto row = results.begin(); + od.zone_id = atoi(row[0]); + od.zone_instance = atoi(row[1]); + od.object_type = atoi(row[2]); + + if (od.zone_id != zone->GetZoneID()) { + c->Message(Chat::White, "ERROR: Object %u is not in this zone", id); + return; + } + + if (od.zone_instance != zone->GetInstanceVersion()) { + c->Message(Chat::White, "ERROR: Object %u is not in this instance version", id); + return; + } + + switch (od.object_type) { + case 0: + c->Message( + Chat::White, "ERROR: Object %u is not yet unlocked for editing. Use '#object Edit' " + "then zone out and back in to move it.", + id + ); + return; + + case staticType: + c->Message( + Chat::White, "ERROR: Object %u has been unlocked for editing, but you must zone out " + "and back in before your client sees the change and will allow you to " + "move it.", + id + ); + return; + + case 1: + c->Message( + Chat::White, "ERROR: Object %u is a temporary spawned object and cannot be " + "manipulated with #object. See the 'ground_spawns' table in the " + "database.", + id + ); + return; + + default: + c->Message(Chat::White, "ERROR: Object %u not located in zone.", id); + return; + } + } + + // Move To Me + if ((sep->arg[3][0] & 0xDF) == 'T') { + od.x = c->GetX(); + od.y = c->GetY(); + od.z = c->GetZ() - + (c->GetSize() * + 0.625f); // Compensate for #loc bumping up Z coordinate by 62.5% of character's size. + + o->SetHeading(c->GetHeading()); + + // Bump player back to avoid getting stuck inside object + + x2 = 10.0f * std::sin(c->GetHeading() / 256.0f * 3.14159265f); + y2 = 10.0f * std::cos(c->GetHeading() / 256.0f * 3.14159265f); + c->MovePC(c->GetX() - x2, c->GetY() - y2, c->GetZ(), c->GetHeading()); + } // Move to x, y, z [h] + else { + od.x = atof(sep->arg[3]); + if (sep->argnum > 3) { + od.y = atof(sep->arg[4]); + } + else { + o->GetLocation(nullptr, &od.y, nullptr); + } + + if (sep->argnum > 4) { + od.z = atof(sep->arg[5]); + } + else { + o->GetLocation(nullptr, nullptr, &od.z); + } + + if (sep->argnum > 5) { + o->SetHeading(atof(sep->arg[6])); + } + } + + o->SetLocation(od.x, od.y, od.z); + + // Despawn and respawn object to reflect change + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + + app = new EQApplicationPacket(); + o->CreateSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + return; + } + + if (strcasecmp(sep->arg[1], "rotate") == 0) { + // Insufficient or invalid arguments + if ((sep->argnum < 3) || ((id = atoi(sep->arg[2])) == 0)) { + c->Message(Chat::White, "Usage: #object Rotate (ObjectID) (Heading, 0-512)"); + return; + } + + if ((o = entity_list.FindObject(id)) == nullptr) { + c->Message( + Chat::White, "ERROR: Object %u not found in zone, or is a static object not yet unlocked with " + "'#object Edit' for editing.", + id + ); + return; + } + + o->SetHeading(atof(sep->arg[3])); + + // Despawn and respawn object to reflect change + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + + app = new EQApplicationPacket(); + o->CreateSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + return; + } + + if (strcasecmp(sep->arg[1], "save") == 0) { + // Insufficient or invalid arguments + if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) == 0)) { + c->Message(Chat::White, "Usage: #object Save (ObjectID)"); + return; + } + + o = entity_list.FindObject(id); + + od.zone_id = 0; + od.zone_instance = 0; + od.object_type = 0; + + // If this ID isn't in the database yet, it's a new object + bNewObject = true; + std::string query = StringFormat("SELECT zoneid, version, type FROM object WHERE id = %u", id); + auto results = content_db.QueryDatabase(query); + if (results.Success() && results.RowCount() != 0) { + auto row = results.begin(); + od.zone_id = atoi(row[0]); + od.zone_instance = atoi(row[1]); + od.object_type = atoi(row[2]); + + // ID already in database. Not a new object. + bNewObject = false; + } + + if (!o) { + // Object not found in zone. Can't save an object we can't see. + + if (bNewObject) { + c->Message(Chat::White, "ERROR: Object %u not found", id); + return; + } + + if (od.zone_id != zone->GetZoneID()) { + c->Message(Chat::White, "ERROR: Wrong Object ID. %u is not part of this zone.", id); + return; + } + + if (od.zone_instance != zone->GetInstanceVersion()) { + c->Message(Chat::White, "ERROR: Wrong Object ID. %u is not part of this instance version.", id); + return; + } + + if (od.object_type == 0) { + c->Message( + Chat::White, "ERROR: Static Object %u has already been committed. Use '#object Edit " + "%u' and zone out and back in to make changes.", + id, id + ); + return; + } + + if (od.object_type == 1) { + c->Message( + Chat::White, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, " + "which is not supported with #object. See the 'ground_spawns' table in " + "the database.", + id + ); + return; + } + + c->Message(Chat::White, "ERROR: Object %u not found.", id); + return; + } + + // Oops! Another GM already saved an object with our id from another zone. + // We'll have to get a new one. + if ((od.zone_id > 0) && (od.zone_id != zone->GetZoneID())) { + id = 0; + } + + // Oops! Another GM already saved an object with our id from another instance. + // We'll have to get a new one. + if ((id > 0) && (od.zone_instance != zone->GetInstanceVersion())) { + id = 0; + } + + // If we're asking for a new ID, it's a new object. + bNewObject |= (id == 0); + + o->GetObjectData(&od); + od.object_type = o->GetType(); + icon = o->GetIcon(); + + // We're committing to the database now. Return temporary object type to actual. + if (od.object_type == staticType) { + od.object_type = 0; + } + + if (!bNewObject) { + query = StringFormat( + "UPDATE object SET zoneid = %u, version = %u, " + "xpos = %.1f, ypos=%.1f, zpos=%.1f, heading=%.1f, " + "objectname = '%s', type = %u, icon = %u, " + "unknown08 = %u, unknown10 = %u, unknown20 = %u " + "WHERE ID = %u", + zone->GetZoneID(), zone->GetInstanceVersion(), od.x, od.y, od.z, + od.heading, od.object_name, od.object_type, icon, od.size, + od.solidtype, od.unknown020, id + ); + } + else if (id == 0) { + query = StringFormat( + "INSERT INTO object " + "(zoneid, version, xpos, ypos, zpos, heading, objectname, " + "type, icon, unknown08, unknown10, unknown20) " + "VALUES (%u, %u, %.1f, %.1f, %.1f, %.1f, '%s', %u, %u, %u, %u, %u)", + zone->GetZoneID(), zone->GetInstanceVersion(), od.x, od.y, od.z, + od.heading, od.object_name, od.object_type, icon, od.size, + od.solidtype, od.unknown020 + ); + } + else { + query = StringFormat( + "INSERT INTO object " + "(id, zoneid, version, xpos, ypos, zpos, heading, objectname, " + "type, icon, unknown08, unknown10, unknown20) " + "VALUES (%u, %u, %u, %.1f, %.1f, %.1f, %.1f, '%s', %u, %u, %u, %u, %u)", + id, zone->GetZoneID(), zone->GetInstanceVersion(), od.x, od.y, od.z, + od.heading, od.object_name, od.object_type, icon, od.size, + od.solidtype, od.unknown020 + ); + } + + results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Database Error: %s", results.ErrorMessage().c_str()); + return; + } + + if (results.RowsAffected() == 0) { + // No change made, but no error message given + c->Message(Chat::White, "Database Error: Could not save change to Object %u", id); + return; + } + + if (bNewObject) { + if (newid == results.LastInsertedID()) { + c->Message(Chat::White, "Saved new Object %u to database", id); + return; + } + + c->Message(Chat::White, "Saved Object. NOTE: Database returned a new ID number for object: %u", newid); + id = newid; + return; + } + + c->Message(Chat::White, "Saved changes to Object %u", id); + newid = id; + + if (od.object_type == 0) { + // Static Object - Respawn as nonfunctional door + + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + + entity_list.RemoveObject(o->GetID()); + + auto door = DoorsRepository::NewEntity(); + + door.zone = zone->GetShortName(); + + door.id = 1000000000 + id; // Out of range of normal use for doors.id + door.doorid = -1; // Client doesn't care if these are all the same door_id + door.pos_x = od.x; + door.pos_y = od.y; + door.pos_z = od.z; + door.heading = od.heading; + + door.name = od.object_name; + + // Strip trailing "_ACTORDEF" if present. Client won't accept it for doors. + int pos = door.name.size() - strlen("_ACTORDEF"); + if (pos > 0 && door.name.compare(pos, std::string::npos, "_ACTORDEF") == 0) { + door.name.erase(pos); + } + + door.dest_zone = "NONE"; + + if ((door.size = od.size) == 0) { // unknown08 = optional size percentage + door.size = 100; + } + + door.opentype = od.solidtype; + + switch (door.opentype) // unknown10 = optional request_nonsolid (0 or 1 or experimental number) + { + case 0: + door.opentype = 31; + break; + + case 1: + door.opentype = 9; + break; + } + + door.incline = od.unknown020; // unknown20 = optional incline value + door.client_version_mask = 0xFFFFFFFF; + + Doors *doors = new Doors(door); + + entity_list.AddDoor(doors); + + app = new EQApplicationPacket(OP_SpawnDoor, sizeof(Door_Struct)); + ds = (Door_Struct *) app->pBuffer; + + memset(ds, 0, sizeof(Door_Struct)); + memcpy(ds->name, door.name.c_str(), 32); + ds->xPos = door.pos_x; + ds->yPos = door.pos_y; + ds->zPos = door.pos_z; + ds->heading = door.heading; + ds->incline = door.incline; + ds->size = door.size; + ds->doorId = door.doorid; + ds->opentype = door.opentype; + ds->unknown0052[9] = 1; // *ptr-1 and *ptr-3 from EntityList::MakeDoorSpawnPacket() + ds->unknown0052[11] = 1; + + entity_list.QueueClients(0, app); + safe_delete(app); + + c->Message( + Chat::White, "NOTE: Object %u is now a static object, and is unchangeable. To make future " + "changes, use '#object Edit' to convert it to a changeable form, then zone out " + "and back in.", + id + ); + } + return; + } + + if (strcasecmp(sep->arg[1], "copy") == 0) { + // Insufficient or invalid arguments + if ((sep->argnum < 3) || + (((sep->arg[2][0] & 0xDF) != 'A') && ((sep->arg[2][0] < '0') || (sep->arg[2][0] > '9')))) { + c->Message(Chat::White, "Usage: #object Copy All|(ObjectID) (InstanceVersion)"); + c->Message(Chat::White, "- Note: Only objects saved in the database can be copied to another instance."); + return; + } + + od.zone_instance = atoi(sep->arg[3]); + + if (od.zone_instance == zone->GetInstanceVersion()) { + c->Message(Chat::White, "ERROR: Source and destination instance versions are the same."); + return; + } + + if ((sep->arg[2][0] & 0xDF) == 'A') { + // Copy All + + std::string query = + StringFormat( + "INSERT INTO object " + "(zoneid, version, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20) " + "SELECT zoneid, %u, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20 " + "FROM object WHERE zoneid = %u) AND version = %u", + od.zone_instance, zone->GetZoneID(), zone->GetInstanceVersion()); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Database Error: %s", results.ErrorMessage().c_str()); + return; + } + + c->Message( + Chat::White, "Copied %u object%s into instance version %u", results.RowCount(), + (results.RowCount() == 1) ? "" : "s", od.zone_instance + ); + return; + } + + id = atoi(sep->arg[2]); + + std::string query = StringFormat( + "INSERT INTO object " + "(zoneid, version, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20) " + "SELECT zoneid, %u, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20 " + "FROM object WHERE id = %u AND zoneid = %u AND version = %u", + od.zone_instance, id, zone->GetZoneID(), zone->GetInstanceVersion()); + auto results = content_db.QueryDatabase(query); + if (results.Success() && results.RowsAffected() > 0) { + c->Message(Chat::White, "Copied Object %u into instance version %u", id, od.zone_instance); + return; + } + + // Couldn't copy the object. + + // got an error message + if (!results.Success()) { + c->Message(Chat::White, "Database Error: %s", results.ErrorMessage().c_str()); + return; + } + + // No database error returned. See if we can figure out why. + + query = StringFormat("SELECT zoneid, version FROM object WHERE id = %u", id); + results = content_db.QueryDatabase(query); + if (!results.Success()) { + return; + } + + if (results.RowCount() == 0) { + c->Message(Chat::White, "ERROR: Object %u not found", id); + return; + } + + auto row = results.begin(); + // Wrong ZoneID? + if (atoi(row[0]) != zone->GetZoneID()) { + c->Message(Chat::White, "ERROR: Object %u is not part of this zone.", id); + return; + } + + // Wrong Instance Version? + if (atoi(row[1]) != zone->GetInstanceVersion()) { + c->Message(Chat::White, "ERROR: Object %u is not part of this instance version.", id); + return; + } + + // Well, NO clue at this point. Just let 'em know something screwed up. + c->Message( + Chat::White, "ERROR: Unknown database error copying Object %u to instance version %u", id, + od.zone_instance + ); + return; + } + + if (strcasecmp(sep->arg[1], "delete") == 0) { + + if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) <= 0)) { + c->Message( + Chat::White, "Usage: #object Delete (ObjectID) -- NOTE: Object deletions are permanent and " + "cannot be undone!" + ); + return; + } + + o = entity_list.FindObject(id); + + if (o) { + // Object found in zone. + + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(nullptr, app); + + entity_list.RemoveObject(o->GetID()); + + // Verifying ZoneID and Version in case someone else ended up adding an object with our ID + // from a different zone/version. Don't want to delete someone else's work. + std::string query = StringFormat( + "DELETE FROM object " + "WHERE id = %u AND zoneid = %u " + "AND version = %u LIMIT 1", + id, zone->GetZoneID(), zone->GetInstanceVersion()); + auto results = content_db.QueryDatabase(query); + + c->Message(Chat::White, "Object %u deleted", id); + return; + } + + // Object not found in zone. + std::string query = StringFormat( + "SELECT type FROM object " + "WHERE id = %u AND zoneid = %u " + "AND version = %u LIMIT 1", + id, zone->GetZoneID(), zone->GetInstanceVersion()); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + return; + } + + if (results.RowCount() == 0) { + c->Message(Chat::White, "ERROR: Object %u not found in this zone or instance!", id); + return; + } + + auto row = results.begin(); + + switch (atoi(row[0])) { + case 0: // Static Object + query = StringFormat( + "DELETE FROM object WHERE id = %u " + "AND zoneid = %u AND version = %u LIMIT 1", + id, zone->GetZoneID(), zone->GetInstanceVersion()); + results = content_db.QueryDatabase(query); + + c->Message( + Chat::White, "Object %u deleted. NOTE: This static object will remain for anyone currently in " + "the zone until they next zone out and in.", + id + ); + return; + + case 1: // Temporary Spawn + c->Message( + Chat::White, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, which " + "is not supported with #object. See the 'ground_spawns' table in the database.", + id + ); + return; + } + + return; + } + + if (strcasecmp(sep->arg[1], "undo") == 0) { + // Insufficient or invalid arguments + if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) == 0)) { + c->Message( + Chat::White, "Usage: #object Undo (ObjectID) -- Reload object from database, undoing any " + "changes you have made" + ); + return; + } + + o = entity_list.FindObject(id); + + if (!o) { + c->Message( + Chat::White, "ERROR: Object %u not found in zone in a manipulable form. No changes to undo.", + id + ); + return; + } + + if (o->GetType() == OT_DROPPEDITEM) { + c->Message( + Chat::White, "ERROR: Object %u is a temporary spawned item and cannot be manipulated with " + "#object. See the 'ground_spawns' table in the database.", + id + ); + return; + } + + // Despawn current item for reloading from database + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(0, app); + entity_list.RemoveObject(o->GetID()); + safe_delete(app); + + std::string query = StringFormat( + "SELECT xpos, ypos, zpos, " + "heading, objectname, type, icon, " + "unknown08, unknown10, unknown20 " + "FROM object WHERE id = %u", + id + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) { + c->Message(Chat::White, "Database Error: %s", results.ErrorMessage().c_str()); + return; + } + + memset(&od, 0, sizeof(od)); + + auto row = results.begin(); + + od.x = atof(row[0]); + od.y = atof(row[1]); + od.z = atof(row[2]); + od.heading = atof(row[3]); + strn0cpy(od.object_name, row[4], sizeof(od.object_name)); + od.object_type = atoi(row[5]); + icon = atoi(row[6]); + od.size = atoi(row[7]); + od.solidtype = atoi(row[8]); + od.unknown020 = atoi(row[9]); + + if (od.object_type == 0) { + od.object_type = staticType; + } + + o = new Object(id, od.object_type, icon, od, nullptr); + entity_list.AddObject(o, true); + + c->Message(Chat::White, "Object %u reloaded from database.", id); + return; + } + + c->Message(Chat::White, usage_string); +} + diff --git a/zone/gm_commands/oocmute.cpp b/zone/gm_commands/oocmute.cpp new file mode 100755 index 000000000..a1e1199a2 --- /dev/null +++ b/zone/gm_commands/oocmute.cpp @@ -0,0 +1,18 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_oocmute(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0 || !(sep->arg[1][0] == '1' || sep->arg[1][0] == '0')) { + c->Message(Chat::White, "Usage: #oocmute [1/0]"); + } + else { + auto outapp = new ServerPacket(ServerOP_OOCMute, 1); + *(outapp->pBuffer) = atoi(sep->arg[1]); + worldserver.SendPacket(outapp); + safe_delete(outapp); + } +} + diff --git a/zone/gm_commands/opcode.cpp b/zone/gm_commands/opcode.cpp new file mode 100755 index 000000000..8c8601e96 --- /dev/null +++ b/zone/gm_commands/opcode.cpp @@ -0,0 +1,11 @@ +#include "../client.h" +#include "../../common/patches/patches.h" + +void command_opcode(Client *c, const Seperator *sep) +{ + if (!strcasecmp(sep->arg[1], "reload")) { + ReloadAllPatches(); + c->Message(Chat::White, "Opcodes for all patches have been reloaded"); + } +} + diff --git a/zone/gm_commands/path.cpp b/zone/gm_commands/path.cpp new file mode 100755 index 000000000..69604e271 --- /dev/null +++ b/zone/gm_commands/path.cpp @@ -0,0 +1,27 @@ +#include "../client.h" + +void command_path(Client *c, const Seperator *sep) +{ + if (zone->pathing) { + zone->pathing->DebugCommand(c, sep); + } +} + +void Client::Undye() +{ + for (int cur_slot = EQ::textures::textureBegin; cur_slot <= EQ::textures::LastTexture; cur_slot++) { + uint8 slot2 = SlotConvert(cur_slot); + EQ::ItemInstance *inst = m_inv.GetItem(slot2); + + if (inst != nullptr) { + inst->SetColor(inst->GetItem()->Color); + database.SaveInventory(CharacterID(), inst, slot2); + } + + m_pp.item_tint.Slot[cur_slot].Color = 0; + SendWearChange(cur_slot); + } + + database.DeleteCharacterDye(this->CharacterID()); +} + diff --git a/zone/gm_commands/peekinv.cpp b/zone/gm_commands/peekinv.cpp new file mode 100755 index 000000000..fe6956191 --- /dev/null +++ b/zone/gm_commands/peekinv.cpp @@ -0,0 +1,332 @@ +#include "../client.h" +#include "../object.h" + +void command_peekinv(Client *c, const Seperator *sep) +{ + // this can be cleaned up once inventory is cleaned up + enum { + peekNone = 0x0000, + peekEquip = 0x0001, + peekGen = 0x0002, + peekCursor = 0x0004, + peekLimbo = 0x0008, + peekTrib = 0x0010, + peekBank = 0x0020, + peekShBank = 0x0040, + peekTrade = 0x0080, + peekWorld = 0x0100, + peekOutOfScope = (peekWorld * 2) // less than + }; + + static const char *scope_prefix[] = {"equip", "gen", "cursor", "limbo", "trib", "bank", "shbank", "trade", "world"}; + + static const int16 scope_range[][2] = { + {EQ::invslot::EQUIPMENT_BEGIN, EQ::invslot::EQUIPMENT_END}, + {EQ::invslot::GENERAL_BEGIN, EQ::invslot::GENERAL_END}, + {EQ::invslot::slotCursor, EQ::invslot::slotCursor}, + {EQ::invslot::SLOT_INVALID, EQ::invslot::SLOT_INVALID}, + {EQ::invslot::TRIBUTE_BEGIN, EQ::invslot::TRIBUTE_END}, + {EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END}, + {EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END}, + {EQ::invslot::TRADE_BEGIN, EQ::invslot::TRADE_END}, + {EQ::invslot::SLOT_BEGIN, (EQ::invtype::WORLD_SIZE - 1)} + }; + + static const bool scope_bag[] = {false, true, true, true, false, true, true, true, true}; + + if (!c) { + return; + } + + if (c->GetTarget() && !c->GetTarget()->IsClient()) { + c->Message(Chat::White, "You must target a PC for this command."); + return; + } + + int scopeMask = peekNone; + + if (strcasecmp(sep->arg[1], "all") == 0) { scopeMask = (peekOutOfScope - 1); } + else if (strcasecmp(sep->arg[1], "equip") == 0) { scopeMask |= peekEquip; } + else if (strcasecmp(sep->arg[1], "gen") == 0) { scopeMask |= peekGen; } + else if (strcasecmp(sep->arg[1], "cursor") == 0) { scopeMask |= peekCursor; } + else if (strcasecmp(sep->arg[1], "poss") == 0) { scopeMask |= (peekEquip | peekGen | peekCursor); } + else if (strcasecmp(sep->arg[1], "limbo") == 0) { scopeMask |= peekLimbo; } + else if (strcasecmp(sep->arg[1], "curlim") == 0) { scopeMask |= (peekCursor | peekLimbo); } + else if (strcasecmp(sep->arg[1], "trib") == 0) { scopeMask |= peekTrib; } + else if (strcasecmp(sep->arg[1], "bank") == 0) { scopeMask |= peekBank; } + else if (strcasecmp(sep->arg[1], "shbank") == 0) { scopeMask |= peekShBank; } + else if (strcasecmp(sep->arg[1], "allbank") == 0) { scopeMask |= (peekBank | peekShBank); } + else if (strcasecmp(sep->arg[1], "trade") == 0) { scopeMask |= peekTrade; } + else if (strcasecmp(sep->arg[1], "world") == 0) { scopeMask |= peekWorld; } + + if (!scopeMask) { + c->Message( + Chat::White, + "Usage: #peekinv [equip|gen|cursor|poss|limbo|curlim|trib|bank|shbank|allbank|trade|world|all]" + ); + c->Message(Chat::White, "- Displays a portion of the targeted user's inventory"); + c->Message(Chat::White, "- Caution: 'all' is a lot of information!"); + return; + } + + Client *targetClient = c; + if (c->GetTarget()) { + targetClient = c->GetTarget()->CastToClient(); + } + + const EQ::ItemInstance *inst_main = nullptr; + const EQ::ItemInstance *inst_sub = nullptr; + const EQ::ItemInstance *inst_aug = nullptr; + const EQ::ItemData *item_data = nullptr; + + EQ::SayLinkEngine linker; + linker.SetLinkType(EQ::saylink::SayLinkItemInst); + + c->Message(Chat::White, "Displaying inventory for %s...", targetClient->GetName()); + + Object *objectTradeskill = targetClient->GetTradeskillObject(); + + bool itemsFound = false; + + for (int scopeIndex = 0, scopeBit = peekEquip; scopeBit < peekOutOfScope; ++scopeIndex, scopeBit <<= 1) { + if (scopeBit & ~scopeMask) { + continue; + } + + if (scopeBit & peekWorld) { + if (objectTradeskill == nullptr) { + c->Message(Chat::Default, "No world tradeskill object selected..."); + continue; + } + else { + c->Message( + Chat::White, + "[WorldObject DBID: %i (entityid: %i)]", + objectTradeskill->GetDBID(), + objectTradeskill->GetID()); + } + } + + for (int16 indexMain = scope_range[scopeIndex][0]; indexMain <= scope_range[scopeIndex][1]; ++indexMain) { + if (indexMain == EQ::invslot::SLOT_INVALID) { + continue; + } + + inst_main = ((scopeBit & peekWorld) ? objectTradeskill->GetItem(indexMain) : targetClient->GetInv().GetItem( + indexMain + )); + if (inst_main) { + itemsFound = true; + item_data = inst_main->GetItem(); + } + else { + item_data = nullptr; + } + + linker.SetItemInst(inst_main); + + c->Message( + (item_data == nullptr), + "%sSlot: %i, Item: %i (%s), Charges: %i", + scope_prefix[scopeIndex], + ((scopeBit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + indexMain) : indexMain), + ((item_data == nullptr) ? 0 : item_data->ID), + linker.GenerateLink().c_str(), + ((inst_main == nullptr) ? 0 : inst_main->GetCharges()) + ); + + if (inst_main && inst_main->IsClassCommon()) { + for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; indexAug <= EQ::invaug::SOCKET_END; ++indexAug) { + inst_aug = inst_main->GetItem(indexAug); + if (!inst_aug) { // extant only + continue; + } + + item_data = inst_aug->GetItem(); + linker.SetItemInst(inst_aug); + + c->Message( + (item_data == nullptr), + ".%sAugSlot: %i (Slot #%i, Aug idx #%i), Item: %i (%s), Charges: %i", + scope_prefix[scopeIndex], + INVALID_INDEX, + ((scopeBit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + indexMain) : indexMain), + indexAug, + ((item_data == nullptr) ? 0 : item_data->ID), + linker.GenerateLink().c_str(), + ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) + ); + } + } + + if (!scope_bag[scopeIndex] || !(inst_main && inst_main->IsClassBag())) { + continue; + } + + for (uint8 indexSub = EQ::invbag::SLOT_BEGIN; indexSub <= EQ::invbag::SLOT_END; ++indexSub) { + inst_sub = inst_main->GetItem(indexSub); + if (!inst_sub) { // extant only + continue; + } + + item_data = inst_sub->GetItem(); + linker.SetItemInst(inst_sub); + + c->Message( + (item_data == nullptr), + "..%sBagSlot: %i (Slot #%i, Bag idx #%i), Item: %i (%s), Charges: %i", + scope_prefix[scopeIndex], + ((scopeBit & peekWorld) ? INVALID_INDEX : EQ::InventoryProfile::CalcSlotId(indexMain, indexSub)), + ((scopeBit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + indexMain) : indexMain), + indexSub, + ((item_data == nullptr) ? 0 : item_data->ID), + linker.GenerateLink().c_str(), + ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) + ); + + if (inst_sub->IsClassCommon()) { + for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; indexAug <= EQ::invaug::SOCKET_END; ++indexAug) { + inst_aug = inst_sub->GetItem(indexAug); + if (!inst_aug) { // extant only + continue; + } + + item_data = inst_aug->GetItem(); + linker.SetItemInst(inst_aug); + + c->Message( + (item_data == nullptr), + "...%sAugSlot: %i (Slot #%i, Sub idx #%i, Aug idx #%i), Item: %i (%s), Charges: %i", + scope_prefix[scopeIndex], + INVALID_INDEX, + ((scopeBit & peekWorld) ? INVALID_INDEX : EQ::InventoryProfile::CalcSlotId( + indexMain, + indexSub + )), + indexSub, + indexAug, + ((item_data == nullptr) ? 0 : item_data->ID), + linker.GenerateLink().c_str(), + ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) + ); + } + } + } + } + + if (scopeBit & peekLimbo) { + int limboIndex = 0; + for (auto it = targetClient->GetInv().cursor_cbegin(); + (it != targetClient->GetInv().cursor_cend()); + ++it, ++limboIndex) { + if (it == targetClient->GetInv().cursor_cbegin()) { + continue; + } + + inst_main = *it; + if (inst_main) { + itemsFound = true; + item_data = inst_main->GetItem(); + } + else { + item_data = nullptr; + } + + linker.SetItemInst(inst_main); + + c->Message( + (item_data == nullptr), + "%sSlot: %i, Item: %i (%s), Charges: %i", + scope_prefix[scopeIndex], + (8000 + limboIndex), + ((item_data == nullptr) ? 0 : item_data->ID), + linker.GenerateLink().c_str(), + ((inst_main == nullptr) ? 0 : inst_main->GetCharges()) + ); + + if (inst_main && inst_main->IsClassCommon()) { + for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; indexAug <= EQ::invaug::SOCKET_END; ++indexAug) { + inst_aug = inst_main->GetItem(indexAug); + if (!inst_aug) { // extant only + continue; + } + + item_data = inst_aug->GetItem(); + linker.SetItemInst(inst_aug); + + c->Message( + (item_data == nullptr), + ".%sAugSlot: %i (Slot #%i, Aug idx #%i), Item: %i (%s), Charges: %i", + scope_prefix[scopeIndex], + INVALID_INDEX, + (8000 + limboIndex), + indexAug, + ((item_data == nullptr) ? 0 : item_data->ID), + linker.GenerateLink().c_str(), + ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) + ); + } + } + + if (!scope_bag[scopeIndex] || !(inst_main && inst_main->IsClassBag())) { + continue; + } + + for (uint8 indexSub = EQ::invbag::SLOT_BEGIN; indexSub <= EQ::invbag::SLOT_END; ++indexSub) { + inst_sub = inst_main->GetItem(indexSub); + if (!inst_sub) { + continue; + } + + item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem(); + + linker.SetItemInst(inst_sub); + + c->Message( + (item_data == nullptr), + "..%sBagSlot: %i (Slot #%i, Bag idx #%i), Item: %i (%s), Charges: %i", + scope_prefix[scopeIndex], + INVALID_INDEX, + (8000 + limboIndex), + indexSub, + ((item_data == nullptr) ? 0 : item_data->ID), + linker.GenerateLink().c_str(), + ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) + ); + + if (inst_sub->IsClassCommon()) { + for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; + indexAug <= EQ::invaug::SOCKET_END; + ++indexAug) { + inst_aug = inst_sub->GetItem(indexAug); + if (!inst_aug) { // extant only + continue; + } + + item_data = inst_aug->GetItem(); + linker.SetItemInst(inst_aug); + + c->Message( + (item_data == nullptr), + "...%sAugSlot: %i (Slot #%i, Sub idx #%i, Aug idx #%i), Item: %i (%s), Charges: %i", + scope_prefix[scopeIndex], + INVALID_INDEX, + (8000 + limboIndex), + indexSub, + indexAug, + ((item_data == nullptr) ? 0 : item_data->ID), + linker.GenerateLink().c_str(), + ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) + ); + } + } + } + } + } + } + + if (!itemsFound) { + c->Message(Chat::White, "No items found."); + } +} + diff --git a/zone/gm_commands/peqzone.cpp b/zone/gm_commands/peqzone.cpp new file mode 100755 index 000000000..704e6dd1d --- /dev/null +++ b/zone/gm_commands/peqzone.cpp @@ -0,0 +1,75 @@ +#include "../client.h" + +void command_peqzone(Client *c, const Seperator *sep) +{ + uint32 timeleft = c->GetPTimers().GetRemainingTime(pTimerPeqzoneReuse) / 60; + + if (!c->GetPTimers().Expired(&database, pTimerPeqzoneReuse, false)) { + c->Message(Chat::Red, "You must wait %i minute(s) before using this ability again.", timeleft); + return; + } + + if (c->GetHPRatio() < 75) { + c->Message(Chat::White, "You cannot use this command with less than 75 percent health."); + return; + } + + //this isnt perfect, but its better... + if ( + c->IsInvisible(c) + || c->IsRooted() + || c->IsStunned() + || c->IsMezzed() + || c->AutoAttackEnabled() + || c->GetInvul() + ) { + c->Message(Chat::White, "You cannot use this command in your current state. Settle down and wait."); + return; + } + + uint16 zoneid = 0; + uint8 destzone = 0; + if (sep->IsNumber(1)) { + zoneid = atoi(sep->arg[1]); + destzone = content_db.GetPEQZone(zoneid, 0); + if (destzone == 0) { + c->Message(Chat::Red, "You cannot use this command to enter that zone!"); + return; + } + if (zoneid == zone->GetZoneID()) { + c->Message(Chat::Red, "You cannot use this command on the zone you are in!"); + return; + } + } + else if (sep->arg[1][0] == 0 || sep->IsNumber(2) || sep->IsNumber(3) || sep->IsNumber(4) || sep->IsNumber(5)) { + c->Message(Chat::White, "Usage: #peqzone [zonename]"); + c->Message(Chat::White, "Optional Usage: #peqzone [zoneid]"); + return; + } + else { + zoneid = ZoneID(sep->arg[1]); + destzone = content_db.GetPEQZone(zoneid, 0); + if (zoneid == 0) { + c->Message(Chat::White, "Unable to locate zone '%s'", sep->arg[1]); + return; + } + if (destzone == 0) { + c->Message(Chat::Red, "You cannot use this command to enter that zone!"); + return; + } + if (zoneid == zone->GetZoneID()) { + c->Message(Chat::Red, "You cannot use this command on the zone you are in!"); + return; + } + } + + if (RuleB (Zone, UsePEQZoneDebuffs)) { + c->SpellOnTarget(RuleI(Zone, PEQZoneDebuff1), c); + c->SpellOnTarget(RuleI(Zone, PEQZoneDebuff2), c); + } + + //zone to safe coords + c->GetPTimers().Start(pTimerPeqzoneReuse, RuleI(Zone, PEQZoneReuseTime)); + c->MovePC(zoneid, 0.0f, 0.0f, 0.0f, 0.0f, 0, ZoneToSafeCoords); +} + diff --git a/zone/gm_commands/permaclass.cpp b/zone/gm_commands/permaclass.cpp new file mode 100755 index 000000000..72c69b80f --- /dev/null +++ b/zone/gm_commands/permaclass.cpp @@ -0,0 +1,28 @@ +#include "../client.h" + +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."); + } +} + diff --git a/zone/gm_commands/permagender.cpp b/zone/gm_commands/permagender.cpp new file mode 100755 index 000000000..ee0c7ecf9 --- /dev/null +++ b/zone/gm_commands/permagender.cpp @@ -0,0 +1,29 @@ +#include "../client.h" + +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])); + } +} + diff --git a/zone/gm_commands/permarace.cpp b/zone/gm_commands/permarace.cpp new file mode 100755 index 000000000..ac33c8605 --- /dev/null +++ b/zone/gm_commands/permarace.cpp @@ -0,0 +1,34 @@ +#include "../client.h" + +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])); + } +} + diff --git a/zone/gm_commands/petitioninfo.cpp b/zone/gm_commands/petitioninfo.cpp new file mode 100755 index 000000000..1700e4100 --- /dev/null +++ b/zone/gm_commands/petitioninfo.cpp @@ -0,0 +1,39 @@ +#include "../client.h" + +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] + ); + } + +} + diff --git a/zone/gm_commands/petname.cpp b/zone/gm_commands/petname.cpp new file mode 100755 index 000000000..47eea80c3 --- /dev/null +++ b/zone/gm_commands/petname.cpp @@ -0,0 +1,22 @@ +#include "../client.h" + +void command_petname(Client *c, const Seperator *sep) +{ + Mob *target; + target = c->GetTarget(); + + if (!target) { + c->Message(Chat::White, "Usage: #petname newname (requires a target)"); + } + else if (target->IsPet() && (target->GetOwnerID() == c->GetID()) && strlen(sep->arg[1]) > 0) { + char *oldname = strdup(target->GetName()); + target->TempName(sep->arg[1]); + c->Message(Chat::White, "Renamed %s to %s", oldname, sep->arg[1]); + free(oldname); + } + else { + target->TempName(); + c->Message(Chat::White, "Restored the original name"); + } +} + diff --git a/zone/gm_commands/pf.cpp b/zone/gm_commands/pf.cpp new file mode 100755 index 000000000..5867e346b --- /dev/null +++ b/zone/gm_commands/pf.cpp @@ -0,0 +1,21 @@ +#include "../client.h" + +void command_pf(Client *c, const Seperator *sep) +{ + if (c->GetTarget()) { + Mob *who = c->GetTarget(); + c->Message(Chat::White, "POS: (%.2f, %.2f, %.2f)", who->GetX(), who->GetY(), who->GetZ()); + c->Message( + Chat::White, + "WP: %s (%d/%d)", + to_string(who->GetCurrentWayPoint()).c_str(), + who->IsNPC() ? who->CastToNPC()->GetMaxWp() : -1 + ); + c->Message(Chat::White, "pause=%d RAspeed=%d", who->GetCWPP(), who->GetRunAnimSpeed()); + //who->DumpMovement(c); + } + else { + c->Message(Chat::White, "ERROR: target required"); + } +} + diff --git a/zone/gm_commands/picklock.cpp b/zone/gm_commands/picklock.cpp new file mode 100755 index 000000000..c4b538eff --- /dev/null +++ b/zone/gm_commands/picklock.cpp @@ -0,0 +1,24 @@ +#include "../client.h" + +void command_picklock(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!target) { + c->Message(Chat::Red, "You must have a target."); + return; + } + + if (target->IsNPC()) { + if (c->HasSkill(EQ::skills::SkillPickLock)) { + if (DistanceSquaredNoZ(c->GetPosition(), target->GetPosition()) > RuleI(Adventure, LDoNTrapDistanceUse)) { + c->Message(Chat::Red, "%s is too far away.", target->GetCleanName()); + return; + } + c->HandleLDoNPickLock(target->CastToNPC(), c->GetSkill(EQ::skills::SkillPickLock), LDoNTypeMechanical); + } + else { + c->Message(Chat::Red, "You do not have the pick locks skill."); + } + } +} + diff --git a/zone/gm_commands/profanity.cpp b/zone/gm_commands/profanity.cpp new file mode 100755 index 000000000..6774c43c6 --- /dev/null +++ b/zone/gm_commands/profanity.cpp @@ -0,0 +1,74 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +#include "../../common/profanity_manager.h" + +void command_profanity(Client *c, const Seperator *sep) +{ + std::string arg1(sep->arg[1]); + + while (true) { + if (arg1.compare("list") == 0) { + // do nothing + } + else if (arg1.compare("clear") == 0) { + EQ::ProfanityManager::DeleteProfanityList(&database); + auto pack = new ServerPacket(ServerOP_RefreshCensorship); + worldserver.SendPacket(pack); + safe_delete(pack); + } + else if (arg1.compare("add") == 0) { + if (!EQ::ProfanityManager::AddProfanity(&database, sep->arg[2])) { + c->Message(Chat::Red, "Could not add '%s' to the profanity list.", sep->arg[2]); + } + auto pack = new ServerPacket(ServerOP_RefreshCensorship); + worldserver.SendPacket(pack); + safe_delete(pack); + } + else if (arg1.compare("del") == 0) { + if (!EQ::ProfanityManager::RemoveProfanity(&database, sep->arg[2])) { + c->Message(Chat::Red, "Could not delete '%s' from the profanity list.", sep->arg[2]); + } + auto pack = new ServerPacket(ServerOP_RefreshCensorship); + worldserver.SendPacket(pack); + safe_delete(pack); + } + else if (arg1.compare("reload") == 0) { + if (!EQ::ProfanityManager::UpdateProfanityList(&database)) { + c->Message(Chat::Red, "Could not reload the profanity list."); + } + auto pack = new ServerPacket(ServerOP_RefreshCensorship); + worldserver.SendPacket(pack); + safe_delete(pack); + } + else { + break; + } + + std::string popup; + const auto &list = EQ::ProfanityManager::GetProfanityList(); + for (const auto &iter : list) { + popup.append(iter); + popup.append("
"); + } + if (list.empty()) { + popup.append("** Censorship Inactive **
"); + } + else { + popup.append("** End of List **
"); + } + + c->SendPopupToClient("Profanity List", popup.c_str()); + + return; + } + + c->Message(Chat::White, "Usage: #profanity [list] - shows profanity list"); + c->Message(Chat::White, "Usage: #profanity [clear] - deletes all entries"); + c->Message(Chat::White, "Usage: #profanity [add] [] - adds entry"); + c->Message(Chat::White, "Usage: #profanity [del] [] - deletes entry"); + c->Message(Chat::White, "Usage: #profanity [reload] - reloads profanity list"); +} + diff --git a/zone/gm_commands/profilereset.cpp b/zone/gm_commands/profilereset.cpp new file mode 100755 index 000000000..0f98dae23 --- /dev/null +++ b/zone/gm_commands/profilereset.cpp @@ -0,0 +1,8 @@ +#include "../client.h" + +void command_profilereset(Client *c, const Seperator *sep) +{ + ResetZoneProfile(); +} +#endif + diff --git a/zone/gm_commands/proximity.cpp b/zone/gm_commands/proximity.cpp new file mode 100755 index 000000000..3db0a714e --- /dev/null +++ b/zone/gm_commands/proximity.cpp @@ -0,0 +1,74 @@ +#include "../client.h" + +void command_proximity(Client *c, const Seperator *sep) +{ + if (!c->GetTarget() || (c->GetTarget() && !c->GetTarget()->IsNPC())) { + c->Message(Chat::White, "You must target an NPC"); + return; + } + + for (auto &iter : entity_list.GetNPCList()) { + auto npc = iter.second; + std::string name = npc->GetName(); + + if (name.find("Proximity") != std::string::npos) { + npc->Depop(); + } + } + + NPC *npc = c->GetTarget()->CastToNPC(); + + std::vector points; + + FindPerson_Point p{}; + + if (npc->IsProximitySet()) { + glm::vec4 position; + position.w = npc->GetHeading(); + position.x = npc->GetProximityMinX(); + position.y = npc->GetProximityMinY(); + position.z = npc->GetZ(); + + position.x = npc->GetProximityMinX(); + position.y = npc->GetProximityMinY(); + NPC::SpawnNodeNPC("Proximity", "", position); + + position.x = npc->GetProximityMinX(); + position.y = npc->GetProximityMaxY(); + NPC::SpawnNodeNPC("Proximity", "", position); + + position.x = npc->GetProximityMaxX(); + position.y = npc->GetProximityMinY(); + NPC::SpawnNodeNPC("Proximity", "", position); + + position.x = npc->GetProximityMaxX(); + position.y = npc->GetProximityMaxY(); + NPC::SpawnNodeNPC("Proximity", "", position); + + p.x = npc->GetProximityMinX(); + p.y = npc->GetProximityMinY(); + p.z = npc->GetZ(); + points.push_back(p); + + p.x = npc->GetProximityMinX(); + p.y = npc->GetProximityMaxY(); + points.push_back(p); + + p.x = npc->GetProximityMaxX(); + p.y = npc->GetProximityMaxY(); + points.push_back(p); + + p.x = npc->GetProximityMaxX(); + p.y = npc->GetProximityMinY(); + points.push_back(p); + + p.x = npc->GetProximityMinX(); + p.y = npc->GetProximityMinY(); + points.push_back(p); + } + + if (c->ClientVersion() >= EQ::versions::ClientVersion::RoF) { + c->SendPathPacket(points); + } +} + diff --git a/zone/gm_commands/push.cpp b/zone/gm_commands/push.cpp new file mode 100755 index 000000000..6ca548640 --- /dev/null +++ b/zone/gm_commands/push.cpp @@ -0,0 +1,35 @@ +#include "../client.h" +#include "../fastmath.h" + +extern FastMath g_Math; + +void command_push(Client *c, const Seperator *sep) +{ + Mob *t = c; + if (c->GetTarget() != nullptr) { + t = c->GetTarget(); + } + + if (!sep->arg[1] || !sep->IsNumber(1)) { + c->Message(Chat::White, "ERROR: Must provide at least a push back."); + return; + } + + float back = atof(sep->arg[1]); + float up = 0.0f; + + if (sep->arg[2] && sep->IsNumber(2)) { + up = atof(sep->arg[2]); + } + + if (t->IsNPC()) { + t->IncDeltaX(back * g_Math.FastSin(c->GetHeading())); + t->IncDeltaY(back * g_Math.FastCos(c->GetHeading())); + t->IncDeltaZ(up); + t->SetForcedMovement(6); + } + else if (t->IsClient()) { + // TODO: send packet to push + } +} + diff --git a/zone/gm_commands/pvp.cpp b/zone/gm_commands/pvp.cpp new file mode 100755 index 000000000..1f31f29b9 --- /dev/null +++ b/zone/gm_commands/pvp.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +void command_pvp(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->SetPVP(state); + c->Message(Chat::White, "%s now follows the ways of %s.", t->GetName(), state ? "discord" : "order"); + } + else { + c->Message(Chat::White, "Usage: #pvp [on/off]"); + } +} + diff --git a/zone/gm_commands/qglobal.cpp b/zone/gm_commands/qglobal.cpp new file mode 100755 index 000000000..7625a2604 --- /dev/null +++ b/zone/gm_commands/qglobal.cpp @@ -0,0 +1,62 @@ +#include "../client.h" + +void command_qglobal(Client *c, const Seperator *sep) +{ + //In-game switch for qglobal column + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Syntax: #qglobal [on/off/view]. Requires NPC target."); + return; + } + + Mob *target = c->GetTarget(); + + if (!target || !target->IsNPC()) { + c->Message(Chat::Red, "NPC Target Required!"); + return; + } + + if (!strcasecmp(sep->arg[1], "on")) { + std::string query = StringFormat( + "UPDATE npc_types SET qglobal = 1 WHERE id = '%i'", + target->GetNPCTypeID()); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::Yellow, "Could not update database."); + return; + } + + c->Message(Chat::Yellow, "Success! Changes take effect on zone reboot."); + return; + } + + if (!strcasecmp(sep->arg[1], "off")) { + std::string query = StringFormat( + "UPDATE npc_types SET qglobal = 0 WHERE id = '%i'", + target->GetNPCTypeID()); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::Yellow, "Could not update database."); + return; + } + + c->Message(Chat::Yellow, "Success! Changes take effect on zone reboot."); + return; + } + + if (!strcasecmp(sep->arg[1], "view")) { + const NPCType *type = content_db.LoadNPCTypesData(target->GetNPCTypeID()); + if (!type) { + c->Message(Chat::Yellow, "Invalid NPC type."); + } + else if (type->qglobal) { + c->Message(Chat::Yellow, "This NPC has quest globals active."); + } + else { + c->Message(Chat::Yellow, "This NPC has quest globals disabled."); + } + return; + } + + c->Message(Chat::Yellow, "Invalid action specified."); +} + diff --git a/zone/gm_commands/questerrors.cpp b/zone/gm_commands/questerrors.cpp new file mode 100755 index 000000000..cbc35e599 --- /dev/null +++ b/zone/gm_commands/questerrors.cpp @@ -0,0 +1,23 @@ +#include "../client.h" +#include "../quest_parser_collection.h" + +void command_questerrors(Client *c, const Seperator *sep) +{ + std::list err; + parse->GetErrors(err); + c->Message(Chat::White, "Current Quest Errors:"); + + auto iter = err.begin(); + int i = 0; + while (iter != err.end()) { + if (i >= 30) { + c->Message(Chat::White, "Maximum of 30 Errors shown..."); + break; + } + + c->Message(Chat::White, iter->c_str()); + ++i; + ++iter; + } +} + diff --git a/zone/gm_commands/race.cpp b/zone/gm_commands/race.cpp new file mode 100755 index 000000000..2729965b1 --- /dev/null +++ b/zone/gm_commands/race.cpp @@ -0,0 +1,29 @@ +#include "../client.h" + +void command_race(Client *c, const Seperator *sep) +{ + Mob *target = c->CastToMob(); + + if (sep->IsNumber(1)) { + auto race = atoi(sep->arg[1]); + if ((race >= 0 && race <= RuleI(NPC, MaxRaceID)) || (race >= 2253 && race <= 2259)) { + if ((c->GetTarget()) && c->Admin() >= commandRaceOthers) { + target = c->GetTarget(); + } + target->SendIllusionPacket(race); + } + else { + c->Message( + Chat::White, + fmt::format( + "Usage: #race [0-{}, 2253-2259] (0 for back to normal)", + RuleI(NPC, MaxRaceID)).c_str()); + } + } + else { + c->Message( + Chat::White, + fmt::format("Usage: #race [0-{}, 2253-2259] (0 for back to normal)", RuleI(NPC, MaxRaceID)).c_str()); + } +} + diff --git a/zone/gm_commands/raidloot.cpp b/zone/gm_commands/raidloot.cpp new file mode 100755 index 000000000..9dfb1c21c --- /dev/null +++ b/zone/gm_commands/raidloot.cpp @@ -0,0 +1,70 @@ +#include "../client.h" +#include "../groups.h" +#include "../raids.h" +#include "../raids.h" + +void command_raidloot(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #raidloot [All|GroupLeader|RaidLeader|Selected]"); + return; + } + + auto client_raid = c->GetRaid(); + if (!client_raid) { + c->Message(Chat::White, "You must be in a Raid to use this command."); + return; + } + + if (!client_raid->IsLeader(c)) { + c->Message(Chat::White, "You must be the Raid Leader to use this command."); + return; + } + + std::string raid_loot_type = str_tolower(sep->arg[1]); + bool is_all = raid_loot_type.find("all") != std::string::npos; + bool is_group_leader = raid_loot_type.find("groupleader") != std::string::npos; + bool is_raid_leader = raid_loot_type.find("raidleader") != std::string::npos; + bool is_selected = raid_loot_type.find("selected") != std::string::npos; + if ( + !is_all && + !is_group_leader && + !is_raid_leader && + !is_selected + ) { + c->Message(Chat::White, "Usage: #raidloot [All|GroupLeader|RaidLeader|Selected]"); + return; + } + + std::map loot_types = { + {RaidLootTypes::All, "All"}, + {RaidLootTypes::GroupLeader, "GroupLeader"}, + {RaidLootTypes::RaidLeader, "RaidLeader"}, + {RaidLootTypes::Selected, "Selected"} + }; + + uint32 loot_type; + if (is_all) { + loot_type = RaidLootTypes::All; + } + else if (is_group_leader) { + loot_type = RaidLootTypes::GroupLeader; + } + else if (is_raid_leader) { + loot_type = RaidLootTypes::RaidLeader; + } + else if (is_selected) { + loot_type = RaidLootTypes::Selected; + } + + c->Message( + Chat::White, + fmt::format( + "Loot type changed to {} ({}).", + loot_types[loot_type], + loot_type + ).c_str() + ); +} + diff --git a/zone/gm_commands/randomfeatures.cpp b/zone/gm_commands/randomfeatures.cpp new file mode 100755 index 000000000..9b2c85c10 --- /dev/null +++ b/zone/gm_commands/randomfeatures.cpp @@ -0,0 +1,18 @@ +#include "../client.h" + +void command_randomfeatures(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!target) { + c->Message(Chat::White, "Error: This command requires a target"); + } + else { + if (target->RandomizeFeatures()) { + c->Message(Chat::White, "Features Randomized"); + } + else { + c->Message(Chat::White, "This command requires a Playable Race as the target"); + } + } +} + diff --git a/zone/gm_commands/refreshgroup.cpp b/zone/gm_commands/refreshgroup.cpp new file mode 100755 index 000000000..243997141 --- /dev/null +++ b/zone/gm_commands/refreshgroup.cpp @@ -0,0 +1,19 @@ +#include "../client.h" +#include "../groups.h" + +void command_refreshgroup(Client *c, const Seperator *sep) +{ + if (!c) { + return; + } + + Group *g = c->GetGroup(); + + if (!g) { + return; + } + + database.RefreshGroupFromDB(c); + //g->SendUpdate(7, c); +} + diff --git a/zone/gm_commands/reloadaa.cpp b/zone/gm_commands/reloadaa.cpp new file mode 100755 index 000000000..5df40b105 --- /dev/null +++ b/zone/gm_commands/reloadaa.cpp @@ -0,0 +1,17 @@ +#include "../client.h" +#include "../../common/file_util.h" + +void command_reloadaa(Client *c, const Seperator *sep) +{ + c->Message(Chat::White, "Reloading Alternate Advancement Data..."); + zone->LoadAlternateAdvancement(); + c->Message(Chat::White, "Alternate Advancement Data Reloaded"); + entity_list.SendAlternateAdvancementStats(); +} + +inline bool file_exists(const std::string &name) +{ + std::ifstream f(name.c_str()); + return f.good(); +} + diff --git a/zone/gm_commands/reloadallrules.cpp b/zone/gm_commands/reloadallrules.cpp new file mode 100755 index 000000000..d5fb8b757 --- /dev/null +++ b/zone/gm_commands/reloadallrules.cpp @@ -0,0 +1,16 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_reloadallrules(Client *c, const Seperator *sep) +{ + if (c) { + auto pack = new ServerPacket(ServerOP_ReloadRules, 0); + worldserver.SendPacket(pack); + c->Message(Chat::Red, "Successfully sent the packet to world to reload rules globally. (including world)"); + safe_delete(pack); + + } +} + diff --git a/zone/gm_commands/reloademote.cpp b/zone/gm_commands/reloademote.cpp new file mode 100755 index 000000000..3003776e4 --- /dev/null +++ b/zone/gm_commands/reloademote.cpp @@ -0,0 +1,9 @@ +#include "../client.h" + +void command_reloademote(Client *c, const Seperator *sep) +{ + zone->NPCEmoteList.Clear(); + zone->LoadNPCEmotes(&zone->NPCEmoteList); + c->Message(Chat::White, "NPC emotes reloaded."); +} + diff --git a/zone/gm_commands/reloadlevelmods.cpp b/zone/gm_commands/reloadlevelmods.cpp new file mode 100755 index 000000000..29ce28c11 --- /dev/null +++ b/zone/gm_commands/reloadlevelmods.cpp @@ -0,0 +1,15 @@ +#include "../client.h" + +void command_reloadlevelmods(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + if (RuleB(Zone, LevelBasedEXPMods)) { + zone->LoadLevelEXPMods(); + c->Message(Chat::Yellow, "Level based EXP Mods have been reloaded zonewide"); + } + else { + c->Message(Chat::Yellow, "Level based EXP Mods are disabled in rules!"); + } + } +} + diff --git a/zone/gm_commands/reloadmerchants.cpp b/zone/gm_commands/reloadmerchants.cpp new file mode 100755 index 000000000..560551682 --- /dev/null +++ b/zone/gm_commands/reloadmerchants.cpp @@ -0,0 +1,8 @@ +#include "../client.h" + +void command_reloadmerchants(Client *c, const Seperator *sep) +{ + entity_list.ReloadMerchants(); + c->Message(Chat::Yellow, "Reloading merchants."); +} + diff --git a/zone/gm_commands/reloadperlexportsettings.cpp b/zone/gm_commands/reloadperlexportsettings.cpp new file mode 100755 index 000000000..1c3137728 --- /dev/null +++ b/zone/gm_commands/reloadperlexportsettings.cpp @@ -0,0 +1,16 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_reloadperlexportsettings(Client *c, const Seperator *sep) +{ + if (c) { + auto pack = new ServerPacket(ServerOP_ReloadPerlExportSettings, 0); + worldserver.SendPacket(pack); + c->Message(Chat::Red, "Successfully sent the packet to world to reload Perl Export settings"); + safe_delete(pack); + + } +} + diff --git a/zone/gm_commands/reloadqst.cpp b/zone/gm_commands/reloadqst.cpp new file mode 100755 index 000000000..48909377a --- /dev/null +++ b/zone/gm_commands/reloadqst.cpp @@ -0,0 +1,23 @@ +#include "../client.h" +#include "../quest_parser_collection.h" + +void command_reloadqst(Client *c, const Seperator *sep) +{ + bool stop_timers = false; + + if (sep->IsNumber(1)) { + stop_timers = std::stoi(sep->arg[1]) != 0 ? true : false; + } + + std::string stop_timers_message = stop_timers ? " and stopping timers" : ""; + c->Message( + Chat::White, + fmt::format( + "Clearing quest memory cache{}.", + stop_timers_message + ).c_str() + ); + entity_list.ClearAreas(); + parse->ReloadQuests(stop_timers); +} + diff --git a/zone/gm_commands/reloadstatic.cpp b/zone/gm_commands/reloadstatic.cpp new file mode 100755 index 000000000..39666f724 --- /dev/null +++ b/zone/gm_commands/reloadstatic.cpp @@ -0,0 +1,8 @@ +#include "../client.h" + +void command_reloadstatic(Client *c, const Seperator *sep) +{ + c->Message(Chat::White, "Reloading zone static data..."); + zone->ReloadStaticData(); +} + diff --git a/zone/gm_commands/reloadtitles.cpp b/zone/gm_commands/reloadtitles.cpp new file mode 100755 index 000000000..f9143b966 --- /dev/null +++ b/zone/gm_commands/reloadtitles.cpp @@ -0,0 +1,14 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_reloadtitles(Client *c, const Seperator *sep) +{ + auto pack = new ServerPacket(ServerOP_ReloadTitles, 0); + worldserver.SendPacket(pack); + safe_delete(pack); + c->Message(Chat::Yellow, "Player Titles Reloaded."); + +} + diff --git a/zone/gm_commands/reloadtraps.cpp b/zone/gm_commands/reloadtraps.cpp new file mode 100755 index 000000000..1057ec771 --- /dev/null +++ b/zone/gm_commands/reloadtraps.cpp @@ -0,0 +1,8 @@ +#include "../client.h" + +void command_reloadtraps(Client *c, const Seperator *sep) +{ + entity_list.UpdateAllTraps(true, true); + c->Message(Chat::Default, "Traps reloaded for %s.", zone->GetShortName()); +} + diff --git a/zone/gm_commands/reloadworld.cpp b/zone/gm_commands/reloadworld.cpp new file mode 100755 index 000000000..f87574d79 --- /dev/null +++ b/zone/gm_commands/reloadworld.cpp @@ -0,0 +1,22 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_reloadworld(Client *c, const Seperator *sep) +{ + int world_repop = atoi(sep->arg[1]); + if (world_repop == 0) { + c->Message(Chat::White, "Reloading quest cache worldwide."); + } + else { + c->Message(Chat::White, "Reloading quest cache and repopping zones worldwide."); + } + + auto pack = new ServerPacket(ServerOP_ReloadWorld, sizeof(ReloadWorld_Struct)); + ReloadWorld_Struct *RW = (ReloadWorld_Struct *) pack->pBuffer; + RW->Option = world_repop; + worldserver.SendPacket(pack); + safe_delete(pack); +} + diff --git a/zone/gm_commands/reloadworldrules.cpp b/zone/gm_commands/reloadworldrules.cpp new file mode 100755 index 000000000..8e1b12ff3 --- /dev/null +++ b/zone/gm_commands/reloadworldrules.cpp @@ -0,0 +1,15 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_reloadworldrules(Client *c, const Seperator *sep) +{ + if (c) { + auto pack = new ServerPacket(ServerOP_ReloadRulesWorld, 0); + worldserver.SendPacket(pack); + c->Message(Chat::Red, "Successfully sent the packet to world to reload rules. (only world)"); + safe_delete(pack); + } +} + diff --git a/zone/gm_commands/reloadzps.cpp b/zone/gm_commands/reloadzps.cpp new file mode 100755 index 000000000..353499d88 --- /dev/null +++ b/zone/gm_commands/reloadzps.cpp @@ -0,0 +1,8 @@ +#include "../client.h" + +void command_reloadzps(Client *c, const Seperator *sep) +{ + content_db.LoadStaticZonePoints(&zone->zone_point_list, zone->GetShortName(), zone->GetInstanceVersion()); + c->Message(Chat::White, "Reloading server zone_points."); +} + diff --git a/zone/gm_commands/repop.cpp b/zone/gm_commands/repop.cpp new file mode 100755 index 000000000..114ef6fa5 --- /dev/null +++ b/zone/gm_commands/repop.cpp @@ -0,0 +1,40 @@ +#include "../client.h" + +void command_repop(Client *c, const Seperator *sep) +{ + int timearg = 1; + int delay = 0; + + if (sep->arg[1] && strcasecmp(sep->arg[1], "force") == 0) { + timearg++; + + LinkedListIterator iterator(zone->spawn2_list); + iterator.Reset(); + while (iterator.MoreElements()) { + std::string query = StringFormat( + "DELETE FROM respawn_times WHERE id = %lu AND instance_id = %lu", + (unsigned long) iterator.GetData()->GetID(), + (unsigned long) zone->GetInstanceID() + ); + auto results = database.QueryDatabase(query); + iterator.Advance(); + } + c->Message(Chat::White, "Zone depop: Force resetting spawn timers."); + } + + if (!sep->IsNumber(timearg)) { + c->Message(Chat::White, "Zone depopped - repopping now."); + + zone->Repop(); + + /* Force a spawn2 timer trigger so we don't delay actually spawning the NPC's */ + zone->spawn2_timer.Trigger(); + return; + } + + c->Message(Chat::White, "Zone depoped. Repop in %i seconds", atoi(sep->arg[timearg])); + zone->Repop(atoi(sep->arg[timearg]) * 1000); + + zone->spawn2_timer.Trigger(); +} + diff --git a/zone/gm_commands/resetaa.cpp b/zone/gm_commands/resetaa.cpp new file mode 100755 index 000000000..d83ba6beb --- /dev/null +++ b/zone/gm_commands/resetaa.cpp @@ -0,0 +1,13 @@ +#include "../client.h" + +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."); + } +} + diff --git a/zone/gm_commands/resetaa_timer.cpp b/zone/gm_commands/resetaa_timer.cpp new file mode 100755 index 000000000..452ed4266 --- /dev/null +++ b/zone/gm_commands/resetaa_timer.cpp @@ -0,0 +1,26 @@ +#include "../client.h" + +void command_resetaa_timer(Client *c, const Seperator *sep) +{ + Client *target = nullptr; + if (!c->GetTarget() || !c->GetTarget()->IsClient()) { + target = c; + } + else { + target = c->GetTarget()->CastToClient(); + } + + if (sep->IsNumber(1)) { + int timer_id = atoi(sep->arg[1]); + c->Message(Chat::White, "Reset of timer %i for %s", timer_id, c->GetName()); + c->ResetAlternateAdvancementTimer(timer_id); + } + else if (!strcasecmp(sep->arg[1], "all")) { + c->Message(Chat::White, "Reset all timers for %s", c->GetName()); + c->ResetAlternateAdvancementTimers(); + } + else { + c->Message(Chat::White, "usage: #resetaa_timer [all | timer_id]"); + } +} + diff --git a/zone/gm_commands/resetdisc_timer.cpp b/zone/gm_commands/resetdisc_timer.cpp new file mode 100755 index 000000000..0c3cfe267 --- /dev/null +++ b/zone/gm_commands/resetdisc_timer.cpp @@ -0,0 +1,23 @@ +#include "../client.h" + +void command_resetdisc_timer(Client *c, const Seperator *sep) +{ + Client *target = c->GetTarget()->CastToClient(); + if (!c->GetTarget() || !c->GetTarget()->IsClient()) { + target = c; + } + + if (sep->IsNumber(1)) { + int timer_id = atoi(sep->arg[1]); + c->Message(Chat::White, "Reset of disc timer %i for %s", timer_id, c->GetName()); + c->ResetDisciplineTimer(timer_id); + } + else if (!strcasecmp(sep->arg[1], "all")) { + c->Message(Chat::White, "Reset all disc timers for %s", c->GetName()); + c->ResetAllDisciplineTimers(); + } + else { + c->Message(Chat::White, "usage: #resetdisc_timer [all | timer_id]"); + } +} + diff --git a/zone/gm_commands/revoke.cpp b/zone/gm_commands/revoke.cpp new file mode 100755 index 000000000..34ee10ee3 --- /dev/null +++ b/zone/gm_commands/revoke.cpp @@ -0,0 +1,48 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_revoke(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0 || sep->arg[2][0] == 0) { + c->Message(Chat::White, "Usage: #revoke [charname] [1/0]"); + return; + } + + uint32 characterID = database.GetAccountIDByChar(sep->arg[1]); + if (characterID == 0) { + c->Message(Chat::Red, "Character does not exist."); + return; + } + + int flag = sep->arg[2][0] == '1' ? true : false; + std::string query = StringFormat("UPDATE account SET revoked = %d WHERE id = %i", flag, characterID); + auto results = database.QueryDatabase(query); + + c->Message( + Chat::Red, + "%s account number %i with the character %s.", + flag ? "Revoking" : "Unrevoking", + characterID, + sep->arg[1] + ); + + Client *revokee = entity_list.GetClientByAccID(characterID); + if (revokee) { + c->Message(Chat::White, "Found %s in this zone.", revokee->GetName()); + revokee->SetRevoked(flag); + return; + } + + c->Message(Chat::Red, "#revoke: Couldn't find %s in this zone, passing request to worldserver.", sep->arg[1]); + + auto outapp = new ServerPacket(ServerOP_Revoke, sizeof(RevokeStruct)); + RevokeStruct *revoke = (RevokeStruct *) outapp->pBuffer; + strn0cpy(revoke->adminname, c->GetName(), 64); + strn0cpy(revoke->name, sep->arg[1], 64); + revoke->toggle = flag; + worldserver.SendPacket(outapp); + safe_delete(outapp); +} + diff --git a/zone/gm_commands/roambox.cpp b/zone/gm_commands/roambox.cpp new file mode 100755 index 000000000..3c31f2312 --- /dev/null +++ b/zone/gm_commands/roambox.cpp @@ -0,0 +1,93 @@ +#include "../client.h" +#include "../groups.h" + +void command_roambox(Client *c, const Seperator *sep) +{ + std::string arg1 = sep->arg[1]; + + Mob *target = c->GetTarget(); + if (!target || !target->IsNPC()) { + c->Message(Chat::Red, "You need a valid NPC target for this command"); + return; + } + + NPC *npc = dynamic_cast(target); + int spawn_group_id = npc->GetSpawnGroupId(); + if (spawn_group_id <= 0) { + c->Message(Chat::Red, "NPC needs a valid SpawnGroup!"); + return; + } + + if (arg1 == "set") { + int box_size = (sep->arg[2] ? atoi(sep->arg[2]) : 0); + int delay = (sep->arg[3] ? atoi(sep->arg[3]) : 15000); + if (box_size > 0) { + std::string query = fmt::format( + SQL( + UPDATE spawngroup SET + dist = {}, + min_x = {}, + max_x = {}, + min_y = {}, + max_y = {}, + delay = {} + WHERE id = {} + ), + (box_size / 2), + npc->GetX() - (box_size / 2), + npc->GetX() + (box_size / 2), + npc->GetY() - (box_size / 2), + npc->GetY() + (box_size / 2), + delay, + spawn_group_id + ); + + database.QueryDatabase(query); + + c->Message( + Chat::Yellow, + "NPC (%s) Roam Box set to box size of [%i] SpawnGroupId [%i] delay [%i]", + npc->GetCleanName(), + box_size, + spawn_group_id, + delay + ); + + return; + } + + c->Message(Chat::Red, "Box size must be set!"); + } + + if (arg1 == "remove") { + std::string query = fmt::format( + SQL( + UPDATE spawngroup SET + dist = 0, + min_x = 0, + max_x = 0, + min_y = 0, + max_y = 0, + delay = 0 + WHERE id = {} + ), + spawn_group_id + ); + + database.QueryDatabase(query); + + c->Message( + Chat::Yellow, + "NPC (%s) Roam Box has been removed from SpawnGroupID [%i]", + npc->GetCleanName(), + spawn_group_id + ); + + return; + } + + c->Message(Chat::Yellow, "> Command Usage"); + c->Message(Chat::Yellow, "#roambox set box_size [delay = 0]"); + c->Message(Chat::Yellow, "#roambox remove"); +} + diff --git a/zone/gm_commands/rules.cpp b/zone/gm_commands/rules.cpp new file mode 100755 index 000000000..65ef8bc37 --- /dev/null +++ b/zone/gm_commands/rules.cpp @@ -0,0 +1,232 @@ +#include "../client.h" + +void command_rules(Client *c, const Seperator *sep) +{ + //super-command for managing rules settings + if (sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { + c->Message(Chat::White, "Syntax: #rules [subcommand]."); + c->Message(Chat::White, "-- Rule Set Manipulation --"); + c->Message(Chat::White, "...listsets - List avaliable rule sets"); + c->Message(Chat::White, "...current - gives the name of the ruleset currently running in this zone"); + c->Message(Chat::White, "...reload - Reload the selected ruleset in this zone"); + c->Message(Chat::White, "...switch (ruleset name) - Change the selected ruleset and load it"); + c->Message( + Chat::White, + "...load (ruleset name) - Load a ruleset in just this zone without changing the selected set" + ); +//too lazy to write this right now: +// c->Message(Chat::White, "...wload (ruleset name) - Load a ruleset in all zones without changing the selected set"); + c->Message(Chat::White, "...store [ruleset name] - Store the running ruleset as the specified name"); + c->Message(Chat::White, "---------------------"); + c->Message(Chat::White, "-- Running Rule Manipulation --"); + c->Message(Chat::White, "...reset - Reset all rules to their default values"); + c->Message(Chat::White, "...get [rule] - Get the specified rule's local value"); + c->Message(Chat::White, "...set (rule) (value) - Set the specified rule to the specified value locally only"); + c->Message( + Chat::White, + "...setdb (rule) (value) - Set the specified rule to the specified value locally and in the DB" + ); + c->Message( + Chat::White, + "...list [catname] - List all rules in the specified category (or all categiries if omitted)" + ); + c->Message(Chat::White, "...values [catname] - List the value of all rules in the specified category"); + return; + } + + if (!strcasecmp(sep->arg[1], "current")) { + c->Message( + Chat::White, "Currently running ruleset '%s' (%d)", RuleManager::Instance()->GetActiveRuleset(), + RuleManager::Instance()->GetActiveRulesetID()); + } + else if (!strcasecmp(sep->arg[1], "listsets")) { + std::map sets; + if (!RuleManager::Instance()->ListRulesets(&database, sets)) { + c->Message(Chat::Red, "Failed to list rule sets!"); + return; + } + + c->Message(Chat::White, "Avaliable rule sets:"); + std::map::iterator cur, end; + cur = sets.begin(); + end = sets.end(); + for (; cur != end; ++cur) { + c->Message(Chat::White, "(%d) %s", cur->first, cur->second.c_str()); + } + } + else if (!strcasecmp(sep->arg[1], "reload")) { + RuleManager::Instance()->LoadRules(&database, RuleManager::Instance()->GetActiveRuleset(), true); + c->Message( + Chat::White, "The active ruleset (%s (%d)) has been reloaded", RuleManager::Instance()->GetActiveRuleset(), + RuleManager::Instance()->GetActiveRulesetID()); + } + else if (!strcasecmp(sep->arg[1], "switch")) { + //make sure this is a valid rule set.. + int rsid = RuleManager::Instance()->GetRulesetID(&database, sep->arg[2]); + if (rsid < 0) { + c->Message(Chat::Red, "Unknown rule set '%s'", sep->arg[2]); + return; + } + if (!database.SetVariable("RuleSet", sep->arg[2])) { + c->Message(Chat::Red, "Failed to update variables table to change selected rule set"); + return; + } + + //TODO: we likely want to reload this ruleset everywhere... + RuleManager::Instance()->LoadRules(&database, sep->arg[2], true); + + c->Message( + Chat::White, + "The selected ruleset has been changed to (%s (%d)) and reloaded locally", + sep->arg[2], + rsid + ); + } + else if (!strcasecmp(sep->arg[1], "load")) { + //make sure this is a valid rule set.. + int rsid = RuleManager::Instance()->GetRulesetID(&database, sep->arg[2]); + if (rsid < 0) { + c->Message(Chat::Red, "Unknown rule set '%s'", sep->arg[2]); + return; + } + RuleManager::Instance()->LoadRules(&database, sep->arg[2], true); + c->Message(Chat::White, "Loaded ruleset '%s' (%d) locally", sep->arg[2], rsid); + } + else if (!strcasecmp(sep->arg[1], "store")) { + if (sep->argnum == 1) { + //store current rule set. + RuleManager::Instance()->SaveRules(&database); + c->Message(Chat::White, "Rules saved"); + } + else if (sep->argnum == 2) { + RuleManager::Instance()->SaveRules(&database, sep->arg[2]); + int prersid = RuleManager::Instance()->GetActiveRulesetID(); + int rsid = RuleManager::Instance()->GetRulesetID(&database, sep->arg[2]); + if (rsid < 0) { + c->Message(Chat::Red, "Unable to query ruleset ID after store, it most likely failed."); + } + else { + c->Message(Chat::White, "Stored rules as ruleset '%s' (%d)", sep->arg[2], rsid); + if (prersid != rsid) { + c->Message(Chat::White, "Rule set %s (%d) is now active in this zone", sep->arg[2], rsid); + } + } + } + else { + c->Message(Chat::Red, "Invalid argument count, see help."); + return; + } + } + else if (!strcasecmp(sep->arg[1], "reset")) { + RuleManager::Instance()->ResetRules(true); + c->Message(Chat::White, "The running ruleset has been set to defaults"); + + } + else if (!strcasecmp(sep->arg[1], "get")) { + if (sep->argnum != 2) { + c->Message(Chat::Red, "Invalid argument count, see help."); + return; + } + std::string value; + if (!RuleManager::Instance()->GetRule(sep->arg[2], value)) { + c->Message(Chat::Red, "Unable to find rule %s", sep->arg[2]); + } + else { + c->Message(Chat::White, "%s - %s", sep->arg[2], value.c_str()); + } + + } + else if (!strcasecmp(sep->arg[1], "set")) { + if (sep->argnum != 3) { + c->Message(Chat::Red, "Invalid argument count, see help."); + return; + } + if (!RuleManager::Instance()->SetRule(sep->arg[2], sep->arg[3], nullptr, false, true)) { + c->Message(Chat::Red, "Failed to modify rule"); + } + else { + c->Message(Chat::White, "Rule modified locally."); + } + } + else if (!strcasecmp(sep->arg[1], "setdb")) { + if (sep->argnum != 3) { + c->Message(Chat::Red, "Invalid argument count, see help."); + return; + } + if (!RuleManager::Instance()->SetRule(sep->arg[2], sep->arg[3], &database, true, true)) { + c->Message(Chat::Red, "Failed to modify rule"); + } + else { + c->Message(Chat::White, "Rule modified locally and in the database."); + } + } + else if (!strcasecmp(sep->arg[1], "list")) { + if (sep->argnum == 1) { + std::vector rule_list; + if (!RuleManager::Instance()->ListCategories(rule_list)) { + c->Message(Chat::Red, "Failed to list categories!"); + return; + } + c->Message(Chat::White, "Rule Categories:"); + std::vector::iterator cur, end; + cur = rule_list.begin(); + end = rule_list.end(); + for (; cur != end; ++cur) { + c->Message(Chat::White, " %s", *cur); + } + } + else if (sep->argnum == 2) { + const char *catfilt = nullptr; + if (std::string("all") != sep->arg[2]) { + catfilt = sep->arg[2]; + } + std::vector rule_list; + if (!RuleManager::Instance()->ListRules(catfilt, rule_list)) { + c->Message(Chat::Red, "Failed to list rules!"); + return; + } + c->Message(Chat::White, "Rules in category %s:", sep->arg[2]); + std::vector::iterator cur, end; + cur = rule_list.begin(); + end = rule_list.end(); + for (; cur != end; ++cur) { + c->Message(Chat::White, " %s", *cur); + } + } + else { + c->Message(Chat::Red, "Invalid argument count, see help."); + } + } + else if (!strcasecmp(sep->arg[1], "values")) { + if (sep->argnum != 2) { + c->Message(Chat::Red, "Invalid argument count, see help."); + return; + } + else { + const char *catfilt = nullptr; + if (std::string("all") != sep->arg[2]) { + catfilt = sep->arg[2]; + } + std::vector rule_list; + if (!RuleManager::Instance()->ListRules(catfilt, rule_list)) { + c->Message(Chat::Red, "Failed to list rules!"); + return; + } + c->Message(Chat::White, "Rules & values in category %s:", sep->arg[2]); + std::vector::iterator cur, end; + cur = rule_list.begin(); + end = rule_list.end(); + for (std::string tmp_value; cur != end; ++cur) { + if (RuleManager::Instance()->GetRule(*cur, tmp_value)) { + c->Message(Chat::White, " %s - %s", *cur, tmp_value.c_str()); + } + } + } + + } + else { + c->Message(Chat::Yellow, "Invalid action specified. use '#rules help' for help"); + } +} + + diff --git a/zone/gm_commands/save.cpp b/zone/gm_commands/save.cpp new file mode 100755 index 000000000..e22b2cbfd --- /dev/null +++ b/zone/gm_commands/save.cpp @@ -0,0 +1,33 @@ +#include "../client.h" +#include "../corpse.h" + +void command_save(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0) { + c->Message(Chat::White, "Error: no target"); + } + else if (c->GetTarget()->IsClient()) { + if (c->GetTarget()->CastToClient()->Save(2)) { + c->Message(Chat::White, "%s successfully saved.", c->GetTarget()->GetName()); + } + else { + c->Message(Chat::White, "Manual save for %s failed.", c->GetTarget()->GetName()); + } + } + else if (c->GetTarget()->IsPlayerCorpse()) { + if (c->GetTarget()->CastToMob()->Save()) { + c->Message( + Chat::White, + "%s successfully saved. (dbid=%u)", + c->GetTarget()->GetName(), + c->GetTarget()->CastToCorpse()->GetCorpseDBID()); + } + else { + c->Message(Chat::White, "Manual save for %s failed.", c->GetTarget()->GetName()); + } + } + else { + c->Message(Chat::White, "Error: target not a Client/PlayerCorpse"); + } +} + diff --git a/zone/gm_commands/scale.cpp b/zone/gm_commands/scale.cpp new file mode 100755 index 000000000..d260a25e4 --- /dev/null +++ b/zone/gm_commands/scale.cpp @@ -0,0 +1,136 @@ +#include "../client.h" +#include "../npc_scale_manager.h" + +void command_scale(Client *c, const Seperator *sep) +{ + if (sep->argnum == 0) { + c->Message(Chat::Yellow, "# Usage # "); + c->Message(Chat::Yellow, "#scale [static/dynamic] (With targeted NPC)"); + c->Message(Chat::Yellow, "#scale [npc_name_search] [static/dynamic] (To make zone-wide changes)"); + c->Message(Chat::Yellow, "#scale all [static/dynamic]"); + return; + } + + /** + * Targeted changes + */ + if (c->GetTarget() && c->GetTarget()->IsNPC() && sep->argnum < 2) { + NPC *npc = c->GetTarget()->CastToNPC(); + + bool apply_status = false; + if (strcasecmp(sep->arg[1], "dynamic") == 0) { + c->Message(Chat::Yellow, "Applying global base scaling to npc dynamically (All stats set to zeroes)..."); + apply_status = npc_scale_manager->ApplyGlobalBaseScalingToNPCDynamically(npc); + } + else if (strcasecmp(sep->arg[1], "static") == 0) { + c->Message(Chat::Yellow, "Applying global base scaling to npc statically (Copying base stats onto NPC)..."); + apply_status = npc_scale_manager->ApplyGlobalBaseScalingToNPCStatically(npc); + } + else { + return; + } + + if (apply_status) { + c->Message(Chat::Yellow, "Applied to NPC '%s' successfully!", npc->GetName()); + } + else { + c->Message( + Chat::Yellow, "Failed to load scaling data from the database " + "for this npc / type, see 'NPCScaling' log for more info" + ); + } + } + else if (c->GetTarget() && sep->argnum < 2) { + c->Message(Chat::Yellow, "Target must be an npc!"); + } + + /** + * Zonewide + */ + if (sep->argnum > 1) { + + std::string scale_type; + if (strcasecmp(sep->arg[2], "dynamic") == 0) { + scale_type = "dynamic"; + } + else if (strcasecmp(sep->arg[2], "static") == 0) { + scale_type = "static"; + } + + if (scale_type.length() <= 0) { + c->Message( + Chat::Yellow, + "You must first set if you intend on using static versus dynamic for these changes" + ); + c->Message(Chat::Yellow, "#scale [npc_name_search] [static/dynamic]"); + c->Message(Chat::Yellow, "#scale all [static/dynamic]"); + return; + } + + std::string search_string = sep->arg[1]; + + auto &entity_list_search = entity_list.GetNPCList(); + + int found_count = 0; + for (auto &itr : entity_list_search) { + NPC *entity = itr.second; + + std::string entity_name = entity->GetName(); + + /** + * Filter by name + */ + if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos && + strcasecmp(sep->arg[1], "all") != 0) { + continue; + } + + std::string status = "(Searching)"; + + if (strcasecmp(sep->arg[3], "apply") == 0) { + status = "(Applying)"; + + if (strcasecmp(sep->arg[2], "dynamic") == 0) { + npc_scale_manager->ApplyGlobalBaseScalingToNPCDynamically(entity); + } + if (strcasecmp(sep->arg[2], "static") == 0) { + npc_scale_manager->ApplyGlobalBaseScalingToNPCStatically(entity); + } + } + + c->Message( + 15, + "| ID %5d | %s | x %.0f | y %0.f | z %.0f | DBID %u %s", + entity->GetID(), + entity->GetName(), + entity->GetX(), + entity->GetY(), + entity->GetZ(), + entity->GetNPCTypeID(), + status.c_str() + ); + + found_count++; + } + + if (strcasecmp(sep->arg[3], "apply") == 0) { + c->Message(Chat::Yellow, "%s scaling applied against (%i) NPC's", sep->arg[2], found_count); + } + else { + + std::string saylink = StringFormat( + "#scale %s %s apply", + sep->arg[1], + sep->arg[2] + ); + + c->Message(Chat::Yellow, "Found (%i) NPC's that match this search...", found_count); + c->Message( + Chat::Yellow, "To apply these changes, click <%s> or type %s", + EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Apply").c_str(), + saylink.c_str() + ); + } + } +} + diff --git a/zone/gm_commands/scribespell.cpp b/zone/gm_commands/scribespell.cpp new file mode 100755 index 000000000..5acfd3a8d --- /dev/null +++ b/zone/gm_commands/scribespell.cpp @@ -0,0 +1,66 @@ +#include "../client.h" + +void command_scribespell(Client *c, const Seperator *sep) +{ + uint16 spell_id = 0; + uint16 book_slot = -1; + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + t = c->GetTarget()->CastToClient(); + } + + if (!sep->arg[1][0]) { + c->Message(Chat::White, "FORMAT: #scribespell "); + return; + } + + spell_id = atoi(sep->arg[1]); + + if (IsValidSpell(spell_id)) { + t->Message(Chat::White, "Scribing spell: %s (%i) to spellbook.", spells[spell_id].name, spell_id); + + if (t != c) { + c->Message(Chat::White, "Scribing spell: %s (%i) for %s.", spells[spell_id].name, spell_id, t->GetName()); + } + + LogInfo("Scribe spell: [{}] ([{}]) request for [{}] from [{}]", + spells[spell_id].name, + spell_id, + t->GetName(), + c->GetName()); + + if (spells[spell_id].classes[WARRIOR] != 0 && spells[spell_id].skill != 52 && + spells[spell_id].classes[t->GetPP().class_ - 1] > 0 && !IsDiscipline(spell_id)) { + book_slot = t->GetNextAvailableSpellBookSlot(); + + if (book_slot >= 0 && t->FindSpellBookSlotBySpellID(spell_id) < 0) { + t->ScribeSpell(spell_id, book_slot); + } + else { + t->Message( + Chat::Red, + "Unable to scribe spell: %s (%i) to your spellbook.", + spells[spell_id].name, + spell_id + ); + + if (t != c) { + c->Message( + Chat::Red, + "Unable to scribe spell: %s (%i) for %s.", + spells[spell_id].name, + spell_id, + t->GetName()); + } + } + } + else { + c->Message(Chat::Red, "Your target can not scribe this spell."); + } + } + else { + c->Message(Chat::Red, "Spell ID: %i is an unknown spell and cannot be scribed.", spell_id); + } +} + diff --git a/zone/gm_commands/scribespells.cpp b/zone/gm_commands/scribespells.cpp new file mode 100755 index 000000000..ef30d6384 --- /dev/null +++ b/zone/gm_commands/scribespells.cpp @@ -0,0 +1,65 @@ +#include "../client.h" + +void command_scribespells(Client *c, const Seperator *sep) +{ + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + target = c->GetTarget()->CastToClient(); + } + + if (sep->argnum < 1 || !sep->IsNumber(1)) { + c->Message(Chat::White, "FORMAT: #scribespells "); + return; + } + + uint8 rule_max_level = (uint8) RuleI(Character, MaxLevel); + uint8 max_level = (uint8) std::stoi(sep->arg[1]); + uint8 min_level = ( + sep->IsNumber(2) ? + (uint8) + std::stoi(sep->arg[2]) : + 1 + ); // Default to Level 1 if there isn't a 2nd argument + + if (!c->GetGM()) { // Default to Character:MaxLevel if we're not a GM and Level is higher than the max level + if (max_level > rule_max_level) { + max_level = rule_max_level; + } + + if (min_level > rule_max_level) { + min_level = rule_max_level; + } + } + + if (max_level < 1 || min_level < 1) { + c->Message(Chat::White, "ERROR: Level must be greater than or equal to 1."); + return; + } + + if (min_level > max_level) { + c->Message(Chat::White, "ERROR: Minimum Level must be less than or equal to Maximum Level."); + return; + } + + uint16 scribed_spells = target->ScribeSpells(min_level, max_level); + if (target != c) { + std::string spell_message = ( + scribed_spells > 0 ? + ( + scribed_spells == 1 ? + "A new spell" : + fmt::format("{} New spells", scribed_spells) + ) : + "No new spells" + ); + c->Message( + Chat::White, + fmt::format( + "{} scribed for {}.", + spell_message, + target->GetCleanName() + ).c_str() + ); + } +} + diff --git a/zone/gm_commands/sendzonespawns.cpp b/zone/gm_commands/sendzonespawns.cpp new file mode 100755 index 000000000..caca8f6d0 --- /dev/null +++ b/zone/gm_commands/sendzonespawns.cpp @@ -0,0 +1,7 @@ +#include "../client.h" + +void command_sendzonespawns(Client *c, const Seperator *sep) +{ + entity_list.SendZoneSpawns(c); +} + diff --git a/zone/gm_commands/sensetrap.cpp b/zone/gm_commands/sensetrap.cpp new file mode 100755 index 000000000..792d7867d --- /dev/null +++ b/zone/gm_commands/sensetrap.cpp @@ -0,0 +1,24 @@ +#include "../client.h" + +void command_sensetrap(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!target) { + c->Message(Chat::Red, "You must have a target."); + return; + } + + if (target->IsNPC()) { + if (c->HasSkill(EQ::skills::SkillSenseTraps)) { + if (DistanceSquaredNoZ(c->GetPosition(), target->GetPosition()) > RuleI(Adventure, LDoNTrapDistanceUse)) { + c->Message(Chat::Red, "%s is too far away.", target->GetCleanName()); + return; + } + c->HandleLDoNSenseTraps(target->CastToNPC(), c->GetSkill(EQ::skills::SkillSenseTraps), LDoNTypeMechanical); + } + else { + c->Message(Chat::Red, "You do not have the sense traps skill."); + } + } +} + diff --git a/zone/gm_commands/serverinfo.cpp b/zone/gm_commands/serverinfo.cpp new file mode 100755 index 000000000..e51da7266 --- /dev/null +++ b/zone/gm_commands/serverinfo.cpp @@ -0,0 +1,33 @@ +#include "../client.h" +#include "../../common/serverinfo.h" + +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, "=================================================="); +} + diff --git a/zone/gm_commands/serverrules.cpp b/zone/gm_commands/serverrules.cpp new file mode 100755 index 000000000..69bf799a7 --- /dev/null +++ b/zone/gm_commands/serverrules.cpp @@ -0,0 +1,7 @@ +#include "../client.h" + +void command_serverrules(Client *c, const Seperator *sep) +{ + c->SendRules(c); +} + diff --git a/zone/gm_commands/set_adventure_points.cpp b/zone/gm_commands/set_adventure_points.cpp new file mode 100755 index 000000000..a9b1f47e3 --- /dev/null +++ b/zone/gm_commands/set_adventure_points.cpp @@ -0,0 +1,24 @@ +#include "../client.h" + +void command_set_adventure_points(Client *c, const Seperator *sep) +{ + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient()) { + t = c->GetTarget()->CastToClient(); + } + + if (!sep->arg[1][0]) { + c->Message(Chat::White, "Usage: #setadventurepoints [theme] [points]"); + return; + } + + if (!sep->IsNumber(1) || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #setadventurepoints [theme] [points]"); + return; + } + + c->Message(Chat::White, "Updating adventure points for %s", t->GetName()); + t->UpdateLDoNPoints(atoi(sep->arg[1]), atoi(sep->arg[2])); +} + diff --git a/zone/gm_commands/setaapts.cpp b/zone/gm_commands/setaapts.cpp new file mode 100755 index 000000000..e8222a08f --- /dev/null +++ b/zone/gm_commands/setaapts.cpp @@ -0,0 +1,63 @@ +#include "../client.h" +#include "../groups.h" +#include "../raids.h" +#include "../raids.h" + +void command_setaapts(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (arguments <= 1 || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #setaapts [AA|Group|Raid] [AA Amount]"); + return; + } + + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + std::string aa_type = str_tolower(sep->arg[1]); + std::string group_raid_string; + uint32 aa_points = static_cast(std::min(std::stoull(sep->arg[2]), (unsigned long long) 2000000000)); + bool is_aa = aa_type.find("aa") != std::string::npos; + bool is_group = aa_type.find("group") != std::string::npos; + bool is_raid = aa_type.find("raid") != std::string::npos; + if (!is_aa && !is_group && !is_raid) { + c->Message(Chat::White, "Usage: #setaapts [AA|Group|Raid] [AA Amount]"); + return; + } + + if (is_aa) { + target->GetPP().aapoints = aa_points; + target->GetPP().expAA = 0; + target->SendAlternateAdvancementStats(); + } + else if (is_group || is_raid) { + if (is_group) { + group_raid_string = "Group "; + target->GetPP().group_leadership_points = aa_points; + target->GetPP().group_leadership_exp = 0; + } + else if (is_raid) { + group_raid_string = "Raid "; + target->GetPP().raid_leadership_points = aa_points; + target->GetPP().raid_leadership_exp = 0; + } + target->SendLeadershipEXPUpdate(); + } + + std::string aa_message = fmt::format( + "{} now {} {} {}AA Point{}.", + c == target ? "You" : target->GetCleanName(), + c == target ? "have" : "has", + aa_points, + group_raid_string, + aa_points != 1 ? "s" : "" + + ); + c->Message( + Chat::White, + aa_message.c_str() + ); +} + diff --git a/zone/gm_commands/setaaxp.cpp b/zone/gm_commands/setaaxp.cpp new file mode 100755 index 000000000..32c060aed --- /dev/null +++ b/zone/gm_commands/setaaxp.cpp @@ -0,0 +1,67 @@ +#include "../client.h" +#include "../groups.h" +#include "../raids.h" +#include "../raids.h" + +void command_setaaxp(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (arguments <= 1 || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #setaaxp [AA|Group|Raid] [AA Experience]"); + return; + } + + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + std::string aa_type = str_tolower(sep->arg[1]); + std::string group_raid_string; + uint32 aa_experience = static_cast(std::min( + std::stoull(sep->arg[2]), + (unsigned long long) 2000000000 + )); + bool is_aa = aa_type.find("aa") != std::string::npos; + bool is_group = aa_type.find("group") != std::string::npos; + bool is_raid = aa_type.find("raid") != std::string::npos; + if (!is_aa && !is_group && !is_raid) { + c->Message(Chat::White, "Usage: #setaaxp [AA|Group|Raid] [AA Experience]"); + return; + } + + if (is_aa) { + target->SetEXP( + target->GetEXP(), + aa_experience, + false + ); + } + else if (is_group) { + group_raid_string = "Group "; + target->SetLeadershipEXP( + aa_experience, + target->GetRaidEXP() + ); + } + else if (is_raid) { + group_raid_string = "Raid "; + target->SetLeadershipEXP( + target->GetGroupEXP(), + aa_experience + ); + } + + std::string aa_exp_message = fmt::format( + "{} now {} {} {}AA Experience.", + c == target ? "You" : target->GetCleanName(), + c == target ? "have" : "has", + aa_experience, + group_raid_string + ); + c->Message( + Chat::White, + aa_exp_message.c_str() + ); +} + diff --git a/zone/gm_commands/setanim.cpp b/zone/gm_commands/setanim.cpp new file mode 100755 index 000000000..d7ffa11e7 --- /dev/null +++ b/zone/gm_commands/setanim.cpp @@ -0,0 +1,16 @@ +#include "../client.h" + +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]"); + } +} + diff --git a/zone/gm_commands/setcrystals.cpp b/zone/gm_commands/setcrystals.cpp new file mode 100755 index 000000000..32d22f449 --- /dev/null +++ b/zone/gm_commands/setcrystals.cpp @@ -0,0 +1,53 @@ +#include "../client.h" + +void command_setcrystals(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (arguments <= 1 || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #setcrystals [Ebon|Radiant] [Crystal Amount]"); + return; + } + + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + std::string crystal_type = str_tolower(sep->arg[1]); + uint32 crystal_amount = static_cast(std::min( + std::stoull(sep->arg[2]), + (unsigned long long) 2000000000 + )); + bool is_ebon = crystal_type.find("ebon") != std::string::npos; + bool is_radiant = crystal_type.find("radiant") != std::string::npos; + if (!is_ebon && !is_radiant) { + c->Message(Chat::White, "Usage: #setcrystals [Ebon|Radiant] [Crystal Amount]"); + return; + } + + uint32 crystal_item_id = ( + is_ebon ? + RuleI(Zone, EbonCrystalItemID) : + RuleI(Zone, RadiantCrystalItemID) + ); + + auto crystal_link = database.CreateItemLink(crystal_item_id); + if (is_radiant) { + target->SetRadiantCrystals(crystal_amount); + } + else { + target->SetEbonCrystals(crystal_amount); + } + + c->Message( + Chat::White, + fmt::format( + "{} now {} {} {}.", + c == target ? "You" : target->GetCleanName(), + c == target ? "have" : "has", + crystal_amount, + crystal_link + ).c_str() + ); +} + diff --git a/zone/gm_commands/setfaction.cpp b/zone/gm_commands/setfaction.cpp new file mode 100755 index 000000000..36662d3df --- /dev/null +++ b/zone/gm_commands/setfaction.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +void command_setfaction(Client *c, const Seperator *sep) +{ + if ((sep->arg[1][0] == 0 || strcasecmp(sep->arg[1], "*") == 0) || + ((c->GetTarget() == 0) || (c->GetTarget()->IsClient()))) { + c->Message(Chat::White, "Usage: #setfaction [faction number]"); + return; + } + + auto npcTypeID = c->GetTarget()->CastToNPC()->GetNPCTypeID(); + c->Message(Chat::Yellow, "Setting NPC %u to faction %i", npcTypeID, atoi(sep->argplus[1])); + + std::string query = StringFormat( + "UPDATE npc_types SET npc_faction_id = %i WHERE id = %i", + atoi(sep->argplus[1]), npcTypeID + ); + content_db.QueryDatabase(query); +} + diff --git a/zone/gm_commands/setgraveyard.cpp b/zone/gm_commands/setgraveyard.cpp new file mode 100755 index 000000000..e1b043e4b --- /dev/null +++ b/zone/gm_commands/setgraveyard.cpp @@ -0,0 +1,44 @@ +#include "../client.h" + +void command_setgraveyard(Client *c, const Seperator *sep) +{ + uint32 zoneid = 0; + uint32 graveyard_id = 0; + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + t = c->GetTarget()->CastToClient(); + } + + if (!sep->arg[1][0]) { + c->Message(Chat::White, "Usage: #setgraveyard [zonename]"); + return; + } + + zoneid = ZoneID(sep->arg[1]); + + if (zoneid > 0) { + graveyard_id = content_db.CreateGraveyardRecord(zoneid, t->GetPosition()); + + if (graveyard_id > 0) { + c->Message(Chat::White, "Successfuly added a new record for this graveyard!"); + if (content_db.AddGraveyardIDToZone(zoneid, graveyard_id) > 0) { + c->Message(Chat::White, "Successfuly added this new graveyard for the zone %s.", sep->arg[1]); + // TODO: Set graveyard data to the running zone process. + c->Message(Chat::White, "Done!"); + } + else { + c->Message(Chat::White, "Unable to add this new graveyard to the zone %s.", sep->arg[1]); + } + } + else { + c->Message(Chat::White, "Unable to create a new graveyard record in the database."); + } + } + else { + c->Message(Chat::White, "Unable to retrieve a ZoneID for the zone: %s", sep->arg[1]); + } + + return; +} + diff --git a/zone/gm_commands/setlanguage.cpp b/zone/gm_commands/setlanguage.cpp new file mode 100755 index 000000000..50353f150 --- /dev/null +++ b/zone/gm_commands/setlanguage.cpp @@ -0,0 +1,61 @@ +#include "../client.h" +#include "../../common/languages.h" + +void command_setlanguage(Client *c, const Seperator *sep) +{ + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + auto language_id = sep->IsNumber(1) ? std::stoi(sep->arg[1]) : -1; + auto language_value = sep->IsNumber(2) ? std::stoi(sep->arg[2]) : -1; + if (!strcasecmp(sep->arg[1], "list")) { + for (int language = LANG_COMMON_TONGUE; language <= LANG_UNKNOWN; language++) { + c->Message( + Chat::White, + fmt::format( + "Language {}: {}", + language, + EQ::constants::GetLanguageName(language) + ).c_str() + ); + } + } + else if ( + language_id < LANG_COMMON_TONGUE || + language_id > LANG_UNKNOWN || + language_value < 0 || + language_value > 100 + ) { + c->Message(Chat::White, "Usage: #setlanguage [Language ID] [Language Value]"); + c->Message(Chat::White, "Usage: #setlanguage [List]"); + c->Message(Chat::White, "Language ID = 0 to 27", LANG_UNKNOWN); + c->Message(Chat::White, "Language Value = 0 to 100", HIGHEST_CAN_SET_SKILL); + } + else { + LogInfo( + "Set language request from [{}], Target: [{}] Language ID: [{}] Language Value: [{}]", + c->GetCleanName(), + target->GetCleanName(), + language_id, + language_value + ); + + target->SetLanguageSkill(language_id, language_value); + + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "Set {} ({}) to {} for {}.", + EQ::constants::GetLanguageName(language_id), + language_id, + language_value, + target->GetCleanName() + ).c_str() + ); + } + } +} + diff --git a/zone/gm_commands/setlsinfo.cpp b/zone/gm_commands/setlsinfo.cpp new file mode 100755 index 000000000..64f38da01 --- /dev/null +++ b/zone/gm_commands/setlsinfo.cpp @@ -0,0 +1,24 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +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."); + } +} + diff --git a/zone/gm_commands/setpass.cpp b/zone/gm_commands/setpass.cpp new file mode 100755 index 000000000..4bf84d300 --- /dev/null +++ b/zone/gm_commands/setpass.cpp @@ -0,0 +1,29 @@ +#include "../client.h" + +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."); + } + } +} + diff --git a/zone/gm_commands/setpvppoints.cpp b/zone/gm_commands/setpvppoints.cpp new file mode 100755 index 000000000..27eae29cd --- /dev/null +++ b/zone/gm_commands/setpvppoints.cpp @@ -0,0 +1,32 @@ +#include "../client.h" + +void command_setpvppoints(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Command Syntax: #setpvppoints [Amount]"); + return; + } + + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + uint32 pvp_points = static_cast(std::min(std::stoull(sep->arg[1]), (unsigned long long) 2000000000)); + target->SetPVPPoints(pvp_points); + target->Save(); + target->SendPVPStats(); + std::string pvp_message = fmt::format( + "{} now {} {} PVP Point{}.", + c == target ? "You" : target->GetCleanName(), + c == target ? "have" : "has", + pvp_points, + pvp_points != 1 ? "s" : "" + ); + c->Message( + Chat::White, + pvp_message.c_str() + ); +} + diff --git a/zone/gm_commands/setskill.cpp b/zone/gm_commands/setskill.cpp new file mode 100755 index 000000000..625d54e0e --- /dev/null +++ b/zone/gm_commands/setskill.cpp @@ -0,0 +1,55 @@ +#include "../client.h" + +void command_setskill(Client *c, const Seperator *sep) +{ + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + auto skill_id = sep->IsNumber(1) ? std::stoi(sep->arg[1]) : -1; + auto skill_value = sep->IsNumber(2) ? std::stoi(sep->arg[2]) : -1; + if ( + skill_id < 0 || + skill_id > EQ::skills::HIGHEST_SKILL || + skill_value < 0 || + skill_value > HIGHEST_CAN_SET_SKILL + ) { + c->Message(Chat::White, "Usage: #setskill [Skill ID] [Skill Value]"); + c->Message(Chat::White, fmt::format("Skill ID = 0 to {}", EQ::skills::HIGHEST_SKILL).c_str()); + c->Message(Chat::White, fmt::format("Skill Value = 0 to {}", HIGHEST_CAN_SET_SKILL).c_str()); + } + else { + LogInfo( + "Set skill request from [{}], Target: [{}] Skill ID: [{}] Skill Value: [{}]", + c->GetCleanName(), + target->GetCleanName(), + skill_id, + skill_value + ); + + if ( + skill_id >= EQ::skills::Skill1HBlunt && + skill_id <= EQ::skills::HIGHEST_SKILL + ) { + target->SetSkill( + (EQ::skills::SkillType) skill_id, + skill_value + ); + + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "Set {} ({}) to {} for {}.", + EQ::skills::GetSkillName((EQ::skills::SkillType) skill_id), + skill_id, + skill_value, + target->GetCleanName() + ).c_str() + ); + } + } + } +} + diff --git a/zone/gm_commands/setskillall.cpp b/zone/gm_commands/setskillall.cpp new file mode 100755 index 000000000..dd8b9992a --- /dev/null +++ b/zone/gm_commands/setskillall.cpp @@ -0,0 +1,30 @@ +#include "../client.h" + +void command_setskillall(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0) { + c->Message(Chat::White, "Error: #setallskill: No target."); + } + else if (!c->GetTarget()->IsClient()) { + c->Message(Chat::White, "Error: #setskill: Target must be a client."); + } + else if (!sep->IsNumber(1) || atoi(sep->arg[1]) < 0 || atoi(sep->arg[1]) > HIGHEST_CAN_SET_SKILL) { + c->Message(Chat::White, "Usage: #setskillall value "); + c->Message(Chat::White, " value = 0 to %d", HIGHEST_CAN_SET_SKILL); + } + else { + if (c->Admin() >= commandSetSkillsOther || c->GetTarget() == c || c->GetTarget() == 0) { + LogInfo("Set ALL skill request from [{}], target:[{}]", c->GetName(), c->GetTarget()->GetName()); + uint16 level = atoi(sep->arg[1]); + for (EQ::skills::SkillType skill_num = EQ::skills::Skill1HBlunt; + skill_num <= EQ::skills::HIGHEST_SKILL; + skill_num = (EQ::skills::SkillType) (skill_num + 1)) { + c->GetTarget()->CastToClient()->SetSkill(skill_num, level); + } + } + else { + c->Message(Chat::White, "Error: Your status is not high enough to set anothers skills"); + } + } +} + diff --git a/zone/gm_commands/setstartzone.cpp b/zone/gm_commands/setstartzone.cpp new file mode 100755 index 000000000..bff04d296 --- /dev/null +++ b/zone/gm_commands/setstartzone.cpp @@ -0,0 +1,35 @@ +#include "../client.h" + +void command_setstartzone(Client *c, const Seperator *sep) +{ + uint32 startzone = 0; + Client *target = nullptr; + if (c->GetTarget() && c->GetTarget()->IsClient() && sep->arg[1][0] != 0) { + target = c->GetTarget()->CastToClient(); + } + else { + c->Message(Chat::White, "Usage: (needs PC target) #setstartzone zonename"); + c->Message( + Chat::White, + "Optional Usage: Use '#setstartzone reset' or '#setstartzone 0' to clear a starting zone. Player can select a starting zone using /setstartcity" + ); + return; + } + + if (sep->IsNumber(1)) { + startzone = atoi(sep->arg[1]); + } + else if (strcasecmp(sep->arg[1], "reset") == 0) { + startzone = 0; + } + else { + startzone = ZoneID(sep->arg[1]); + if (startzone == 0) { + c->Message(Chat::White, "Unable to locate zone '%s'", sep->arg[1]); + return; + } + } + + target->SetStartZone(startzone); +} + diff --git a/zone/gm_commands/setstat.cpp b/zone/gm_commands/setstat.cpp new file mode 100755 index 000000000..56ff81a05 --- /dev/null +++ b/zone/gm_commands/setstat.cpp @@ -0,0 +1,14 @@ +#include "../client.h" + +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"); + } +} + diff --git a/zone/gm_commands/setxp.cpp b/zone/gm_commands/setxp.cpp new file mode 100755 index 000000000..c82bb87a3 --- /dev/null +++ b/zone/gm_commands/setxp.cpp @@ -0,0 +1,23 @@ +#include "../client.h" + +void command_setxp(Client *c, const Seperator *sep) +{ + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient()) { + t = c->GetTarget()->CastToClient(); + } + + if (sep->IsNumber(1)) { + if (atoi(sep->arg[1]) > 9999999) { + c->Message(Chat::White, "Error: Value too high."); + } + else { + t->AddEXP(atoi(sep->arg[1])); + } + } + else { + c->Message(Chat::White, "Usage: #setxp number"); + } +} + diff --git a/zone/gm_commands/showbonusstats.cpp b/zone/gm_commands/showbonusstats.cpp new file mode 100755 index 000000000..2680b36f1 --- /dev/null +++ b/zone/gm_commands/showbonusstats.cpp @@ -0,0 +1,49 @@ +#include "../client.h" + +void command_showbonusstats(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0) { + c->Message(Chat::White, "ERROR: No target!"); + } + else if (!c->GetTarget()->IsMob() && !c->GetTarget()->IsClient()) { + c->Message(Chat::White, "ERROR: Target is not a Mob or Player!"); + } + else { + bool bAll = false; + if (sep->arg[1][0] == '\0' || strcasecmp(sep->arg[1], "all") == 0) { + bAll = true; + } + if (bAll || (strcasecmp(sep->arg[1], "item") == 0)) { + c->Message(Chat::White, "Target Item Bonuses:"); + c->Message( + Chat::White, + " Accuracy: %i%% Divine Save: %i%%", + c->GetTarget()->GetItemBonuses().Accuracy, + c->GetTarget()->GetItemBonuses().DivineSaveChance + ); + c->Message( + Chat::White, + " Flurry: %i%% HitChance: %i%%", + c->GetTarget()->GetItemBonuses().FlurryChance, + c->GetTarget()->GetItemBonuses().HitChance / 15 + ); + } + if (bAll || (strcasecmp(sep->arg[1], "spell") == 0)) { + c->Message(Chat::White, " Target Spell Bonuses:"); + c->Message( + Chat::White, + " Accuracy: %i%% Divine Save: %i%%", + c->GetTarget()->GetSpellBonuses().Accuracy, + c->GetTarget()->GetSpellBonuses().DivineSaveChance + ); + c->Message( + Chat::White, + " Flurry: %i%% HitChance: %i%% ", + c->GetTarget()->GetSpellBonuses().FlurryChance, + c->GetTarget()->GetSpellBonuses().HitChance / 15 + ); + } + c->Message(Chat::White, " Effective Casting Level: %i", c->GetTarget()->GetCasterLevel(0)); + } +} + diff --git a/zone/gm_commands/showbuffs.cpp b/zone/gm_commands/showbuffs.cpp new file mode 100755 index 000000000..bd8e558d6 --- /dev/null +++ b/zone/gm_commands/showbuffs.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_showbuffs(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0) { + c->CastToMob()->ShowBuffs(c); + } + else { + c->GetTarget()->CastToMob()->ShowBuffs(c); + } +} + diff --git a/zone/gm_commands/shownpcgloballoot.cpp b/zone/gm_commands/shownpcgloballoot.cpp new file mode 100755 index 000000000..546631c5c --- /dev/null +++ b/zone/gm_commands/shownpcgloballoot.cpp @@ -0,0 +1,16 @@ +#include "../client.h" + +void command_shownpcgloballoot(Client *c, const Seperator *sep) +{ + auto tar = c->GetTarget(); + + if (!tar || !tar->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } + + auto npc = tar->CastToNPC(); + c->Message(Chat::White, "GlobalLoot for %s (%d)", npc->GetName(), npc->GetNPCTypeID()); + zone->ShowNPCGlobalLoot(c, npc); +} + diff --git a/zone/gm_commands/shownumhits.cpp b/zone/gm_commands/shownumhits.cpp new file mode 100755 index 000000000..b8b06867b --- /dev/null +++ b/zone/gm_commands/shownumhits.cpp @@ -0,0 +1,8 @@ +#include "../client.h" + +void command_shownumhits(Client *c, const Seperator *sep) +{ + c->ShowNumHits(); + return; +} + diff --git a/zone/gm_commands/showskills.cpp b/zone/gm_commands/showskills.cpp new file mode 100755 index 000000000..e970dd7aa --- /dev/null +++ b/zone/gm_commands/showskills.cpp @@ -0,0 +1,44 @@ +#include "../client.h" + +void command_showskills(Client *c, const Seperator *sep) +{ + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + bool show_all = false; + + if (!strcasecmp("all", sep->arg[1])) { + show_all = true; + } + + c->Message( + Chat::White, + fmt::format( + "Skills | Name: {}", + target->GetCleanName() + ).c_str() + ); + + for ( + EQ::skills::SkillType skill_type = EQ::skills::Skill1HBlunt; + skill_type <= EQ::skills::HIGHEST_SKILL; + skill_type = (EQ::skills::SkillType) (skill_type + 1) + ) { + if (show_all || (target->CanHaveSkill(skill_type) && target->MaxSkill(skill_type))) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) | Current: {} Max: {} Raw: {}", + EQ::skills::GetSkillName(skill_type), + skill_type, + target->GetSkill(skill_type), + target->MaxSkill(skill_type), + target->GetRawSkill(skill_type) + ).c_str() + ); + } + } +} + diff --git a/zone/gm_commands/showspellslist.cpp b/zone/gm_commands/showspellslist.cpp new file mode 100755 index 000000000..2b50864e6 --- /dev/null +++ b/zone/gm_commands/showspellslist.cpp @@ -0,0 +1,13 @@ +#include "../client.h" + +void command_showspellslist(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!target || !target->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } + target->CastToNPC()->AISpellsList(c); + return; +} + diff --git a/zone/gm_commands/showstats.cpp b/zone/gm_commands/showstats.cpp new file mode 100755 index 000000000..cae9609c2 --- /dev/null +++ b/zone/gm_commands/showstats.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_showstats(Client *c, const Seperator *sep) +{ + if (c->GetTarget() != 0) { + c->GetTarget()->ShowStats(c); + } + else { + c->ShowStats(c); + } +} + diff --git a/zone/gm_commands/showzonegloballoot.cpp b/zone/gm_commands/showzonegloballoot.cpp new file mode 100755 index 000000000..ecf774f88 --- /dev/null +++ b/zone/gm_commands/showzonegloballoot.cpp @@ -0,0 +1,13 @@ +#include "../client.h" + +void command_showzonegloballoot(Client *c, const Seperator *sep) +{ + c->Message( + Chat::White, + "GlobalLoot for %s (%d:%d)", + zone->GetShortName(), + zone->GetZoneID(), + zone->GetInstanceVersion()); + zone->ShowZoneGlobalLoot(c); +} + diff --git a/zone/gm_commands/showzonepoints.cpp b/zone/gm_commands/showzonepoints.cpp new file mode 100755 index 000000000..f86dcd29e --- /dev/null +++ b/zone/gm_commands/showzonepoints.cpp @@ -0,0 +1,157 @@ +#include "../client.h" + +void command_showzonepoints(Client *c, const Seperator *sep) +{ + auto &mob_list = entity_list.GetMobList(); + for (auto itr : mob_list) { + Mob *mob = itr.second; + if (mob->IsNPC() && mob->GetRace() == 2254) { + mob->Depop(); + } + } + + int found_zone_points = 0; + + c->Message(Chat::White, "Listing zone points..."); + c->SendChatLineBreak(); + + for (auto &virtual_zone_point : zone->virtual_zone_point_list) { + std::string zone_long_name = zone_store.GetZoneLongName(virtual_zone_point.target_zone_id); + + c->Message( + Chat::White, + fmt::format( + "Virtual Zone Point x [{}] y [{}] z [{}] h [{}] width [{}] height [{}] | To [{}] ({}) x [{}] y [{}] z [{}] h [{}]", + virtual_zone_point.x, + virtual_zone_point.y, + virtual_zone_point.z, + virtual_zone_point.heading, + virtual_zone_point.width, + virtual_zone_point.height, + zone_long_name.c_str(), + virtual_zone_point.target_zone_id, + virtual_zone_point.target_x, + virtual_zone_point.target_y, + virtual_zone_point.target_z, + virtual_zone_point.target_heading + ).c_str() + ); + + std::string node_name = fmt::format("ZonePoint To [{}]", zone_long_name); + + float half_width = ((float) virtual_zone_point.width / 2); + + NPC::SpawnZonePointNodeNPC( + node_name, glm::vec4( + (float) virtual_zone_point.x + half_width, + (float) virtual_zone_point.y + half_width, + virtual_zone_point.z, + virtual_zone_point.heading + )); + + NPC::SpawnZonePointNodeNPC( + node_name, glm::vec4( + (float) virtual_zone_point.x + half_width, + (float) virtual_zone_point.y - half_width, + virtual_zone_point.z, + virtual_zone_point.heading + )); + + NPC::SpawnZonePointNodeNPC( + node_name, glm::vec4( + (float) virtual_zone_point.x - half_width, + (float) virtual_zone_point.y - half_width, + virtual_zone_point.z, + virtual_zone_point.heading + )); + + NPC::SpawnZonePointNodeNPC( + node_name, glm::vec4( + (float) virtual_zone_point.x - half_width, + (float) virtual_zone_point.y + half_width, + virtual_zone_point.z, + virtual_zone_point.heading + )); + + NPC::SpawnZonePointNodeNPC( + node_name, glm::vec4( + (float) virtual_zone_point.x + half_width, + (float) virtual_zone_point.y + half_width, + (float) virtual_zone_point.z + (float) virtual_zone_point.height, + virtual_zone_point.heading + )); + + NPC::SpawnZonePointNodeNPC( + node_name, glm::vec4( + (float) virtual_zone_point.x + half_width, + (float) virtual_zone_point.y - half_width, + (float) virtual_zone_point.z + (float) virtual_zone_point.height, + virtual_zone_point.heading + )); + + NPC::SpawnZonePointNodeNPC( + node_name, glm::vec4( + (float) virtual_zone_point.x - half_width, + (float) virtual_zone_point.y - half_width, + (float) virtual_zone_point.z + (float) virtual_zone_point.height, + virtual_zone_point.heading + )); + + NPC::SpawnZonePointNodeNPC( + node_name, glm::vec4( + (float) virtual_zone_point.x - half_width, + (float) virtual_zone_point.y + half_width, + (float) virtual_zone_point.z + (float) virtual_zone_point.height, + virtual_zone_point.heading + )); + + found_zone_points++; + } + + LinkedListIterator iterator(zone->zone_point_list); + iterator.Reset(); + while (iterator.MoreElements()) { + ZonePoint *zone_point = iterator.GetData(); + std::string zone_long_name = zone_store.GetZoneLongName(zone_point->target_zone_id); + std::string node_name = fmt::format("ZonePoint To [{}]", zone_long_name); + + NPC::SpawnZonePointNodeNPC( + node_name, glm::vec4( + zone_point->x, + zone_point->y, + zone_point->z, + zone_point->heading + ) + ); + + c->Message( + Chat::White, + fmt::format( + "Client Side Zone Point x [{}] y [{}] z [{}] h [{}] number [{}] | To [{}] ({}) x [{}] y [{}] z [{}] h [{}]", + zone_point->x, + zone_point->y, + zone_point->z, + zone_point->heading, + zone_point->number, + zone_long_name.c_str(), + zone_point->target_zone_id, + zone_point->target_x, + zone_point->target_y, + zone_point->target_z, + zone_point->target_heading + ).c_str() + ); + + iterator.Advance(); + + found_zone_points++; + } + + if (found_zone_points == 0) { + c->Message(Chat::White, "There were no zone points found..."); + } + + c->SendChatLineBreak(); + +} + diff --git a/zone/gm_commands/shutdown.cpp b/zone/gm_commands/shutdown.cpp new file mode 100755 index 000000000..2af0eaf67 --- /dev/null +++ b/zone/gm_commands/shutdown.cpp @@ -0,0 +1,8 @@ +#include "../client.h" +#include "../../world/main.h" + +void command_shutdown(Client *c, const Seperator *sep) +{ + CatchSignal(2); +} + diff --git a/zone/gm_commands/size.cpp b/zone/gm_commands/size.cpp new file mode 100755 index 000000000..3ff412549 --- /dev/null +++ b/zone/gm_commands/size.cpp @@ -0,0 +1,46 @@ +#include "../client.h" + +void command_size(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #size [0 - 255] (Decimal increments are allowed)"); + } + else { + float newsize = atof(sep->arg[1]); + if (newsize > 255) { + c->Message(Chat::White, "Error: #size: Size can not be greater than 255."); + } + else if (newsize < 0) { + c->Message(Chat::White, "Error: #size: Size can not be less than 0."); + } + else if (!target) { + c->Message(Chat::White, "Error: this command requires a target"); + } + else { + uint16 Race = target->GetModel(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = target->GetHairStyle(); + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + target->SendIllusionPacket( + Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails, newsize + ); + + c->Message(Chat::White, "Size = %f", atof(sep->arg[1])); + } + } +} + diff --git a/zone/gm_commands/spawn.cpp b/zone/gm_commands/spawn.cpp new file mode 100755 index 000000000..2dd42ccd6 --- /dev/null +++ b/zone/gm_commands/spawn.cpp @@ -0,0 +1,29 @@ +#include "../client.h" + +void command_spawn(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] != 0) { + Client *client = entity_list.GetClientByName(sep->arg[1]); + if (client) { + c->Message(Chat::White, "You cannot spawn a mob with the same name as a character!"); + return; + } + } + + NPC *npc = NPC::SpawnNPC(sep->argplus[1], c->GetPosition(), c); + if (!npc) { + c->Message( + Chat::White, + "Format: #spawn name race level material hp gender class priweapon secweapon merchantid bodytype - spawns a npc those parameters." + ); + c->Message( + Chat::White, + "Name Format: NPCFirstname_NPCLastname - All numbers in a name are stripped and \"_\" characters become a space." + ); + c->Message( + Chat::White, + "Note: Using \"-\" for gender will autoselect the gender for the race. Using \"-\" for HP will use the calculated maximum HP." + ); + } +} + diff --git a/zone/gm_commands/spawnfix.cpp b/zone/gm_commands/spawnfix.cpp new file mode 100755 index 000000000..830ae870a --- /dev/null +++ b/zone/gm_commands/spawnfix.cpp @@ -0,0 +1,39 @@ +#include "../client.h" + +void command_spawnfix(Client *c, const Seperator *sep) +{ + Mob *target_mob = c->GetTarget(); + if (!target_mob || !target_mob->IsNPC()) { + c->Message(Chat::White, "Error: #spawnfix: Need an NPC target."); + return; + } + + Spawn2 *s2 = target_mob->CastToNPC()->respawn2; + + if (!s2) { + c->Message( + Chat::White, + "#spawnfix FAILED -- cannot determine which spawn entry in the database this mob came from." + ); + return; + } + + std::string query = StringFormat( + "UPDATE spawn2 SET x = '%f', y = '%f', z = '%f', heading = '%f' WHERE id = '%i'", + c->GetX(), + c->GetY(), + target_mob->GetFixedZ(c->GetPosition()), + c->GetHeading(), + s2->GetID() + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::Red, "Update failed! MySQL gave the following error:"); + c->Message(Chat::Red, results.ErrorMessage().c_str()); + return; + } + + c->Message(Chat::White, "Updating coordinates successful."); + target_mob->Depop(false); +} + diff --git a/zone/gm_commands/spawnstatus.cpp b/zone/gm_commands/spawnstatus.cpp new file mode 100755 index 000000000..9c9307bd8 --- /dev/null +++ b/zone/gm_commands/spawnstatus.cpp @@ -0,0 +1,28 @@ +#include "../client.h" + +void command_spawnstatus(Client *c, const Seperator *sep) +{ + if ((sep->arg[1][0] == 'e') | (sep->arg[1][0] == 'E')) { + // show only enabled spawns + zone->ShowEnabledSpawnStatus(c); + } + else if ((sep->arg[1][0] == 'd') | (sep->arg[1][0] == 'D')) { + // show only disabled spawns + zone->ShowDisabledSpawnStatus(c); + } + else if ((sep->arg[1][0] == 'a') | (sep->arg[1][0] == 'A')) { + // show all spawn staus with no filters + zone->SpawnStatus(c); + } + else if (sep->IsNumber(1)) { + // show spawn status by spawn2 id + zone->ShowSpawnStatusByID(c, atoi(sep->arg[1])); + } + else if (strcmp(sep->arg[1], "help") == 0) { + c->Message(Chat::White, "Usage: #spawnstatus <[a]ll | [d]isabled | [e]nabled | {Spawn2 ID}>"); + } + else { + zone->SpawnStatus(c); + } +} + diff --git a/zone/gm_commands/spellinfo.cpp b/zone/gm_commands/spellinfo.cpp new file mode 100755 index 000000000..a40a3be66 --- /dev/null +++ b/zone/gm_commands/spellinfo.cpp @@ -0,0 +1,154 @@ +#include "../client.h" + +void command_spellinfo(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #spellinfo [spell_id]"); + } + else { + short int spell_id = atoi(sep->arg[1]); + const struct SPDat_Spell_Struct *s = &spells[spell_id]; + c->Message(Chat::White, "Spell info for spell #%d:", spell_id); + c->Message(Chat::White, " name: %s", s->name); + c->Message(Chat::White, " player_1: %s", s->player_1); + c->Message(Chat::White, " teleport_zone: %s", s->teleport_zone); + c->Message(Chat::White, " you_cast: %s", s->you_cast); + c->Message(Chat::White, " other_casts: %s", s->other_casts); + c->Message(Chat::White, " cast_on_you: %s", s->cast_on_you); + c->Message(Chat::White, " spell_fades: %s", s->spell_fades); + c->Message(Chat::White, " range: %f", s->range); + c->Message(Chat::White, " aoe_range: %f", s->aoe_range); + c->Message(Chat::White, " push_back: %f", s->push_back); + c->Message(Chat::White, " push_up: %f", s->push_up); + c->Message(Chat::White, " cast_time: %d", s->cast_time); + c->Message(Chat::White, " recovery_time: %d", s->recovery_time); + c->Message(Chat::White, " recast_time: %d", s->recast_time); + c->Message(Chat::White, " buff_duration_formula: %d", s->buff_duration_formula); + c->Message(Chat::White, " buff_duration: %d", s->buff_duration); + c->Message(Chat::White, " AEDuration: %d", s->aoe_duration); + c->Message(Chat::White, " mana: %d", s->mana); + c->Message( + Chat::White, + " base[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", + s->base_value[0], + s->base_value[1], + s->base_value[2], + s->base_value[3], + s->base_value[4], + s->base_value[5], + s->base_value[6], + s->base_value[7], + s->base_value[8], + s->base_value[9], + s->base_value[10], + s->base_value[11] + ); + c->Message( + Chat::White, + " base22[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", + s->limit_value[0], + s->limit_value[1], + s->limit_value[2], + s->limit_value[3], + s->limit_value[4], + s->limit_value[5], + s->limit_value[6], + s->limit_value[7], + s->limit_value[8], + s->limit_value[9], + s->limit_value[10], + s->limit_value[11] + ); + c->Message( + Chat::White, + " max[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", + s->max_value[0], + s->max_value[1], + s->max_value[2], + s->max_value[3], + s->max_value[4], + s->max_value[5], + s->max_value[6], + s->max_value[7], + s->max_value[8], + s->max_value[9], + s->max_value[10], + s->max_value[11] + ); + c->Message( + Chat::White, + " components[4]: %d, %d, %d, %d", + s->component[0], + s->component[1], + s->component[2], + s->component[3] + ); + c->Message( + Chat::White, + " component_counts[4]: %d, %d, %d, %d", + s->component_count[0], + s->component_count[1], + s->component_count[2], + s->component_count[3] + ); + c->Message( + Chat::White, + " NoexpendReagent[4]: %d, %d, %d, %d", + s->no_expend_reagent[0], + s->no_expend_reagent[1], + s->no_expend_reagent[2], + s->no_expend_reagent[3] + ); + c->Message( + Chat::White, + " formula[12]: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x", + s->formula[0], + s->formula[1], + s->formula[2], + s->formula[3], + s->formula[4], + s->formula[5], + s->formula[6], + s->formula[7], + s->formula[8], + s->formula[9], + s->formula[10], + s->formula[11] + ); + c->Message(Chat::White, " goodEffect: %d", s->good_effect); + c->Message(Chat::White, " Activated: %d", s->activated); + c->Message(Chat::White, " resisttype: %d", s->resist_type); + c->Message( + Chat::White, + " effectid[12]: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x", + s->effect_id[0], + s->effect_id[1], + s->effect_id[2], + s->effect_id[3], + s->effect_id[4], + s->effect_id[5], + s->effect_id[6], + s->effect_id[7], + s->effect_id[8], + s->effect_id[9], + s->effect_id[10], + s->effect_id[11] + ); + c->Message(Chat::White, " targettype: %d", s->target_type); + c->Message(Chat::White, " basediff: %d", s->base_difficulty); + c->Message(Chat::White, " skill: %d", s->skill); + c->Message(Chat::White, " zonetype: %d", s->zone_type); + c->Message(Chat::White, " EnvironmentType: %d", s->environment_type); + c->Message(Chat::White, " TimeOfDay: %d", s->time_of_day); + c->Message( + Chat::White, " classes[15]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", + s->classes[0], s->classes[1], s->classes[2], s->classes[3], s->classes[4], + s->classes[5], s->classes[6], s->classes[7], s->classes[8], s->classes[9], + s->classes[10], s->classes[11], s->classes[12], s->classes[13], s->classes[14] + ); + c->Message(Chat::White, " CastingAnim: %d", s->casting_animation); + c->Message(Chat::White, " SpellAffectIndex: %d", s->spell_affect_index); + c->Message(Chat::White, " RecourseLink: %d", s->recourse_link); + } +} + diff --git a/zone/gm_commands/stun.cpp b/zone/gm_commands/stun.cpp new file mode 100755 index 000000000..9775a45a3 --- /dev/null +++ b/zone/gm_commands/stun.cpp @@ -0,0 +1,65 @@ +#include "../client.h" + +void command_stun(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #stun [Duration]"); + return; + } + + Mob *target = c; + int duration = static_cast(std::min(std::stoll(sep->arg[1]), (long long) 2000000000)); + + if (duration < 0) { + duration = 0; + } + + if (c->GetTarget()) { + target = c->GetTarget(); + if (target->IsClient()) { + target->CastToClient()->Stun(duration); + } + else if (target->IsNPC()) { + target->CastToNPC()->Stun(duration); + } + } + else { + c->Stun(duration); + } + + std::string stun_message = ( + duration ? + fmt::format( + "You stunned {} for {}.", + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + ConvertSecondsToTime(duration) + ) : + fmt::format( + "You unstunned {}.", + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ) + ) + ); + c->Message( + Chat::White, + stun_message.c_str() + ); +} + + diff --git a/zone/gm_commands/summon.cpp b/zone/gm_commands/summon.cpp new file mode 100755 index 000000000..586217112 --- /dev/null +++ b/zone/gm_commands/summon.cpp @@ -0,0 +1,97 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +#include "../corpse.h" + +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 { + 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()) { + 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 + ); + } +} + diff --git a/zone/gm_commands/summonburiedplayercorpse.cpp b/zone/gm_commands/summonburiedplayercorpse.cpp new file mode 100755 index 000000000..5534ecadc --- /dev/null +++ b/zone/gm_commands/summonburiedplayercorpse.cpp @@ -0,0 +1,28 @@ +#include "../client.h" +#include "../corpse.h" + +void command_summonburiedplayercorpse(Client *c, const Seperator *sep) +{ + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + t = c->GetTarget()->CastToClient(); + } + else { + c->Message(Chat::White, "You must first select a target!"); + return; + } + + Corpse *PlayerCorpse = database.SummonBuriedCharacterCorpses( + t->CharacterID(), + t->GetZoneID(), + zone->GetInstanceID(), + t->GetPosition()); + + if (!PlayerCorpse) { + c->Message(Chat::White, "Your target doesn't have any buried corpses."); + } + + return; +} + diff --git a/zone/gm_commands/summonitem.cpp b/zone/gm_commands/summonitem.cpp new file mode 100755 index 000000000..e3053bd71 --- /dev/null +++ b/zone/gm_commands/summonitem.cpp @@ -0,0 +1,92 @@ +#include "../client.h" + +void command_summonitem(Client *c, const Seperator *sep) +{ + uint32 item_id = 0; + int16 charges = -1; + uint32 augment_one = 0; + uint32 augment_two = 0; + uint32 augment_three = 0; + uint32 augment_four = 0; + uint32 augment_five = 0; + uint32 augment_six = 0; + int arguments = sep->argnum; + std::string cmd_msg = sep->msg; + size_t link_open = cmd_msg.find('\x12'); + size_t link_close = cmd_msg.find_last_of('\x12'); + if (link_open != link_close && (cmd_msg.length() - link_open) > EQ::constants::SAY_LINK_BODY_SIZE) { + EQ::SayLinkBody_Struct link_body; + EQ::saylink::DegenerateLinkBody(link_body, cmd_msg.substr(link_open + 1, EQ::constants::SAY_LINK_BODY_SIZE)); + item_id = link_body.item_id; + augment_one = link_body.augment_1; + augment_two = link_body.augment_2; + augment_three = link_body.augment_3; + augment_four = link_body.augment_4; + augment_five = link_body.augment_5; + augment_six = link_body.augment_6; + } + else if (!sep->IsNumber(1)) { + c->Message( + Chat::White, + "Usage: #summonitem [item id | link] [charges] [augment_one_id] [augment_two_id] [augment_three_id] [augment_four_id] [augment_five_id] [augment_six_id] (Charges are optional.)" + ); + return; + } + else { + item_id = atoi(sep->arg[1]); + } + + if (!item_id) { + c->Message(Chat::White, "Enter a valid item ID."); + return; + } + + uint8 item_status = 0; + uint8 current_status = c->Admin(); + const EQ::ItemData *item = database.GetItem(item_id); + if (item) { + item_status = item->MinStatus; + } + + if (item_status > current_status) { + c->Message( + Chat::White, + fmt::format( + "Insufficient status to summon this item, current status is {}, required status is {}.", + current_status, + item_status + ).c_str() + ); + } + + if (arguments >= 2 && sep->IsNumber(2)) { + charges = atoi(sep->arg[2]); + } + + if (arguments >= 3 && sep->IsNumber(3)) { + augment_one = atoi(sep->arg[3]); + } + + if (arguments >= 4 && sep->IsNumber(4)) { + augment_two = atoi(sep->arg[4]); + } + + if (arguments >= 5 && sep->IsNumber(5)) { + augment_three = atoi(sep->arg[5]); + } + + if (arguments >= 6 && sep->IsNumber(6)) { + augment_four = atoi(sep->arg[6]); + } + + if (arguments >= 7 && sep->IsNumber(7)) { + augment_five = atoi(sep->arg[7]); + } + + if (arguments == 8 && sep->IsNumber(8)) { + augment_six = atoi(sep->arg[8]); + } + + c->SummonItem(item_id, charges, augment_one, augment_two, augment_three, augment_four, augment_five, augment_six); +} + diff --git a/zone/gm_commands/suspend.cpp b/zone/gm_commands/suspend.cpp new file mode 100755 index 000000000..49721507e --- /dev/null +++ b/zone/gm_commands/suspend.cpp @@ -0,0 +1,101 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_suspend(Client *c, const Seperator *sep) +{ + if ((sep->arg[1][0] == 0) || (sep->arg[2][0] == 0)) { + c->Message( + Chat::White, + "Usage: #suspend (Specify 0 days to lift the suspension immediately) " + ); + return; + } + + int duration = atoi(sep->arg[2]); + + if (duration < 0) { + duration = 0; + } + + std::string message; + + if (duration > 0) { + int i = 3; + while (1) { + if (sep->arg[i][0] == 0) { + break; + } + + if (message.length() > 0) { + message.push_back(' '); + } + + message += sep->arg[i]; + ++i; + } + + if (message.length() == 0) { + c->Message( + Chat::White, + "Usage: #suspend (Specify 0 days to lift the suspension immediately) " + ); + return; + } + } + + auto escName = new char[strlen(sep->arg[1]) * 2 + 1]; + database.DoEscapeString(escName, sep->arg[1], strlen(sep->arg[1])); + int accountID = database.GetAccountIDByChar(escName); + safe_delete_array(escName); + + if (accountID <= 0) { + c->Message(Chat::Red, "Character does not exist."); + return; + } + + std::string query = StringFormat( + "UPDATE `account` SET `suspendeduntil` = DATE_ADD(NOW(), INTERVAL %i DAY), " + "suspend_reason = '%s' WHERE `id` = %i", + duration, EscapeString(message).c_str(), accountID + ); + auto results = database.QueryDatabase(query); + + if (duration) { + c->Message( + Chat::Red, + "Account number %i with the character %s has been temporarily suspended for %i day(s).", + accountID, + sep->arg[1], + duration + ); + } + else { + c->Message( + Chat::Red, + "Account number %i with the character %s is no longer suspended.", + accountID, + sep->arg[1] + ); + } + + Client *bannedClient = entity_list.GetClientByName(sep->arg[1]); + + if (bannedClient) { + bannedClient->WorldKick(); + return; + } + + auto pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); + ServerKickPlayer_Struct *sks = (ServerKickPlayer_Struct *) pack->pBuffer; + + strn0cpy(sks->adminname, c->GetName(), sizeof(sks->adminname)); + strn0cpy(sks->name, sep->arg[1], sizeof(sks->name)); + sks->adminrank = c->Admin(); + + worldserver.SendPacket(pack); + + safe_delete(pack); +} + diff --git a/zone/gm_commands/task.cpp b/zone/gm_commands/task.cpp new file mode 100755 index 000000000..a616eb06d --- /dev/null +++ b/zone/gm_commands/task.cpp @@ -0,0 +1,198 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +#include "../../common/shared_tasks.h" + +void command_task(Client *c, const Seperator *sep) +{ + //super-command for managing tasks + if (sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { + c->Message(Chat::White, "Syntax: #task [subcommand]"); + c->Message(Chat::White, "------------------------------------------------"); + c->Message(Chat::White, "# Task System Commands"); + c->Message(Chat::White, "------------------------------------------------"); + c->Message( + Chat::White, + fmt::format( + "--- [{}] List active tasks for a client", + EQ::SayLinkEngine::GenerateQuestSaylink("#task show", false, "show") + ).c_str() + ); + c->Message(Chat::White, "--- update [count] | Updates task"); + c->Message(Chat::White, "--- assign | Assigns task to client"); + c->Message( + Chat::White, + fmt::format( + "--- [{}] Reload all Task information from the database", + EQ::SayLinkEngine::GenerateQuestSaylink("#task reloadall", false, "reloadall") + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "--- [{}] Reload Task and Activity information for a single task", + EQ::SayLinkEngine::GenerateQuestSaylink("#task reload task", false, "reload task") + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "--- [{}] Reload goal/reward list information", + EQ::SayLinkEngine::GenerateQuestSaylink("#task reload lists", false, "reload lists") + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "--- [{}] Reload proximity information", + EQ::SayLinkEngine::GenerateQuestSaylink("#task reload prox", false, "reload prox") + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "--- [{}] Reload task set information", + EQ::SayLinkEngine::GenerateQuestSaylink("#task reload sets", false, "reload sets") + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "--- [{}] Purges targeted characters task timers", + EQ::SayLinkEngine::GenerateQuestSaylink("#task purgetimers", false, "purgetimers") + ).c_str() + ); + + c->Message(Chat::White, "------------------------------------------------"); + c->Message(Chat::White, "# Shared Task System Commands"); + c->Message(Chat::White, "------------------------------------------------"); + c->Message( + Chat::White, + fmt::format( + "--- [{}] Purges all active Shared Tasks in memory and database ", + EQ::SayLinkEngine::GenerateQuestSaylink("#task sharedpurge", false, "sharedpurge") + ).c_str() + ); + + return; + } + + Client *client_target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + client_target = c->GetTarget()->CastToClient(); + } + + if (!strcasecmp(sep->arg[1], "show")) { + c->ShowClientTasks(client_target); + return; + } + + if (!strcasecmp(sep->arg[1], "purgetimers")) { + c->Message(15, fmt::format("{}'s task timers have been purged", client_target->GetCleanName()).c_str()); + if (client_target != c) { + client_target->Message(15, "[GM] Your task timers have been purged by a GM"); + } + client_target->PurgeTaskTimers(); + return; + } + + if (!strcasecmp(sep->arg[1], "update")) { + if (sep->argnum >= 3) { + int task_id = atoi(sep->arg[2]); + int activity_id = atoi(sep->arg[3]); + int count = 1; + + if (sep->argnum >= 4) { + count = atoi(sep->arg[4]); + if (count <= 0) { + count = 1; + } + } + c->Message( + Chat::Yellow, + "Updating Task [%i] Activity [%i] Count [%i] for client [%s]", + task_id, + activity_id, + count, + client_target->GetCleanName() + ); + client_target->UpdateTaskActivity(task_id, activity_id, count); + c->ShowClientTasks(client_target); + } + return; + } + + if (!strcasecmp(sep->arg[1], "sharedpurge")) { + if (!strcasecmp(sep->arg[2], "confirm")) { + LogTasksDetail("Sending purge request"); + auto pack = new ServerPacket(ServerOP_SharedTaskPurgeAllCommand, 0); + worldserver.SendPacket(pack); + safe_delete(pack); + + return; + } + + c->Message( + Chat::White, + fmt::format( + "[WARNING] This will purge all active Shared Tasks [{}]?", + EQ::SayLinkEngine::GenerateQuestSaylink("#task sharedpurge confirm", false, "confirm") + ).c_str() + ); + + return; + } + + if (!strcasecmp(sep->arg[1], "assign")) { + int task_id = atoi(sep->arg[2]); + if ((task_id > 0) && (task_id < MAXTASKS)) { + client_target->AssignTask(task_id, 0, false); + c->Message(Chat::Yellow, "Assigned task [%i] to [%s]", task_id, client_target->GetCleanName()); + } + return; + } + + if (!strcasecmp(sep->arg[1], "reloadall")) { + c->Message(Chat::Yellow, "Sending reloadtasks to world"); + worldserver.SendReloadTasks(RELOADTASKS); + c->Message(Chat::Yellow, "Back again"); + return; + } + + if (!strcasecmp(sep->arg[1], "reload")) { + if (sep->arg[2][0] != '\0') { + if (!strcasecmp(sep->arg[2], "lists")) { + c->Message(Chat::Yellow, "Sending reload lists to world"); + worldserver.SendReloadTasks(RELOADTASKGOALLISTS); + c->Message(Chat::Yellow, "Reloaded"); + return; + } + if (!strcasecmp(sep->arg[2], "prox")) { + c->Message(Chat::Yellow, "Sending reload proximities to world"); + worldserver.SendReloadTasks(RELOADTASKPROXIMITIES); + c->Message(Chat::Yellow, "Reloaded"); + return; + } + if (!strcasecmp(sep->arg[2], "sets")) { + c->Message(Chat::Yellow, "Sending reload task sets to world"); + worldserver.SendReloadTasks(RELOADTASKSETS); + c->Message(Chat::Yellow, "Reloaded"); + return; + } + if (!strcasecmp(sep->arg[2], "task") && (sep->arg[3][0] != '\0')) { + int task_id = atoi(sep->arg[3]); + if ((task_id > 0) && (task_id < MAXTASKS)) { + c->Message(Chat::Yellow, "Sending reload task %i to world", task_id); + worldserver.SendReloadTasks(RELOADTASKS, task_id); + c->Message(Chat::Yellow, "Reloaded"); + return; + } + } + } + + } + c->Message(Chat::White, "Unable to interpret command. Type #task help"); + +} diff --git a/zone/gm_commands/tattoo.cpp b/zone/gm_commands/tattoo.cpp new file mode 100755 index 000000000..8d1433bbe --- /dev/null +++ b/zone/gm_commands/tattoo.cpp @@ -0,0 +1,37 @@ +#include "../client.h" + +void command_tattoo(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #tattoo [number of Drakkin tattoo]"); + } + 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 = atoi(sep->arg[1]); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + target->SendIllusionPacket( + Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails + ); + + c->Message(Chat::White, "Tattoo = %i", atoi(sep->arg[1])); + } +} + diff --git a/zone/gm_commands/tempname.cpp b/zone/gm_commands/tempname.cpp new file mode 100755 index 000000000..849549472 --- /dev/null +++ b/zone/gm_commands/tempname.cpp @@ -0,0 +1,22 @@ +#include "../client.h" + +void command_tempname(Client *c, const Seperator *sep) +{ + Mob *target; + target = c->GetTarget(); + + if (!target) { + c->Message(Chat::White, "Usage: #tempname newname (requires a target)"); + } + else if (strlen(sep->arg[1]) > 0) { + char *oldname = strdup(target->GetName()); + target->TempName(sep->arg[1]); + c->Message(Chat::White, "Renamed %s to %s", oldname, sep->arg[1]); + free(oldname); + } + else { + target->TempName(); + c->Message(Chat::White, "Restored the original name"); + } +} + diff --git a/zone/gm_commands/texture.cpp b/zone/gm_commands/texture.cpp new file mode 100755 index 000000000..d6fdf252c --- /dev/null +++ b/zone/gm_commands/texture.cpp @@ -0,0 +1,52 @@ +#include "../client.h" + +void command_texture(Client *c, const Seperator *sep) +{ + + uint16 texture; + + if (sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 255) { + texture = atoi(sep->arg[1]); + uint8 helm = 0xFF; + + // Player Races Wear Armor, so Wearchange is sent instead + int i; + if (!c->GetTarget()) { + for (i = EQ::textures::textureBegin; i <= EQ::textures::LastTintableTexture; i++) { + c->SendTextureWC(i, texture); + } + } + else if ((c->GetTarget()->GetModel() > 0 && c->GetTarget()->GetModel() <= 12) || + c->GetTarget()->GetModel() == 128 || c->GetTarget()->GetModel() == 130 || + c->GetTarget()->GetModel() == 330 || c->GetTarget()->GetModel() == 522) { + for (i = EQ::textures::textureBegin; i <= EQ::textures::LastTintableTexture; i++) { + c->GetTarget()->SendTextureWC(i, texture); + } + } + else // Non-Player Races only need Illusion Packets to be sent for texture + { + if (sep->IsNumber(2) && atoi(sep->arg[2]) >= 0 && atoi(sep->arg[2]) <= 255) { + helm = atoi(sep->arg[2]); + } + else { + helm = texture; + } + + if (texture == 255) { + texture = 0xFFFF; // Should be pulling these from the database instead + helm = 0xFF; + } + + if ((c->GetTarget()) && (c->Admin() >= commandTextureOthers)) { + c->GetTarget()->SendIllusionPacket(c->GetTarget()->GetModel(), 0xFF, texture, helm); + } + else { + c->SendIllusionPacket(c->GetRace(), 0xFF, texture, helm); + } + } + } + else { + c->Message(Chat::White, "Usage: #texture [texture] [helmtexture] (0-255, 255 for show equipment)"); + } +} + diff --git a/zone/gm_commands/time.cpp b/zone/gm_commands/time.cpp new file mode 100755 index 000000000..fd241d75f --- /dev/null +++ b/zone/gm_commands/time.cpp @@ -0,0 +1,32 @@ +#include "../client.h" + +void command_time(Client *c, const Seperator *sep) +{ + char timeMessage[255]; + int minutes = 0; + if (sep->IsNumber(1)) { + if (sep->IsNumber(2)) { + minutes = atoi(sep->arg[2]); + } + c->Message(Chat::Red, "Setting world time to %s:%i (Timezone: 0)...", sep->arg[1], minutes); + zone->SetTime(atoi(sep->arg[1]) + 1, minutes); + LogInfo("{} :: Setting world time to {}:{} (Timezone: 0)...", c->GetCleanName(), sep->arg[1], minutes); + } + else { + c->Message(Chat::Red, "To set the Time: #time HH [MM]"); + TimeOfDay_Struct eqTime; + zone->zone_time.GetCurrentEQTimeOfDay(time(0), &eqTime); + sprintf( + timeMessage, "%02d:%s%d %s (Timezone: %ih %im)", + ((eqTime.hour - 1) % 12) == 0 ? 12 : ((eqTime.hour - 1) % 12), + (eqTime.minute < 10) ? "0" : "", + eqTime.minute, + (eqTime.hour >= 13) ? "pm" : "am", + zone->zone_time.getEQTimeZoneHr(), + zone->zone_time.getEQTimeZoneMin() + ); + c->Message(Chat::Red, "It is now %s.", timeMessage); + LogInfo("Current Time is: {}", timeMessage); + } +} + diff --git a/zone/gm_commands/timers.cpp b/zone/gm_commands/timers.cpp new file mode 100755 index 000000000..d8e74a8b3 --- /dev/null +++ b/zone/gm_commands/timers.cpp @@ -0,0 +1,22 @@ +#include "../client.h" + +void command_timers(Client *c, const Seperator *sep) +{ + if (!c->GetTarget() || !c->GetTarget()->IsClient()) { + c->Message(Chat::White, "Need a player target for timers."); + return; + } + Client *them = c->GetTarget()->CastToClient(); + + std::vector > res; + them->GetPTimers().ToVector(res); + + c->Message(Chat::White, "Timers for target:"); + + int r; + int l = res.size(); + for (r = 0; r < l; r++) { + c->Message(Chat::White, "Timer %d: %d seconds remain.", res[r].first, res[r].second->GetRemainingTime()); + } +} + diff --git a/zone/gm_commands/timezone.cpp b/zone/gm_commands/timezone.cpp new file mode 100755 index 000000000..d4e628cc0 --- /dev/null +++ b/zone/gm_commands/timezone.cpp @@ -0,0 +1,32 @@ +#include "../client.h" + +void command_timezone(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0 && !sep->IsNumber(1)) { + c->Message(Chat::Red, "Usage: #timezone HH [MM]"); + c->Message( + Chat::Red, + "Current timezone is: %ih %im", + zone->zone_time.getEQTimeZoneHr(), + zone->zone_time.getEQTimeZoneMin()); + } + else { + uint8 hours = atoi(sep->arg[1]); + uint8 minutes = atoi(sep->arg[2]); + if (!sep->IsNumber(2)) { + minutes = 0; + } + c->Message(Chat::Red, "Setting timezone to %i h %i m", hours, minutes); + uint32 ntz = (hours * 60) + minutes; + zone->zone_time.setEQTimeZone(ntz); + content_db.SetZoneTZ(zone->GetZoneID(), zone->GetInstanceVersion(), ntz); + + // Update all clients with new TZ. + auto outapp = new EQApplicationPacket(OP_TimeOfDay, sizeof(TimeOfDay_Struct)); + TimeOfDay_Struct *tod = (TimeOfDay_Struct *) outapp->pBuffer; + zone->zone_time.GetCurrentEQTimeOfDay(time(0), tod); + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } +} + diff --git a/zone/gm_commands/title.cpp b/zone/gm_commands/title.cpp new file mode 100755 index 000000000..0be244d44 --- /dev/null +++ b/zone/gm_commands/title.cpp @@ -0,0 +1,65 @@ +#include "../client.h" +#include "../titles.h" + +void command_title(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message( + Chat::White, + "Usage: #title [remove|text] [1 = Create row in title table] - remove or set title to 'text'" + ); + } + else { + bool Save = (atoi(sep->arg[2]) == 1); + + Mob *target_mob = c->GetTarget(); + if (!target_mob) { + target_mob = c; + } + if (!target_mob->IsClient()) { + c->Message(Chat::Red, "#title only works on players."); + return; + } + Client *t = target_mob->CastToClient(); + + if (strlen(sep->arg[1]) > 31) { + c->Message(Chat::Red, "Title must be 31 characters or less."); + return; + } + + bool removed = false; + if (!strcasecmp(sep->arg[1], "remove")) { + t->SetAATitle(""); + removed = true; + } + else { + for (unsigned int i = 0; i < strlen(sep->arg[1]); i++) + if (sep->arg[1][i] == '_') { + sep->arg[1][i] = ' '; + } + if (!Save) { + t->SetAATitle(sep->arg[1]); + } + else { + title_manager.CreateNewPlayerTitle(t, sep->arg[1]); + } + } + + t->Save(); + + if (removed) { + c->Message(Chat::Red, "%s's title has been removed.", t->GetName(), sep->arg[1]); + if (t != c) { + t->Message(Chat::Red, "Your title has been removed.", sep->arg[1]); + } + } + else { + c->Message(Chat::Red, "%s's title has been changed to '%s'.", t->GetName(), sep->arg[1]); + if (t != c) { + t->Message(Chat::Red, "Your title has been changed to '%s'.", sep->arg[1]); + } + } + } +} + + diff --git a/zone/gm_commands/titlesuffix.cpp b/zone/gm_commands/titlesuffix.cpp new file mode 100755 index 000000000..10f41e563 --- /dev/null +++ b/zone/gm_commands/titlesuffix.cpp @@ -0,0 +1,65 @@ +#include "../client.h" +#include "../titles.h" + +void command_titlesuffix(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message( + Chat::White, + "Usage: #titlesuffix [remove|text] [1 = create row in title table] - remove or set title suffix to 'text'" + ); + } + else { + bool Save = (atoi(sep->arg[2]) == 1); + + Mob *target_mob = c->GetTarget(); + if (!target_mob) { + target_mob = c; + } + if (!target_mob->IsClient()) { + c->Message(Chat::Red, "#titlesuffix only works on players."); + return; + } + Client *t = target_mob->CastToClient(); + + if (strlen(sep->arg[1]) > 31) { + c->Message(Chat::Red, "Title suffix must be 31 characters or less."); + return; + } + + bool removed = false; + if (!strcasecmp(sep->arg[1], "remove")) { + t->SetTitleSuffix(""); + removed = true; + } + else { + for (unsigned int i = 0; i < strlen(sep->arg[1]); i++) + if (sep->arg[1][i] == '_') { + sep->arg[1][i] = ' '; + } + + if (!Save) { + t->SetTitleSuffix(sep->arg[1]); + } + else { + title_manager.CreateNewPlayerSuffix(t, sep->arg[1]); + } + } + + t->Save(); + + if (removed) { + c->Message(Chat::Red, "%s's title suffix has been removed.", t->GetName(), sep->arg[1]); + if (t != c) { + t->Message(Chat::Red, "Your title suffix has been removed.", sep->arg[1]); + } + } + else { + c->Message(Chat::Red, "%s's title suffix has been changed to '%s'.", t->GetName(), sep->arg[1]); + if (t != c) { + t->Message(Chat::Red, "Your title suffix has been changed to '%s'.", sep->arg[1]); + } + } + } +} + diff --git a/zone/gm_commands/traindisc.cpp b/zone/gm_commands/traindisc.cpp new file mode 100755 index 000000000..1e3226a85 --- /dev/null +++ b/zone/gm_commands/traindisc.cpp @@ -0,0 +1,65 @@ +#include "../client.h" + +void command_traindisc(Client *c, const Seperator *sep) +{ + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + target = c->GetTarget()->CastToClient(); + } + + if (sep->argnum < 1 || !sep->IsNumber(1)) { + c->Message(Chat::White, "FORMAT: #traindisc "); + return; + } + + uint8 rule_max_level = (uint8) RuleI(Character, MaxLevel); + uint8 max_level = (uint8) std::stoi(sep->arg[1]); + uint8 min_level = ( + sep->IsNumber(2) ? + (uint8) + std::stoi(sep->arg[2]) : + 1 + ); // Default to Level 1 if there isn't a 2nd argument + + if (!c->GetGM()) { // Default to Character:MaxLevel if we're not a GM and Level is higher than the max level + if (max_level > rule_max_level) { + max_level = rule_max_level; + } + + if (min_level > rule_max_level) { + min_level = rule_max_level; + } + } + + if (max_level < 1 || min_level < 1) { + c->Message(Chat::White, "ERROR: Level must be greater than or equal to 1."); + return; + } + + if (min_level > max_level) { + c->Message(Chat::White, "ERROR: Minimum Level must be less than or equal to Maximum Level."); + return; + } + + uint16 learned_disciplines = target->LearnDisciplines(min_level, max_level); + if (target != c) { + std::string discipline_message = ( + learned_disciplines > 0 ? + ( + learned_disciplines == 1 ? + "A new discipline" : + fmt::format("{} New disciplines", learned_disciplines) + ) : + "No new disciplines" + ); + c->Message( + Chat::White, + fmt::format( + "{} learned for {}.", + discipline_message, + target->GetCleanName() + ).c_str() + ); + } +} + diff --git a/zone/gm_commands/trapinfo.cpp b/zone/gm_commands/trapinfo.cpp new file mode 100755 index 000000000..6d2d8fcf1 --- /dev/null +++ b/zone/gm_commands/trapinfo.cpp @@ -0,0 +1,7 @@ +#include "../client.h" + +void command_trapinfo(Client *c, const Seperator *sep) +{ + entity_list.GetTrapInfo(c); +} + diff --git a/zone/gm_commands/tune.cpp b/zone/gm_commands/tune.cpp new file mode 100755 index 000000000..e34f9aa49 --- /dev/null +++ b/zone/gm_commands/tune.cpp @@ -0,0 +1,524 @@ +#include "../client.h" + +void command_tune(Client *c, const Seperator *sep) +{ + //Work in progress - Kayen + + if (sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { + c->Message(Chat::White, "Syntax: #tune [subcommand]."); + c->Message(Chat::White, "-- Tune System Commands --"); + c->Message( + Chat::White, + "-- Usage: Returns recommended combat statistical values based on a desired outcome through simulated combat." + ); + c->Message( + Chat::White, + "-- This commmand can answer the following difficult questions whening tunings NPCs and Players." + ); + c->Message( + Chat::White, + "-- Question: What is the average damage mitigation my AC provides against a specific targets attacks?" + ); + c->Message( + Chat::White, + "-- Question: What is amount of AC would I need to add to acheive a specific average damage mitigation agianst specific targets attacks?" + ); + c->Message( + Chat::White, + "-- Question: What is amount of AC would I need to add to my target to acheive a specific average damage mitigation from my attacks?" + ); + c->Message(Chat::White, "-- Question: What is my targets average AC damage mitigation based on my ATK stat?"); + c->Message( + Chat::White, + "-- Question: What is amount of ATK would I need to add to myself to acheive a specific average damage mitigation on my target?" + ); + c->Message( + Chat::White, + "-- Question: What is amount of ATK would I need to add to my target to acheive a specific average AC damage mitigation on myself?" + ); + c->Message(Chat::White, "-- Question: What is my hit chance against a target?"); + c->Message( + Chat::White, + "-- Question: What is the amount of avoidance I need to add to my target to achieve a specific hit chance?" + ); + c->Message( + Chat::White, + "-- Question: What is the amount of accuracy I need to add to my target to achieve a specific chance of hitting me?" + ); + c->Message(Chat::White, "-- Question: ... and many more..."); + c->Message(Chat::White, " "); + c->Message(Chat::White, "...#tune stats [A/D]"); + c->Message( + Chat::White, + "...#tune FindATK [A/D] [pct mitigation] [interval] [loop_max] [AC override] [Info Level]" + ); + c->Message( + Chat::White, + "...#tune FindAC [A/D] [pct mitigation] [interval] [loop_max] [ATK override] [Info Level] " + ); + c->Message( + Chat::White, + "...#tune FindAccuracy [A/D] [hit chance] [interval] [loop_max] [Avoidance override] [Info Level]" + ); + c->Message( + Chat::White, + "...#tune FindAvoidance [A/D] [hit chance] [interval] [loop_max] [Accuracy override] [Info Level] " + ); + c->Message(Chat::White, " "); + c->Message(Chat::White, "-- DETAILS AND EXAMPLES ON USAGE"); + c->Message(Chat::White, " "); + c->Message( + Chat::White, + "...Returns combat statistics, including AC mitigation pct, hit chance, and avoid melee chance for attacker and defender." + ); + c->Message(Chat::White, "...#tune stats [A/D]"); + c->Message(Chat::White, "..."); + c->Message( + Chat::White, + "...Returns recommended ATK adjustment (+/-) on ATTACKER that will result in a specific average AC mitigation pct on DEFENDER. " + ); + c->Message( + Chat::White, + "...#tune FindATK [A/D] [pct mitigation] [interval][loop_max][AC override][Info Level]" + ); + c->Message( + Chat::White, + "...Example: Find the amount of ATK stat I need to add to the targeted NPC so that it hits me for 50 pct damage on average." + ); + c->Message(Chat::White, "...Example: #tune FindATK D 50"); + c->Message(Chat::White, "..."); + c->Message( + Chat::White, + "...Returns recommended AC adjustment(+/-) on DEFENDER for a specific average AC mitigation pct from ATTACKER. " + ); + c->Message( + Chat::White, + "...#tune FindAC [A/D] [pct mitigation] [interval][loop_max][ATK override][Info Level] " + ); + c->Message( + Chat::White, + "...Example: Find the amount of AC stat I need to add to the targeted NPC so that I hit it for 70 pct damage on average." + ); + c->Message(Chat::White, "...Example: #tune FindAC D 70"); + c->Message(Chat::White, "..."); + c->Message( + Chat::White, + "...Returns recommended Accuracy adjustment (+/-) on ATTACKER that will result in a specific hit chance pct on DEFENDER. " + ); + c->Message( + Chat::White, + "...#tune FindAccuracy [A/D] [hit chance] [interval][loop_max][Avoidance override][Info Level]" + ); + c->Message( + Chat::White, + "...Example: Find the amount of Accuracy stat I need to add to the targeted NPC so that it has a 60 pct hit chance against me." + ); + c->Message(Chat::White, "...Example: #tune FindAccuracy D 60"); + c->Message(Chat::White, "..."); + c->Message( + Chat::White, + "...Returns recommended Avoidance adjustment (+/-) on DEFENDER for in a specific hit chance pct from ATTACKER. " + ); + c->Message( + Chat::White, + "...#tune FindAvoidance [A/D] [hit chance] [interval][loop_max][Accuracy override][Info Level] " + ); + c->Message( + Chat::White, + "...Example: Find the amount of Avoidance stat I need to add to the targeted NPC so that I have a 30 pct hit chance against it." + ); + c->Message(Chat::White, "...Example: #tune FindAvoidance D 30"); + c->Message(Chat::White, "... "); + c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); + c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); + c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); + c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); + + c->Message(Chat::White, " "); + + c->Message( + Chat::White, + "-- Warning: The calculations done in this process are intense and can potentially cause zone crashes depending on parameters set, use with caution!" + ); + c->Message(Chat::White, "-- Below are OPTIONAL parameters."); + c->Message( + Chat::White, + "-- Note: [interval] Determines how much the stat being checked increases/decreases till it finds the best result. Lower is more accurate. Default=10" + ); + c->Message( + Chat::White, + "-- Note: [loop_max] Determines how many iterations are done to increases/decreases the stat till it finds the best result. Higher is more accurate. Default=1000" + ); + c->Message( + Chat::White, + "-- Note: [Stat Override] Will override that stat on mob being checked with the specified value. Default=0" + ); + c->Message( + Chat::White, + "-- Example: If as the attacker you want to find the ATK value you would need to have agianst a target with 1000 AC to achieve an average AC mitigation of 50 pct." + ); + c->Message(Chat::White, "-- Example: #tune FindATK A 50 0 0 1000"); + c->Message(Chat::White, "-- Note: [Info Level] How much parsing detail is displayed[0 - 1]. Default: [0] "); + c->Message(Chat::White, " "); + + return; + } + /* + Category A: YOU are the attacker and your target is the defender + Category D: YOU are the defender and your target is the attacker + */ + + Mob *attacker = c; + Mob *defender = c->GetTarget(); + + if (!defender) { + c->Message(Chat::White, "[#Tune] - Error no target selected. [#Tune help]"); + return; + } + + //Use if checkings on engaged targets. + Mob *ttarget = attacker->GetTarget(); + if (ttarget) { + defender = ttarget; + } + + if (!strcasecmp(sep->arg[1], "stats")) { + + if (!strcasecmp(sep->arg[2], "A")) { + c->TuneGetStats(defender, attacker); + } + else if (!strcasecmp(sep->arg[2], "D")) { + c->TuneGetStats(attacker, defender); + } + else { + c->TuneGetStats(defender, attacker); + } + return; + } + + if (!strcasecmp(sep->arg[1], "FindATK")) { + float pct_mitigation = atof(sep->arg[3]); + int interval = atoi(sep->arg[4]); + int max_loop = atoi(sep->arg[5]); + int ac_override = atoi(sep->arg[6]); + int info_level = atoi(sep->arg[7]); + + if (!pct_mitigation) { + c->Message(Chat::White, "[#Tune] - Error must enter the desired percent mitigation on defender."); + c->Message( + Chat::White, + "...Returns recommended ATK adjustment (+/-) on ATTACKER that will result in a specific average AC mitigation pct on DEFENDER. " + ); + c->Message( + Chat::White, + "...#tune FindATK [A/D] [pct mitigation] [interval][loop_max][AC override][Info Level]" + ); + c->Message( + Chat::White, + "...Example: Find the amount of ATK stat I need to add to the targeted NPC so that it hits me for 50 pct damage on average." + ); + c->Message(Chat::White, "...Example: #tune FindATK D 50"); + return; + } + + if (!interval) { + interval = 10; + } + if (!max_loop) { + max_loop = 1000; + } + if (!ac_override) { + ac_override = 0; + } + if (!info_level) { + info_level = 0; + } + + if (!strcasecmp(sep->arg[2], "A")) { + c->TuneGetATKByPctMitigation( + defender, + attacker, + pct_mitigation, + interval, + max_loop, + ac_override, + info_level + ); + } + else if (!strcasecmp(sep->arg[2], "D")) { + c->TuneGetATKByPctMitigation( + attacker, + defender, + pct_mitigation, + interval, + max_loop, + ac_override, + info_level + ); + } + else { + c->Message(Chat::White, "#Tune - Error no category selcted. [#Tune help]"); + c->Message( + Chat::White, + "Usage #tune FindATK [A/B] [pct mitigation] [interval][loop_max][AC Overwride][Info Level] " + ); + c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); + c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); + c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); + c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); + c->Message(Chat::White, "... "); + c->Message( + Chat::White, + "...Example: Find the amount of ATK stat I need to add to the targeted NPC so that it hits me for 50 pct damage on average." + ); + c->Message(Chat::White, "...Example: #tune FindATK D 50"); + } + return; + } + + if (!strcasecmp(sep->arg[1], "FindAC")) { + float pct_mitigation = atof(sep->arg[3]); + int interval = atoi(sep->arg[4]); + int max_loop = atoi(sep->arg[5]); + int atk_override = atoi(sep->arg[6]); + int info_level = atoi(sep->arg[7]); + + if (!pct_mitigation) { + c->Message(Chat::White, "#Tune - Error must enter the desired percent mitigation on defender."); + c->Message( + Chat::White, + "...Returns recommended AC adjustment(+/-) on DEFENDER for a specific average AC mitigation pct from ATTACKER. " + ); + c->Message( + Chat::White, + "...#tune FindAC [A/D] [pct mitigation] [interval][loop_max][ATK override][Info Level] " + ); + c->Message( + Chat::White, + "...Example: Find the amount of AC stat I need to add to the targeted NPC so that I hit it for 70 pct damage on average." + ); + c->Message(Chat::White, "...Example: #tune FindAC D 70"); + return; + } + + if (!interval) { + interval = 10; + } + if (!max_loop) { + max_loop = 1000; + } + if (!atk_override) { + atk_override = 0; + } + if (!info_level) { + info_level = 0; + } + + if (!strcasecmp(sep->arg[2], "A")) { + c->TuneGetACByPctMitigation( + defender, + attacker, + pct_mitigation, + interval, + max_loop, + atk_override, + info_level + ); + } + else if (!strcasecmp(sep->arg[2], "D")) { + c->TuneGetACByPctMitigation( + attacker, + defender, + pct_mitigation, + interval, + max_loop, + atk_override, + info_level + ); + } + else { + c->Message(Chat::White, "#Tune - Error no category selcted. [#Tune help]"); + c->Message( + Chat::White, + "Usage #tune FindATK [A/B] [pct mitigation] [interval][loop_max][AC Overwride][Info Level] " + ); + c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); + c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); + c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); + c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); + c->Message(Chat::White, "... "); + c->Message( + Chat::White, + "...Example: Find the amount of AC stat I need to add to the targeted NPC so that I hit it for 70 pct damage on average." + ); + c->Message(Chat::White, "...Example: #tune FindAC D 70"); + } + + return; + } + + if (!strcasecmp(sep->arg[1], "FindAccuracy")) { + float hit_chance = atof(sep->arg[3]); + int interval = atoi(sep->arg[4]); + int max_loop = atoi(sep->arg[5]); + int avoid_override = atoi(sep->arg[6]); + int info_level = atoi(sep->arg[7]); + + if (!hit_chance) { + c->Message(Chat::White, "#Tune - Error must enter the desired hit chance on defender."); + c->Message( + Chat::White, + "...Returns recommended Accuracy adjustment (+/-) on ATTACKER that will result in a specific hit chance pct on DEFENDER. " + ); + c->Message( + Chat::White, + "...#tune FindAccuracy [A/D] [hit chance] [interval][loop_max][Avoidance override][Info Level]" + ); + c->Message( + Chat::White, + "...Example: Find the amount of Accuracy stat I need to add to the targeted NPC so that it has a 60 pct hit chance against me." + ); + c->Message(Chat::White, "...Example: #tune FindAccuracy D 60"); + return; + } + + if (!interval) { + interval = 10; + } + if (!max_loop) { + max_loop = 1000; + } + if (!avoid_override) { + avoid_override = 0; + } + if (!info_level) { + info_level = 0; + } + + if (!strcasecmp(sep->arg[2], "A")) { + c->TuneGetAccuracyByHitChance( + defender, + attacker, + hit_chance, + interval, + max_loop, + avoid_override, + info_level + ); + } + else if (!strcasecmp(sep->arg[2], "D")) { + c->TuneGetAccuracyByHitChance( + attacker, + defender, + hit_chance, + interval, + max_loop, + avoid_override, + info_level + ); + } + else { + c->Message(Chat::White, "#Tune - Error no category selcted. [#Tune help]"); + c->Message( + Chat::White, + "...#tune FindAccuracy [A/D] [hit chance] [interval][loop_max][Avoidance override][Info Level]" + ); + c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); + c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); + c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); + c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); + c->Message(Chat::White, "... "); + c->Message( + Chat::White, + "...Example: Find the amount of Accuracy stat I need to add to the targeted NPC so that it has a 60 pct hit chance against me." + ); + c->Message(Chat::White, "...Example: #tune FindAccuracy D 60"); + } + + return; + } + + if (!strcasecmp(sep->arg[1], "FindAvoidance")) { + float hit_chance = atof(sep->arg[3]); + int interval = atoi(sep->arg[4]); + int max_loop = atoi(sep->arg[5]); + int acc_override = atoi(sep->arg[6]); + int info_level = atoi(sep->arg[7]); + + if (!hit_chance) { + c->Message(Chat::White, "#Tune - Error must enter the desired hit chance on defender."); + c->Message( + Chat::White, + "...Returns recommended Avoidance adjustment (+/-) on DEFENDER for in a specific hit chance pct from ATTACKER. " + ); + c->Message( + Chat::White, + "...#tune FindAvoidance [A/D] [hit chance] [interval][loop_max][Accuracy override][Info Level] " + ); + c->Message( + Chat::White, + "...Example: Find the amount of Avoidance stat I need to add to the targeted NPC so that I have a 30 pct hit chance against it." + ); + c->Message(Chat::White, "...Example: #tune FindAvoidance D 30"); + return; + } + if (!interval) { + interval = 10; + } + if (!max_loop) { + max_loop = 1000; + } + if (!acc_override) { + acc_override = 0; + } + if (!info_level) { + info_level = 0; + } + + if (!strcasecmp(sep->arg[2], "A")) { + c->TuneGetAvoidanceByHitChance( + defender, + attacker, + hit_chance, + interval, + max_loop, + acc_override, + info_level + ); + } + else if (!strcasecmp(sep->arg[2], "D")) { + c->TuneGetAvoidanceByHitChance( + attacker, + defender, + hit_chance, + interval, + max_loop, + acc_override, + info_level + ); + } + else { + c->Message(Chat::White, "#Tune - Error no category selcted. [#Tune help]"); + c->Message( + Chat::White, + "...#tune FindAvoidance [A/D] [hit chance] [interval][loop_max][Accuracy override][Info Level] " + ); + c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); + c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); + c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); + c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); + c->Message(Chat::White, "... "); + c->Message( + Chat::White, + "...Example: Find the amount of Avoidance stat I need to add to the targeted NPC so that I have a 30 pct hit chance against it." + ); + c->Message(Chat::White, "...Example: #tune FindAvoidance D 30"); + } + + return; + } + + c->Message(Chat::White, "#Tune - Error no command [#Tune help]"); + return; +} + diff --git a/zone/gm_commands/ucs.cpp b/zone/gm_commands/ucs.cpp new file mode 100755 index 000000000..6d7d65f6a --- /dev/null +++ b/zone/gm_commands/ucs.cpp @@ -0,0 +1,89 @@ +#include "../client.h" + +void command_ucs(Client *c, const Seperator *sep) +{ + if (!c) { + return; + } + + LogInfo("Character [{}] attempting ucs reconnect while ucs server is [{}] available", + c->GetName(), (zone->IsUCSServerAvailable() ? "" : "un")); + + if (zone->IsUCSServerAvailable()) { + EQApplicationPacket *outapp = nullptr; + std::string buffer; + + std::string MailKey = database.GetMailKey(c->CharacterID(), true); + EQ::versions::UCSVersion ConnectionType = EQ::versions::ucsUnknown; + + // chat server packet + switch (c->ClientVersion()) { + case EQ::versions::ClientVersion::Titanium: + ConnectionType = EQ::versions::ucsTitaniumChat; + break; + case EQ::versions::ClientVersion::SoF: + ConnectionType = EQ::versions::ucsSoFCombined; + break; + case EQ::versions::ClientVersion::SoD: + ConnectionType = EQ::versions::ucsSoDCombined; + break; + case EQ::versions::ClientVersion::UF: + ConnectionType = EQ::versions::ucsUFCombined; + break; + case EQ::versions::ClientVersion::RoF: + ConnectionType = EQ::versions::ucsRoFCombined; + break; + case EQ::versions::ClientVersion::RoF2: + ConnectionType = EQ::versions::ucsRoF2Combined; + break; + default: + ConnectionType = EQ::versions::ucsUnknown; + break; + } + + buffer = StringFormat( + "%s,%i,%s.%s,%c%s", + Config->ChatHost.c_str(), + Config->ChatPort, + Config->ShortName.c_str(), + c->GetName(), + ConnectionType, + MailKey.c_str() + ); + + outapp = new EQApplicationPacket(OP_SetChatServer, (buffer.length() + 1)); + memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); + outapp->pBuffer[buffer.length()] = '\0'; + + c->QueuePacket(outapp); + safe_delete(outapp); + + // mail server packet + switch (c->ClientVersion()) { + case EQ::versions::ClientVersion::Titanium: + ConnectionType = EQ::versions::ucsTitaniumMail; + break; + default: + // retain value from previous switch + break; + } + + buffer = StringFormat( + "%s,%i,%s.%s,%c%s", + Config->MailHost.c_str(), + Config->MailPort, + Config->ShortName.c_str(), + c->GetName(), + ConnectionType, + MailKey.c_str() + ); + + outapp = new EQApplicationPacket(OP_SetChatServer2, (buffer.length() + 1)); + memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); + outapp->pBuffer[buffer.length()] = '\0'; + + c->QueuePacket(outapp); + safe_delete(outapp); + } +} + diff --git a/zone/gm_commands/undye.cpp b/zone/gm_commands/undye.cpp new file mode 100755 index 000000000..af07be7a7 --- /dev/null +++ b/zone/gm_commands/undye.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_undye(Client *c, const Seperator *sep) +{ + if (c->GetTarget() && c->GetTarget()->IsClient()) { + c->GetTarget()->CastToClient()->Undye(); + } + else { + c->Message(Chat::White, "ERROR: Client target required"); + } +} + diff --git a/zone/gm_commands/undyeme.cpp b/zone/gm_commands/undyeme.cpp new file mode 100755 index 000000000..b2e451202 --- /dev/null +++ b/zone/gm_commands/undyeme.cpp @@ -0,0 +1,10 @@ +#include "../client.h" + +void command_undyeme(Client *c, const Seperator *sep) +{ + if (c) { + c->Undye(); + c->Message(Chat::Red, "Dye removed from all slots. Please zone for the process to complete."); + } +} + diff --git a/zone/gm_commands/unfreeze.cpp b/zone/gm_commands/unfreeze.cpp new file mode 100755 index 000000000..9066af500 --- /dev/null +++ b/zone/gm_commands/unfreeze.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_unfreeze(Client *c, const Seperator *sep) +{ + if (c->GetTarget() != 0) { + c->GetTarget()->SendAppearancePacket(AT_Anim, ANIM_STAND); + } + else { + c->Message(Chat::White, "ERROR: Unfreeze requires a target."); + } +} + diff --git a/zone/gm_commands/unlock.cpp b/zone/gm_commands/unlock.cpp new file mode 100755 index 000000000..2d35220e6 --- /dev/null +++ b/zone/gm_commands/unlock.cpp @@ -0,0 +1,15 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_unlock(Client *c, const Seperator *sep) +{ + auto outpack = new ServerPacket(ServerOP_Lock, sizeof(ServerLock_Struct)); + ServerLock_Struct *lss = (ServerLock_Struct *) outpack->pBuffer; + strcpy(lss->myname, c->GetName()); + lss->mode = 0; + worldserver.SendPacket(outpack); + safe_delete(outpack); +} + diff --git a/zone/gm_commands/unscribespell.cpp b/zone/gm_commands/unscribespell.cpp new file mode 100755 index 000000000..2c66bea8f --- /dev/null +++ b/zone/gm_commands/unscribespell.cpp @@ -0,0 +1,62 @@ +#include "../client.h" + +void command_unscribespell(Client *c, const Seperator *sep) +{ + uint16 spell_id = 0; + uint16 book_slot = -1; + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + t = c->GetTarget()->CastToClient(); + } + + if (!sep->arg[1][0]) { + c->Message(Chat::White, "FORMAT: #unscribespell "); + return; + } + + spell_id = atoi(sep->arg[1]); + + if (IsValidSpell(spell_id)) { + book_slot = t->FindSpellBookSlotBySpellID(spell_id); + + if (book_slot >= 0) { + t->UnscribeSpell(book_slot); + + t->Message(Chat::White, "Unscribing spell: %s (%i) from spellbook.", spells[spell_id].name, spell_id); + + if (t != c) { + c->Message( + Chat::White, + "Unscribing spell: %s (%i) for %s.", + spells[spell_id].name, + spell_id, + t->GetName()); + } + + LogInfo("Unscribe spell: [{}] ([{}]) request for [{}] from [{}]", + spells[spell_id].name, + spell_id, + t->GetName(), + c->GetName()); + } + else { + t->Message( + Chat::Red, + "Unable to unscribe spell: %s (%i) from your spellbook. This spell is not scribed.", + spells[spell_id].name, + spell_id + ); + + if (t != c) { + c->Message( + Chat::Red, + "Unable to unscribe spell: %s (%i) for %s due to spell not scribed.", + spells[spell_id].name, + spell_id, + t->GetName()); + } + } + } +} + diff --git a/zone/gm_commands/unscribespells.cpp b/zone/gm_commands/unscribespells.cpp new file mode 100755 index 000000000..7b4b93211 --- /dev/null +++ b/zone/gm_commands/unscribespells.cpp @@ -0,0 +1,13 @@ +#include "../client.h" + +void command_unscribespells(Client *c, const Seperator *sep) +{ + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + t = c->GetTarget()->CastToClient(); + } + + t->UnscribeSpellAll(); +} + diff --git a/zone/gm_commands/untraindisc.cpp b/zone/gm_commands/untraindisc.cpp new file mode 100755 index 000000000..c5d6ca1f2 --- /dev/null +++ b/zone/gm_commands/untraindisc.cpp @@ -0,0 +1,17 @@ +#include "../client.h" + +void command_untraindisc(Client *c, const Seperator *sep) +{ + Client *t = c; + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + t = c->GetTarget()->CastToClient(); + } + + for (int i = 0; i < MAX_PP_DISCIPLINES; i++) { + if (t->GetPP().disciplines.values[i] == atoi(sep->arg[1])) { + t->UntrainDisc(i, 1); + return; + } + } +} + diff --git a/zone/gm_commands/untraindiscs.cpp b/zone/gm_commands/untraindiscs.cpp new file mode 100755 index 000000000..8058c6e76 --- /dev/null +++ b/zone/gm_commands/untraindiscs.cpp @@ -0,0 +1,13 @@ +#include "../client.h" + +void command_untraindiscs(Client *c, const Seperator *sep) +{ + Client *t = c; + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + t = c->GetTarget()->CastToClient(); + } + + t->UntrainDiscAll(); + t->Message(Chat::Yellow, "All disciplines removed."); +} + diff --git a/zone/gm_commands/uptime.cpp b/zone/gm_commands/uptime.cpp new file mode 100755 index 000000000..3c1c7bfbc --- /dev/null +++ b/zone/gm_commands/uptime.cpp @@ -0,0 +1,22 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_uptime(Client *c, const Seperator *sep) +{ + if (!worldserver.Connected()) { + c->Message(Chat::White, "Error: World server disconnected"); + } + else { + auto pack = new ServerPacket(ServerOP_Uptime, sizeof(ServerUptime_Struct)); + ServerUptime_Struct *sus = (ServerUptime_Struct *) pack->pBuffer; + strcpy(sus->adminname, c->GetName()); + if (sep->IsNumber(1) && atoi(sep->arg[1]) > 0) { + sus->zoneserverid = atoi(sep->arg[1]); + } + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + diff --git a/zone/gm_commands/version.cpp b/zone/gm_commands/version.cpp new file mode 100755 index 000000000..ecb9b74d4 --- /dev/null +++ b/zone/gm_commands/version.cpp @@ -0,0 +1,10 @@ +#include "../client.h" + +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); +} + diff --git a/zone/gm_commands/viewnpctype.cpp b/zone/gm_commands/viewnpctype.cpp new file mode 100755 index 000000000..70447c145 --- /dev/null +++ b/zone/gm_commands/viewnpctype.cpp @@ -0,0 +1,31 @@ +#include "../client.h" + +void command_viewnpctype(Client *c, const Seperator *sep) +{ + if (sep->IsNumber(1)) { + uint32 npc_id = std::stoul(sep->arg[1]); + const NPCType *npc_type_data = content_db.LoadNPCTypesData(npc_id); + if (npc_type_data) { + auto npc = new NPC( + npc_type_data, + nullptr, + c->GetPosition(), + GravityBehavior::Water + ); + npc->ShowStats(c); + } + else { + c->Message( + Chat::White, + fmt::format( + "NPC ID {} was not found.", + npc_id + ).c_str() + ); + } + } + else { + c->Message(Chat::White, "Usage: #viewnpctype [NPC ID]"); + } +} + diff --git a/zone/gm_commands/viewpetition.cpp b/zone/gm_commands/viewpetition.cpp new file mode 100755 index 000000000..a59821a81 --- /dev/null +++ b/zone/gm_commands/viewpetition.cpp @@ -0,0 +1,31 @@ +#include "../client.h" + +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]); + } + +} + diff --git a/zone/gm_commands/viewzoneloot.cpp b/zone/gm_commands/viewzoneloot.cpp new file mode 100755 index 000000000..89518750a --- /dev/null +++ b/zone/gm_commands/viewzoneloot.cpp @@ -0,0 +1,117 @@ +#include "../client.h" + +void command_viewzoneloot(Client *c, const Seperator *sep) +{ + std::map zone_loot_list; + auto npc_list = entity_list.GetNPCList(); + uint32 loot_amount = 0, loot_id = 1, search_item_id = 0; + if (sep->argnum == 1 && sep->IsNumber(1)) { + search_item_id = atoi(sep->arg[1]); + } + else if (sep->argnum == 1 && !sep->IsNumber(1)) { + c->Message( + Chat::Yellow, + "Usage: #viewzoneloot [item id]" + ); + return; + } + + for (auto npc_entity : npc_list) { + auto current_npc_item_list = npc_entity.second->GetItemList(); + zone_loot_list.insert({npc_entity.second->GetID(), current_npc_item_list}); + } + + for (auto loot_item : zone_loot_list) { + uint32 current_entity_id = loot_item.first; + auto current_item_list = loot_item.second; + auto current_npc = entity_list.GetNPCByID(current_entity_id); + std::string npc_link; + if (current_npc) { + std::string npc_name = current_npc->GetCleanName(); + uint32 instance_id = zone->GetInstanceID(); + uint32 zone_id = zone->GetZoneID(); + std::string command_link = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format( + "#{} {} {} {} {}", + (instance_id != 0 ? "zoneinstance" : "zone"), + (instance_id != 0 ? instance_id : zone_id), + current_npc->GetX(), + current_npc->GetY(), + current_npc->GetZ() + ), + false, + "Goto" + ); + npc_link = fmt::format( + " NPC: {} (ID {}) [{}]", + npc_name, + current_entity_id, + command_link + ); + } + + for (auto current_item : current_item_list) { + if (search_item_id == 0 || current_item->item_id == search_item_id) { + EQ::SayLinkEngine linker; + linker.SetLinkType(EQ::saylink::SayLinkLootItem); + linker.SetLootData(current_item); + c->Message( + Chat::White, + fmt::format( + "{}. {} ({}){}", + loot_id, + linker.GenerateLink(), + current_item->item_id, + npc_link + ).c_str() + ); + loot_id++; + loot_amount++; + } + } + } + + + if (search_item_id != 0) { + std::string drop_string = ( + loot_amount > 0 ? + fmt::format( + "dropping in {} {}", + loot_amount, + (loot_amount > 1 ? "places" : "place") + ) : + "not dropping" + ); + + c->Message( + Chat::White, + fmt::format( + "{} ({}) is {}.", + database.CreateItemLink(search_item_id), + search_item_id, + drop_string + ).c_str() + ); + } + else { + std::string drop_string = ( + loot_amount > 0 ? + fmt::format( + "{} {} dropping", + (loot_amount > 1 ? "items" : "item"), + (loot_amount > 1 ? "are" : "is") + ) : + "items are dropping" + ); + + c->Message( + Chat::White, + fmt::format( + "{} {}.", + loot_amount, + drop_string + ).c_str() + ); + } +} + diff --git a/zone/gm_commands/wc.cpp b/zone/gm_commands/wc.cpp new file mode 100755 index 000000000..0ec247aca --- /dev/null +++ b/zone/gm_commands/wc.cpp @@ -0,0 +1,44 @@ +#include "../client.h" + +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])); + } +} + diff --git a/zone/gm_commands/weather.cpp b/zone/gm_commands/weather.cpp new file mode 100755 index 000000000..2590c51e3 --- /dev/null +++ b/zone/gm_commands/weather.cpp @@ -0,0 +1,70 @@ +#include "../client.h" + +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); + } + } +} + diff --git a/zone/gm_commands/who.cpp b/zone/gm_commands/who.cpp new file mode 100755 index 000000000..f39733d1e --- /dev/null +++ b/zone/gm_commands/who.cpp @@ -0,0 +1,202 @@ +#include "../client.h" + +void command_who(Client *c, const Seperator *sep) +{ + std::string query = + SQL ( + SELECT + character_data.account_id, + character_data.name, + character_data.zone_id, + character_data.zone_instance, + COALESCE( + ( + select + guilds.name + from + guilds + where + id = ( + ( + select + guild_id + from + guild_members + where + char_id = character_data.id + ) + ) + ), + "" + ) as guild_name, + character_data.level, + character_data.race, + character_data.class, + COALESCE( + ( + select + account.status + from + account + where + account.id = character_data.account_id + LIMIT + 1 + ), 0 + ) as account_status, + COALESCE( + ( + select + account.name + from + account + where + account.id = character_data.account_id + LIMIT + 1 + ), + 0 + ) as account_name, + COALESCE( + ( + select + account_ip.ip + from + account_ip + where + account_ip.accid = character_data.account_id + ORDER BY + account_ip.lastused DESC + LIMIT + 1 + ), + "" + ) as account_ip + FROM + character_data + WHERE + last_login > (UNIX_TIMESTAMP() - 600) + ORDER BY + character_data.name; + ); + + auto results = database.QueryDatabase(query); + if (!results.Success()) { + return; + } + + if (results.RowCount() == 0) { + c->Message(Chat::Yellow, "No results found"); + return; + } + + std::string search_string; + + if (sep->arg[1]) { + search_string = str_tolower(sep->arg[1]); + } + + int found_count = 0; + + c->Message(Chat::Magenta, "Players in EverQuest"); + c->Message(Chat::Magenta, "--------------------"); + + for (auto row = results.begin(); row != results.end(); ++row) { + auto account_id = static_cast(atoi(row[0])); + std::string player_name = row[1]; + auto zone_id = static_cast(atoi(row[2])); + std::string zone_short_name = ZoneName(zone_id); + auto zone_instance = static_cast(atoi(row[3])); + std::string guild_name = row[4]; + auto player_level = static_cast(atoi(row[5])); + auto player_race = static_cast(atoi(row[6])); + auto player_class = static_cast(atoi(row[7])); + auto account_status = static_cast(atoi(row[8])); + std::string account_name = row[9]; + std::string account_ip = row[10]; + std::string base_class_name = GetClassIDName(static_cast(player_class), 1); + std::string displayed_race_name = GetRaceIDName(static_cast(player_race)); + + if (search_string.length() > 0) { + bool found_search_term = + ( + str_tolower(player_name).find(search_string) != std::string::npos || + str_tolower(zone_short_name).find(search_string) != std::string::npos || + str_tolower(displayed_race_name).find(search_string) != std::string::npos || + str_tolower(base_class_name).find(search_string) != std::string::npos || + str_tolower(guild_name).find(search_string) != std::string::npos || + str_tolower(account_name).find(search_string) != std::string::npos || + str_tolower(account_ip).find(search_string) != std::string::npos + ); + + if (!found_search_term) { + continue; + } + } + + std::string displayed_guild_name; + if (guild_name.length() > 0) { + displayed_guild_name = EQ::SayLinkEngine::GenerateQuestSaylink( + StringFormat( + "#who \"%s\"", + guild_name.c_str()), + false, + StringFormat("<%s>", guild_name.c_str()) + ); + } + + std::string goto_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( + StringFormat("#goto %s", player_name.c_str()), false, "Goto" + ); + + std::string display_class_name = GetClassIDName( + static_cast(player_class), + static_cast(player_level)); + + c->Message( + 5, "%s[%u %s] %s (%s) %s ZONE: %s (%u) (%s) (%s) (%s)", + (account_status > 0 ? "* GM * " : ""), + player_level, + EQ::SayLinkEngine::GenerateQuestSaylink( + StringFormat("#who %s", base_class_name.c_str()), + false, + display_class_name + ).c_str(), + player_name.c_str(), + EQ::SayLinkEngine::GenerateQuestSaylink( + StringFormat("#who %s", displayed_race_name.c_str()), + false, + displayed_race_name + ).c_str(), + displayed_guild_name.c_str(), + EQ::SayLinkEngine::GenerateQuestSaylink( + StringFormat("#who %s", zone_short_name.c_str()), + false, + zone_short_name + ).c_str(), + zone_instance, + goto_saylink.c_str(), + EQ::SayLinkEngine::GenerateQuestSaylink( + StringFormat("#who %s", account_name.c_str()), + false, + account_name + ).c_str(), + EQ::SayLinkEngine::GenerateQuestSaylink( + StringFormat("#who %s", account_ip.c_str()), + false, + account_ip + ).c_str() + ); + + found_count++; + } + + std::string message = ( + found_count > 0 ? + StringFormat("There is %i player(s) in EverQuest", found_count).c_str() : + "There are no players in EverQuest that match those who filters." + ); + + c->Message(Chat::Magenta, message.c_str()); +} + diff --git a/zone/gm_commands/worldshutdown.cpp b/zone/gm_commands/worldshutdown.cpp new file mode 100755 index 000000000..4f27c4ad9 --- /dev/null +++ b/zone/gm_commands/worldshutdown.cpp @@ -0,0 +1,79 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_worldshutdown(Client *c, const Seperator *sep) +{ + // GM command to shutdown world server and all zone servers + uint32 time = 0; + uint32 interval = 0; + if (worldserver.Connected()) { + if ( + sep->IsNumber(1) && + sep->IsNumber(2) && + (time = std::stoi(sep->arg[1]) > 0) && + (interval = std::stoi(sep->arg[2]) > 0) + ) { + int time_minutes = (time / 60); + quest_manager.WorldWideMessage( + Chat::Yellow, + fmt::format( + "[SYSTEM] World will be shutting down in {} minutes.", + time_minutes + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "World will be shutting down in {} minutes, notifying every {} seconds", + time_minutes, + interval + ).c_str() + ); + auto pack = new ServerPacket(ServerOP_ShutdownAll, sizeof(WorldShutDown_Struct)); + WorldShutDown_Struct *wsd = (WorldShutDown_Struct *) pack->pBuffer; + wsd->time = (time * 1000); + wsd->interval = (interval * 1000); + worldserver.SendPacket(pack); + safe_delete(pack); + } + else if (!strcasecmp(sep->arg[1], "now")) { + quest_manager.WorldWideMessage( + Chat::Yellow, + "[SYSTEM] World is shutting down now." + ); + c->Message(Chat::White, "World is shutting down now."); + auto pack = new ServerPacket; + pack->opcode = ServerOP_ShutdownAll; + pack->size = 0; + worldserver.SendPacket(pack); + safe_delete(pack); + } + else if (!strcasecmp(sep->arg[1], "disable")) { + c->Message(Chat::White, "World shutdown has been aborted."); + auto pack = new ServerPacket(ServerOP_ShutdownAll, sizeof(WorldShutDown_Struct)); + WorldShutDown_Struct *wsd = (WorldShutDown_Struct *) pack->pBuffer; + wsd->time = 0; + wsd->interval = 0; + worldserver.SendPacket(pack); + safe_delete(pack); + } + else { + c->Message(Chat::White, "#worldshutdown - Shuts down the server and all zones."); + c->Message(Chat::White, "Usage: #worldshutdown now - Shuts down the server and all zones immediately."); + c->Message( + Chat::White, + "Usage: #worldshutdown disable - Stops the server from a previously scheduled shut down." + ); + c->Message( + Chat::White, + "Usage: #worldshutdown [timer] [interval] - Shuts down the server and all zones after [timer] seconds and notifies players every [interval] seconds." + ); + } + } + else { + c->Message(Chat::White, "Error: World server is disconnected."); + } +} + diff --git a/zone/gm_commands/worldwide.cpp b/zone/gm_commands/worldwide.cpp new file mode 100755 index 000000000..29b617a05 --- /dev/null +++ b/zone/gm_commands/worldwide.cpp @@ -0,0 +1,148 @@ +#include "../client.h" + +void command_worldwide(Client *c, const Seperator *sep) +{ + std::string sub_command; + if (sep->arg[1]) { + sub_command = sep->arg[1]; + } + + if (sub_command == "cast") { + if (sep->arg[2] && Seperator::IsNumber(sep->arg[2])) { + uint8 update_type = WWSpellUpdateType_Cast; + auto spell_id = std::stoul(sep->arg[2]); + bool disable_message = false; + if (sep->arg[3] && Seperator::IsNumber(sep->arg[3])) { + disable_message = std::stoi(sep->arg[3]) ? true : false; + } + + c->Message( + Chat::White, + fmt::format( + "World Wide Cast Spell | Spell: {} ({})", + GetSpellName(spell_id), + spell_id + ).c_str() + ); + + quest_manager.WorldWideSpell(update_type, spell_id); + if (!disable_message) { + quest_manager.WorldWideMessage( + Chat::Yellow, + fmt::format( + "[SYSTEM] A GM has cast [{}] world-wide!", + GetSpellName(spell_id) + ).c_str() + ); + } + } + else { + c->Message(Chat::White, "Usage: #worldwide cast [Spell ID] [Disable Message]"); + } + } + else if (sub_command == "remove") { + if (sep->arg[2] && Seperator::IsNumber(sep->arg[2])) { + uint8 update_type = WWSpellUpdateType_Remove; + auto spell_id = std::stoul(sep->arg[2]); + + c->Message( + Chat::White, + fmt::format( + "World Wide Remove Spell | Spell: {} ({})", + GetSpellName(spell_id), + spell_id + ).c_str() + ); + + quest_manager.WorldWideSpell(update_type, spell_id); + } + else { + c->Message(Chat::White, "Usage: #worldwide remove [Spell ID]"); + } + } + else if (sub_command == "message") { + if (sep->arg[2]) { + std::string message = sep->arg[2]; + quest_manager.WorldWideMessage( + Chat::White, + fmt::format( + "{}", + message + ).c_str() + ); + } + else { + c->Message(Chat::White, "Usage: #worldwide message [Message]"); + } + } + else if (sub_command == "move") { + if (sep->arg[2]) { + uint8 update_type = WWMoveUpdateType_MoveZone; + uint32 zone_id = 0; + std::string zone_short_name; + if (Seperator::IsNumber(sep->arg[2])) { + zone_id = std::stoul(sep->arg[2]); + } + + if (zone_id) { + zone_short_name = ZoneName(zone_id); + } + else { + zone_short_name = sep->arg[2]; + } + + c->Message( + Chat::White, + fmt::format( + "World Wide Zone | Zone: {} ({}) ID: {}", + ZoneLongName( + ZoneID(zone_short_name) + ), + zone_short_name, + ZoneID(zone_short_name) + ).c_str() + ); + + quest_manager.WorldWideMove(update_type, zone_short_name.c_str()); + } + else { + c->Message( + Chat::White, + "Usage: #worldwide move [Zone ID] or #worldwide move [Zone Short Name]" + ); + } + } + else if (sub_command == "moveinstance") { + if (Seperator::IsNumber(sep->arg[2])) { + uint8 update_type = WWMoveUpdateType_MoveZoneInstance; + const char *zone_short_name = ""; + uint16 instance_id = std::stoi(sep->arg[2]); + + c->Message( + Chat::White, + fmt::format( + "World Wide Zone Instance | Instance ID: {}", + instance_id + ).c_str() + ); + + quest_manager.WorldWideMove(update_type, zone_short_name, instance_id); + } + else { + c->Message(Chat::White, "Usage: #worldwide moveinstance [Instance ID]"); + } + } + + if (!sep->arg[1]) { + c->Message(Chat::White, "This command is used to perform world-wide tasks."); + c->Message(Chat::White, "Usage: #worldwide cast [Spell ID] [Disable Message]"); + c->Message(Chat::White, "Usage: #worldwide remove [Spell ID]"); + c->Message(Chat::White, "Usage: #worldwide message [Message]"); + c->Message( + Chat::White, + "Usage: #worldwide move [Zone ID] or #worldwide move [Zone Short Name]" + ); + c->Message(Chat::White, "Usage: #worldwide moveinstance [Instance ID]"); + } +} + diff --git a/zone/gm_commands/wp.cpp b/zone/gm_commands/wp.cpp new file mode 100755 index 000000000..4f0b9330e --- /dev/null +++ b/zone/gm_commands/wp.cpp @@ -0,0 +1,51 @@ +#include "../client.h" + +void command_wp(Client *c, const Seperator *sep) +{ + auto command_type = sep->arg[1]; + auto grid_id = atoi(sep->arg[2]); + if (grid_id != 0) { + auto pause = atoi(sep->arg[3]); + auto waypoint = atoi(sep->arg[4]); + auto zone_id = zone->GetZoneID(); + if (strcasecmp("add", command_type) == 0) { + if (waypoint == 0) { // Default to highest if it's left blank, or we enter 0 + waypoint = (content_db.GetHighestWaypoint(zone_id, grid_id) + 1); + } + + if (strcasecmp("-h", sep->arg[5]) == 0) { + content_db.AddWP(c, grid_id, waypoint, c->GetPosition(), pause, zone_id); + } + else { + auto position = c->GetPosition(); + position.w = -1; + content_db.AddWP(c, grid_id, waypoint, position, pause, zone_id); + } + c->Message( + Chat::White, + fmt::format( + "Waypoint {} added to grid {} with a pause of {} {}.", + waypoint, + grid_id, + pause, + (pause == 1 ? "second" : "seconds") + ).c_str() + ); + } + else if (strcasecmp("delete", command_type) == 0) { + content_db.DeleteWaypoint(c, grid_id, waypoint, zone_id); + c->Message( + Chat::White, + fmt::format( + "Waypoint {} deleted from grid {}.", + waypoint, + grid_id + ).c_str() + ); + } + } + else { + c->Message(Chat::White, "Usage: #wp [add|delete] [grid_id] [pause] [waypoint_id] [-h]"); + } +} + diff --git a/zone/gm_commands/wpadd.cpp b/zone/gm_commands/wpadd.cpp new file mode 100755 index 000000000..df74c03e4 --- /dev/null +++ b/zone/gm_commands/wpadd.cpp @@ -0,0 +1,52 @@ +#include "../client.h" + +void command_wpadd(Client *c, const Seperator *sep) +{ + int type1 = 0, type2 = 0, pause = 0; // Defaults for a new grid + Mob *target = c->GetTarget(); + if (target && target->IsNPC()) { + Spawn2 *s2info = target->CastToNPC()->respawn2; + if (s2info == nullptr) { + c->Message( + Chat::White, + "#wpadd Failed, you must target a valid spawn." + ); + return; + } + + if (sep->arg[1][0]) { + if (atoi(sep->arg[1]) >= 0) { + pause = atoi(sep->arg[1]); + } + else { + c->Message(Chat::White, "Usage: #wpadd [pause] [-h]"); + return; + } + } + auto position = c->GetPosition(); + if (strcmp("-h", sep->arg[2]) != 0) { + position.w = -1; + } + + auto zone_id = zone->GetZoneID(); + uint32 tmp_grid = content_db.AddWPForSpawn(c, s2info->GetID(), position, pause, type1, type2, zone_id); + if (tmp_grid) { + target->CastToNPC()->SetGrid(tmp_grid); + } + + auto grid_id = target->CastToNPC()->GetGrid(); + target->CastToNPC()->AssignWaypoints(grid_id); + c->Message( + Chat::White, + fmt::format( + "Waypoint added to grid {} in zone ID {}. Use #wpinfo to see waypoints for this NPC (may need to #repop first).", + grid_id, + zone_id + ).c_str() + ); + } + else { + c->Message(Chat::White, "You must target an NPC to use this."); + } +} + diff --git a/zone/gm_commands/wpinfo.cpp b/zone/gm_commands/wpinfo.cpp new file mode 100755 index 000000000..4f0f988e5 --- /dev/null +++ b/zone/gm_commands/wpinfo.cpp @@ -0,0 +1,15 @@ +#include "../client.h" + +void command_wpinfo(Client *c, const Seperator *sep) +{ + Mob *t = c->GetTarget(); + + if (t == nullptr || !t->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this."); + return; + } + + NPC *n = t->CastToNPC(); + n->DisplayWaypointInfo(c); +} + diff --git a/zone/gm_commands/xtargets.cpp b/zone/gm_commands/xtargets.cpp new file mode 100755 index 000000000..bd300e819 --- /dev/null +++ b/zone/gm_commands/xtargets.cpp @@ -0,0 +1,28 @@ +#include "../client.h" + +void command_xtargets(Client *c, const Seperator *sep) +{ + Client *t; + + if (c->GetTarget() && c->GetTarget()->IsClient()) { + t = c->GetTarget()->CastToClient(); + } + else { + t = c; + } + + if (sep->arg[1][0]) { + uint8 NewMax = atoi(sep->arg[1]); + + if ((NewMax < 5) || (NewMax > XTARGET_HARDCAP)) { + c->Message(Chat::Red, "Number of XTargets must be between 5 and %i", XTARGET_HARDCAP); + return; + } + t->SetMaxXTargets(NewMax); + c->Message(Chat::White, "Max number of XTargets set to %i", NewMax); + } + else { + t->ShowXTargets(c); + } +} + diff --git a/zone/gm_commands/zclip.cpp b/zone/gm_commands/zclip.cpp new file mode 100755 index 000000000..4f825c745 --- /dev/null +++ b/zone/gm_commands/zclip.cpp @@ -0,0 +1,39 @@ +#include "../client.h" + +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); + } +} + diff --git a/zone/gm_commands/zcolor.cpp b/zone/gm_commands/zcolor.cpp new file mode 100755 index 000000000..feac259b9 --- /dev/null +++ b/zone/gm_commands/zcolor.cpp @@ -0,0 +1,30 @@ +#include "../client.h" + +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); + } +} + diff --git a/zone/gm_commands/zheader.cpp b/zone/gm_commands/zheader.cpp new file mode 100755 index 000000000..788140dbe --- /dev/null +++ b/zone/gm_commands/zheader.cpp @@ -0,0 +1,26 @@ +#include "../client.h" + +void command_zheader(Client *c, const Seperator *sep) +{ + // sends zhdr packet + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #zheader "); + } + else if (ZoneID(sep->argplus[1]) == 0) { + c->Message(Chat::White, "Invalid Zone Name: %s", sep->argplus[1]); + } + else { + + if (zone->LoadZoneCFG(sep->argplus[1], 0)) { + c->Message(Chat::White, "Successfully loaded zone header for %s from database.", sep->argplus[1]); + } + else { + c->Message(Chat::White, "Failed to load zone header %s from database", sep->argplus[1]); + } + auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } +} + diff --git a/zone/gm_commands/zonebootup.cpp b/zone/gm_commands/zonebootup.cpp new file mode 100755 index 000000000..7c54e24fb --- /dev/null +++ b/zone/gm_commands/zonebootup.cpp @@ -0,0 +1,25 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_zonebootup(Client *c, const Seperator *sep) +{ + if (!worldserver.Connected()) { + c->Message(Chat::White, "Error: World server disconnected"); + } + else if (sep->arg[2][0] == 0) { + c->Message(Chat::White, "Usage: #zonebootup ZoneServerID# zoneshortname"); + } + else { + auto pack = new ServerPacket(ServerOP_ZoneBootup, sizeof(ServerZoneStateChange_struct)); + ServerZoneStateChange_struct *s = (ServerZoneStateChange_struct *) pack->pBuffer; + s->ZoneServerID = atoi(sep->arg[1]); + strcpy(s->adminname, c->GetName()); + s->zoneid = ZoneID(sep->arg[2]); + s->makestatic = (bool) (strcasecmp(sep->arg[3], "static") == 0); + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + diff --git a/zone/gm_commands/zonelock.cpp b/zone/gm_commands/zonelock.cpp new file mode 100755 index 000000000..f89f1e9ad --- /dev/null +++ b/zone/gm_commands/zonelock.cpp @@ -0,0 +1,76 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_zonelock(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #zonelock list - Lists Locked Zones"); + if (c->Admin() >= commandLockZones) { + c->Message( + Chat::White, + "Usage: #zonelock lock [Zone ID] or #zonelock lock [Zone Short Name] - Locks a Zone by ID or Short Name" + ); + c->Message( + Chat::White, + "Usage: #zonelock unlock [Zone ID] or #zonelock unlock [Zone Short Name] - Unlocks a Zone by ID or Short Name" + ); + } + return; + } + + std::string lock_type = str_tolower(sep->arg[1]); + bool is_list = lock_type.find("list") != std::string::npos; + bool is_lock = lock_type.find("lock") != std::string::npos; + bool is_unlock = lock_type.find("unlock") != std::string::npos; + if (!is_list && !is_lock && !is_unlock) { + c->Message(Chat::White, "Usage: #zonelock list - Lists Locked Zones"); + if (c->Admin() >= commandLockZones) { + c->Message( + Chat::White, + "Usage: #zonelock lock [Zone ID] or #zonelock lock [Zone Short Name] - Locks a Zone by ID or Short Name" + ); + c->Message( + Chat::White, + "Usage: #zonelock unlock [Zone ID] or #zonelock unlock [Zone Short Name] - Unlocks a Zone by ID or Short Name" + ); + } + return; + } + + auto pack = new ServerPacket(ServerOP_LockZone, sizeof(ServerLockZone_Struct)); + ServerLockZone_Struct *lock_zone = (ServerLockZone_Struct *) pack->pBuffer; + strn0cpy(lock_zone->adminname, c->GetName(), sizeof(lock_zone->adminname)); + + if (is_list) { + lock_zone->op = ServerLockType::List; + worldserver.SendPacket(pack); + } + else if (!is_list && c->Admin() >= commandLockZones) { + auto zone_id = ( + sep->IsNumber(2) ? + static_cast(std::stoul(sep->arg[2])) : + static_cast(ZoneID(sep->arg[2])) + ); + std::string zone_short_name = str_tolower(ZoneName(zone_id, true)); + bool is_unknown_zone = zone_short_name.find("unknown") != std::string::npos; + if (zone_id && !is_unknown_zone) { + lock_zone->op = is_lock ? ServerLockType::Lock : ServerLockType::Unlock; + lock_zone->zoneID = zone_id; + worldserver.SendPacket(pack); + } + else { + c->Message( + Chat::White, + fmt::format( + "Usage: #zonelock {} [Zone ID] or #zonelock {} [Zone Short Name]", + is_lock ? "lock" : "unlock" + ).c_str() + ); + } + } + safe_delete(pack); +} + diff --git a/zone/gm_commands/zoneshutdown.cpp b/zone/gm_commands/zoneshutdown.cpp new file mode 100755 index 000000000..14550b9db --- /dev/null +++ b/zone/gm_commands/zoneshutdown.cpp @@ -0,0 +1,30 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_zoneshutdown(Client *c, const Seperator *sep) +{ + if (!worldserver.Connected()) { + c->Message(Chat::White, "Error: World server disconnected"); + } + else if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #zoneshutdown zoneshortname"); + } + else { + auto pack = new ServerPacket( + ServerOP_ZoneShutdown, + sizeof(ServerZoneStateChange_struct)); + ServerZoneStateChange_struct *s = (ServerZoneStateChange_struct *) pack->pBuffer; + strcpy(s->adminname, c->GetName()); + if (sep->arg[1][0] >= '0' && sep->arg[1][0] <= '9') { + s->ZoneServerID = atoi(sep->arg[1]); + } + else { + s->zoneid = ZoneID(sep->arg[1]); + } + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + diff --git a/zone/gm_commands/zonespawn.cpp b/zone/gm_commands/zonespawn.cpp new file mode 100755 index 000000000..01a3a8683 --- /dev/null +++ b/zone/gm_commands/zonespawn.cpp @@ -0,0 +1,40 @@ +#include "../client.h" + +void command_zonespawn(Client *c, const Seperator *sep) +{ + c->Message(Chat::White, "This command is not yet implemented."); + return; + +/* this was kept from client.cpp verbatim (it was commented out) */ + // if (target && target->IsNPC()) { + // Message(0, "Inside main if."); + // if (strcasecmp(sep->arg[1], "add")==0) { + // Message(0, "Inside add if."); + // database.DBSpawn(1, StaticGetZoneName(this->GetPP().current_zone), target->CastToNPC()); + // } + // else if (strcasecmp(sep->arg[1], "update")==0) { + // database.DBSpawn(2, StaticGetZoneName(this->GetPP().current_zone), target->CastToNPC()); + // } + // else if (strcasecmp(sep->arg[1], "remove")==0) { + // if (strcasecmp(sep->arg[2], "all")==0) { + // database.DBSpawn(4, StaticGetZoneName(this->GetPP().current_zone)); + // } + // else { + // if (database.DBSpawn(3, StaticGetZoneName(this->GetPP().current_zone), target->CastToNPC())) { + // Message(0, "#zonespawn: %s removed successfully!", target->GetName()); + // target->CastToNPC()->Death(target, target->GetHP()); + // } + // } + // } + // else + // Message(0, "Error: #dbspawn: Invalid command. (Note: EDIT and REMOVE are NOT in yet.)"); + // if (target->CastToNPC()->GetNPCTypeID() > 0) { + // Message(0, "Spawn is type %i", target->CastToNPC()->GetNPCTypeID()); + // } + // } + // else if(!target || !target->IsNPC()) + // Message(0, "Error: #zonespawn: You must have a NPC targeted!"); + // else + // Message(0, "Usage: #zonespawn [add|edit|remove|remove all]"); +} + diff --git a/zone/gm_commands/zonestatus.cpp b/zone/gm_commands/zonestatus.cpp new file mode 100755 index 000000000..51c5f763e --- /dev/null +++ b/zone/gm_commands/zonestatus.cpp @@ -0,0 +1,19 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_zonestatus(Client *c, const Seperator *sep) +{ + if (!worldserver.Connected()) { + c->Message(Chat::White, "Error: World server disconnected"); + } + else { + auto pack = new ServerPacket(ServerOP_ZoneStatus, strlen(c->GetName()) + 2); + memset(pack->pBuffer, (uint8) c->Admin(), 1); + strcpy((char *) &pack->pBuffer[1], c->GetName()); + worldserver.SendPacket(pack); + delete pack; + } +} + diff --git a/zone/gm_commands/zopp.cpp b/zone/gm_commands/zopp.cpp new file mode 100755 index 000000000..08f10395b --- /dev/null +++ b/zone/gm_commands/zopp.cpp @@ -0,0 +1,64 @@ +#include "../client.h" + +void command_zopp(Client *c, const Seperator *sep) +{ // - Owner only command..non-targetable to eliminate malicious or mischievious activities. + if (!c) { + return; + } + else if (sep->argnum < 3 || sep->argnum > 4) { + c->Message(Chat::White, "Usage: #zopp [trade/summon] [slot id] [item id] [*charges]"); + } + else if (!strcasecmp(sep->arg[1], "trade") == 0 && !strcasecmp(sep->arg[1], "t") == 0 && + !strcasecmp(sep->arg[1], "summon") == 0 && !strcasecmp(sep->arg[1], "s") == 0) { + c->Message(Chat::White, "Usage: #zopp [trade/summon] [slot id] [item id] [*charges]"); + } + else if (!sep->IsNumber(2) || !sep->IsNumber(3) || (sep->argnum == 4 && !sep->IsNumber(4))) { + c->Message(Chat::White, "Usage: #zopp [trade/summon] [slot id] [item id] [*charges]"); + } + else { + ItemPacketType packettype; + + if (strcasecmp(sep->arg[1], "trade") == 0 || strcasecmp(sep->arg[1], "t") == 0) { + packettype = ItemPacketTrade; + } + else { + packettype = ItemPacketLimbo; + } + + int16 slotid = atoi(sep->arg[2]); + uint32 itemid = atoi(sep->arg[3]); + int16 charges = sep->argnum == 4 ? atoi(sep->arg[4]) : 1; // defaults to 1 charge if not specified + + const EQ::ItemData *FakeItem = database.GetItem(itemid); + + if (!FakeItem) { + c->Message(Chat::Red, "Error: Item [%u] is not a valid item id.", itemid); + return; + } + + int16 item_status = 0; + const EQ::ItemData *item = database.GetItem(itemid); + if (item) { + item_status = static_cast(item->MinStatus); + } + if (item_status > c->Admin()) { + c->Message(Chat::Red, "Error: Insufficient status to use this command."); + return; + } + + if (charges < 0 || charges > FakeItem->StackSize) { + c->Message(Chat::Red, "Warning: The specified charge count does not meet expected criteria!"); + c->Message(Chat::White, "Processing request..results may cause unpredictable behavior."); + } + + EQ::ItemInstance *FakeItemInst = database.CreateItem(FakeItem, charges); + c->SendItemPacket(slotid, FakeItemInst, packettype); + c->Message( + Chat::White, "Sending zephyr op packet to client - [%s] %s (%u) with %i %s to slot %i.", + packettype == ItemPacketTrade ? "Trade" : "Summon", FakeItem->Name, itemid, charges, + std::abs(charges == 1) ? "charge" : "charges", slotid + ); + safe_delete(FakeItemInst); + } +} + diff --git a/zone/gm_commands/zsafecoords.cpp b/zone/gm_commands/zsafecoords.cpp new file mode 100755 index 000000000..237f288da --- /dev/null +++ b/zone/gm_commands/zsafecoords.cpp @@ -0,0 +1,26 @@ +#include "../client.h" + +void command_zsafecoords(Client *c, const Seperator *sep) +{ + // modifys and resends zhdr packet + if (sep->arg[3][0] == 0) { + c->Message(Chat::White, "Usage: #zsafecoords "); + } + else { + zone->newzone_data.safe_x = atof(sep->arg[1]); + zone->newzone_data.safe_y = atof(sep->arg[2]); + zone->newzone_data.safe_z = atof(sep->arg[3]); + //float newdatax = atof(sep->arg[1]); + //float newdatay = atof(sep->arg[2]); + //float newdataz = atof(sep->arg[3]); + //memcpy(&zone->zone_header_data[114], &newdatax, sizeof(float)); + //memcpy(&zone->zone_header_data[118], &newdatay, sizeof(float)); + //memcpy(&zone->zone_header_data[122], &newdataz, sizeof(float)); + //zone->SetSafeCoords(); + 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); + } +} + diff --git a/zone/gm_commands/zsave.cpp b/zone/gm_commands/zsave.cpp new file mode 100755 index 000000000..8c1cf85dc --- /dev/null +++ b/zone/gm_commands/zsave.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +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."); + } +} + diff --git a/zone/gm_commands/zsky.cpp b/zone/gm_commands/zsky.cpp new file mode 100755 index 000000000..8a3dc2c7f --- /dev/null +++ b/zone/gm_commands/zsky.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +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); + } +} + diff --git a/zone/gm_commands/zstats.cpp b/zone/gm_commands/zstats.cpp new file mode 100755 index 000000000..9c4330ae5 --- /dev/null +++ b/zone/gm_commands/zstats.cpp @@ -0,0 +1,208 @@ +#include "../client.h" + +void command_zstats(Client *c, const Seperator *sep) +{ + // Zone + c->Message( + Chat::White, + fmt::format( + "Zone | ID: {} Instance ID: {} Name: {} ({})", + zone->GetZoneID(), + zone->GetInstanceID(), + zone->GetLongName(), + zone->GetShortName() + ).c_str() + ); + + // Type + c->Message( + Chat::White, + fmt::format( + "Type: {}", + zone->newzone_data.ztype + ).c_str() + ); + + // Fog Data + for (int fog_index = 0; fog_index < 4; fog_index++) { + int fog_number = (fog_index + 1); + c->Message( + Chat::White, + fmt::format( + "Fog {} Colors | Red: {} Blue: {} Green: {} ", + fog_number, + zone->newzone_data.fog_red[fog_index], + zone->newzone_data.fog_green[fog_index], + zone->newzone_data.fog_blue[fog_index] + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Fog {} Clipping Distance | Min: {} Max: {}", + fog_number, + zone->newzone_data.fog_minclip[fog_index], + zone->newzone_data.fog_maxclip[fog_index] + ).c_str() + ); + } + + // Fog Density + c->Message( + Chat::White, + fmt::format( + "Fog Density: {}", + zone->newzone_data.fog_density + ).c_str() + ); + + + // Gravity + c->Message( + Chat::White, + fmt::format( + "Gravity: {}", + zone->newzone_data.gravity + ).c_str() + ); + + // Time Type + c->Message( + Chat::White, + fmt::format( + "Time Type: {}", + zone->newzone_data.time_type + ).c_str() + ); + + // Experience Multiplier + c->Message( + Chat::White, + fmt::format( + "Experience Multiplier: {}", + zone->newzone_data.zone_exp_multiplier + ).c_str() + ); + + // Safe Coordinates + c->Message( + Chat::White, + fmt::format( + "Safe Coordinates: {}, {}, {}", + zone->newzone_data.safe_x, + zone->newzone_data.safe_y, + zone->newzone_data.safe_z + ).c_str() + ); + + // Max Z + c->Message( + Chat::White, + fmt::format( + "Max Z: {}", + zone->newzone_data.max_z + ).c_str() + ); + + // Underworld Z + c->Message( + Chat::White, + fmt::format( + "Underworld Z: {}", + zone->newzone_data.underworld + ).c_str() + ); + + // Clipping Distance + c->Message( + Chat::White, + fmt::format( + "Clipping Distance | Min: {} Max: {}", + zone->newzone_data.minclip, + zone->newzone_data.maxclip + ).c_str() + ); + + // Weather Data + for (int weather_index = 0; weather_index < 4; weather_index++) { + int weather_number = (weather_index + 1); + c->Message( + Chat::White, + fmt::format( + "Rain {} | Chance: {} Duration: {} ", + weather_number, + zone->newzone_data.rain_chance[weather_index], + zone->newzone_data.rain_duration[weather_index] + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Snow {} | Chance: {} Duration: {}", + weather_number, + zone->newzone_data.snow_chance[weather_index], + zone->newzone_data.snow_duration[weather_index] + ).c_str() + ); + } + + // Sky Type + c->Message( + Chat::White, + fmt::format( + "Sky Type: {}", + zone->newzone_data.sky + ).c_str() + ); + + // Suspend Buffs + c->Message( + Chat::White, + fmt::format( + "Suspend Buffs: {}", + zone->newzone_data.SuspendBuffs + ).c_str() + ); + + // Regeneration Data + c->Message( + Chat::White, + fmt::format( + "Regen | Health: {} Mana: {} Endurance: {}", + zone->newzone_data.FastRegenHP, + zone->newzone_data.FastRegenMana, + zone->newzone_data.FastRegenEndurance + ).c_str() + ); + + // NPC Max Aggro Distance + c->Message( + Chat::White, + fmt::format( + "NPC Max Aggro Distance: {}", + zone->newzone_data.NPCAggroMaxDist + ).c_str() + ); + + // Underworld Teleport Index + c->Message( + Chat::White, + fmt::format( + "Underworld Teleport Index: {}", + zone->newzone_data.underworld_teleport_index + ).c_str() + ); + + // Lava Damage + c->Message( + Chat::White, + fmt::format( + "Lava Damage | Min: {} Max: {}", + zone->newzone_data.MinLavaDamage, + zone->newzone_data.LavaDamage + ).c_str() + ); +} + diff --git a/zone/gm_commands/zunderworld.cpp b/zone/gm_commands/zunderworld.cpp new file mode 100755 index 000000000..0ba52f156 --- /dev/null +++ b/zone/gm_commands/zunderworld.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_zunderworld(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #zunderworld "); + } + else { + zone->newzone_data.underworld = atof(sep->arg[1]); + } +} + diff --git a/zone/gm_commands/zuwcoords.cpp b/zone/gm_commands/zuwcoords.cpp new file mode 100755 index 000000000..353b48739 --- /dev/null +++ b/zone/gm_commands/zuwcoords.cpp @@ -0,0 +1,19 @@ +#include "../client.h" + +void command_zuwcoords(Client *c, const Seperator *sep) +{ + // modifys and resends zhdr packet + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #zuwcoords "); + } + else { + zone->newzone_data.underworld = atof(sep->arg[1]); + //float newdata = atof(sep->arg[1]); + //memcpy(&zone->zone_header_data[130], &newdata, sizeof(float)); + 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); + } +} +