#include <group_handler.h>
#include <function.h>
#include <player.h>
// Define IN_TESTING below if you want to restrict the usage of
// these commands only to creators, playtesters and test
// characters.
#undef IN_TESTING
// Define IN_STRICT_TESTING in addition to IN_TESTING if
// you wish only creators to be able to use the commands.
#undef IN_STRICT_TESTING
mixed *_patterns;
mapping _sub_commands;
class sub_command {
// The verb is the key of the _sub_commands mapping.
string file_name; // Where the sub-command file is located
mixed *data; // This contains alternating elements of
// (string)command patterns and (function)
// function pointers to the command.
}
#if defined( IN_TESTING ) || defined( IN_STRICT_TESTING )
int allowed_to_use( object user );
#endif
void rehash_group_sub_commands();
void make_patterns_array();
mixed *query_patterns();
int group_command_control( string verb, mixed *indirect_obs,
string dir_match, string indir_match, mixed *args, string pattern );
int do_help( string on_what );
void create() {
rehash_group_sub_commands();
make_patterns_array();
} /* create() */
/* This function reads the GROUP_SUB_CMDS_DIR for files matching
* GROUP_SUB_CMDS_FILE_WILDCARD, loads them one by one, and extracts
* information from them if they respond to the group sub command
* signature function.
*/
void rehash_group_sub_commands() {
mixed *files; // An array of the files in the dir.
object cmd_object; // The active object being used.
string dir, verb, file, pattern; // Stuff to be extracted and shuffled
// around.
// flush the sub commands
_sub_commands = ([ ]);
dir = GROUP_SUB_CMDS_DIR;
dir += GROUP_SUB_CMDS_FILE_WILDCARD;
// read the group sub-command directory for command files
files = get_dir( dir );
// if there were no files found, stop
if( !sizeof( files ) ) {
return;
}
// iterate through the files and add data to the mapping
foreach( file in files ) {
// remove the file extension
sscanf( file, "%s.%*s", file );
// load the file or find it if it's already loaded
cmd_object = load_object( ( GROUP_SUB_CMDS_DIR + file ) );
if( !cmd_object ) {
// can't be loaded. Probably broken, so let's skip it.
continue;
}
// if the file isn't a group sub-command file
if( !cmd_object->query_group_sub_command_amount() ) {
continue;
}
// find all verbs the command has declared and
// iterate through them
foreach( verb in cmd_object->query_group_sub_command_verbs() ) {
// if this is an entirely new verb and not just an additional
// pattern
if( !_sub_commands[ verb ] ) {
// assign a new entry for it
_sub_commands += ([ verb : new( class sub_command ) ]);
}
_sub_commands[ verb ]->data = ( mixed * )({ });
_sub_commands[ verb ]->file_name = ( string )file_name( cmd_object );
// fetch all the patterns for that verb
foreach( pattern in
cmd_object->query_group_sub_command_patterns( verb ) ) {
// add the pattern and the corresponding command function to
// the entry
_sub_commands[ verb ]->data +=
({
pattern,
cmd_object->query_group_sub_command_function( verb, pattern )
});
} // foreach pattern
} // foreach verb
} // foreach file
} /* rehash_group_sub_commands() */
int group_command_control( string verb, mixed *indirect_obs,
string dir_match, string indir_match, mixed *args, string pattern ) {
int count, size;
string cmd_pattern, mangled_pattern, group;
function cmd_fun;
object cmd_object;
class sub_command info;
#if defined( IN_TESTING ) || defined( IN_STRICT_TESTING )
if( !allowed_to_use( this_player() ) ) {
return 0;
}
#endif
info = _sub_commands[ verb ];
if( !info ) {
printf( "ERROR: Command information for \"" + verb + "\""
" not found.\n" );
return 0;
}
// find the right version of the command (there can be different
// patterns for the same verb)
if( pattern != verb ) {
// means the command takes arguments -- let's get rid
// of the verb name from the front
sscanf( pattern, verb + " %s", mangled_pattern );
}
else {
// means the command takes no arguments
mangled_pattern = "";
}
size = sizeof( info->data );
for( count = 0; count < size; count += 2 ) {
if( mangled_pattern == info->data[ count ] ) {
// we now have a match on the verb and the pattern wanted.
cmd_pattern = info->data[ count ];
cmd_fun = info->data[ count + 1 ];
break;
}
}
if( !cmd_pattern || !cmd_fun ) {
printf( "ERROR: Correct version of \"" + verb + "\" not found.\n" );
tell_creator( this_player(), "DEBUG: Verb: %s, pattern: %s\n",
verb, pattern );
return 0;
}
if( !cmd_object = load_object( info->file_name ) ) {
// object's probably broken
printf( "ERROR: Cannot load command \"" + verb + "\"!\n" );
tell_creator( this_player(), "DEBUG: File name: %s\n", info->file_name );
return 0;
}
// if the command function's pointer has been destructed.. This happens
// when the command object is unloaded after a period of unuse.
if( functionp( cmd_fun ) & FP_OWNER_DESTED ) {
// try to re-acquire the pointer and update the entry as well
info->data[ count + 1 ] = cmd_fun =
cmd_object->query_group_sub_command_function( verb, cmd_pattern );
}
// if the pointer couldn't be re-acquired
if( !cmd_fun || !functionp( cmd_fun ) ) {
printf( "ERROR: Could not find command function for verb " +
"\"" + verb + "\".\n" );
return 0;
}
group = this_player()->query_group();
// Check if the command requires this_player() to be a member of
// a group.
if( cmd_object->query_membership_required( verb, cmd_pattern ) > 0 ) {
if( !group ) {
return notify_fail( "You must be a member of a group in order "
"to use this command.\n" );
}
}
// Check if the command requires the command user to be the leader
// of the group.
if( cmd_object->query_leadership_required( verb, cmd_pattern ) > 0 ) {
if( GROUP->leader_of( group ) != this_player() ) {
return notify_fail( "Only the leader of a group can use this "
"command.\n" );
}
}
// Call the command function on it and return what it returned
// The parameters for it are identical to a normal add_command(),
// with the exception of an additional one which designates the
// command user's current group, if any.
return evaluate( cmd_fun, indirect_obs, dir_match, indir_match,
args, pattern, group );
} /* group_command_control() */
int do_help( string on_what ) {
int top_left;
string help, message;
object command;
on_what = lower_case( on_what );
#if defined( IN_TESTING ) || defined( IN_STRICT_TESTING )
if( !allowed_to_use( this_player() ) ) {
return 0;
}
#endif
if( on_what == "help" ) {
// "group help help"
return notify_fail( "To get help on a command, use \"group help <sub-"
"command>\". That is, if you wanted to get help on the command "
"\"group create <name>\", you would type \"group help create\".\n" );
}
// such a sub-command doesn't exist
if( !_sub_commands[ on_what ] ) {
return notify_fail( "There is no sub-command called \"" + on_what +
"\".\n" );
}
command = load_object( _sub_commands[ on_what ]->file_name );
if( !command ) {
tell_creator( this_player(), "Alleged file name: %s\n",
_sub_commands[ on_what ]->file_name );
return notify_fail( "ERROR: Cannot find or load sub-command "
"\"" + on_what + "\".\n" );
}
help = command->query_help_string_for( on_what );
if( !help ) {
return notify_fail( "No help found for sub-command \"" + on_what +
"\".\n" );
}
top_left = this_player()->query_cols();
message = sprintf(
"\n%' '|*s\n"
"%' '-=*s\n",
top_left, "Help on sub-command \"" + on_what + "\":\n",
top_left, help );
this_player()->more_string( message );
return 1;
} /* do_help() */
/* This function parses all defined group subcommand classes into
* a form that the parser understands. I.e. one that can be
* returned with query_patterns()
*/
void make_patterns_array() {
int count, size;
string verb, pattern;
class sub_command command;
// flush the patterns
_patterns = ({ });
// Establish a separate command for the help thingie.
_patterns += ({
"help <word'sub-command'>", (: do_help( $4[ 0 ] ) :) });
// iterate through the commands
foreach( verb, command in _sub_commands ) {
// get the verb and the pattern from the command
// and stick them into a pattern entry to the
// group command controller.
size = sizeof( command->data );
// multiple patterns for one verb? Certainly possible.
for( count = 0; count < size; count += 2 ) {
// these checks are to ensure things' not breaking
// if a verb doesn't take any arguments
if( sizeof( command->data[ count ] ) ) {
pattern = verb + " " + command->data[ count ];
}
else {
pattern = verb;
}
_patterns += ({
pattern, (: group_command_control( $( verb ), $1, $2, $3, $4,
$5 ) :) });
}
}
} /* make_patterns_array() */
mixed *query_patterns() {
return _patterns;
} /* query_patterns() */
#if defined( IN_TESTING ) || defined( IN_STRICT_TESTING )
int allowed_to_use( object user ) {
#endif
#ifdef IN_STICT_TESTING
if( !user->query_creator() ) {
tell_object( user, "You are not allowed to use this feature at this "
"time.\n" );
return 0;
}
#endif
#ifdef IN_TESTING
if( !PLAYTESTER_HAND->query_tester( user ) ) {
tell_object( user, "You are not allowed to use this feature at this "
"time.\n" );
return 0;
}
#endif
#if defined( IN_TESTING ) || defined( IN_STRICT_TESTING )
return 1;
} /* allowed_to_use() */
#endif
// Debug.
mapping dump_info() { return _sub_commands; }
mixed *dump_patterns() { return _patterns; }