/* cmdutils.c */ #include "copyright.h" #include "config.h" #include <stdio.h> #ifdef STRING_H #include <string.h> #else #include <strings.h> #endif /* STRING_H */ #include <ctype.h> #include "teeny.h" #include "match.h" /* * Utility functions for use by the command handlers in cmds.c, speech.c, * wiz.c and buildcmds.c */ #ifdef OLD_RAND long rand(); #else long random(); #endif /* OLD_RAND */ /* * A work buffer. Different from the one in cmds.c/buildcmds.c, though it * probably need not be. */ char work[LARGEBUFFSIZ]; /* * Send an thing IN A CONTENTS LIST home. */ void send_home(obj, loc) int obj; int loc; { int home, next; if (get_int_elt(obj, HOME, &home) == -1) { warning("send_home", "could not get home"); return; } list_drop(obj, loc, 1); /* Drop it from contents list here */ if (!exists_object(home)) { home = STARTING_LOC; /* Fake it, eh? */ } if (set_int_elt(obj, LOC, home) == -1) { warning("send_home", "could not set location"); return; } if (get_int_elt(home, CONTENTS, &next) == -1) { warning("send_home", "could not get contents"); return; } if (set_int_elt(obj, NEXT, next) == -1) { warning("send_home", "could not set next"); return; } if (set_int_elt(home, CONTENTS, obj) == -1) { warning("send_home", "could not set contents"); return; } } /* * Resolves a string into an object reference, if it can. The last parameter * is a flag. Returns -1 if no matching object, -2 if ambiguous. This routine * now *only* matches non-exits. If you want exits matched, call either * resolve_exit() or resolve_anything(). */ int resolve_object(player, name, splat_ok) int player; char *name; int splat_ok; { int matched, location; int match1, match2, match3; /* Check these first. Don't want to confuse these. */ if (get_int_elt(player, LOC, &location) == -1) { warning("resolve_object", "bad location"); return (-1); } if (strcasecmp("here", name) == 0) { if (get_int_elt(player, LOC, &location) == -1) { warning("resolve_object", "couldn't get player's location"); return (-1); /* no match, hehe */ } return (location); } if (strcasecmp("me", name) == 0) { return (player); } if (name[0] == '#' && isdigit(name[1])) { matched = atoi(name + 1); if (exists_object(matched) && !isexit(matched)) return (matched); } /* if match_here tells us "tie" then we return the info immediately. */ if ((match1 = match_here(player, player, name, MAT_THINGS)) == -2) return (-2); if ((match2 = match_here(player, location, name, MAT_THINGS)) == -2) return (-2); if ((match3 = match_here(player, location, name, MAT_PLAYERS)) == -2) return (-2); if ((match1 == -1) && (match2 == -1) && (match3 == -1)) { /* * OK. Last ditch. If this player's a wiz or we're splat_ok, try * *<playername>. */ if (*name == '*') { if (!splat_ok && !iswiz(player)) { /* If not a Wiz, bag out */ return (-1); } name++; if ((match1 = match_active_player(name)) == -1 || match1 == -2) { if ((match1 = match_who(name)) == -1) { if ((match1 = match_player(name)) == -1) { return (-1); } } } return (match1); } return (-1); } return (best_match(name, match1, match2, match3)); } /* * This routine tries to match a player name every way possible. It should be * used more often. */ int resolve_player(player, name, splat_ok) int player; char *name; int splat_ok; { int matched, loc; if (get_int_elt(player, LOC, &loc) == -1) { warning("resolve_player", "bad loc ref on player"); return -1; } matched = match_here(player, loc, name, MAT_PLAYERS); if (exists_object(matched) && isplayer(matched)) return (matched); if (!strcmp(name, "me")) return (player); if (!splat_ok && !iswiz(player)) return (-1); if (name[0] == '*') name++; if (name[0] == '#') { matched = atoi(name + 1); if (exists_object(matched) && isplayer(matched)) return (matched); else return (-1); } if ((matched = match_active_player(name)) == -1 || matched == -2) { if ((matched = match_who(name)) == -1) { if ((matched = match_player(name)) == -1) { return (-1); } } } return (matched); } /* * This routine tries to match its argument to an exit, exact matches only. * #<objnum> is not allowed. Returns -1 on no match, -2 if ambiguous, or the * object number. */ int resolve_exit(player, name) int player; char *name; { int matched, location; int match1, match2; if (get_int_elt(player, LOC, &location) == -1) { warning("resolve_exit", "bad location ref on player"); return (-1); } match1 = match_here(player, location, name, MAT_EXITS); match2 = match_here(player, player, name, MAT_EXITS); if (((match1 > -1) && (match2 > -1)) || match1 == -2 || match2 == -2) return (-2); if (match1 != -1) { matched = match1; } else if (match2 != -1) { matched = match2; } else { /* go ahead and *carefully* parse an object number */ matched = -1; if (*name && (*name == '#') && *(name + 1) && isdigit(name[1])) { matched = atoi(name + 1); if (!exists_object(matched) || !isexit(matched)) matched = -1; } } return (matched); } /* * This simply calls resolve_object() and then resolve_exit(). Use this if * you really, really don't give a damn about match order. */ int resolve_anything(player, name, splat_ok) int player; char *name; int splat_ok; { int matched, match1, match2; match1 = resolve_object(player, name, splat_ok); match2 = resolve_exit(player, name); if ((match1 == -2) || (match2 == -2)) { matched = -2; } else if ((match1 != -1) && (match2 != -1)) { matched = -2; } else if (match1 != -1) { matched = match1; } else if (match2 != -1) { matched = match2; } else matched = -1; return (matched); } /* * Crams the name of a thing into a buffer. If the player controls the thing, * or if the thing is link_ok, we show the number. */ char *stuff_name(player, thing) int player; int thing; { char *name, *p; static char buff[BUFFSIZ + 32]; int flags; if (!exists_object(thing)) { (void) strcpy(buff, "<nothing>"); return (buff); } if (get_str_elt(thing, NAME, &name) == -1 || get_int_elt(thing, FLAGS, &flags) == -1) { log_error("stuff_name: couldn't get name or flags for #%d\n", thing); (void) strcpy(buff, "<spammed name>"); return (buff); } for (p = buff; name && *name && (p - buff) < BUFFSIZ; *p++ = *name++); *p = 0; if (controls(player, thing) || islinkok(thing) || isjumpok(thing) || isabode(thing)) { (void) sprintf(p, "(#%d", thing); /* Flags */ switch (flags & TYPE_MASK) { case TYP_PLAYER: (void) strcat(buff, "P"); break; case TYP_ROOM: (void) strcat(buff, "R"); break; case TYP_EXIT: (void) strcat(buff, "E"); } if (flags & GOD) (void) strcat(buff, "G"); if (flags & WIZARD) (void) strcat(buff, "W"); if ((flags & ROBOT) && (flags & TYPE_MASK) == TYP_PLAYER) (void) strcat(buff, "R"); if (flags & STICKY) (void) strcat(buff, "S"); if (flags & LINK_OK) (void) strcat(buff, "L"); if (flags & JUMP_OK) (void) strcat(buff, "J"); if ((flags & ABODE) && (flags & TYPE_MASK) != TYP_EXIT) (void) strcat(buff, "A"); else if (flags & ABODE) (void) strcat(buff, "O"); if (flags & HAVEN) (void) strcat(buff, "H"); if (flags & DARK) (void) strcat(buff, "D"); if (flags & BUILDER) (void) strcat(buff, "B"); if ((flags & ACTION) && (flags & TYPE_MASK) == TYP_EXIT) (void) strcat(buff, "a"); if (flags & ENTER_OK) (void) strcat(buff, "e"); if (flags & GUEST) (void) strcat(buff, "g"); if ((flags & GENDER_MALE) && (flags & TYPE_MASK) == TYP_PLAYER) (void) strcat(buff, "m"); if ((flags & GENDER_FEMALE) && (flags & TYPE_MASK) == TYP_PLAYER) (void) strcat(buff, "f"); if ((flags & GENDER_NEUTER) && (flags & TYPE_MASK) == TYP_PLAYER) (void) strcat(buff, "n"); if (isalive(thing) && iswiz(player)) (void) strcat(buff, "c"); (void) strcat(buff, ")"); } return (buff); } /* * Given a non-empty matchlist of exits, this will try to get the player * through one of them. It takes an unlocked exit in preference to a locked * one. */ void do_go_attempt(player, here, exlist) int player; int here; struct match *exlist; { struct match *current, *locklist, *next; int locked; int total_locked, total_unlocked, total; int theex, dest; char *name; int count; /* loop over the list once, to count it. This is clumsy. Cope. */ current = exlist; for (count = 1; current->fwd != exlist && count < 1000; count++) { current = current->fwd; } current = exlist; locklist = NULL; total_locked = total_unlocked = 0; /* Loop over the list, putting locked exits on the locklist */ do { next = current->fwd; /* Guard this with your LIFE. */ if (get_int_elt(current->obj, DESTINATION, &dest) == -1) { warning("do_go_attempt", "bed dest ref on exit"); return; } /* Exits that are unlinked, or have dests that don't exist */ /* are considered locked. home (-3) is OK. */ if (islocked(player, current->obj, LOCK) || (dest != -3 && !exists_object(dest))) { total_locked++; /* Remove this match from exlist */ if (current->fwd == current) { exlist = NULL; } else { if (exlist == current) { exlist = next; } (current->back)->fwd = current->fwd; (current->fwd)->back = current->back; } /* Stuff it in to locklist */ if (locklist == NULL) { locklist = current->back = current->fwd = current; } else { current->back = locklist->back; current->fwd = locklist; (locklist->back)->fwd = current; locklist->back = current; } } else { total_unlocked++; } current = next; count--; } while (count > 0); /* If there are no unlocked exits, then go with a locked one. */ if (exlist == NULL) { exlist = locklist; total = total_locked; locked = 1; } else { free_match_list(locklist); total = total_unlocked; locked = 0; } /* Now choose an exit from exlist */ current = exlist; while (total) { #ifndef OLD_RAND if (random() & 0x0010L) /* Break with 50% probability */ #else if (rand() & 0x0010L) /* Break with 50% probability */ #endif /* OLD_RAND */ break; current = current->fwd; total--; } /* Actually, current points one too far ahead. */ theex = (current->back)->obj; free_match_list(exlist); /* Now do the exit */ if (locked) { act_object(player, theex, FAIL, OFAIL, -1, "You can't go that way.", (char *) NULL); return; } /* Grab the destination */ if (get_int_elt(theex, DESTINATION, &dest) == -1) { warning("do_go_attempt", "bad dest ref"); return; } /* dest will be an existing object, or -3, otherwise 'locked' */ if (dest == -3) { /* home */ if (get_int_elt(player, HOME, &dest) == -1) { warning("do_go_attempt", "bad home ref on player"); return; } if (!exists_object(dest)) { notify_player(player, "Your home does not exist!\r\n"); dest = STARTING_LOC; } } act_object(player, theex, SUC, (isdark(player) || isdark(here)) ? -1 : OSUC, -1, (char *) NULL, (char *) NULL); /* Tell people here that the player has left */ stamp(theex); if (get_str_elt(player, NAME, &name) == -1) { warning("do_go_attempt", "bad player name reference"); return; } if (!isdark(theex) && !isdark(player) && !isdark(here)) { sprintf(work, "%s has left.\r\n", name); notify_oall(player, work); } act_object(player, theex, DROP, (isdark(player) || isdark(dest)) ? -1 : ODROP, dest, (char *) NULL, (char *) NULL); /* Get the player out of here. */ list_drop(player, here, CONTENTS_LIST); /* stuff player in at destination. */ list_add(player, dest, CONTENTS_LIST); if (set_int_elt(player, LOC, dest) == -1) { warning("do_go_attempt", "could not set player location"); return; } /* Tell folks the player has arrived. */ if (!isdark(theex) && !isdark(player) && !isdark(dest)) { sprintf(work, "%s has arrived.\r\n", name); notify_oall(player, work); } do_look(player, (char *) NULL); flush_room(here); } void flush_room(room) int room; { int flags, dest, list; /* Check for sticky droptos */ if (get_int_elt(room, FLAGS, &flags) == -1) { warning("flush_room", "bad flags ref on room"); return; } if ((flags & TYPE_MASK) != TYP_ROOM) return; if (flags & STICKY) { /* Check for a dropto to get activated. */ if (get_int_elt(room, DROPTO, &dest) == -1) { warning("flush_room", "bad dropto reference"); return; } /* If no dropto, or it's to somewhere that doesn't exist.. */ if (dest == -1 || (dest != -3 && !exists_object(dest))) { return; } /* OK. See if there are any players here. */ if (get_int_elt(room, CONTENTS, &list) == -1) { warning("flush_room", "bad contents list ref"); return; } while (list != -1) { if (get_int_elt(list, FLAGS, &flags) == -1) { warning("flush_room", "bad flags ref in contents list"); return; } if ((TYPE_MASK & flags) == TYP_PLAYER) { return; } if (get_int_elt(list, NEXT, &list) == -1) { warning("flush_room", "bad NEXT ref in contents list"); return; } } /* No players left, toss everything here down the dropto */ if (get_int_elt(room, CONTENTS, &list) == -1) { warning("flush_room", "bad contents list ref"); return; } while (list != -1) { int sendto, next; if (get_int_elt(list, NEXT, &next) == -1) { warning("flush_room", "bad NEXT ref in contents list"); return; } if (get_int_elt(list, FLAGS, &flags) == -1) { warning("flush_room", "bad flags ref in contents list"); return; } if ((TYPE_MASK & flags) == TYP_THING) { list_drop(list, room, 1); if ((dest == -3) || (flags & STICKY)) { /* send it home */ if (get_int_elt(list, HOME, &sendto) == -1) { warning("flush_room", "bad home ref in dropto"); sendto = STARTING_LOC; } if (!exists_object(sendto)) { sendto = 0; } } else { sendto = dest; } list_add(list, sendto, 1); if (set_int_elt(list, LOC, sendto) == -1) { warning("flush_room", "could not set LOC on dropto"); return; } } list = next; } } } /* * This replaces fail_object(), succeed_object(), and much other kooky * coding. */ void act_object(player, thing, code, ocode, oloc, def, odef) int player; int thing; /* things they're acting upon */ int code; /* code of the "success" string */ int ocode; /* code of the "osuccess" string */ int oloc; /* location for the o-string, optional. the * player must not be here! */ char *def; /* Default suc/fail/etc string. */ char *odef; { char *str, *ostr, *name; if (code != -1) { if (get_str_elt(thing, code, &str) == -1) { warning("act_object", "bad string reference"); return; } if (str == NULL) { str = def; } if (str != NULL) { sprintf(work, "%s\r\n", str); notify_player(player, work); } } if (ocode != -1) { if (get_str_elt(thing, ocode, &ostr) == -1) { warning("act_object", "bad ostring reference"); return; } if (ostr == NULL) ostr = odef; if (ostr != NULL && !isdark(player)) { name = getname(player); sprintf(work, "%s%s%s\r\n", name, (ostr[0] == '\'' || ostr[0] == ',') ? "" : " ", pronoun_sub(player, ostr)); if (oloc == -1) notify_oall(player, work); else notify_contents(oloc, work); } } } /* * Spits a file in the cwd to the player. */ void spit_file(player, name) int player; char *name; { FILE *in; char filebuff[128]; if ((in = fopen(name, "r")) == NULL) { extern char cmdwork[]; sprintf(cmdwork, "Sorry, %s is broken. Your wizards are no doubt toiling over it now.\r\n"); notify_player(player, cmdwork); return; } /* Grab thing outta the file and shove 'em */ while (fgets(filebuff, 127, in) != NULL) { fix_newline(filebuff); notify_player(player, filebuff); } (void) fclose(in); } /* * Returns 1 if the player can see anything, 0 otherwise. */ int can_see_anything(player, location) int player; int location; { int list, contents; if (location == -1) { /* get their location */ if (get_int_elt(player, LOC, &location) == -1) { warning("can_see_anything", "bad player loc ref"); notify_bad(player); return 0; } } if (get_int_elt(location, CONTENTS, &contents) == -1) { warning("can_see_anything", "bad location contents ref"); notify_bad(player); return 0; } if (contents == player) { int foo; if (get_int_elt(player, NEXT, &foo) == -1) { warning("can_see_anything", "bad player next ref"); notify_bad(player); return 0; } if (foo == -1) return 0; } /* still here... hmm... loop through list. */ list = contents; while (list != -1) { if (can_see(player, list)) return 1; if (get_int_elt(list, NEXT, &list) == -1) { warning("can_see_anything", "bad next ref in contents list"); notify_bad(player); return 0; } } /* they cannot see a damn thing! */ return 0; } /* * Returns a 1 if player can see that specific object, 0 otherwise. */ int can_see(player, obj) int player; int obj; { int loc, owner; if (player == obj) return 0; #ifdef DARK_SLEEP if (isplayer(obj) && !isalive(obj)) return 0; #endif /* DARK_SLEEP */ if (isroom(obj)) return 0; /* wee... */ if (get_int_elt(obj, LOC, &loc) == -1) { warning("can_see", "couldn't get object's location"); return (0); } if (get_int_elt(obj, OWNER, &owner) == -1) { warning("can_see", "couldn't get object's owner"); return (0); } /* this is so a wizz won't see so much junk a dark room. */ if (isdark(loc) && (owner != player)) return 0; if (!isdark(obj)) return 1; if (isdark(obj) && controls(player, obj)) return 1; return 0; } int legal_parent_check(source, test) int source, test; { int parent; if (source == test) return 0; if (!exists_object(test) || !isroom(test)) return 0; if (test == ROOT_PARENT) return 1; if (get_int_elt(test, LOC, &parent) == -1) return 0; return (legal_parent_check(source, parent)); } #ifdef VERBOSE_FLAGS char *display_flags(obj) int obj; { static char buffer[1024]; int flags; if (get_int_elt(obj, FLAGS, &flags) == -1) { strcpy(buffer, "<spammed flags>"); return buffer; } else { strcpy(buffer, "Type: "); switch (flags & TYPE_MASK) { case TYP_ROOM: strcat(buffer, "Room"); break; case TYP_EXIT: strcat(buffer, "Exit"); break; case TYP_PLAYER: strcat(buffer, "Player"); break; case TYP_THING: strcat(buffer, "Thing"); break; default: strcat(buffer, "*UNKNOWN TYPE*"); } if (flags & ~(TYPE_MASK | INTERNAL_FLAGS)) { strcat(buffer, " Flags: "); if (flags & GOD) strcat(buffer, "GOD "); if (flags & WIZARD) strcat(buffer, "WIZARD "); if ((flags & ROBOT) && (flags & TYPE_MASK) == TYP_PLAYER) strcat(buffer, "ROBOT "); if (flags & STICKY) strcat(buffer, "STICKY "); if (flags & LINK_OK) strcat(buffer, "LINK_OK "); if (flags & JUMP_OK) strcat(buffer, "JUMP_OK "); if (flags & ABODE) { if ((flags & TYPE_MASK) == TYP_EXIT) strcat(buffer, "OBVIOUS "); else strcat(buffer, "ABODE "); } if (flags & HAVEN) strcat(buffer, "HAVEN "); if (flags & DARK) strcat(buffer, "DARK "); #ifdef BUILDING_OK if (flags & BUILDER) { if ((flags & TYPE_MASK) != TYP_PLAYER) strcat(buffer, "BUILDING_OK "); else strcat(buffer, "BUILDER "); } #else if (flags & BUILDER) strcat(buffer, "BUILDER "); #endif /* BUILDING_OK */ if ((flags & ACTION) && (flags & TYPE_MASK) == TYP_EXIT) strcat(buffer, "ACTION "); if ((flags & ENTER_OK) && (flags & TYPE_MASK) != TYP_EXIT) strcat(buffer, "ENTER_OK "); else if (flags & ENTER_OK) strcat(buffer, "EXTERNAL "); if (flags & GUEST) strcat(buffer, "GUEST "); if ((flags & GENDER_MALE) && (flags & TYPE_MASK) == TYP_PLAYER) strcat(buffer, "MALE "); if ((flags & GENDER_FEMALE) && (flags & TYPE_MASK) == TYP_PLAYER) strcat(buffer, "FEMALE "); if ((flags & GENDER_NEUTER) && (flags & TYPE_MASK) == TYP_PLAYER) strcat(buffer, "NEUTER "); } if (buffer[strlen(buffer) - 1] == ' ') buffer[strlen(buffer) - 1] = 0; strcat(buffer, "\r\n"); return buffer; } } #endif /* VERBOSE_FLAGS */