2021-03-12 19:07:26 +00:00
# include "CustomCommands.hpp"
2022-07-16 23:19:40 +00:00
# include "db/Database.hpp"
# include "Player.hpp"
2021-03-12 19:07:26 +00:00
# include "PlayerManager.hpp"
2022-07-16 23:19:40 +00:00
# include "Chat.hpp"
2021-03-12 19:07:26 +00:00
# include "TableData.hpp"
# include "NPCManager.hpp"
2021-03-13 22:55:16 +00:00
# include "MobAI.hpp"
2021-03-16 22:29:13 +00:00
# include "Missions.hpp"
2022-07-16 23:19:40 +00:00
# include "Eggs.hpp"
# include "Items.hpp"
2022-07-17 06:33:57 +00:00
# include "Abilities.hpp"
2021-03-12 19:07:26 +00:00
# include <sstream>
2021-09-19 02:46:28 +00:00
# include <limits.h>
2021-03-12 19:07:26 +00:00
typedef void ( * CommandHandler ) ( std : : string fullString , std : : vector < std : : string > & args , CNSocket * sock ) ;
struct ChatCommand {
int requiredAccLevel ;
std : : string help ;
CommandHandler handlr ;
ChatCommand ( int r , CommandHandler h ) : requiredAccLevel ( r ) , handlr ( h ) { }
ChatCommand ( int r , CommandHandler h , std : : string str ) : requiredAccLevel ( r ) , help ( str ) , handlr ( h ) { }
ChatCommand ( ) : ChatCommand ( 0 , nullptr ) { }
} ;
static std : : map < std : : string , ChatCommand > commands ;
static std : : vector < std : : string > parseArgs ( std : : string full ) {
std : : stringstream ss ( full ) ;
std : : istream_iterator < std : : string > begin ( ss ) ;
std : : istream_iterator < std : : string > end ;
return std : : vector < std : : string > ( begin , end ) ;
}
bool CustomCommands : : runCmd ( std : : string full , CNSocket * sock ) {
std : : vector < std : : string > args = parseArgs ( full ) ;
std : : string cmd = args [ 0 ] . substr ( 1 , args [ 0 ] . size ( ) - 1 ) ;
// check if the command exists
if ( commands . find ( cmd ) ! = commands . end ( ) ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
ChatCommand command = commands [ cmd ] ;
// sanity check + does the player have the required account level to use the command?
if ( plr ! = nullptr & & plr - > accountLevel < = command . requiredAccLevel ) {
command . handlr ( full , args , sock ) ;
return true ;
} else {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " You don't have access to that command! " ) ;
2021-03-12 19:07:26 +00:00
return false ;
}
}
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " Unknown command! " ) ;
2021-03-12 19:07:26 +00:00
return false ;
}
2021-05-06 16:06:40 +00:00
//
static void updatePathMarkers ( CNSocket * sock ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
if ( TableData : : RunningNPCPaths . find ( plr - > iID ) ! = TableData : : RunningNPCPaths . end ( ) ) {
for ( BaseNPC * marker : TableData : : RunningNPCPaths [ plr - > iID ] . second )
marker - > enterIntoViewOf ( sock ) ;
}
}
//
2021-03-12 19:07:26 +00:00
static void helpCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " Commands available to you: " ) ;
2021-03-12 19:07:26 +00:00
Player * plr = PlayerManager : : getPlayer ( sock ) ;
for ( auto & cmd : commands ) {
if ( cmd . second . requiredAccLevel > = plr - > accountLevel )
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " / " + cmd . first + ( cmd . second . help . length ( ) > 0 ? " - " + cmd . second . help : " " ) ) ;
2021-03-12 19:07:26 +00:00
}
}
static void accessCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " Your access level is " + std : : to_string ( PlayerManager : : getPlayer ( sock ) - > accountLevel ) ) ;
2021-03-12 19:07:26 +00:00
}
static void populationCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , std : : to_string ( PlayerManager : : players . size ( ) ) + " players online " ) ;
2021-03-12 19:07:26 +00:00
}
static void levelCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
if ( args . size ( ) < 2 ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " /level: no level specified " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
Player * plr = PlayerManager : : getPlayer ( sock ) ;
char * tmp ;
int level = std : : strtol ( args [ 1 ] . c_str ( ) , & tmp , 10 ) ;
if ( * tmp )
return ;
if ( ( level < 1 | | level > 36 ) & & plr - > accountLevel > 30 )
return ;
if ( ! ( level < 1 | | level > 36 ) )
plr - > level = level ;
INITSTRUCT ( sP_FE2CL_REP_PC_CHANGE_LEVEL , resp ) ;
resp . iPC_ID = plr - > iID ;
resp . iPC_Level = level ;
2021-03-20 20:19:48 +00:00
sock - > sendPacket ( resp , P_FE2CL_REP_PC_CHANGE_LEVEL ) ;
PlayerManager : : sendToViewable ( sock , resp , P_FE2CL_REP_PC_CHANGE_LEVEL ) ;
2021-03-12 19:07:26 +00:00
}
static void mssCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
if ( args . size ( ) < 2 ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [MSS] Too few arguments " ) ;
Chat : : sendServerMessage ( sock , " [MSS] Usage: /mss <route> <add/remove/goto/clear/test/export> <<height>> " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
// Validate route number
char * routeNumC ;
int routeNum = std : : strtol ( args [ 1 ] . c_str ( ) , & routeNumC , 10 ) ;
if ( * routeNumC ) {
// not an integer
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [MSS] Invalid route number ' " + args [ 1 ] + " ' " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
if ( args . size ( ) < 3 ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [MSS] Too few arguments " ) ;
Chat : : sendServerMessage ( sock , " [MSS] Usage: /mss <route> <add/remove/goto/clear/test> <<height>> " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
// get the route (if it doesn't exist yet, this will also make it)
2021-05-01 15:04:28 +00:00
std : : vector < Vec3 > * route = & TableData : : RunningSkywayRoutes [ routeNum ] ;
2021-03-12 19:07:26 +00:00
// mss <route> add <height>
if ( args [ 2 ] = = " add " ) {
// make sure height token exists
if ( args . size ( ) < 4 ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [MSS] Point height must be specified " ) ;
Chat : : sendServerMessage ( sock , " [MSS] Usage: /mss <route> add <height> " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
// validate height token
char * heightC ;
int height = std : : strtol ( args [ 3 ] . c_str ( ) , & heightC , 10 ) ;
if ( * heightC ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [MSS] Invalid height " + args [ 3 ] ) ;
2021-03-12 19:07:26 +00:00
return ;
}
Player * plr = PlayerManager : : getPlayer ( sock ) ;
route - > push_back ( { plr - > x , plr - > y , height } ) ; // add point
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [MSS] Added point ( " + std : : to_string ( plr - > x ) + " , " + std : : to_string ( plr - > y ) + " , " + std : : to_string ( height ) + " ) to route " + std : : to_string ( routeNum ) ) ;
2021-03-12 19:07:26 +00:00
return ;
}
// mss <route> remove
if ( args [ 2 ] = = " remove " ) {
if ( route - > empty ( ) ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [MSS] Route " + std : : to_string ( routeNum ) + " is empty " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
2021-05-01 15:04:28 +00:00
Vec3 pulled = route - > back ( ) ;
2021-03-12 19:07:26 +00:00
route - > pop_back ( ) ; // remove point at top of stack
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [MSS] Removed point ( " + std : : to_string ( pulled . x ) + " , " + std : : to_string ( pulled . y ) + " , " + std : : to_string ( pulled . z ) + " ) from route " + std : : to_string ( routeNum ) ) ;
2021-03-12 19:07:26 +00:00
return ;
}
// mss <route> goto
if ( args [ 2 ] = = " goto " ) {
if ( route - > empty ( ) ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [MSS] Route " + std : : to_string ( routeNum ) + " is empty " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
2021-05-01 15:04:28 +00:00
Vec3 pulled = route - > back ( ) ;
2021-03-12 19:07:26 +00:00
PlayerManager : : sendPlayerTo ( sock , pulled . x , pulled . y , pulled . z ) ;
return ;
}
// mss <route> clear
if ( args [ 2 ] = = " clear " ) {
route - > clear ( ) ;
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [MSS] Cleared route " + std : : to_string ( routeNum ) ) ;
2021-03-12 19:07:26 +00:00
return ;
}
// mss <route> test
if ( args [ 2 ] = = " test " ) {
if ( route - > empty ( ) ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [MSS] Route " + std : : to_string ( routeNum ) + " is empty " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
2021-05-01 15:04:28 +00:00
Vec3 pulled = route - > front ( ) ;
2021-03-12 19:07:26 +00:00
PlayerManager : : sendPlayerTo ( sock , pulled . x , pulled . y , pulled . z ) ;
2021-03-16 22:29:13 +00:00
Transport : : testMssRoute ( sock , route ) ;
2021-03-12 19:07:26 +00:00
return ;
}
// for compatibility: mss <route> export
if ( args [ 2 ] = = " export " ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " Wrote gruntwork to " + settings : : GRUNTWORKJSON ) ;
2021-03-12 19:07:26 +00:00
TableData : : flush ( ) ;
return ;
}
// mss ????
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [MSS] Unknown command ' " + args [ 2 ] + " ' " ) ;
2021-03-12 19:07:26 +00:00
}
static void summonWCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
if ( args . size ( ) < 2 ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " /summonW: no mob type specified " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
Player * plr = PlayerManager : : getPlayer ( sock ) ;
char * rest ;
int type = std : : strtol ( args [ 1 ] . c_str ( ) , & rest , 10 ) ;
if ( * rest ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " Invalid NPC number: " + args [ 1 ] ) ;
2021-03-12 19:07:26 +00:00
return ;
}
int limit = NPCManager : : NPCData . back ( ) [ " m_iNpcNumber " ] ;
// permission & sanity check
if ( type > limit )
return ;
BaseNPC * npc = NPCManager : : summonNPC ( plr - > x , plr - > y , plr - > z , plr - > instanceID , type , true ) ;
// update angle
2021-06-20 18:37:37 +00:00
npc - > angle = ( plr - > angle + 180 ) % 360 ;
NPCManager : : updateNPCPosition ( npc - > id , plr - > x , plr - > y , plr - > z , plr - > instanceID , npc - > angle ) ;
2021-03-12 19:07:26 +00:00
// if we're in a lair, we need to spawn the NPC in both the private instance and the template
if ( PLAYERID ( plr - > instanceID ) ! = 0 ) {
npc = NPCManager : : summonNPC ( plr - > x , plr - > y , plr - > z , plr - > instanceID , type , true , true ) ;
2021-06-20 18:37:37 +00:00
npc - > angle = ( plr - > angle + 180 ) % 360 ;
NPCManager : : updateNPCPosition ( npc - > id , plr - > x , plr - > y , plr - > z , npc - > instanceID , npc - > angle ) ;
2021-03-12 19:07:26 +00:00
}
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " /summonW: placed mob with type: " + std : : to_string ( type ) +
2021-06-20 18:37:37 +00:00
" , id: " + std : : to_string ( npc - > id ) ) ;
TableData : : RunningMobs [ npc - > id ] = npc ; // only record the one in the template
2021-03-12 19:07:26 +00:00
}
static void unsummonWCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
2021-03-20 01:23:53 +00:00
BaseNPC * npc = NPCManager : : getNearestNPC ( & plr - > viewableChunks , plr - > x , plr - > y , plr - > z ) ;
2021-03-12 19:07:26 +00:00
if ( npc = = nullptr ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " /unsummonW: No NPCs found nearby " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
2021-06-20 18:37:37 +00:00
if ( TableData : : RunningEggs . find ( npc - > id ) ! = TableData : : RunningEggs . end ( ) ) {
Chat : : sendServerMessage ( sock , " /unsummonW: removed egg with type: " + std : : to_string ( npc - > type ) +
" , id: " + std : : to_string ( npc - > id ) ) ;
TableData : : RunningEggs . erase ( npc - > id ) ;
NPCManager : : destroyNPC ( npc - > id ) ;
2021-03-12 19:07:26 +00:00
return ;
}
2021-06-20 18:37:37 +00:00
if ( TableData : : RunningMobs . find ( npc - > id ) = = TableData : : RunningMobs . end ( )
& & TableData : : RunningGroups . find ( npc - > id ) = = TableData : : RunningGroups . end ( ) ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " /unsummonW: Closest NPC is not a gruntwork mob. " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
2022-04-13 19:09:43 +00:00
if ( NPCManager : : NPCs . find ( npc - > id ) ! = NPCManager : : NPCs . end ( ) & & NPCManager : : NPCs [ npc - > id ] - > kind = = EntityKind : : MOB ) {
2021-03-12 19:07:26 +00:00
int leadId = ( ( Mob * ) npc ) - > groupLeader ;
if ( leadId ! = 0 ) {
2022-04-13 19:09:43 +00:00
if ( NPCManager : : NPCs . find ( leadId ) = = NPCManager : : NPCs . end ( ) | | NPCManager : : NPCs [ leadId ] - > kind ! = EntityKind : : MOB ) {
2021-03-12 19:07:26 +00:00
std : : cout < < " [WARN] unsummonW: leader not found! " < < std : : endl ;
}
2021-04-07 00:43:43 +00:00
Mob * leadNpc = ( Mob * ) NPCManager : : NPCs [ leadId ] ;
2021-03-12 19:07:26 +00:00
for ( int i = 0 ; i < 4 ; i + + ) {
if ( leadNpc - > groupMember [ i ] = = 0 )
break ;
2022-04-13 19:09:43 +00:00
if ( NPCManager : : NPCs . find ( leadNpc - > groupMember [ i ] ) = = NPCManager : : NPCs . end ( ) | | NPCManager : : NPCs [ leadNpc - > groupMember [ i ] ] - > kind ! = EntityKind : : MOB ) {
2021-03-12 19:07:26 +00:00
std : : cout < < " [WARN] unsommonW: leader can't find a group member! " < < std : : endl ;
continue ;
}
NPCManager : : destroyNPC ( leadNpc - > groupMember [ i ] ) ;
}
TableData : : RunningGroups . erase ( leadId ) ;
NPCManager : : destroyNPC ( leadId ) ;
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " /unsummonW: Mob group destroyed. " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
}
2021-06-20 18:37:37 +00:00
Chat : : sendServerMessage ( sock , " /unsummonW: removed mob with type: " + std : : to_string ( npc - > type ) +
" , id: " + std : : to_string ( npc - > id ) ) ;
2021-03-12 19:07:26 +00:00
2021-06-20 18:37:37 +00:00
TableData : : RunningMobs . erase ( npc - > id ) ;
2021-03-12 19:07:26 +00:00
2021-06-20 18:37:37 +00:00
NPCManager : : destroyNPC ( npc - > id ) ;
2021-03-12 19:07:26 +00:00
}
static void toggleAiCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
2021-03-13 22:55:16 +00:00
MobAI : : simulateMobs = ! MobAI : : simulateMobs ;
2021-03-12 19:07:26 +00:00
2021-03-13 22:55:16 +00:00
if ( MobAI : : simulateMobs )
2021-03-12 19:07:26 +00:00
return ;
// return all mobs to their spawn points
2021-04-07 00:43:43 +00:00
for ( auto & pair : NPCManager : : NPCs ) {
2022-04-13 19:09:43 +00:00
if ( pair . second - > kind ! = EntityKind : : MOB )
2021-04-07 00:43:43 +00:00
continue ;
Mob * mob = ( Mob * ) pair . second ;
2022-04-11 14:26:57 +00:00
mob - > state = AIState : : RETREAT ;
2021-04-07 00:43:43 +00:00
mob - > target = nullptr ;
mob - > nextMovement = getTime ( ) ;
2021-03-12 19:07:26 +00:00
// mobs with static paths can chill where they are
2021-04-07 00:43:43 +00:00
if ( mob - > staticPath ) {
2021-04-14 00:57:24 +00:00
mob - > roamX = mob - > x ;
mob - > roamY = mob - > y ;
mob - > roamZ = mob - > z ;
2021-03-12 19:07:26 +00:00
} else {
2021-04-07 00:43:43 +00:00
mob - > roamX = mob - > spawnX ;
mob - > roamY = mob - > spawnY ;
mob - > roamZ = mob - > spawnZ ;
2021-03-12 19:07:26 +00:00
}
}
}
static void npcRotateCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
2021-03-20 01:23:53 +00:00
BaseNPC * npc = NPCManager : : getNearestNPC ( & plr - > viewableChunks , plr - > x , plr - > y , plr - > z ) ;
2021-03-12 19:07:26 +00:00
if ( npc = = nullptr ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [NPCR] No NPCs found nearby " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
int angle = ( plr - > angle + 180 ) % 360 ;
2021-06-20 18:37:37 +00:00
NPCManager : : updateNPCPosition ( npc - > id , npc - > x , npc - > y , npc - > z , npc - > instanceID , angle ) ;
2021-03-12 19:07:26 +00:00
2023-08-20 03:01:38 +00:00
bool isGruntworkNpc = true ;
2021-03-12 19:07:26 +00:00
2023-08-20 03:01:38 +00:00
// add a rotation entry to the gruntwork file, unless it's already a gruntwork NPC
2023-10-08 20:52:22 +00:00
if ( TableData : : RunningMobs . find ( npc - > id ) = = TableData : : RunningMobs . end ( ) ) {
2021-06-20 18:37:37 +00:00
TableData : : RunningNPCRotations [ npc - > id ] = angle ;
2023-08-20 03:01:38 +00:00
isGruntworkNpc = false ;
2021-03-12 19:07:26 +00:00
}
2023-08-20 03:01:38 +00:00
Chat : : sendServerMessage ( sock , " [NPCR] Successfully set angle to " + std : : to_string ( angle ) +
2023-10-08 20:52:22 +00:00
" for " + ( isGruntworkNpc ? " gruntwork " : " " ) + " NPC " + std : : to_string ( npc - > id ) ) ;
2023-08-20 03:01:38 +00:00
// update rotation clientside by refreshing the player's chunks (same as the /refresh command)
PlayerManager : : updatePlayerPositionForWarp ( sock , plr - > x , plr - > y , plr - > z , plr - > instanceID ) ;
2021-03-12 19:07:26 +00:00
}
static void refreshCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
2022-07-22 00:40:33 +00:00
Player * plr = PlayerManager : : getPlayer ( sock ) ;
PlayerManager : : updatePlayerPositionForWarp ( sock , plr - > x , plr - > y , plr - > z , plr - > instanceID ) ;
2021-03-12 19:07:26 +00:00
}
static void instanceCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
// no additional arguments: report current instance ID
if ( args . size ( ) < 2 ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [INST] Current instance ID: " + std : : to_string ( plr - > instanceID ) ) ;
Chat : : sendServerMessage ( sock , " [INST] (Map " + std : : to_string ( MAPNUM ( plr - > instanceID ) ) + " , instance " + std : : to_string ( PLAYERID ( plr - > instanceID ) ) + " ) " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
// move player to specified instance
// validate instance ID
char * instanceS ;
uint64_t instance = std : : strtoll ( args [ 1 ] . c_str ( ) , & instanceS , 10 ) ;
if ( * instanceS ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [INST] Invalid instance ID: " + args [ 1 ] ) ;
2021-03-12 19:07:26 +00:00
return ;
}
if ( args . size ( ) > = 3 ) {
char * playeridS ;
uint64_t playerid = std : : strtoll ( args [ 2 ] . c_str ( ) , & playeridS , 10 ) ;
if ( playerid ! = 0 ) {
instance | = playerid < < 32ULL ;
2021-03-16 22:29:13 +00:00
Chunking : : createInstance ( instance ) ;
2021-03-12 19:07:26 +00:00
// a precaution
plr - > recallInstance = 0 ;
}
}
PlayerManager : : sendPlayerTo ( sock , plr - > x , plr - > y , plr - > z , instance ) ;
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [INST] Switched to instance with ID " + std : : to_string ( instance ) ) ;
2021-03-12 19:07:26 +00:00
}
static void npcInstanceCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
if ( args . size ( ) < 2 ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [NPCI] Instance ID must be specified " ) ;
Chat : : sendServerMessage ( sock , " [NPCI] Usage: /npci <instance ID> " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
2021-03-20 01:23:53 +00:00
BaseNPC * npc = NPCManager : : getNearestNPC ( & plr - > viewableChunks , plr - > x , plr - > y , plr - > z ) ;
2021-03-12 19:07:26 +00:00
if ( npc = = nullptr ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [NPCI] No NPCs found nearby " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
// validate instance ID
char * instanceS ;
int instance = std : : strtol ( args [ 1 ] . c_str ( ) , & instanceS , 10 ) ;
if ( * instanceS ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [NPCI] Invalid instance ID: " + args [ 1 ] ) ;
2021-03-12 19:07:26 +00:00
return ;
}
2021-06-20 18:37:37 +00:00
Chat : : sendServerMessage ( sock , " [NPCI] Moving NPC with ID " + std : : to_string ( npc - > id ) + " to instance " + std : : to_string ( instance ) ) ;
TableData : : RunningNPCMapNumbers [ npc - > id ] = instance ;
NPCManager : : updateNPCPosition ( npc - > id , npc - > x , npc - > y , npc - > z , instance , npc - > angle ) ;
2021-03-12 19:07:26 +00:00
}
static void minfoCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [MINFO] Current mission ID: " + std : : to_string ( plr - > CurrentMissionID ) ) ;
2021-03-12 19:07:26 +00:00
for ( int i = 0 ; i < ACTIVE_MISSION_COUNT ; i + + ) {
if ( plr - > tasks [ i ] ! = 0 ) {
2021-03-16 22:29:13 +00:00
TaskData & task = * Missions : : Tasks [ plr - > tasks [ i ] ] ;
2021-03-12 19:07:26 +00:00
if ( ( int ) ( task [ " m_iHMissionID " ] ) = = plr - > CurrentMissionID ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [MINFO] Current task ID: " + std : : to_string ( plr - > tasks [ i ] ) ) ;
Chat : : sendServerMessage ( sock , " [MINFO] Current task type: " + std : : to_string ( ( int ) ( task [ " m_iHTaskType " ] ) ) ) ;
Chat : : sendServerMessage ( sock , " [MINFO] Current waypoint NPC ID: " + std : : to_string ( ( int ) ( task [ " m_iSTGrantWayPoint " ] ) ) ) ;
Chat : : sendServerMessage ( sock , " [MINFO] Current terminator NPC ID: " + std : : to_string ( ( int ) ( task [ " m_iHTerminatorNPCID " ] ) ) ) ;
2021-03-12 19:07:26 +00:00
if ( ( int ) ( task [ " m_iSTGrantTimer " ] ) ! = 0 )
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [MINFO] Current task timer: " + std : : to_string ( ( int ) ( task [ " m_iSTGrantTimer " ] ) ) ) ;
2021-03-12 19:07:26 +00:00
for ( int j = 0 ; j < 3 ; j + + )
if ( ( int ) ( task [ " m_iCSUEnemyID " ] [ j ] ) ! = 0 )
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [MINFO] Current task mob # " + std : : to_string ( j + 1 ) + " : " + std : : to_string ( ( int ) ( task [ " m_iCSUEnemyID " ] [ j ] ) ) ) ;
2021-03-12 19:07:26 +00:00
return ;
}
}
}
}
static void tasksCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
for ( int i = 0 ; i < ACTIVE_MISSION_COUNT ; i + + ) {
if ( plr - > tasks [ i ] ! = 0 ) {
2021-03-16 22:29:13 +00:00
TaskData & task = * Missions : : Tasks [ plr - > tasks [ i ] ] ;
Chat : : sendServerMessage ( sock , " [TASK- " + std : : to_string ( i ) + " ] mission ID: " + std : : to_string ( ( int ) ( task [ " m_iHMissionID " ] ) ) ) ;
Chat : : sendServerMessage ( sock , " [TASK- " + std : : to_string ( i ) + " ] task ID: " + std : : to_string ( plr - > tasks [ i ] ) ) ;
2021-03-12 19:07:26 +00:00
}
}
}
static void buffCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
if ( args . size ( ) < 3 ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " /buff: no skill Id and duration time specified " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
char * tmp ;
int skillId = std : : strtol ( args [ 1 ] . c_str ( ) , & tmp , 10 ) ;
if ( * tmp )
return ;
int duration = std : : strtol ( args [ 2 ] . c_str ( ) , & tmp , 10 ) ;
if ( * tmp )
return ;
2022-07-17 06:33:57 +00:00
if ( Abilities : : SkillTable . count ( skillId ) = = 0 ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " /buff: unknown skill Id " ) ;
2022-07-17 06:33:57 +00:00
return ;
}
Eggs : : eggBuffPlayer ( sock , skillId , 0 , duration ) ;
2021-03-12 19:07:26 +00:00
}
static void eggCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
if ( args . size ( ) < 2 ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " /egg: no egg type specified " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
char * tmp ;
2021-03-22 16:53:46 +00:00
int eggType = std : : strtol ( args [ 1 ] . c_str ( ) , & tmp , 10 ) ;
2021-03-12 19:07:26 +00:00
if ( * tmp )
return ;
2021-03-17 21:28:24 +00:00
if ( Eggs : : EggTypes . find ( eggType ) = = Eggs : : EggTypes . end ( ) ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " /egg: Unknown egg type " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
2021-04-26 17:50:51 +00:00
//assert(NPCManager::nextId < INT32_MAX);
int id = NPCManager : : nextId - - ;
2021-03-12 19:07:26 +00:00
Player * plr = PlayerManager : : getPlayer ( sock ) ;
// some math to place egg nicely in front of the player
// temporarly disabled for sake of gruntwork
int addX = 0 ; //-500.0f * sin(plr->angle / 180.0f * M_PI);
int addY = 0 ; //-500.0f * cos(plr->angle / 180.0f * M_PI);
2021-10-19 22:30:53 +00:00
Egg * egg = new Egg ( plr - > instanceID , eggType , id , false ) ; // change last arg to true after gruntwork
2021-03-12 19:07:26 +00:00
NPCManager : : NPCs [ id ] = egg ;
NPCManager : : updateNPCPosition ( id , plr - > x + addX , plr - > y + addY , plr - > z , plr - > instanceID , plr - > angle ) ;
// add to template
TableData : : RunningEggs [ id ] = egg ;
}
static void notifyCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
if ( plr - > notify ) {
plr - > notify = false ;
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [ADMIN] No longer receiving join notifications " ) ;
2021-03-12 19:07:26 +00:00
} else {
plr - > notify = true ;
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [ADMIN] Receiving join notifications " ) ;
2021-03-12 19:07:26 +00:00
}
}
static void playersCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [ADMIN] Players on the server: " ) ;
2021-03-12 19:07:26 +00:00
for ( auto pair : PlayerManager : : players )
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , PlayerManager : : getPlayerName ( pair . second ) ) ;
2021-03-12 19:07:26 +00:00
}
static void summonGroupCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
if ( args . size ( ) < 4 ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " /summonGroup(W) <leadermob> <mob> <number> [distance] " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
Player * plr = PlayerManager : : getPlayer ( sock ) ;
char * rest ;
bool wCommand = ( args [ 0 ] = = " /summonGroupW " ) ;
int type = std : : strtol ( args [ 1 ] . c_str ( ) , & rest , 10 ) ;
int type2 = std : : strtol ( args [ 2 ] . c_str ( ) , & rest , 10 ) ;
int count = std : : strtol ( args [ 3 ] . c_str ( ) , & rest , 10 ) ;
int distance = 150 ;
if ( args . size ( ) > 4 )
distance = std : : strtol ( args [ 4 ] . c_str ( ) , & rest , 10 ) ;
if ( * rest ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " Invalid NPC number: " + args [ 1 ] ) ;
2021-03-12 19:07:26 +00:00
return ;
}
int limit = NPCManager : : NPCData . back ( ) [ " m_iNpcNumber " ] ;
// permission & sanity check
if ( type > limit | | type2 > limit | | count > 5 ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " Invalid parameters; double check types and count " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
Mob * leadNpc = nullptr ;
for ( int i = 0 ; i < count ; i + + ) {
int team = NPCManager : : NPCData [ type ] [ " m_iTeam " ] ;
int x = plr - > x ;
int y = plr - > y ;
int z = plr - > z ;
if ( i > 0 ) {
int angle = 360.0f / ( count - 1 ) * ( i - 1 ) ;
if ( count = = 3 )
angle = 90 + 60 * i ;
angle + = ( plr - > angle + 180 ) % 360 ;
x + = - 1.0f * sin ( angle / 180.0f * M_PI ) * distance ;
y + = - 1.0f * cos ( angle / 180.0f * M_PI ) * distance ;
z = plr - > z ;
}
BaseNPC * npc = NPCManager : : summonNPC ( x , y , z , plr - > instanceID , type , wCommand ) ;
2022-04-13 19:09:43 +00:00
if ( team = = 2 & & i > 0 & & npc - > kind = = EntityKind : : MOB ) {
2021-06-20 18:37:37 +00:00
leadNpc - > groupMember [ i - 1 ] = npc - > id ;
Mob * mob = ( Mob * ) NPCManager : : NPCs [ npc - > id ] ;
mob - > groupLeader = leadNpc - > id ;
2021-03-12 19:07:26 +00:00
mob - > offsetX = x - plr - > x ;
mob - > offsetY = y - plr - > y ;
}
2021-06-20 18:37:37 +00:00
npc - > angle = ( plr - > angle + 180 ) % 360 ;
NPCManager : : updateNPCPosition ( npc - > id , x , y , z , plr - > instanceID , npc - > angle ) ;
2021-03-12 19:07:26 +00:00
// if we're in a lair, we need to spawn the NPC in both the private instance and the template
if ( PLAYERID ( plr - > instanceID ) ! = 0 ) {
npc = NPCManager : : summonNPC ( plr - > x , plr - > y , plr - > z , plr - > instanceID , type , wCommand , true ) ;
2022-04-13 19:09:43 +00:00
if ( team = = 2 & & i > 0 & & npc - > kind = = EntityKind : : MOB ) {
2021-06-20 18:37:37 +00:00
leadNpc - > groupMember [ i - 1 ] = npc - > id ;
Mob * mob = ( Mob * ) NPCManager : : NPCs [ npc - > id ] ;
mob - > groupLeader = leadNpc - > id ;
2021-03-12 19:07:26 +00:00
mob - > offsetX = x - plr - > x ;
mob - > offsetY = y - plr - > y ;
}
2021-06-20 18:37:37 +00:00
npc - > angle = ( plr - > angle + 180 ) % 360 ;
NPCManager : : updateNPCPosition ( npc - > id , x , y , z , plr - > instanceID , npc - > angle ) ;
2021-03-12 19:07:26 +00:00
}
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " /summonGroup(W): placed mob with type: " + std : : to_string ( type ) +
2021-06-20 18:37:37 +00:00
" , id: " + std : : to_string ( npc - > id ) ) ;
2021-03-12 19:07:26 +00:00
2022-04-13 19:09:43 +00:00
if ( i = = 0 & & team = = 2 & & npc - > kind = = EntityKind : : MOB ) {
2021-03-12 19:07:26 +00:00
type = type2 ;
2021-06-20 18:37:37 +00:00
leadNpc = ( Mob * ) NPCManager : : NPCs [ npc - > id ] ;
leadNpc - > groupLeader = leadNpc - > id ;
2021-03-12 19:07:26 +00:00
}
}
if ( ! wCommand )
return ; // not writing; don't add to running mobs
if ( leadNpc = = nullptr ) {
std : : cout < < " /summonGroupW: can't find group leader! Won't be saved! \n " ;
return ;
}
2021-06-20 18:37:37 +00:00
TableData : : RunningGroups [ leadNpc - > id ] = leadNpc ; // only record the leader
2021-03-12 19:07:26 +00:00
}
static void flushCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
TableData : : flush ( ) ;
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " Wrote gruntwork to " + settings : : GRUNTWORKJSON ) ;
2021-03-12 19:07:26 +00:00
}
static void whoisCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
2021-03-20 01:23:53 +00:00
BaseNPC * npc = NPCManager : : getNearestNPC ( & plr - > viewableChunks , plr - > x , plr - > y , plr - > z ) ;
2021-03-12 19:07:26 +00:00
if ( npc = = nullptr ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [WHOIS] No NPCs found nearby " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
2021-06-20 18:37:37 +00:00
Chat : : sendServerMessage ( sock , " [WHOIS] ID: " + std : : to_string ( npc - > id ) ) ;
Chat : : sendServerMessage ( sock , " [WHOIS] Type: " + std : : to_string ( npc - > type ) ) ;
Chat : : sendServerMessage ( sock , " [WHOIS] HP: " + std : : to_string ( npc - > hp ) ) ;
2021-06-20 18:12:40 +00:00
Chat : : sendServerMessage ( sock , " [WHOIS] EntityType: " + std : : to_string ( ( int ) npc - > kind ) ) ;
2021-04-14 00:57:24 +00:00
Chat : : sendServerMessage ( sock , " [WHOIS] X: " + std : : to_string ( npc - > x ) ) ;
Chat : : sendServerMessage ( sock , " [WHOIS] Y: " + std : : to_string ( npc - > y ) ) ;
Chat : : sendServerMessage ( sock , " [WHOIS] Z: " + std : : to_string ( npc - > z ) ) ;
2021-06-20 18:37:37 +00:00
Chat : : sendServerMessage ( sock , " [WHOIS] Angle: " + std : : to_string ( npc - > angle ) ) ;
2021-03-12 19:07:26 +00:00
std : : string chunkPosition = std : : to_string ( std : : get < 0 > ( npc - > chunkPos ) ) + " , " + std : : to_string ( std : : get < 1 > ( npc - > chunkPos ) ) + " , " + std : : to_string ( std : : get < 2 > ( npc - > chunkPos ) ) ;
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [WHOIS] Chunk: { " + chunkPosition + " } " ) ;
Chat : : sendServerMessage ( sock , " [WHOIS] MapNum: " + std : : to_string ( MAPNUM ( npc - > instanceID ) ) ) ;
Chat : : sendServerMessage ( sock , " [WHOIS] Instance: " + std : : to_string ( PLAYERID ( npc - > instanceID ) ) ) ;
2021-03-12 19:07:26 +00:00
}
static void lairUnlockCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
int taskID = - 1 ;
int missionID = - 1 ;
2021-09-19 02:46:28 +00:00
int lastDist = INT_MAX ;
for ( Chunk * chnk : Chunking : : getViewableChunks ( plr - > chunkPos ) ) {
for ( const EntityRef & ref : chnk - > entities ) {
2022-04-13 19:09:43 +00:00
if ( ref . kind = = EntityKind : : PLAYER )
2021-09-19 02:46:28 +00:00
continue ;
BaseNPC * npc = ( BaseNPC * ) ref . getEntity ( ) ;
int distXY = std : : hypot ( plr - > x - npc - > x , plr - > y - npc - > y ) ;
int dist = std : : hypot ( distXY , plr - > z - npc - > z ) ;
if ( dist > = lastDist )
continue ;
for ( auto it = NPCManager : : Warps . begin ( ) ; it ! = NPCManager : : Warps . end ( ) ; it + + ) {
2021-06-20 18:37:37 +00:00
if ( it - > second . npcID = = npc - > type ) {
2021-09-19 02:46:28 +00:00
taskID = it - > second . limitTaskID ;
missionID = Missions : : Tasks [ taskID ] - > task [ " m_iHMissionID " ] ;
lastDist = dist ;
break ;
}
2021-03-12 19:07:26 +00:00
}
}
}
if ( missionID = = - 1 | | taskID = = - 1 ) {
2021-09-19 02:46:28 +00:00
Chat : : sendServerMessage ( sock , " No nearby Lair portals found. " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
INITSTRUCT ( sP_FE2CL_REP_PC_TASK_START_SUCC , taskResp ) ;
2021-03-16 22:29:13 +00:00
Missions : : startTask ( plr , taskID ) ;
2021-03-12 19:07:26 +00:00
taskResp . iTaskNum = taskID ;
taskResp . iRemainTime = 0 ;
2021-03-20 20:19:48 +00:00
sock - > sendPacket ( taskResp , P_FE2CL_REP_PC_TASK_START_SUCC ) ;
2021-03-12 19:07:26 +00:00
INITSTRUCT ( sP_FE2CL_REP_PC_SET_CURRENT_MISSION_ID , missionResp ) ;
missionResp . iCurrentMissionID = missionID ;
plr - > CurrentMissionID = missionID ;
2021-03-20 20:19:48 +00:00
sock - > sendPacket ( missionResp , P_FE2CL_REP_PC_SET_CURRENT_MISSION_ID ) ;
2021-03-12 19:07:26 +00:00
}
static void hideCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
if ( plr - > hidden ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [HIDE] You're already hidden from the map. " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
plr - > hidden = true ;
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [HIDE] Successfully hidden from the map. " ) ;
2021-03-12 19:07:26 +00:00
}
static void unhideCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
if ( ! plr - > hidden ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [HIDE] You're already visible from the map. " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
plr - > hidden = false ;
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " [HIDE] Successfully un-hidden from the map. " ) ;
2021-03-12 19:07:26 +00:00
}
static void redeemCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
if ( args . size ( ) < 2 ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " /redeem: No code specified " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
// convert string to all lowercase
const char * codeRaw = args [ 1 ] . c_str ( ) ;
if ( args [ 1 ] . size ( ) > 256 ) { // prevent overflow
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " /redeem: Code too long " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
char buf [ 256 ] ;
for ( int i = 0 ; i < args [ 1 ] . size ( ) ; i + + )
buf [ i ] = std : : tolower ( codeRaw [ i ] ) ;
std : : string code ( buf , args [ 1 ] . size ( ) ) ;
2021-03-16 22:29:13 +00:00
if ( Items : : CodeItems . find ( code ) = = Items : : CodeItems . end ( ) ) {
Chat : : sendServerMessage ( sock , " /redeem: Unknown code " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
Player * plr = PlayerManager : : getPlayer ( sock ) ;
if ( Database : : isCodeRedeemed ( plr - > iID , code ) ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " /redeem: You have already redeemed this code item " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
2021-03-16 22:29:13 +00:00
int itemCount = Items : : CodeItems [ code ] . size ( ) ;
2021-03-12 19:07:26 +00:00
std : : vector < int > slots ;
for ( int i = 0 ; i < itemCount ; i + + ) {
2021-03-16 22:29:13 +00:00
slots . push_back ( Items : : findFreeSlot ( plr ) ) ;
2021-03-12 19:07:26 +00:00
if ( slots [ i ] = = - 1 ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " /redeem: Not enough space in inventory " ) ;
2021-03-12 19:07:26 +00:00
// delete any temp items we might have set
for ( int j = 0 ; j < i ; j + + ) {
plr - > Inven [ slots [ j ] ] = { 0 , 0 , 0 , 0 } ; // empty
}
return ;
}
plr - > Inven [ slots [ i ] ] = { 999 , 999 , 999 , 0 } ; // temp item; overwritten later
}
Database : : recordCodeRedemption ( plr - > iID , code ) ;
for ( int i = 0 ; i < itemCount ; i + + ) {
2021-03-16 22:29:13 +00:00
std : : pair < int32_t , int32_t > item = Items : : CodeItems [ code ] [ i ] ;
2021-03-12 19:07:26 +00:00
INITSTRUCT ( sP_FE2CL_REP_PC_GIVE_ITEM_SUCC , resp ) ;
resp . eIL = 1 ;
resp . iSlotNum = slots [ i ] ;
resp . Item . iID = item . first ;
resp . Item . iType = item . second ;
// I think it is safe? :eyes
resp . Item . iOpt = 1 ;
// save serverside
plr - > Inven [ resp . iSlotNum ] = resp . Item ;
2021-03-20 20:19:48 +00:00
sock - > sendPacket ( resp , P_FE2CL_REP_PC_GIVE_ITEM_SUCC ) ;
2021-03-12 19:07:26 +00:00
}
std : : string msg = itemCount = = 1 ? " You have redeemed a code item " : " You have redeemed code items " ;
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , msg ) ;
2021-03-12 19:07:26 +00:00
}
static void unwarpableCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
plr - > unwarpable = true ;
}
static void warpableCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
plr - > unwarpable = false ;
}
static void registerallCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
plr - > iWarpLocationFlag = UINT32_MAX ;
plr - > aSkywayLocationFlag [ 0 ] = UINT64_MAX ;
plr - > aSkywayLocationFlag [ 1 ] = UINT64_MAX ;
// update the client
INITSTRUCT ( sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_SUCC , resp ) ;
resp . iWarpLocationFlag = plr - > iWarpLocationFlag ;
resp . aWyvernLocationFlag [ 0 ] = plr - > aSkywayLocationFlag [ 0 ] ;
resp . aWyvernLocationFlag [ 1 ] = plr - > aSkywayLocationFlag [ 1 ] ;
2021-03-20 20:19:48 +00:00
sock - > sendPacket ( resp , P_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_SUCC ) ;
2021-03-12 19:07:26 +00:00
}
static void unregisterallCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
plr - > iWarpLocationFlag = 0 ;
plr - > aSkywayLocationFlag [ 0 ] = 0 ;
plr - > aSkywayLocationFlag [ 1 ] = 0 ;
// update the client
INITSTRUCT ( sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_SUCC , resp ) ;
resp . iWarpLocationFlag = plr - > iWarpLocationFlag ;
resp . aWyvernLocationFlag [ 0 ] = plr - > aSkywayLocationFlag [ 0 ] ;
resp . aWyvernLocationFlag [ 1 ] = plr - > aSkywayLocationFlag [ 1 ] ;
2021-03-20 20:19:48 +00:00
sock - > sendPacket ( resp , P_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_SUCC ) ;
2021-03-12 19:07:26 +00:00
}
static void banCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
if ( args . size ( ) < 2 ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " Usage: /ban PlayerID [reason...] " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
char * rest ;
int playerId = std : : strtol ( args [ 1 ] . c_str ( ) , & rest , 10 ) ;
if ( * rest ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " Invalid PlayerID: " + args [ 1 ] ) ;
2021-03-12 19:07:26 +00:00
return ;
}
std : : string reason ;
if ( args . size ( ) = = 2 ) {
reason = " no reason given " ;
} else {
reason = args [ 2 ] ;
for ( int i = 3 ; i < args . size ( ) ; i + + )
reason + = " " + args [ i ] ;
}
// ban the account that player belongs to
if ( ! Database : : banPlayer ( playerId , reason ) ) {
// propagating a more descriptive error message from banPlayer() would be too much work
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " Failed to ban target player. Check server logs. " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " Banned target player. " ) ;
2021-03-12 19:07:26 +00:00
std : : cout < < " [INFO] " < < PlayerManager : : getPlayerName ( plr ) < < " banned player " < < playerId < < std : : endl ;
// if the player is online, kick them
CNSocket * otherSock = PlayerManager : : getSockFromID ( playerId ) ;
if ( otherSock = = nullptr ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " Player wasn't online. Didn't need to kick. " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
Player * otherPlr = PlayerManager : : getPlayer ( otherSock ) ;
INITSTRUCT ( sP_FE2CL_REP_PC_EXIT_SUCC , pkt ) ;
pkt . iID = otherPlr - > iID ;
pkt . iExitCode = 3 ; // "a GM has terminated your connection"
// send to target player
2021-03-20 20:19:48 +00:00
otherSock - > sendPacket ( pkt , P_FE2CL_REP_PC_EXIT_SUCC ) ;
2021-03-12 19:07:26 +00:00
// ensure that the connection has terminated
otherSock - > kill ( ) ;
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , PlayerManager : : getPlayerName ( otherPlr ) + " was online. Kicked. " ) ;
2021-03-12 19:07:26 +00:00
}
static void unbanCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
if ( args . size ( ) < 2 ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " Usage: /unban PlayerID " ) ;
2021-03-12 19:07:26 +00:00
return ;
}
char * rest ;
int playerId = std : : strtol ( args [ 1 ] . c_str ( ) , & rest , 10 ) ;
if ( * rest ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " Invalid PlayerID: " + args [ 1 ] ) ;
2021-03-12 19:07:26 +00:00
return ;
}
if ( Database : : unbanPlayer ( playerId ) ) {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " Unbanned player. " ) ;
2021-03-12 19:07:26 +00:00
std : : cout < < " [INFO] " < < PlayerManager : : getPlayerName ( plr ) < < " unbanned player " < < playerId < < std : : endl ;
} else {
2021-03-16 22:29:13 +00:00
Chat : : sendServerMessage ( sock , " Failed to unban player. Check server logs. " ) ;
2021-03-12 19:07:26 +00:00
}
}
2021-05-04 23:57:06 +00:00
static void pathCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
if ( args . size ( ) < 2 ) {
Chat : : sendServerMessage ( sock , " [PATH] Too few arguments " ) ;
2021-05-05 19:24:40 +00:00
Chat : : sendServerMessage ( sock , " [PATH] Usage: /path <start/kf/undo/here/test/cancel/end> " ) ;
2021-05-06 16:06:40 +00:00
updatePathMarkers ( sock ) ;
return ;
2021-05-04 23:57:06 +00:00
}
// /path start
if ( args [ 1 ] = = " start " ) {
// make sure the player doesn't have a follower
if ( TableData : : RunningNPCPaths . find ( plr - > iID ) ! = TableData : : RunningNPCPaths . end ( ) ) {
Chat : : sendServerMessage ( sock , " [PATH] An NPC is already following you " ) ;
2021-05-05 19:24:40 +00:00
Chat : : sendServerMessage ( sock , " [PATH] Run '/path end' first, if you're done " ) ;
2021-05-06 16:06:40 +00:00
updatePathMarkers ( sock ) ;
return ;
2021-05-04 23:57:06 +00:00
}
// find nearest NPC
BaseNPC * npc = NPCManager : : getNearestNPC ( & plr - > viewableChunks , plr - > x , plr - > y , plr - > z ) ;
if ( npc = = nullptr ) {
Chat : : sendServerMessage ( sock , " [PATH] No NPCs found nearby " ) ;
2021-05-06 16:06:40 +00:00
return ;
2021-05-04 23:57:06 +00:00
}
// add first point at NPC's current location
std : : vector < BaseNPC * > pathPoints ;
2021-10-19 22:30:53 +00:00
BaseNPC * marker = new BaseNPC ( 0 , plr - > instanceID , 1386 , NPCManager : : nextId - - ) ;
// assign coords manually, since we aren't actually adding markers to the world
marker - > x = npc - > x ;
marker - > y = npc - > y ;
marker - > z = npc - > z ;
2021-05-04 23:57:06 +00:00
pathPoints . push_back ( marker ) ;
// map from player
TableData : : RunningNPCPaths [ plr - > iID ] = std : : make_pair ( npc , pathPoints ) ;
2021-06-20 18:37:37 +00:00
Chat : : sendServerMessage ( sock , " [PATH] NPC " + std : : to_string ( npc - > id ) + " is now following you " ) ;
2021-05-06 16:06:40 +00:00
updatePathMarkers ( sock ) ;
return ;
}
2021-05-04 23:57:06 +00:00
2021-05-06 16:06:40 +00:00
// make sure the player has a follower
if ( TableData : : RunningNPCPaths . find ( plr - > iID ) = = TableData : : RunningNPCPaths . end ( ) ) {
Chat : : sendServerMessage ( sock , " [PATH] No NPC is currently following you " ) ;
Chat : : sendServerMessage ( sock , " [PATH] Run '/path start' near an NPC first " ) ;
return ;
}
2021-05-04 23:57:06 +00:00
2021-05-06 16:06:40 +00:00
std : : pair < BaseNPC * , std : : vector < BaseNPC * > > * entry = & TableData : : RunningNPCPaths [ plr - > iID ] ;
BaseNPC * npc = entry - > first ;
// /path kf
if ( args [ 1 ] = = " kf " ) {
2021-10-19 22:30:53 +00:00
BaseNPC * marker = new BaseNPC ( 0 , plr - > instanceID , 1386 , NPCManager : : nextId - - ) ;
marker - > x = npc - > x ;
marker - > y = npc - > y ;
marker - > z = npc - > z ;
2021-05-06 16:06:40 +00:00
entry - > second . push_back ( marker ) ;
Chat : : sendServerMessage ( sock , " [PATH] Added keyframe " ) ;
updatePathMarkers ( sock ) ;
return ;
}
// /path here
if ( args [ 1 ] = = " here " ) {
// bring the NPC to where the player is standing
2021-06-20 18:37:37 +00:00
Transport : : NPCQueues . erase ( npc - > id ) ; // delete transport queue
NPCManager : : updateNPCPosition ( npc - > id , plr - > x , plr - > y , plr - > z , npc - > instanceID , 0 ) ;
2021-05-06 16:06:40 +00:00
npc - > disappearFromViewOf ( sock ) ;
npc - > enterIntoViewOf ( sock ) ;
Chat : : sendServerMessage ( sock , " [PATH] Come here " ) ;
return ;
}
2021-05-04 23:57:06 +00:00
2021-05-06 16:06:40 +00:00
// /path undo
if ( args [ 1 ] = = " undo " ) {
if ( entry - > second . size ( ) = = 1 ) {
Chat : : sendServerMessage ( sock , " [PATH] Nothing to undo " ) ;
return ;
2021-05-05 19:24:40 +00:00
}
2021-05-06 16:06:40 +00:00
BaseNPC * marker = entry - > second . back ( ) ;
marker - > disappearFromViewOf ( sock ) ; // vanish
delete marker ; // destroy
2021-05-04 23:57:06 +00:00
2021-05-06 16:06:40 +00:00
entry - > second . pop_back ( ) ; // remove from the vector
Chat : : sendServerMessage ( sock , " [PATH] Undid last keyframe " ) ;
updatePathMarkers ( sock ) ;
return ;
}
2021-05-04 23:57:06 +00:00
2021-05-06 16:06:40 +00:00
// /path test
if ( args [ 1 ] = = " test " ) {
int speed = NPC_DEFAULT_SPEED ;
if ( args . size ( ) > 2 ) { // speed specified
char * buf ;
int speedArg = std : : strtol ( args [ 2 ] . c_str ( ) , & buf , 10 ) ;
if ( * buf ) {
Chat : : sendServerMessage ( sock , " [PATH] Invalid speed " + args [ 2 ] ) ;
return ;
}
speed = speedArg ;
}
// return NPC to home
2021-06-20 18:37:37 +00:00
Transport : : NPCQueues . erase ( npc - > id ) ; // delete transport queue
2021-05-06 16:06:40 +00:00
BaseNPC * home = entry - > second [ 0 ] ;
2021-06-20 18:37:37 +00:00
NPCManager : : updateNPCPosition ( npc - > id , home - > x , home - > y , home - > z , npc - > instanceID , 0 ) ;
2021-05-06 16:06:40 +00:00
npc - > disappearFromViewOf ( sock ) ;
npc - > enterIntoViewOf ( sock ) ;
// do lerping magic
entry - > second . push_back ( home ) ; // temporary end point for loop completion
std : : queue < Vec3 > keyframes ;
auto _point = entry - > second . begin ( ) ;
Vec3 from = { ( * _point ) - > x , ( * _point ) - > y , ( * _point ) - > z } ; // point A coords
for ( _point + + ; _point ! = entry - > second . end ( ) ; _point + + ) { // loop through all point Bs
Vec3 to = { ( * _point ) - > x , ( * _point ) - > y , ( * _point ) - > z } ; // point B coords
// add point A to the queue
keyframes . push ( from ) ;
Transport : : lerp ( & keyframes , from , to , speed ) ; // lerp from A to B
from = to ; // update point A
2021-05-04 23:57:06 +00:00
}
2021-06-20 18:37:37 +00:00
Transport : : NPCQueues [ npc - > id ] = keyframes ;
2021-05-06 16:06:40 +00:00
entry - > second . pop_back ( ) ; // remove temp end point
2021-05-04 23:57:06 +00:00
2021-05-06 16:06:40 +00:00
Chat : : sendServerMessage ( sock , " [PATH] Testing NPC path " ) ;
return ;
}
2021-05-04 23:57:06 +00:00
2021-05-06 16:06:40 +00:00
// /path cancel
if ( args [ 1 ] = = " cancel " ) {
// return NPC to home
2021-06-20 18:37:37 +00:00
Transport : : NPCQueues . erase ( npc - > id ) ; // delete transport queue
2021-05-06 16:06:40 +00:00
BaseNPC * home = entry - > second [ 0 ] ;
2021-06-20 18:37:37 +00:00
NPCManager : : updateNPCPosition ( npc - > id , home - > x , home - > y , home - > z , npc - > instanceID , 0 ) ;
2021-05-06 16:06:40 +00:00
npc - > disappearFromViewOf ( sock ) ;
npc - > enterIntoViewOf ( sock ) ;
// deallocate markers
for ( BaseNPC * marker : entry - > second ) {
marker - > disappearFromViewOf ( sock ) ; // poof
delete marker ; // destroy
}
// unmap
TableData : : RunningNPCPaths . erase ( plr - > iID ) ;
2021-06-20 18:37:37 +00:00
Chat : : sendServerMessage ( sock , " [PATH] NPC " + std : : to_string ( npc - > id ) + " is no longer following you " ) ;
2021-05-06 16:06:40 +00:00
return ;
}
2021-05-04 23:57:06 +00:00
2021-05-06 16:06:40 +00:00
// /path end
if ( args [ 1 ] = = " end " ) {
if ( args . size ( ) < 2 ) {
Chat : : sendServerMessage ( sock , " [PATH] Too few arguments " ) ;
Chat : : sendServerMessage ( sock , " [PATH] Usage: /path end [speed] [a/r] " ) ;
return ;
2021-05-04 23:57:06 +00:00
}
2021-05-06 16:06:40 +00:00
int speed = NPC_DEFAULT_SPEED ;
bool relative = false ;
if ( args . size ( ) > 2 ) { // speed specified
char * buf ;
int speedArg = std : : strtol ( args [ 2 ] . c_str ( ) , & buf , 10 ) ;
if ( * buf ) {
Chat : : sendServerMessage ( sock , " [PATH] Invalid speed " + args [ 2 ] ) ;
return ;
2021-05-04 23:57:06 +00:00
}
2021-05-06 16:06:40 +00:00
speed = speedArg ;
2021-05-04 23:57:06 +00:00
}
2021-05-06 16:06:40 +00:00
if ( args . size ( ) > 3 & & args [ 3 ] = = " r " ) { // relativity specified
relative = true ;
}
2021-05-09 12:37:36 +00:00
// return NPC to home and set path to repeat
2021-06-20 18:37:37 +00:00
Transport : : NPCQueues . erase ( npc - > id ) ; // delete transport queue
2021-05-06 16:06:40 +00:00
BaseNPC * home = entry - > second [ 0 ] ;
2021-06-20 18:37:37 +00:00
NPCManager : : updateNPCPosition ( npc - > id , home - > x , home - > y , home - > z , npc - > instanceID , 0 ) ;
2021-05-06 16:06:40 +00:00
npc - > disappearFromViewOf ( sock ) ;
npc - > enterIntoViewOf ( sock ) ;
2021-05-09 12:37:36 +00:00
npc - > loopingPath = true ;
2021-05-06 16:06:40 +00:00
// do lerping magic
entry - > second . push_back ( home ) ; // temporary end point for loop completion
std : : queue < Vec3 > keyframes ;
auto _point = entry - > second . begin ( ) ;
Vec3 from = { ( * _point ) - > x , ( * _point ) - > y , ( * _point ) - > z } ; // point A coords
for ( _point + + ; _point ! = entry - > second . end ( ) ; _point + + ) { // loop through all point Bs
Vec3 to = { ( * _point ) - > x , ( * _point ) - > y , ( * _point ) - > z } ; // point B coords
// add point A to the queue
keyframes . push ( from ) ;
Transport : : lerp ( & keyframes , from , to , speed ) ; // lerp from A to B
from = to ; // update point A
}
2021-06-20 18:37:37 +00:00
Transport : : NPCQueues [ npc - > id ] = keyframes ;
2021-05-06 16:06:40 +00:00
entry - > second . pop_back ( ) ; // remove temp end point
// save to gruntwork
std : : vector < Vec3 > finalPoints ;
for ( BaseNPC * marker : entry - > second ) {
Vec3 coords = { marker - > x , marker - > y , marker - > z } ;
if ( relative ) {
coords . x - = home - > x ;
coords . y - = home - > y ;
coords . z - = home - > z ;
2021-05-04 23:57:06 +00:00
}
2021-05-06 16:06:40 +00:00
finalPoints . push_back ( coords ) ;
}
2021-05-04 23:57:06 +00:00
2021-05-06 16:06:40 +00:00
// end point to complete the circuit
if ( relative )
finalPoints . push_back ( { 0 , 0 , 0 } ) ;
else
finalPoints . push_back ( { home - > x , home - > y , home - > z } ) ;
2021-05-04 23:57:06 +00:00
2021-05-06 16:06:40 +00:00
NPCPath finishedPath ;
finishedPath . escortTaskID = - 1 ;
finishedPath . isRelative = relative ;
2021-05-09 12:37:36 +00:00
finishedPath . isLoop = true ;
2021-05-06 16:06:40 +00:00
finishedPath . speed = speed ;
finishedPath . points = finalPoints ;
2021-06-20 18:37:37 +00:00
finishedPath . targetIDs . push_back ( npc - > id ) ;
2021-05-04 23:57:06 +00:00
2021-05-06 16:06:40 +00:00
TableData : : FinishedNPCPaths . push_back ( finishedPath ) ;
2021-05-05 00:43:44 +00:00
2021-05-06 16:06:40 +00:00
// deallocate markers
for ( BaseNPC * marker : entry - > second ) {
marker - > disappearFromViewOf ( sock ) ; // poof
delete marker ; // destroy
2021-05-04 23:57:06 +00:00
}
2021-05-06 16:06:40 +00:00
// unmap
TableData : : RunningNPCPaths . erase ( plr - > iID ) ;
2021-05-04 23:57:06 +00:00
2021-06-20 18:37:37 +00:00
Chat : : sendServerMessage ( sock , " [PATH] NPC " + std : : to_string ( npc - > id ) + " is no longer following you " ) ;
2021-05-04 23:57:06 +00:00
2021-05-06 16:06:40 +00:00
TableData : : flush ( ) ;
Chat : : sendServerMessage ( sock , " [PATH] Path saved to gruntwork " ) ;
return ;
2021-05-04 23:57:06 +00:00
}
2021-05-06 16:06:40 +00:00
// /path ???
Chat : : sendServerMessage ( sock , " [PATH] Unknown argument ' " + args [ 1 ] + " ' " ) ;
2021-05-04 23:57:06 +00:00
}
2021-03-12 19:07:26 +00:00
static void registerCommand ( std : : string cmd , int requiredLevel , CommandHandler handlr , std : : string help ) {
commands [ cmd ] = ChatCommand ( requiredLevel , handlr , help ) ;
}
void CustomCommands : : init ( ) {
registerCommand ( " help " , 100 , helpCommand , " list all unlocked server-side commands " ) ;
registerCommand ( " access " , 100 , accessCommand , " print your access level " ) ;
registerCommand ( " instance " , 30 , instanceCommand , " print or change your current instance " ) ;
registerCommand ( " mss " , 30 , mssCommand , " edit Monkey Skyway routes " ) ;
registerCommand ( " npcr " , 30 , npcRotateCommand , " rotate NPCs " ) ;
registerCommand ( " npci " , 30 , npcInstanceCommand , " move NPCs across instances " ) ;
registerCommand ( " summonW " , 30 , summonWCommand , " permanently summon NPCs " ) ;
registerCommand ( " unsummonW " , 30 , unsummonWCommand , " delete permanently summoned NPCs " ) ;
registerCommand ( " toggleai " , 30 , toggleAiCommand , " enable/disable mob AI " ) ;
registerCommand ( " flush " , 30 , flushCommand , " save gruntwork to file " ) ;
registerCommand ( " level " , 50 , levelCommand , " change your character's level " ) ;
registerCommand ( " levelx " , 50 , levelCommand , " change your character's level " ) ; // for Academy
registerCommand ( " population " , 100 , populationCommand , " check how many players are online " ) ;
registerCommand ( " refresh " , 100 , refreshCommand , " teleport yourself to your current location " ) ;
registerCommand ( " minfo " , 30 , minfoCommand , " show details of the current mission and task. " ) ;
registerCommand ( " buff " , 50 , buffCommand , " give yourself a buff effect " ) ;
registerCommand ( " egg " , 30 , eggCommand , " summon a coco egg " ) ;
registerCommand ( " tasks " , 30 , tasksCommand , " list all active missions and their respective task ids. " ) ;
registerCommand ( " notify " , 30 , notifyCommand , " receive a message whenever a player joins the server " ) ;
registerCommand ( " players " , 30 , playersCommand , " print all players on the server " ) ;
registerCommand ( " summonGroup " , 30 , summonGroupCommand , " summon group NPCs " ) ;
registerCommand ( " summonGroupW " , 30 , summonGroupCommand , " permanently summon group NPCs " ) ;
registerCommand ( " ban " , 30 , banCommand , " ban the account the given PlayerID belongs to " ) ;
registerCommand ( " unban " , 30 , unbanCommand , " unban the account the given PlayerID belongs to " ) ;
registerCommand ( " whois " , 50 , whoisCommand , " describe nearest NPC " ) ;
registerCommand ( " lair " , 50 , lairUnlockCommand , " get the required mission for the nearest fusion lair " ) ;
registerCommand ( " hide " , 100 , hideCommand , " hide yourself from the global player map " ) ;
registerCommand ( " unhide " , 100 , unhideCommand , " un-hide yourself from the global player map " ) ;
registerCommand ( " unwarpable " , 100 , unwarpableCommand , " prevent buddies from warping to you " ) ;
registerCommand ( " warpable " , 100 , warpableCommand , " re-allow buddies to warp to you " ) ;
registerCommand ( " registerall " , 50 , registerallCommand , " register all SCAMPER and MSS destinations " ) ;
registerCommand ( " unregisterall " , 50 , unregisterallCommand , " clear all SCAMPER and MSS destinations " ) ;
registerCommand ( " redeem " , 100 , redeemCommand , " redeem a code item " ) ;
2021-05-04 23:57:06 +00:00
registerCommand ( " path " , 30 , pathCommand , " edit NPC paths " ) ;
2021-03-12 19:07:26 +00:00
}