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