/* 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