/* 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[BUFFSIZ + 64]; /* * 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(). To match objects and exits * correctly, call resolve_object_or_exit(). */ int resolve_object(player, name, splat_ok) int player; char *name; int splat_ok; { int matched, location; int match1, match2, match3; int flags; if (*name == '#' && isdigit(name[1])) { name++; matched = atoi(name); if (exists_object(matched) && !isexit(matched)) { return (matched); } else { return (-1); } } /* 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 (strcmp("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 (strcmp("me", name) == 0) { return (player); } /* 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); */ match1 = match_here(player, player, name, MAT_THINGS); match2 = match_here(player, location, name, MAT_THINGS); match3 = match_here(player, location, name, MAT_PLAYERS); 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) { /* If not a Wiz, bag out */ if (get_int_elt(player, FLAGS, &flags) == -1) { warning("resolve_object", "bad flags ref on player"); return (-1); } if (!(flags & WIZARD)) { return (-1); } } name++; return (match_player(name)); } return (-1); } return (best_match(name, match1, match2, match3, -1)); } /* * 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 * also matches exits, using rules that are similar to TinyMUD, but not * exactly the same. TinyMUD would give all exact matches an equal * chance, but this gives only the best of each category an equal chance, * if the match is exact. */ int resolve_object_or_exit(player, name, splat_ok) int player; char *name; int splat_ok; { int matched, location; int match1, match2, match3, match4; int flags; if (*name == '#' && isdigit(name[1])) { name++; matched = atoi(name); if (exists_object(matched)) { return (matched); } else { return (-1); } } /* Check these first. Don't want to confuse these. */ if (get_int_elt(player, LOC, &location) == -1) { warning("resolve_object_or_exit", "bad location"); return (-1); } if (strcmp("here", name) == 0) { if(get_int_elt(player, LOC, &location) == -1){ warning("resolve_object_or_exit", "couldn't get player's location"); return(-1); /* no match, hehe */ } return (location); } if (strcmp("me", name) == 0) { return (player); } /* 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((match4 = resolve_exit(player, name)) == -2) return (-2); */ match1 = match_here(player, player, name, MAT_THINGS); match2 = match_here(player, location, name, MAT_THINGS); match3 = match_here(player, location, name, MAT_PLAYERS); match4 = resolve_exit(player, name); if ((match1 == -1) && (match2 == -1) && (match3 == -1) && (match4 == -1)) { /* * OK. Last ditch. If this player's a wiz or we're splat_ok, try * *<playername>. */ if (*name == '*') { if (!splat_ok) { /* If not a Wiz, bag out */ if (get_int_elt(player, FLAGS, &flags) == -1) { warning("resolve_object_or_exit", "bad flags ref on player"); return (-1); } if (!(flags & WIZARD)) { return (-1); } } name++; return (match_player(name)); } return (-1); } return (best_match(name, match1, match2, match3, match4)); } /* resolve_object_or_exit */ /* * 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) return (-1); if (name[0] == '*') name++; if (name[0] == '#') { matched = atoi(name + 1); if (exists_object(matched) && isplayer(matched)) return (matched); else return (-1); } return (match_player(name)); } /* * 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); } /* * Drops an element from a list. You'd better be sure the element is actually * on the list. If code == 0, exits list, if code == 1, things list. */ void list_drop(elt, place, code) int elt; int place; int code; { int current, last, next; int listcode; if (code == 1) { listcode = CONTENTS; } else { listcode = EXITS; } if (get_int_elt(place, listcode, ¤t) == -1) { warning("list_drop", "bad list reference."); return; } last = -1; while (current != elt && current != -1) { last = current; if (get_int_elt(current, NEXT, ¤t) == -1) { warning("list_drop", "bad ref inside list"); return; } } /* Post Mortem. */ /* grab the next thing on the list anyway. */ if (get_int_elt(current, NEXT, &next) == -1) { warning("list_drop", "bad ref inside list"); return; } if (last == -1) { /* Was 1st thing on list */ if (set_int_elt(place, listcode, next) == -1) { warning("list_drop", "bad list reference."); return; } } else { if (set_int_elt(last, NEXT, next) == -1) { warning("list_drop", "bad list reference."); return; } } } /* * Adds a thing to the top of the contents or exits list of the specified * place. */ void list_add(thing, place, code) int thing; int place; int code; { int list; int listcode; if (code == 1) { listcode = CONTENTS; } else { listcode = EXITS; } if (get_int_elt(place, listcode, &list) == -1) { warning("list_add", "can't get contents"); return; } if (set_int_elt(thing, NEXT, list) == -1) { warning("list_add", "bad can't set NEXT"); return; } if (set_int_elt(place, listcode, thing) == -1) { warning("list_add", "can't set contents"); return; } } /* * 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. */ int stuff_name(player, thing, buff, siz) int player; int thing; char *buff; int siz; { char *name, *oldbuff; int flags; if (!exists_object(thing)) { name = "<nothing>"; for (; *name && siz > 0; siz--) { *buff++ = *name++; } return (9); } oldbuff = buff; if (get_str_elt(thing, NAME, &name) == -1 || get_int_elt(thing, FLAGS, &flags) == -1) { warning("stuff_name", "bad object name or flags ref"); return (0); /* Pretend it was a blank name. */ } if (name == NULL) return (0); if ((TYPE_MASK & flags) == TYP_PLAYER) { while (!isspace(*name) && *name && siz) { *buff++ = *name++; siz--; } if (!isspace(*name) && *name) { return (-1); /* Failed to fit */ } } else { while (*name && siz) { *buff++ = *name++; siz--; } if (*name) { return (-1); /* Failed to fit */ } } /* Name fit OK. Do a number? */ if (controls(player, thing) || islinkok(thing) || isjumpok(thing) || isabode(thing)) { if (siz < 24) { return (-1); /* Frob it. */ } *buff++ = '('; *buff++ = '#'; buff = ty_itoa(buff, thing); /* Flags */ switch (flags & TYPE_MASK) { case TYP_PLAYER: *buff++ = 'P'; break; case TYP_ROOM: *buff++ = 'R'; break; case TYP_EXIT: *buff++ = 'E'; } if (flags & WIZARD) *buff++ = 'W'; if (flags & TEMPLE) *buff++ = 'T'; if (flags & STICKY) *buff++ = 'S'; if (flags & LINK_OK) *buff++ = 'L'; if (flags & JUMP_OK) *buff++ = 'J'; if (flags & ABODE) *buff++ = 'A'; if (flags & HAVEN) *buff++ = 'H'; if (flags & DARK) *buff++ = 'D'; if (flags & BUILDER) *buff++ = 'B'; *buff++ = ')'; } return (buff - oldbuff); } /* * 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 *p, *q, *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) || (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) { fail_object(player, theex, "You can't go that way."); 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; } } if (!isdark(player) && !isdark(here)) succeed_object(player, theex, (char *) NULL); else { char *succ; if (get_str_elt(theex, SUC, &succ) == -1) { warning("do_go_attempt", "bad exit success reference"); return; } if (succ != NULL) { notify_player(player, succ); notify_player(player, "\r\n"); } } /* Tell people here that the player has left */ #ifdef TIMESTAMPS stamp(theex); #endif /* TIMESTAMPS */ if (get_str_elt(player, NAME, &name) == -1) { warning("do_go_attempt", "bad player name reference"); return; } p = work; q = name; for (count = 0; !isspace(*q) && *q && count < BUFFSIZ - 32; count++) { *p++ = *q++; } if (!isdark(player) && !isdark(here)) { strcpy(p, " has left.\r\n"); notify_oall(player, work); } #ifdef DROP_FIELDS { char *drop; if (get_str_elt(theex, DROP, &drop) == -1) { warning("do_go_attempt", "bad drop ref on exit"); notify_bad(player); return; } if (drop != NULL) { notify_player(player, drop); notify_player(player, "\r\n"); } } #endif /* DROP_FIELDS */ /* Get the player out of here. */ list_drop(player, here, 1); /* From the things list */ /* stuff player in at destination. */ list_add(player, dest, 1); /* Into contents list */ if (set_int_elt(player, LOC, dest) == -1) { warning("do_go_attempt", "could not set player location"); return; } #ifdef DROP_FIELDS if (!isdark(player) && !isdark(here)) { char *odrop; if (get_str_elt(theex, ODROP, &odrop) == -1) { warning("do_go_attempt", "bad odrop ref on exit"); notify_bad(player); } else if (odrop != NULL) { char *sub, *r; sub = pronoun_sub(player, odrop); r = p; *r++ = ' '; for (; sub && *sub && (r - work) < BUFFSIZ - 3; *r++ = *sub++); *r++ = '\r'; *r++ = '\n'; *r = '\0'; notify_oall(player, work); } } #endif /* DROP_FIELDS */ /* Tell folks the player has arrived. p is still correct, really. */ if (!isdark(player) && !isdark(dest)) { strcpy(p, " has arrived.\r\n"); 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 & 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; } } } void fail_object(player, thing, def) int player; int thing; char *def; /* Default fail string. */ { char *str, *ostr, *name, *p, *sub; char buffer[BUFFSIZ]; /* Just grab the fail/ofail and do 'em */ if (get_str_elt(thing, FAIL, &str) == -1) { warning("fail_object", "bad fail reference"); return; } if (str == NULL) str = def; if (str != NULL) { notify_player(player, str); notify_player(player, "\r\n"); } if (get_str_elt(thing, OFAIL, &ostr) == -1) { warning("fail_object", "bad ofail reference"); return; } if (ostr != NULL) { sub = pronoun_sub(player, ostr); if (get_str_elt(player, NAME, &name) == -1) { warning("fail_object", "bad player name ref"); return; } for (p = buffer; name && *name && *name != ' '; *p++ = *name++); *p++ = ' '; for (; sub && *sub && (p - buffer) < BUFFSIZ - 3; *p++ = *sub++); *p++ = '\r'; *p++ = '\n'; *p = '\0'; notify_oall(player, buffer); } } void succeed_object(player, thing, def) int player; int thing; char *def; /* Default success string. */ { char *str, *ostr, *name, *p, *sub; char buffer[BUFFSIZ]; /* Grab the succ and osucc and do 'em */ if (get_str_elt(thing, SUC, &str) == -1) { warning("succeed_object", "bad success reference"); return; } if (str == NULL) str = def; if (str != NULL) { notify_player(player, str); notify_player(player, "\r\n"); } if (get_str_elt(thing, OSUC, &ostr) == -1) { warning("succeed_object", "bad osuccess reference"); return; } if (ostr != NULL) { sub = pronoun_sub(player, ostr); if (get_str_elt(player, NAME, &name) == -1) { warning("succeed_object", "bad player name ref"); return; } for (p = buffer; name && *name && *name != ' '; *p++ = *name++); *p++ = ' '; for (; sub && *sub && (p - buffer) < BUFFSIZ - 3; *p++ = *sub++); *p++ = '\r'; *p++ = '\n'; *p = '\0'; notify_oall(player, buffer); } } /* * Spits a file in the cwd to the player. */ void spit_file(player, name) int player; char *name; { FILE *in; char filebuff[130]; int i; if ((in = fopen(name, "r")) == NULL) { notify_player(player, "Sorry, "); notify_player(player, name); notify_player(player, " is broken. Your wizards are no doubt toiling over it now.\r\n"); return; } /* Grab thing outta the file and shove 'em */ while (fgets(filebuff, 128, in) != NULL) { /* Replace the \n\0 with a \r\n\0, if present. */ i = strlen(filebuff); if(filebuff[i-1] == '\n') { filebuff[i-1] = '\r'; filebuff[i] = '\n'; filebuff[i+1] = '\0'; } 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; }