/**************************************************************************
* Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. *
* *
* Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* *
* In order to use any part of this Merc Diku Mud, you must comply with *
* both the original Diku license in 'license.doc' as well the Merc *
* license in 'license.txt'. In particular, you may not remove either of *
* these copyright notices. *
* *
* Much time and thought has gone into this software and you are *
* benefiting. We hope that you share your changes too. What goes *
* around, comes around. *
***************************************************************************
* ROM 2.4 is copyright 1993-1998 Russ Taylor *
* ROM has been brought to you by the ROM consortium *
* Russ Taylor (rtaylor@hypercube.org) *
* Gabrielle Taylor (gtaylor@hypercube.org) *
* Brian Moore (zump@rom.org) *
* By using this code, you have agreed to follow the terms of the *
* ROM license, in the file Rom24/doc/rom.license *
***************************************************************************
* 1stMUD ROM Derivative (c) 2001-2003 by Ryan Jennings *
* http://1stmud.dlmud.com/ <r-jenn@shaw.ca> *
***************************************************************************/
#include "merc.h"
#include "interp.h"
#include "signals.h"
PROTOTYPE(bool check_social, (CHAR_DATA *, char *, const char *));
PROTOTYPE(bool check_disabled, (CHAR_DATA *, CMD_DATA *));
#define END_MARKER "END" /* for load_disabled() and save_disabled() */
/*
* Log-all switch.
*/
CH_CMD(do_null)
{
chprintln(ch, "This is not a command, notify the immortals.");
return;
}
bool cmd_level_ok(CHAR_DATA * ch, CMD_DATA * cmd)
{
return (get_trust(ch) >= cmd->level);
}
/*
* The main entry point for executing commands.
* Can be recursively called from 'at', 'order', 'force'.
*/
void interpret(CHAR_DATA * ch, const char *argument)
{
char command[MAX_INPUT_LENGTH];
char logline[MAX_INPUT_LENGTH];
CMD_DATA *cmd;
/*
* Strip leading spaces.
*/
while (isspace(*argument))
argument++;
if (IS_NULLSTR(argument))
return;
/*
* No hiding.
*/
REMOVE_BIT(ch->affected_by, AFF_HIDE);
/*
* Implement freeze command.
*/
if (!IS_NPC(ch) && IS_SET(ch->act, PLR_FREEZE))
{
chprintln(ch, "You're totally frozen!");
return;
}
strcpy(logline, argument);
crash_info.desc = ch->desc;
strcpy(crash_info.shrt_cmd, logline);
sprintf(crash_info.long_cmd, "[%5ld] %s in [%5ld] %s: %s",
IS_NPC(ch) ? ch->pIndexData->vnum : 0, IS_NPC(ch) ?
ch->short_descr : ch->name, ch->in_room ?
ch->in_room->vnum : 0, ch->in_room ? ch->in_room->name :
"(not in a room)", logline);
crash_info.cmd_status = 2;
/*
* Grab the command word.
* Special parsing so ' can be a command,
* also no spaces needed after punctuation.
*/
if (!isalpha(argument[0]) && !isdigit(argument[0]))
{
command[0] = argument[0];
command[1] = '\0';
argument++;
while (isspace(*argument))
argument++;
}
else
{
argument = one_argument(argument, command);
}
/*
* Look for command in command table.
*/
for (cmd = command_hash[LOWER(command[0]) % 126]; cmd; cmd = cmd->next_hash)
{
if (!str_prefix(command, cmd->name) && cmd_level_ok(ch, cmd))
break;
}
/*
* Log and snoop.
*/
if (cmd)
{
if (cmd->log == LOG_NEVER)
strcpy(logline, "");
if ((!IS_NPC(ch) && IS_SET(ch->act, PLR_LOG))
|| IS_SET(mud_info.mud_flags, MUD_LOGALL) || cmd->log == LOG_ALWAYS)
{
sprintf(log_buf, "Log %s: %s", ch->name, logline);
wiznet(log_buf, ch, NULL, WIZ_SECURE, 0, get_trust(ch));
log_string(log_buf);
}
}
if (ch->desc != NULL && ch->desc->snoop_by != NULL)
{
d_print(ch->desc->snoop_by, "% ", 2);
d_println(ch->desc->snoop_by, logline, 0);
}
if (!cmd)
{
/*
* Look for command in socials table.
*/
if (!check_social(ch, command, argument))
chprintln(ch, "Huh?");
return;
}
else if (check_disabled(ch, cmd))
{
chprintln(ch, "This command has been temporarily disabled.");
return;
}
/*
* Character not in position for command?
*/
if (ch->position < cmd->position)
{
switch (ch->position)
{
case POS_DEAD:
chprintln(ch, "Lie still; you are DEAD.");
break;
case POS_MORTAL:
case POS_INCAP:
chprintln(ch, "You are hurt far too bad for that.");
break;
case POS_STUNNED:
chprintln(ch, "You are too stunned to do that.");
break;
case POS_SLEEPING:
chprintln(ch, "In your dreams, or what?");
break;
case POS_RESTING:
chprintln(ch, "Nah... You feel too relaxed...");
break;
case POS_SITTING:
chprintln(ch, "Better stand up first.");
break;
case POS_FIGHTING:
chprintln(ch, "No way! You are still fighting!");
break;
default:
break;
}
return;
}
crash_info.cmd_status = 1;
/*
* Dispatch the command.
*/
(*cmd->do_fun) (ch, argument);
crash_info.cmd_status = 3;
strcat(crash_info.shrt_cmd, " (Finished)");
strcat(crash_info.long_cmd, " (Finished)");
crash_info.cmd_status = 0;
tail_chain();
return;
}
/* function to keep argument safe in all commands -- no static strings */
void do_function(CHAR_DATA * ch, DO_FUN * do_fun, const char *argument)
{
const char *command_string;
/* copy the string */
command_string = str_dup(argument);
/* dispatch the command */
(*do_fun) (ch, command_string);
/* free the string */
free_string(command_string);
}
SOCIAL_DATA *find_social(const char *command)
{
SOCIAL_DATA *social;
int hash;
if (LOWER(command[0]) < 'a' || LOWER(command[0]) > 'z')
hash = 0;
else
hash = (LOWER(command[0]) - 'a') + 1;
for (social = social_hash[hash]; social; social = social->next_hash)
{
if (!str_prefix(command, social->name))
return social;
}
return NULL;
}
bool check_social(CHAR_DATA * ch, char *command, const char *argument)
{
char arg[MAX_INPUT_LENGTH];
CHAR_DATA *victim;
SOCIAL_DATA *cmd;
if ((cmd = find_social(command)) == NULL)
return FALSE;
if (!IS_NPC(ch) && IS_SET(ch->comm, COMM_NOEMOTE))
{
chprintln(ch, "You are anti-social!");
return TRUE;
}
switch (ch->position)
{
case POS_DEAD:
chprintln(ch, "Lie still; you are DEAD.");
return TRUE;
case POS_INCAP:
case POS_MORTAL:
chprintln(ch, "You are hurt far too bad for that.");
return TRUE;
case POS_STUNNED:
chprintln(ch, "You are too stunned to do that.");
return TRUE;
case POS_SLEEPING:
/*
* I just know this is the path to a 12" 'if' statement. :(
* But two players asked for it already! -- Furey
*/
if (!str_cmp(cmd->name, "snore"))
break;
chprintln(ch, "In your dreams, or what?");
return TRUE;
default:
break;
}
one_argument(argument, arg);
victim = NULL;
if (IS_NULLSTR(arg))
{
act(cmd->others_no_arg, ch, NULL, victim, TO_ROOM);
act(cmd->char_no_arg, ch, NULL, victim, TO_CHAR);
}
else if ((victim = get_char_room(ch, NULL, arg)) == NULL)
{
chprintln(ch, "They aren't here.");
}
else if (victim == ch)
{
act(cmd->others_auto, ch, NULL, victim, TO_ROOM);
act(cmd->char_auto, ch, NULL, victim, TO_CHAR);
}
else
{
if (is_ignoring(victim, ch->name, IGNORE_SOCIALS))
{
act("$N is ignoring socials from you.", ch, NULL, victim, TO_CHAR);
return TRUE;
}
act(cmd->others_found, ch, NULL, victim, TO_NOTVICT);
act(cmd->char_found, ch, NULL, victim, TO_CHAR);
act(cmd->vict_found, ch, NULL, victim, TO_VICT);
if (!IS_NPC(ch) && IS_NPC(victim) &&
!IS_AFFECTED(victim, AFF_CHARM) && IS_AWAKE(victim) &&
victim->desc == NULL)
{
switch (number_bits(4))
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
act(cmd->others_found, victim, NULL, ch, TO_NOTVICT);
act(cmd->char_found, victim, NULL, ch, TO_CHAR);
act(cmd->vict_found, victim, NULL, ch, TO_VICT);
break;
case 9:
case 10:
case 11:
case 12:
act("$n slaps $N.", victim, NULL, ch, TO_NOTVICT);
act("You slap $N.", victim, NULL, ch, TO_CHAR);
act("$n slaps you.", victim, NULL, ch, TO_VICT);
break;
}
}
}
return TRUE;
}
/*
* Return true if an argument is completely numeric.
*/
bool is_number(const char *arg)
{
if (*arg == '\0')
return FALSE;
if (*arg == '+' || *arg == '-')
arg++;
for (; *arg != '\0'; arg++)
{
if (!isdigit(*arg))
return FALSE;
}
return TRUE;
}
static unsigned int x_argument(const char *argument,
char arg[MAX_INPUT_LENGTH], char c)
{
char *p;
char *q;
int number;
p = strchr(argument, c);
if (p == NULL)
{
strcpy(arg, argument);
return 1;
}
number = strtoul(argument, &q, 0);
if (q != p)
number = 0;
strncpy(arg, p + 1, MAX_INPUT_LENGTH);
return number;
}
/*
* Given a string like 14.foo, return 14 and 'foo'
*/
unsigned int number_argument(const char *argument, char *arg)
{
return x_argument(argument, arg, '.');
}
/*
* Given a string like 14*foo, return 14 and 'foo'
*/
unsigned int mult_argument(const char *argument, char *arg)
{
return x_argument(argument, arg, '*');
}
/*
* Pick off one argument from a string and return the rest.
* Understands quotes.
*/
const char *one_argument(const char *argument, char *arg_first)
{
char cEnd;
while (isspace(*argument))
argument++;
cEnd = ' ';
if (*argument == '\'' || *argument == '"')
cEnd = *argument++;
while (*argument != '\0')
{
if (*argument == cEnd)
{
argument++;
break;
}
*arg_first = LOWER(*argument);
arg_first++;
argument++;
}
*arg_first = '\0';
while (isspace(*argument))
argument++;
return argument;
}
/*
* Contributed by Alander.
*/
CH_CMD(do_commands)
{
CMD_DATA *cmd;
int col;
col = 0;
for (cmd = cmd_first_sorted; cmd; cmd = cmd->next_sort)
{
if (cmd->level < LEVEL_HERO && cmd_level_ok(ch, cmd) && cmd->show)
{
chprintf(ch, "%-12s", cmd->name);
if (++col % 6 == 0)
chprintln(ch, "");
}
}
if (col % 6 != 0)
chprintln(ch, "");
return;
}
CH_CMD(do_wizhelp)
{
CMD_DATA *cmd;
int col;
col = 0;
for (cmd = cmd_first_sorted; cmd; cmd = cmd->next_sort)
{
if (cmd->level >= LEVEL_HERO && cmd_level_ok(ch, cmd) && cmd->show)
{
chprintf(ch, "%-12s", cmd->name);
if (++col % 6 == 0)
chprintln(ch, "");
}
}
if (col % 6 != 0)
chprintln(ch, "");
return;
}
/* Syntax is:
disable - shows disabled commands
disable <command> - toggles disable status of command
*/
CH_CMD(do_disable)
{
CMD_DATA *i;
DISABLED_DATA *p;
char arg[MIL];
if (IS_NPC(ch))
{
chprintln(ch, "RETURN first.");
return;
}
argument = one_argument(argument, arg);
if (IS_NULLSTR(arg)) /* Nothing specified. Show disabled commands. */
{
if (!disabled_first) /* Any disabled at all ? */
{
chprintln(ch, "There are no commands disabled.");
return;
}
chprintln(ch, "Disabled commands:\n\r"
"Command Level Disabled by Disabled for");
for (p = disabled_first; p; p = p->next)
{
chprintlnf(ch, "%-12s %5d %-12s %s", p->command->name,
p->level, p->disabled_by, p->disabled_for);
}
return;
}
/* command given */
/* First check if it is one of the disabled commands */
for (p = disabled_first; p; p = p->next)
if (!str_cmp(arg, p->command->name))
break;
if (p) /* this command is disabled */
{
/* Optional: The level of the imm to enable the command must equal or exceed level
of the one that disabled it */
if (get_trust(ch) < p->level)
{
chprintln(ch, "This command was disabled by a higher power.");
return;
}
/* Remove */
UNLINK(p, disabled_first, disabled_last, next, prev);
free_string(p->disabled_by); /* free name of disabler */
free_string(p->disabled_for);
free_mem(p); /* free node */
save_disabled(); /* save to disk */
chprintln(ch, "Command enabled.");
}
else /* not a disabled command, check if that command exists */
{
/* Search for the command */
for (i = command_hash[LOWER(argument[0]) % 126]; i; i = i->next_hash)
if (!str_cmp(i->name, argument))
break;
/* Found? */
if (!i)
{
chprintln(ch, "No such command.");
return;
}
/* IQ test */
if (i->do_fun == do_disable)
{
chprintln(ch, "You cannot disable the disable command.");
return;
}
/* Can the imm use this command at all ? */
if (i->level > get_trust(ch))
{
chprintln(ch,
"You don't have access to that command; you cannot disable it.");
return;
}
/* Disable the command */
alloc_mem(p, DISABLED_DATA, 1);
p->command = i;
p->disabled_by = str_dup(ch->name); /* save name of disabler */
p->disabled_for = str_dup(argument);
p->level = get_trust(ch); /* save trust */
LINK(p, disabled_first, disabled_last, next, prev);
chprintln(ch, "Command disabled.");
save_disabled(); /* save to disk */
}
}
/* Check if that command is disabled
Note that we check for equivalence of the do_fun pointers; this means
that disabling 'chat' will also disable the '.' command
*/
bool check_disabled(CHAR_DATA * ch, CMD_DATA * command)
{
DISABLED_DATA *p;
for (p = disabled_first; p; p = p->next)
if (p->command->do_fun == command->do_fun)
break;
if (!p)
return FALSE;
if (!ch || IS_NULLSTR(p->disabled_for))
return TRUE;
return is_exact_name(ch->name, p->disabled_for);
}
/* Load disabled commands */
void load_disabled()
{
READ_DATA *fp;
DISABLED_DATA *p;
char *name;
CMD_DATA *i;
disabled_first = NULL;
fp = open_read(DISABLED_FILE);
if (!fp) /* No disabled file.. no disabled commands : */
return;
name = read_word(fp);
while (str_cmp(name, END_MARKER)) /* as long as name is NOT END_MARKER :) */
{
/* Find the command in the table */
for (i = command_hash[LOWER(name[0]) % 126]; i; i = i->next_hash)
if (!str_cmp(i->name, name))
break;
if (!i) /* command does not exist? */
{
bug("Skipping uknown command in " DISABLED_FILE " file.");
read_to_eol(fp);
}
else /* add new disabled command */
{
alloc_mem(p, DISABLED_DATA, 1);
p->command = i;
p->level = read_number(fp);
p->disabled_by = str_dup(read_word(fp));
p->disabled_for = read_string(fp);
LINK(p, disabled_first, disabled_last, next, prev);
}
name = read_word(fp);
}
close_read(fp);
}
/* Save disabled commands */
void save_disabled()
{
WRITE_DATA *fp;
DISABLED_DATA *p;
if (!disabled_first) /* delete file if no commands are disabled */
{
unlink(DISABLED_FILE);
return;
}
fp = open_write(DISABLED_FILE);
if (!fp)
{
bug("Could not open " DISABLED_FILE " for writing");
return;
}
for (p = disabled_first; p; p = p->next)
fprintf(fp->stream, "'%s' %d '%s' %s~\n", p->command->name, p->level,
p->disabled_by, p->disabled_for);
fprintf(fp->stream, "%s\n", END_MARKER);
close_write(fp);
}