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