/*
Mudadmin.m Class definition.
Copyright (C) 1995 David Flater.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cheezmud.h"
@implementation Mudadmin
+ new
{
self = [super new];
capacity = 1000000;
monitor_heartbeat = ignore_attacks = 0;
return self;
}
- hit: fromwho: (float) damage
{
if (dead)
return self;
if (!ignore_attacks)
[enemies addIfAbsent: fromwho];
[location emote: self: "suffer": "suffers": " no damage"];
return self;
}
- help
{
[self echo: "Global mud administrator commands:"];
[self echo: " listworld -- list all objects in the world"];
[self echo:
" halt -- shut down the mud immediately, without saving players"];
[self echo:
" savehalt -- save players, then halt"];
[self echo:
" goto name -- magically go to a particular person, place, or thing"];
[self echo:
" goto void -- go to nowhere (NULL)"];
[self echo:
" create name indef/def/description -- create an inert object"];
[self echo:
" heart -- toggle audible heartbeat"];
[self echo:
" nice -- toggle ignore attacks"];
[self echo:
" summon name -- teleport in the specified object from wherever it is"];
[self echo:
" fetch name -- teleport it into your inventory, if possible"];
/* [self echo: */
/* " clone name -- fetch a copy of an object"]; */
[self echo:
" see name -- see something that is far away"];
[self echo:
" audit name -- audit / unaudit a player (or whatever)"];
[self echo:
" adduser name -- add a new player with password same as username"];
[self echo:
" torch name -- delete a player"];
[self echo:
" heal name -- heal a player or monster"];
[self echo: "Localized mud administrator commands:"];
[self echo:
" free name -- destroy it"];
[self echo:
" stash name -- teleport it to dave_workshop"];
[self echo:
" havoc -- make everybody attack everybody (for testing, of course)"];
[super help];
return self;
}
- (int) level
{
return -1;
}
- (float) maxstamina
{
return 30.0;
}
- halt
{
cheezlog ("Mud halted by mud admin");
exit (0);
return self;
}
- savehalt
{
checkpoint ();
return [self halt];
}
- heart
{
monitor_heartbeat = 1 - monitor_heartbeat;
return self;
}
- nice
{
ignore_attacks = 1 - ignore_attacks;
return self;
}
/* The mud admin's examine gives more information than a player's */
- examine: who: dobj
{
char temp[160];
[super examine: who: dobj];
sprintf (temp, "Name: '%s' Class: '%s'", [dobj mudname], [dobj name]);
[self echo: temp];
sprintf (temp, "Global reference: %s %d", [dobj mudname],
global_number ([dobj mudname], dobj));
[self echo: temp];
if (location) {
if ([dobj getlocation] == location) {
sprintf (temp, "'Get' reference: %s %d", [dobj mudname],
generic_number ([location contents], [dobj mudname], dobj));
[self echo: temp];
} else if ([dobj getlocation] == self) {
sprintf (temp, "'Drop' reference: %s %d", [dobj mudname],
generic_number ([self contents], [dobj mudname], dobj));
[self echo: temp];
}
sprintf (temp, "Normal reference: %s %d", [dobj mudname],
generic_number_cascade ([self contents], [location contents],
[dobj mudname], dobj));
[self echo: temp];
} else {
sprintf (temp, "Normal reference: %s %d", [dobj mudname],
generic_number ([self contents], [dobj mudname], dobj));
[self echo: temp];
}
return self;
}
/* Parse the command line. */
- (int) ohce: (char *) text
{
/* Special mud administrator commands. */
if (!strncmp (text, "create", 6)) {
char name[80], *newindef, *newdef, *newdesc;
if (strlen (text) < 8)
[self echo: "Create what?"];
else {
if ((sscanf (text+7, "%s", name) != 1) ||
(!(newindef = strchr (text+7, ' ')))) {
[self echo: "Malformed command."];
return 1;
}
newindef++;
if (!(newdef = strchr (newindef, '/'))) {
[self echo: "Malformed command."];
return 1;
}
*(newdef++) = '\0';
if (!(newdesc = strchr (newdef, '/'))) {
[self echo: "Malformed command."];
return 1;
}
*(newdesc++) = '\0';
[self create: name: newindef: newdef: newdesc];
}
return 1;
}
if (!strncmp (text, "adduser", 7)) {
char name[80], fname[80];
FILE *fp;
if (sscanf (text, "adduser %s", name) != 1) {
[self echo: "Malformed command."];
return 1;
}
sprintf (fname, "db/%s", name);
if ((fp = fopen (fname, "r"))) {
fclose (fp);
[self echo: "Already exists!"];
return 1;
}
assert (fp = fopen (fname, "w"));
fprintf (fp, "%s\n", name);
fprintf (fp, "%s\n", "This is a newbie player.");
fprintf (fp, "start\n15.0 0\n");
fclose (fp);
set_password (name, name);
[self echo: "Player added."];
return 1;
}
if (!strncmp (text, "torch", 5)) {
char name[80], fname[80];
FILE *fp;
id sucker;
if (sscanf (text, "torch %s", name) != 1) {
[self echo: "Malformed command."];
return 1;
}
if (!strcmp (name, mudadmin)) {
[self echo: "Idiot!"];
return 1;
}
sprintf (fname, "db/%s", name);
if (!(fp = fopen (fname, "r"))) {
[self echo: "Does not exist!"];
return 1;
}
fclose (fp);
[location emote: self: "weave": "weaves": " a deadly spell"];
/* If the player is logged in, torch the sucker */
if ((sucker = global_find (name, 1))) {
[[sucker getlocation] emote: sucker: "suddenly burst into flames":
"suddenly bursts into flames": ""];
[[sucker getlocation] emote: sucker: "are": "is":
" consumed by hellfire"];
[sucker logout];
}
unlink (fname);
delete_user (name);
return 1;
}
if ([self doadmin: text])
return 1;
/* Regular player commands (inherited) */
return [super ohce: text];
}
/* This nifty function insures that we always arrive in a room and not */
/* in somebody's saddlebag. */
- goto: dobj
{
if (!dobj) {
[self teleport: NULL];
return self;
}
while (dobj) {
if ([dobj isKindOf: [Room class]])
break;
dobj = [dobj getlocation];
}
[self teleport: dobj];
return self;
}
- see: dobj
{
[location emote: self: "seem": "seems": " to zone out for a moment"];
while (dobj) {
if ([dobj isKindOf: [Room class]])
break;
dobj = [dobj getlocation];
}
if (!dobj) {
[self echo:
"This place is without form, and void. You cannot see or hear or feel\n\
anything, and you are no longer sure if you even exist."];
return self;
}
[self echo: [dobj def]];
[self echo: [dobj longdesc]];
[dobj listcontents: self];
return self;
}
- summon: dobj
{
if ([dobj isKindOf: [Room class]]) {
[self echo: "You can't summon a room. That would be stupid."];
return self;
}
[location emote: self: "mutter": "mutters": " some mystic words"];
[dobj teleport: location];
return self;
}
- heal: dobj
{
if (![dobj isKindOf: [Fighter class]]) {
[self echo: "You can't heal that."];
return self;
}
[location emote: self: "mutter": "mutters": " some mystic words"];
[dobj heal];
return self;
}
- stash: dobj
{
[location emote: self: "wiggle some fingers at": "wiggles some fingers at":
dobj: ""];
[dobj teleport: global_find ("dave_workshop", 1)];
return self;
}
- free: dobj
{
[location emote: self: "cast": "casts": " a powerful spell"];
[dobj logout];
return self;
}
- listworld
{
int i,n = [world size];
for(i=0;i<n;i++) {
id whatever = [world at:i];
text_sock_write (tty, [whatever mudname]);
text_sock_write (tty, "\t");
}
text_sock_write (tty, "\n");
return self;
}
- create: (char *) name: (char *) newindef: (char *) newdef: (char *) newdesc
{
[location emote: self: "wave": "waves":
" dramatically at the ground"];
return [[[[[[Nonroom new] setmudname: name] setindef: newindef]
setdef: newdef] setlongdesc: newdesc] teleport: location];
}
- (void) heartbeat
{
if (dead)
return;
if (monitor_heartbeat)
[self echo: "Thump"];
[super heartbeat];
}
- havoc
{
id cnts;
int i,j,n;
if (!location)
return self;
[location emote: self: "cry": "cries": " havoc"];
cnts = [location contents];
n = [cnts size];
for(i=0;i<n;i++) {
id whatever = [cnts at:i];
if ([whatever isKindOf: [Fighter class]]) {
for(j=0;j<n;j++) {
id w2 = [cnts at:j];
if (whatever == w2)
continue;
if ([w2 isKindOf: [Fighter class]])
[w2 unclue: whatever];
}
}
}
return self;
}
- fetch: dobj
{
if ([dobj isKindOf: [Room class]]) {
[self echo: "You can't fetch a room. That would be stupid."];
return self;
}
if ([dobj isKindOf: [Fighter class]]) {
[self echo: "You can't fetch that. Use summon instead."];
return self;
}
[location emote: self: "mutter": "mutters": " some mystic words"];
[[dobj getlocation] emote: dobj: "vanish": "vanishes":
" into a cloud of magic smoke"];
[dobj setlocation: self];
[location emote: self: "yank": "yanks": dobj:
" out of a cloud of magic smoke"];
return self;
}
- audit: dobj
{
if (dobj == self) {
[self echo: "Idiot!"];
return self;
}
[dobj toggle_audit];
[self echo: "OK"];
return self;
}
/* Cloning has been disabled because it is more of a maintenance hassle */
/* to make sure that cloning works right for every class of object than */
/* it is worth. */
#if 0
- clone: dobj
{
if ([dobj isKindOf: [Room class]]) {
[self echo: "You can't clone a room. That would be stupid."];
return self;
}
if ([dobj isKindOf: [Player class]]) {
[self echo: "You can't clone a player. That would be stupid."];
return self;
}
[location emote: self: "cast": "casts": " a powerful spell"];
[[dobj clone] setlocation: self];
[location emote: self: "yank": "yanks": dobj:
" out of a cloud of magic smoke"];
return self;
}
#endif
/* This is similar to resolve_action except that it simply says yes or no */
/* to whether the action is a valid mud admin action. */
/* 0 Not supported. */
/* 1 Supported, global. */
/* 2 Supported, local. */
- (int) check_admin_action: (char *) action: (int) numargs
{
if (numargs == 1) {
if (!strcmp (action, "listworld"))
return 1;
if (!strcmp (action, "halt"))
return 1;
if (!strcmp (action, "savehalt"))
return 1;
if (!strcmp (action, "heart"))
return 1;
if (!strcmp (action, "nice"))
return 1;
if (!strcmp (action, "havoc"))
return 1;
}
if (numargs == 2) {
if (!strcmp (action, "summon"))
return 1;
if (!strcmp (action, "heal"))
return 1;
if (!strcmp (action, "audit"))
return 1;
if (!strcmp (action, "goto"))
return 1;
if (!strcmp (action, "see"))
return 1;
if (!strcmp (action, "clone"))
return 1;
if (!strcmp (action, "fetch"))
return 1;
if (!strcmp (action, "free"))
return 2;
if (!strcmp (action, "stash"))
return 2;
}
return 0;
}
/* Do a special mud administrator action. Compare and contrast with do */
/* in Nonroom.m. This one does not allow actions to be overridden and */
/* resolves object references against the whole world instead of just the */
/* room and inventory. Note also that mud admin actions implicitly take */
/* self as who and have one less argument than is claimed. (Sorry....) */
- (int) doadmin: (char *) someaction
{
id dobj = NULL; /* Gets rid of stupid compiler warning */
SEL a;
char verb[80], directobject[80];
int numargs, number;
numargs = sscanf (someaction, "%s %s %d", verb, directobject, &number);
switch (numargs) {
case 1:
number = 1;
break;
case 2:
number = 1;
/* Fall through */
case 3:
numargs = 2;
switch ([self check_admin_action: verb: numargs]) {
case 1: /* global */
/* Goto void is special. */
if ((!strcmp (verb, "goto")) && (!strcmp (directobject, "void")))
dobj = NULL;
else {
dobj = global_find (directobject, number);
if (!dobj) {
[self echo: "Not found."];
return 1;
}
}
break;
case 2: /* local */
if (!location)
dobj = [self find: directobject: number];
else
dobj = generic_find_cascade ([self contents], [location contents],
directobject, number);
if (!dobj) {
[self echo: "Not found."];
return 1;
}
}
break;
default:
return 0;
}
/* It is important to verify that the action is one of the accepted ones */
/* rather than simply to check if the message could be received; otherwise */
/* none of the regular actions that we _want_ to be overridden will work. */
if ([self check_admin_action: verb: numargs]) {
char temp[80];
strcpy (temp, verb);
if (numargs > 1)
strcat (temp, ":");
a = [Object findSel:temp];
if (numargs == 1)
[self perform: a];
else
[self perform: a with: dobj];
return 1;
}
/* Try to print a helpful message for missing args. */
if (numargs == 1) {
if ([self check_admin_action: verb: 2]) {
char temp[80];
sprintf (temp, "%s what?", capitalize (verb));
[self echo: temp];
return 1;
}
}
if (numargs == 2) {
if ([self check_admin_action: verb: 1]) {
[self echo: "That action does not get a direct object."];
return 1;
}
}
return 0;
}
@end