/**************************************************************************
* 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-2002 by Ryan Jennings *
* http://1stmud.dlmud.com/ <r-jenn@shaw.ca> *
***************************************************************************/
#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if !defined(WIN32)
#include <unistd.h>
#endif
#include <time.h>
#include "merc.h"
#include "interp.h"
bool check_social args((CHAR_DATA * ch, char *command, const char *argument));
bool check_disabled(const struct cmd_type *command);
#define END_MARKER "END" /* for load_disabled() and save_disabled() */
/*
* Log-all switch.
*/
char last_func_long[MAX_STRING_LENGTH];
char last_func_short[MAX_INPUT_LENGTH];
DESCRIPTOR_DATA *last_descriptor;
CH_CMD(do_null)
{
chprintln(ch, "This is not a command, notify the immortals.");
return;
}
/*
* 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;
int trust;
bool found;
/*
* Strip leading spaces.
*/
while (isspace(*argument))
argument++;
if (argument[0] == '\0')
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;
}
/*
* Grab the command word.
* Special parsing so ' can be a command,
* also no spaces needed after punctuation.
*/
strcpy(logline, argument);
update_last_func(ch->desc, FORMATF("%s in room[%ld]: %s.", ch->name,
ch->in_room ? ch->in_room->vnum : 0,
argument), argument);
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.
*/
found = FALSE;
trust = get_trust(ch);
for (cmd = command_hash[LOWER(command[0]) % 126]; cmd; cmd = cmd->next_hash)
{
if (!str_prefix(command, cmd->name) && cmd->level <= trust)
{
found = TRUE;
break;
}
}
/*
* Log and snoop.
*/
if (found)
{
if (cmd->log == LOG_NEVER)
strcpy(logline, "");
if ((!IS_NPC(ch) && IS_SET(ch->act, PLR_LOG)) || fLogAll ||
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)
{
write_to_buffer(ch->desc->snoop_by, "% ", 2);
write_to_buffer(ch->desc->snoop_by, logline, 0);
write_to_buffer(ch->desc->snoop_by, "\n\r", 2);
}
if (!found)
{
/*
* Look for command in socials table.
*/
if (!check_social(ch, command, argument))
chprintln(ch, "Huh?");
return;
}
else if (check_disabled(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;
}
return;
}
/*
* Dispatch the command.
*/
(*cmd->do_fun) (ch, argument);
update_last_func(ch->desc,
FORMATF("(Finished) %s in room[%ld]: %s.", ch->name,
ch->in_room ? ch->in_room->vnum : 0, cmd->name),
FORMATF("(Finished) %s", cmd->name));
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;
}
one_argument(argument, arg);
victim = NULL;
if (arg[0] == '\0')
{
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
{
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 <= get_trust(ch) && 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 <= get_trust(ch) && 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;
if (IS_NPC(ch))
{
chprintln(ch, "RETURN first.");
return;
}
if (!argument[0]) /* 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");
for (p = disabled_first; p; p = p->next)
{
chprintlnf(ch, "%-12s %5d %-12s", p->command->name,
p->level, p->disabled_by);
}
return;
}
/* command given */
/* First check if it is one of the disabled commands */
for (p = disabled_first; p; p = p->next)
if (!str_cmp(argument, 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_mem(p); /* free node */
save_disabled(); /* save to disk */
chprintln(ch, "Command enabled.");
}
else /* not a disabled command, check if that command exists */
{
/* IQ test */
if (!str_cmp(argument, "disable"))
{
chprintln(ch, "You cannot disable the disable command.");
return;
}
/* 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;
}
/* 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->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(const struct cmd_type * command)
{
DISABLED_DATA *p;
for (p = disabled_first; p; p = p->next)
if (p->command->do_fun == command->do_fun)
return TRUE;
return FALSE;
}
/* Load disabled commands */
void load_disabled()
{
FILE *fp;
DISABLED_DATA *p;
char *name;
CMD_DATA *i;
disabled_first = NULL;
fp = file_open(DISABLED_FILE, "r");
if (!fp) /* No disabled file.. no disabled commands : */
return;
name = fread_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.", 0);
fread_number(fp); /* level */
fread_word(fp); /* disabled_by */
}
else /* add new disabled command */
{
alloc_mem(p, DISABLED_DATA, 1);
p->command = i;
p->level = fread_number(fp);
p->disabled_by = str_dup(fread_word(fp));
LINK(p, disabled_first, disabled_last, next, prev);
}
name = fread_word(fp);
}
file_close(fp);
}
/* Save disabled commands */
void save_disabled()
{
FILE_DATA *fp;
DISABLED_DATA *p;
if (!disabled_first) /* delete file if no commands are disabled */
{
unlink(DISABLED_FILE);
return;
}
fp = fopen_temp(DISABLED_FILE);
if (!fp)
{
bug("Could not open " DISABLED_FILE " for writing", 0);
return;
}
for (p = disabled_first; p; p = p->next)
fprintf(fp->file, "%s %d %s\n", p->command->name, p->level,
p->disabled_by);
fprintf(fp->file, "%s\n", END_MARKER);
fclose_temp(fp);
}