/* predicates.c */
#include "autoconf.h"
#include "copyright.h"
#ifndef lint
static char *RCSid = "$Id: predicates.c,v 1.8 1995/03/21 00:01:03 ambar Exp $";
USE(RCSid);
#endif
#include "interface.h"
#include "match.h"
#include "command.h"
#include "attrs.h"
extern dbref FDECL(match_thing, (dbref, char *));
extern int FDECL(do_command, (DESC *, char *));
extern void FDECL(load_quota, (int *, dbref, int));
extern void FDECL(save_quota, (int *, dbref, int));
static int FDECL(type_quota, (int));
static int FDECL(pay_quota, (dbref, int, int));
static void FDECL(add_quota, (dbref, int, int));
#ifdef NEED_VSPRINTF_DCL
extern char *FDECL(vsprintf, (char *, char *, va_list));
#endif
#if defined(__STDC__) && defined(STDC_HEADERS)
char *
tprintf(char *format,...)
#else
char *
tprintf(va_alist)
va_dcl
#endif
{
static char buff[10000];
va_list ap;
#if defined(__STDC__) && defined(STDC_HEADERS)
va_start(ap, format);
#else
char *format;
va_start(ap);
format = va_arg(ap, char *);
#endif
vsprintf(buff, format, ap);
va_end(ap);
buff[9999] = '\0';
return buff;
}
/* ---------------------------------------------------------------------------
* insert_first, remove_first: Insert or remove objects from lists.
*/
dbref
insert_first(head, thing)
dbref head, thing;
{
s_Next(thing, head);
return thing;
}
dbref
remove_first(head, thing)
dbref head, thing;
{
dbref prev;
if (head == thing)
return (Next(thing));
DOLIST(prev, head) {
if (Next(prev) == thing) {
s_Next(prev, Next(thing));
return head;
}
}
return head;
}
/* ---------------------------------------------------------------------------
* reverse_list: Reverse the order of members in a list.
*/
dbref
reverse_list(list)
dbref list;
{
dbref newlist, rest;
newlist = NOTHING;
while (list != NOTHING) {
rest = Next(list);
s_Next(list, newlist);
newlist = list;
list = rest;
}
return newlist;
}
/* ---------------------------------------------------------------------------
* member - indicate if thing is in list
*/
int
member(thing, list)
dbref thing, list;
{
DOLIST(list, list) {
if (list == thing)
return 1;
}
return 0;
}
/* ---------------------------------------------------------------------------
* is_integer, is_number: see if string contains just a number.
*/
int
is_integer(str)
char *str;
{
while (*str && isspace(*str))
str++; /* Leading spaces */
if (*str == '-') { /* Leading minus */
str++;
if (!*str)
return 0; /* but not if just a minus */
}
if (!isdigit(*str)) /* Need at least 1 integer */
return 0;
while (*str && isdigit(*str))
str++; /* The number (int) */
while (*str && isspace(*str))
str++; /* Trailing spaces */
return (*str ? 0 : 1);
}
int
is_number(str)
char *str;
{
int got_one;
while (*str && isspace(*str))
str++; /* Leading spaces */
if (*str == '-') { /* Leading minus */
str++;
if (!*str)
return 0; /* but not if just a minus */
}
got_one = 0;
if (isdigit(*str))
got_one = 1; /* Need at least one digit */
while (*str && isdigit(*str))
str++; /* The number (int) */
if (*str == '.')
str++; /* decimal point */
if (isdigit(*str))
got_one = 1; /* Need at least one digit */
while (*str && isdigit(*str))
str++; /* The number (fract) */
while (*str && isspace(*str))
str++; /* Trailing spaces */
return ((*str || !got_one) ? 0 : 1);
}
#ifndef STANDALONE
int
could_doit(player, thing, locknum)
dbref player, thing;
int locknum;
{
char *key;
dbref aowner;
int aflags, doit;
/* no if nonplayer trys to get key */
if (!isPlayer(player) && Key(thing)) {
return 0;
}
key = atr_get(thing, locknum, &aowner, &aflags);
doit = eval_boolexp_atr(player, thing, thing, key);
free_lbuf(key);
return doit;
}
int
can_see(player, thing, can_see_loc)
dbref player, thing;
int can_see_loc;
{
/* Don't show if all the following apply:
* Sleeping players should not be seen.
* The thing is a disconnected player.
* The player is not a puppet.
*/
if (mudconf.dark_sleepers && isPlayer(thing) &&
!Connected(thing) && !Puppet(thing)) {
return 0;
}
/* You don't see yourself or exits */
if ((player == thing) || isExit(thing)) {
return 0;
}
/* If loc is not dark, you see it if it's not dark or you control it.
* If loc is dark, you see it if you control it. Seeing your own
* dark objects is controlled by mudconf.see_own_dark.
* In dark locations, you also see things that are LIGHT and !DARK.
*/
if (can_see_loc) {
return (!Dark(thing) ||
(mudconf.see_own_dark && MyopicExam(player, thing)));
} else {
return ((Light(thing) && !Dark(thing)) ||
(mudconf.see_own_dark && MyopicExam(player, thing)));
}
}
static int
canpayquota(player, who, cost, objtype)
dbref player, who;
int cost, objtype;
{
dbref aowner;
char *quota_str;
int aflags;
register int quota, qtype;
int q_list[5];
/* If no cost, succeed */
if (cost <= 0)
return 1;
#ifndef STANDALONE
/* determine basic quota */
load_quota(q_list, Owner(who), A_RQUOTA);
quota = q_list[QTYPE_ALL];
/* enough to build? Wizards always have enough. */
quota -= cost;
if ((quota < 0) && !Wizard(who) && !Wizard(Owner(who)))
return 0;
if (mudconf.typed_quotas) {
quota = q_list[type_quota(objtype)];
if ((quota <= 0) && !Wizard(player) && !Wizard(Owner(player)))
return 0;
}
#endif /* ! STANDALONE */
return 1;
}
int
canpayfees(player, who, pennies, quota, objtype)
dbref player, who;
int pennies, quota, objtype;
{
if (!Wizard(who) && !Wizard(Owner(who)) &&
!Immortal(who) && !Immortal(Owner(who)) &&
(Pennies(Owner(who)) < pennies)) {
if (player == who) {
notify(player,
tprintf("Sorry, you don't have enough %s.",
mudconf.many_coins));
} else {
notify(player,
tprintf("Sorry, that player doesn't have enough %s.",
mudconf.many_coins));
}
return 0;
}
if (mudconf.quotas) {
if (!canpayquota(player, who, quota, objtype)) {
if (player == who) {
notify(player,
"Sorry, your building contract has run out.");
} else {
notify(player,
"Sorry, that player's building contract has run out.");
}
return 0;
}
}
return 1;
}
#endif /* STANDALONE */
static int
type_quota(objtype)
int objtype;
{
int qtype;
/* determine typed quota */
switch (objtype) {
case TYPE_ROOM:
qtype = QTYPE_ROOM;
break;
case TYPE_EXIT:
qtype = QTYPE_EXIT;
break;
case TYPE_PLAYER:
qtype = QTYPE_PLAYER;
break;
default:
qtype = QTYPE_THING;
}
return (qtype);
}
int
payfees(who, pennies, quota, objtype)
dbref who;
int pennies, quota, objtype;
{
/* You /must/ have called canpayfees() first. If not, your
* database will be eaten by rabid squirrels. */
if (mudconf.quotas)
pay_quota(who, quota, objtype);
return payfor(who, pennies);
}
static int
pay_quota(who, cost, objtype)
dbref who;
int cost, objtype;
{
/* If no cost, succeed. Negative costs /must/ be managed, however */
if (cost == 0)
return 1;
add_quota(who, -cost, type_quota(objtype));
return 1;
}
int
payfor(who, cost)
dbref who;
int cost;
{
dbref tmp;
if (Wizard(who) || Wizard(Owner(who)) ||
Immortal(who) || Immortal(Owner(who))) {
return 1;
}
who = Owner(who);
if ((tmp = Pennies(who)) >= cost) {
s_Pennies(who, tmp - cost);
return 1;
}
return 0;
}
static void
add_quota(who, payment, type)
dbref who;
int payment, type;
{
#ifndef STANDALONE
dbref aowner;
int aflags;
char buf[20], *quota;
int q_list[5];
load_quota(q_list, Owner(who), A_RQUOTA);
q_list[QTYPE_ALL] += payment;
if (mudconf.typed_quotas)
q_list[type] += payment;
save_quota(q_list, Owner(who), A_RQUOTA);
#endif /* ! STANDALONE */
}
void
giveto(who, pennies)
dbref who;
int pennies;
{
if (Wizard(who) || Wizard(Owner(who)) ||
Immortal(who) || Immortal(Owner(who))) {
return;
}
who = Owner(who);
s_Pennies(who, Pennies(who) + pennies);
}
int
ok_name(name)
const char *name;
{
const char *cp;
/* Disallow leading spaces */
if (isspace(*name))
return 0;
/* Only printable characters */
for (cp = name; cp && *cp; cp++) {
if (!isprint(*cp))
return 0;
}
/* Disallow trailing spaces */
cp--;
if (isspace(*cp))
return 0;
/* Exclude names that start with or contain certain magic cookies */
return (name &&
*name &&
*name != LOOKUP_TOKEN &&
*name != NUMBER_TOKEN &&
*name != NOT_TOKEN &&
!index(name, ARG_DELIMITER) &&
!index(name, AND_TOKEN) &&
!index(name, OR_TOKEN) &&
string_compare(name, "me") &&
string_compare(name, "home") &&
string_compare(name, "here"));
}
int
ok_player_name(name)
const char *name;
{
const char *cp, *good_chars;
/* No leading spaces */
if (isspace(*name))
return 0;
/* Not too long and a good name for a thing */
if (!ok_name(name) || (strlen(name) >= PLAYER_NAME_LIMIT))
return 0;
#ifndef STANDALONE
if (mudconf.name_spaces)
good_chars = " `$_-.,'";
else
good_chars = "`$_-.,'";
#else
good_chars = " `$_-.,'";
#endif
/* Make sure name only contains legal characters */
for (cp = name; cp && *cp; cp++) {
if (isalnum(*cp))
continue;
if (!index(good_chars, *cp))
return 0;
}
return 1;
}
int
ok_attr_name(attrname)
const char *attrname;
{
const char *scan;
if (!isalpha(*attrname))
return 0;
for (scan = attrname; *scan; scan++) {
if (isalnum(*scan))
continue;
if (!(index("-_.@#$^&~=+", *scan)))
return 0;
}
return 1;
}
#ifndef STANDALONE
int
ok_password(password, player)
const char *password;
dbref player;
{
const char *scan;
int num_upper = 0;
int num_special = 0;
int num_lower = 0;
if (*password == '\0') {
notify_quiet(player, "Null passwords are not allowed.");
return 0;
}
for (scan = password; *scan; scan++) {
if (!(isprint(*scan) && !isspace(*scan))) {
notify_quiet(player, "Illegal character in password.");
return 0;
}
if (isupper(*scan))
num_upper++;
else if (islower(*scan))
num_lower++;
else if ((*scan != '\'') && (*scan != '-'))
num_special++;
}
/* Needed. Change it if you like, but be sure yours is the same. */
if ((strlen(password) == 13) &&
(password[0] == 'X') &&
(password[1] == 'X'))
return 0;
if (mudconf.safer_passwords) {
if (num_upper < 1) {
notify_quiet(player,
"The password must contain at least one capital letter.");
return 0;
}
if (num_lower < 1) {
notify_quiet(player,
"The password must contain at least one lowercase letter.");
return 0;
}
if (num_special < 1) {
notify_quiet(player,
"The password must contain at least one number or a symbol other than the apostrophe or dash.");
return 0;
}
}
return 1;
}
/* ---------------------------------------------------------------------------
* handle_ears: Generate the 'grows ears' and 'loses ears' messages.
*/
void
handle_ears(thing, could_hear, can_hear)
dbref thing;
int could_hear, can_hear;
{
char *buff, *bp;
int gender;
if (!could_hear && can_hear) {
buff = alloc_lbuf("handle_ears.grow");
strcpy(buff, Name(thing));
if (isExit(thing)) {
for (bp = buff; *bp && (*bp != ';'); bp++);
*bp = '\0';
}
gender = get_gender(thing);
notify_check(thing, thing,
tprintf("%s %s now listening.",
buff, (gender == 4) ? "are" : "is"),
(MSG_ME | MSG_NBR | MSG_LOC | MSG_INV));
free_lbuf(buff);
} else if (could_hear && !can_hear) {
buff = alloc_lbuf("handle_ears.lose");
strcpy(buff, Name(thing));
if (isExit(thing)) {
for (bp = buff; *bp && (*bp != ';'); bp++);
*bp = '\0';
}
gender = get_gender(thing);
notify_check(thing, thing,
tprintf("%s %s no longer listening.",
buff, (gender == 4) ? "are" : "is"),
(MSG_ME | MSG_NBR | MSG_LOC | MSG_INV));
free_lbuf(buff);
}
}
/* for lack of better place the @switch code is here */
void
do_switch(player, cause, key, expr, args, nargs, cargs, ncargs)
dbref player, cause;
int key, nargs, ncargs;
char *expr, *args[], *cargs[];
{
int a, any;
char *buff, *tbuf;
if (!expr || (nargs <= 0))
return;
if (key == SWITCH_DEFAULT) {
if (mudconf.switch_df_all)
key = SWITCH_ANY;
else
key = SWITCH_ONE;
}
/* now try a wild card match of buff with stuff in coms */
any = 0;
for (a = 0; (a < (nargs - 1)) && args[a] && args[a + 1]; a += 2) {
buff = exec(player, cause, EV_FCHECK | EV_EVAL | EV_TOP, args[a],
cargs, ncargs);
if (wild_match(buff, expr, (char **) NULL, 0, 1)) {
tbuf = replace_string(SWITCH_VAR, expr, args[a + 1]);
wait_que(player, cause, 0, NOTHING, tbuf,
cargs, ncargs, mudstate.global_regs);
free_lbuf(tbuf);
if (key == SWITCH_ONE) {
free_lbuf(buff);
return;
}
any = 1;
}
free_lbuf(buff);
}
if ((a < nargs) && !any && args[a]) {
tbuf = replace_string(SWITCH_VAR, expr, args[a]);
wait_que(player, cause, 0, NOTHING, tbuf, cargs, ncargs,
mudstate.global_regs);
free_lbuf(tbuf);
}
}
/* ---------------------------------------------------------------------------
* Support for all the various @program things. This is taken from
* TinyMUX, with modifications.
*
* @program "glues" a user's input to a command. Once executed, the
* first string input from any of the doer's logged in descriptors
* will go into A_PROGMSG, which can be substituted in <command>
* with %0. Commands already queued by the doer will be processed
* normally.
*/
void handle_prog(d, message)
DESC *d;
char *message;
{
DESC *all;
PROG *prog;
char *cmd;
dbref aowner;
int aflags, i;
/* Allow the player to pipe a command while in interactive mode.
* Use the telnet protocol's GOAHEAD command to show prompt.
*/
if (*message == '|') {
do_command(d, message + 1);
/* We might have been handed a @quitprogram, so check to make
* sure we really do want to display a prompt.
*/
if (d->program_data != NULL)
queue_rawstring(d, (char *) "> \377\371");
return;
}
cmd = atr_get(d->player, A_PROGCMD, &aowner, &aflags);
wait_que(d->program_data->wait_cause, d->player, 0, NOTHING, cmd,
(char **) &message, 1, (char **) d->program_data->wait_regs);
/* First, set 'all' to a descriptor we find for this player.
* Then, free up associated data, and set info for all
* the player's descriptors to NULL.
*/
all = (DESC *) nhashfind(d->player, &mudstate.desc_htab);
for (i = 0; i < MAX_GLOBAL_REGS; i++)
free_lbuf(all->program_data->wait_regs[i]);
free(all->program_data);
DESC_ITER_PLAYER(d->player, all)
all->program_data = NULL;
atr_clr(d->player, A_PROGCMD);
free_lbuf(cmd);
}
static int ok_program(player, doer)
dbref player;
dbref doer;
{
if (! Controls(player, doer)) {
notify(player, "Permission denied.");
return 0;
}
if (!isPlayer(doer) || !Good_obj(doer)) {
notify(player, "No such player.");
return 0;
}
if (!Connected(doer)) {
notify(player, "Sorry, that player is not connected.");
return 0;
}
return 1;
}
void do_quitprog(player, cause, key, name)
dbref player, cause;
int key;
char *name;
{
DESC *d;
dbref doer;
int i, isprog = 0;
if (*name)
doer = match_thing(player, name);
else
doer = player;
if (!ok_program(player, doer))
return;
DESC_ITER_PLAYER(doer, d) {
if (d->program_data != NULL)
isprog = 1;
}
if (!isprog) {
notify(player, "That player is not in a program.");
return;
}
/* Clear out information. */
d = (DESC *) nhashfind(doer, &mudstate.desc_htab);
for (i = 0; i < MAX_GLOBAL_REGS; i++)
free_lbuf(d->program_data->wait_regs[i]);
free(d->program_data);
DESC_ITER_PLAYER(doer, d) {
d->program_data = NULL;
}
atr_clr(doer, A_PROGCMD);
notify(player, "Program cleared.");
notify(doer, "Your program has been terminated.");
}
void do_prog(player, cause, key, name, command)
dbref player, cause;
int key;
char *name, *command;
{
DESC *d;
PROG *program;
int i, atr, aflags;
dbref doer, thing, aowner;
ATTR *ap;
char *attrib, *msg;
if (!name || !*name) {
notify(player, "No players specified.");
return;
}
doer = match_thing(player, name);
if (!ok_program(player, doer))
return;
msg = command;
attrib = parse_to(&msg, ':', 1);
if (msg && *msg) {
notify(doer, msg);
}
parse_attrib(player, attrib, &thing, &atr);
if (atr == NOTHING) {
notify(player, "No such attribute.");
return;
}
if (!atr_get_info(thing, atr, &aowner, &aflags)) {
notify(player, "Attribute not present on object.");
return;
}
ap = atr_num(atr);
if ((!God(thing) && See_attr(player, thing, ap, aowner, aflags) &&
(Wizard(player) || (aowner == Owner(player)))) || God(player)) {
atr_add_raw(doer, A_PROGCMD, atr_get_raw(thing, atr));
} else {
notify(player, "Permission denied.");
return;
}
/* Check if cause already has @program input pending. */
DESC_ITER_PLAYER(doer, d) {
if (d->program_data != NULL) {
notify(player, "Input already pending.");
return;
}
}
program = (PROG *) malloc(sizeof(PROG));
program->wait_cause = player;
for (i = 0; i < MAX_GLOBAL_REGS; i++) {
program->wait_regs[i] = alloc_lbuf("prog_regs");
strcpy(program->wait_regs[i], mudstate.global_regs[i]);
}
/* Wait for stuff. Use telnet protocol's GOAHEAD command to show
* prompt.
*/
DESC_ITER_PLAYER(doer, d) {
d->program_data = program;
queue_rawstring(d, (char *) "> \377\371");
}
}
/* ---------------------------------------------------------------------------
* do_comment: Implement the @@ (comment) command. Very cpu-intensive :-)
*/
void
do_comment(player, cause, key)
dbref player, cause;
int key;
{
}
static dbref
promote_dflt(old, new)
dbref old, new;
{
switch (new) {
case NOPERM:
return NOPERM;
case AMBIGUOUS:
if (old == NOPERM)
return old;
else
return new;
}
if ((old == NOPERM) || (old == AMBIGUOUS))
return old;
return NOTHING;
}
dbref
match_possessed(player, thing, target, dflt, check_enter)
dbref player, thing, dflt;
char *target;
int check_enter;
{
dbref result, result1;
int control;
char *buff, *start, *place, *s1, *d1, *temp;
/* First, check normally */
if (Good_obj(dflt))
return dflt;
/* Didn't find it directly. Recursively do a contents check */
start = target;
while (*target) {
/* Fail if no ' characters */
place = target;
target = (char *) index(place, '\'');
if ((target == NULL) || !*target)
return dflt;
/* If string started with a ', skip past it */
if (place == target) {
target++;
continue;
}
/* If next character is not an s or a space, skip past */
temp = target++;
if (!*target)
return dflt;
if ((*target != 's') && (*target != 'S') && (*target != ' '))
continue;
/* If character was not a space make sure the following
* character is a space.
*/
if (*target != ' ') {
target++;
if (!*target)
return dflt;
if (*target != ' ')
continue;
}
/* Copy the container name to a new buffer so we can
* terminate it.
*/
buff = alloc_lbuf("is_posess");
for (s1 = start, d1 = buff; *s1 && (s1 < temp); *d1++ = (*s1++));
*d1 = '\0';
/* Look for the container here and in our inventory. Skip
* past if we can't find it.
*/
init_match(thing, buff, NOTYPE);
if (player == thing) {
match_neighbor();
match_possession();
} else {
match_possession();
}
result1 = match_result();
free_lbuf(buff);
if (!Good_obj(result1)) {
dflt = promote_dflt(dflt, result1);
continue;
}
/* If we don't control it and it is either dark or opaque,
* skip past.
*/
control = Controls(player, result1);
if ((Dark(result1) || Opaque(result1)) && !control) {
dflt = promote_dflt(dflt, NOTHING);
continue;
}
/* Validate object has the ENTER bit set, if requested */
if ((check_enter) && !Enter_ok(result1) && !control) {
dflt = promote_dflt(dflt, NOPERM);
continue;
}
/* Look for the object in the container */
init_match(result1, target, NOTYPE);
match_possession();
result = match_result();
result = match_possessed(player, result1, target, result,
check_enter);
if (Good_obj(result))
return result;
dflt = promote_dflt(dflt, result);
}
return dflt;
}
/* ---------------------------------------------------------------------------
* parse_range: break up <what>,<low>,<high> syntax
*/
void
parse_range(name, low_bound, high_bound)
char **name;
dbref *low_bound, *high_bound;
{
char *buff1, *buff2;
buff1 = *name;
if (buff1 && *buff1)
*name = parse_to(&buff1, ',', EV_STRIP_TS);
if (buff1 && *buff1) {
buff2 = parse_to(&buff1, ',', EV_STRIP_TS);
if (buff1 && *buff1) {
while (*buff1 && isspace(*buff1))
buff1++;
if (*buff1 == NUMBER_TOKEN)
buff1++;
*high_bound = atoi(buff1);
if (*high_bound >= mudstate.db_top)
*high_bound = mudstate.db_top - 1;
} else {
*high_bound = mudstate.db_top - 1;
}
while (*buff2 && isspace(*buff2))
buff2++;
if (*buff2 == NUMBER_TOKEN)
buff2++;
*low_bound = atoi(buff2);
if (*low_bound < 0)
*low_bound = 0;
} else {
*low_bound = 0;
*high_bound = mudstate.db_top - 1;
}
}
int
parse_thing_slash(player, thing, after, it)
dbref player, *it;
char *thing, **after;
{
char *str;
/* get name up to / */
for (str = thing; *str && (*str != '/'); str++);
/* If no / in string, return failure */
if (!*str) {
*after = NULL;
*it = NOTHING;
return 0;
}
*str++ = '\0';
*after = str;
/* Look for the object */
init_match(player, thing, NOTYPE);
match_everything(MAT_EXIT_PARENTS);
*it = match_result();
/* Return status of search */
return (Good_obj(*it));
}
extern NAMETAB lock_sw[];
int
get_obj_and_lock(player, what, it, attr, errmsg)
dbref player, *it;
char *what, *errmsg;
ATTR **attr;
{
char *str, *tbuf;
int anum;
tbuf = alloc_lbuf("get_obj_and_lock");
strcpy(tbuf, what);
if (parse_thing_slash(player, tbuf, &str, it)) {
/* <obj>/<lock> syntax, use the named lock */
anum = search_nametab(player, lock_sw, str);
if (anum == -1) {
free_lbuf(tbuf);
strcpy(errmsg, "#-1 LOCK NOT FOUND");
return 0;
}
} else {
/* Not <obj>/<lock>, do a normal get of the default lock */
*it = match_thing(player, what);
if (!Good_obj(*it)) {
free_lbuf(tbuf);
strcpy(errmsg, "#-1 NOT FOUND");
return 0;
}
anum = A_LOCK;
}
/* Get the attribute definition, fail if not found */
free_lbuf(tbuf);
*attr = atr_num(anum);
if (!(*attr)) {
strcpy(errmsg, "#-1 LOCK NOT FOUND");
return 0;
}
return 1;
}
#endif /* STANDALONE */
/* ---------------------------------------------------------------------------
* where_is: Returns place where obj is linked into a list.
* ie. location for players/things, source for exits, NOTHING for rooms.
*/
dbref
where_is(what)
dbref what;
{
dbref loc;
if (!Good_obj(what))
return NOTHING;
switch (Typeof(what)) {
case TYPE_PLAYER:
case TYPE_THING:
case TYPE_ZONE:
loc = Location(what);
break;
case TYPE_EXIT:
loc = Exits(what);
break;
default:
loc = NOTHING;
break;
}
return loc;
}
/* ---------------------------------------------------------------------------
* where_room: Return room containing player, or NOTHING if no room or
* recursion exceeded. If player is a room, returns itself.
*/
dbref
where_room(what)
dbref what;
{
int count;
for (count = mudconf.ntfy_nest_lim; count > 0; count--) {
if (!Good_obj(what))
break;
if (isRoom(what))
return what;
if (!Has_location(what))
break;
what = Location(what);
}
return NOTHING;
}
int
locatable(player, it, cause)
dbref player, it, cause;
{
dbref loc_it, room_it;
int findable_room;
/* No sense if trying to locate a bad object */
if (!Good_obj(it))
return 0;
loc_it = where_is(it);
/* Succeed if we can examine the target, if we are the target,
* if we can examine the location, if a wizard caused the lookup,
* or if the target caused the lookup.
*/
if (Examinable(player, it) ||
(loc_it == player) ||
((loc_it != NOTHING) &&
(Examinable(player, loc_it) || loc_it == where_is(player))) ||
Wizard(cause) ||
(it == cause))
return 1;
room_it = where_room(it);
if (Good_obj(room_it))
findable_room = !Hideout(room_it);
else
findable_room = 1;
/* Succeed if we control the containing room or if the target is
* findable and the containing room is not unfindable.
*/
if (((room_it != NOTHING) && Examinable(player, room_it)) ||
(Findable(it) && findable_room))
return 1;
/* We can't do it. */
return 0;
}
/* ---------------------------------------------------------------------------
* nearby: Check if thing is nearby player (in inventory, in same room, or
* IS the room.
*/
int
nearby(player, thing)
dbref player, thing;
{
int thing_loc, player_loc;
if (!Good_obj(player) || !Good_obj(thing))
return 0;
thing_loc = where_is(thing);
if (thing_loc == player)
return 1;
player_loc = where_is(player);
if ((thing_loc == player_loc) || (thing == player_loc))
return 1;
return 0;
}
/* ---------------------------------------------------------------------------
* next_exit: return next exit that is ok to see.
*/
dbref
next_exit(player, this, exam_here)
dbref player, this;
int exam_here;
{
if (isRoom(this))
return NOTHING;
if (isExit(this) && exam_here)
return this;
while ((this != NOTHING) && Dark(this) && !Light(this) &&
!Examinable(player, this))
this = Next(this);
return this;
}
#ifndef STANDALONE
/* ---------------------------------------------------------------------------
* did_it: Have player do something to/with thing
*/
void
did_it(player, thing, what, def, owhat, odef, awhat, args, nargs)
dbref player, thing;
int what, owhat, awhat, nargs;
char *args[];
const char *def, *odef;
{
char *d, *buff, *act, *charges, *preserve[MAX_GLOBAL_REGS];
dbref loc, aowner;
int num, aflags, need_pres;
/* If we need to call exec() from within this function, we first save
* the state of the global registers, in order to avoid munging them
* inappropriately. Do note that the restoration to their original
* values occurs BEFORE the execution of the @a-attribute. Therefore,
* any changing of setq() values done in the @-attribute and @o-attribute
* will NOT be passed on. This prevents odd behaviors that result from
* odd @verbs and so forth (the idea is to preserve the caller's control
* of the global register values).
*/
need_pres = 0;
/* message to player */
if (what > 0) {
d = atr_pget(thing, what, &aowner, &aflags);
if (*d) {
need_pres = 1;
save_global_regs("did_it_save", preserve);
buff = exec(thing, player, EV_EVAL | EV_FIGNORE | EV_TOP,
d, args, nargs);
#ifdef PUEBLO_SUPPORT
if ((aflags & AF_HTML) && Html(player)) {
char *buff_cp = buff + strlen(buff);
safe_str("\r\n", buff, &buff_cp);
notify_html(player, buff);
}
else
notify(player, buff);
#else
notify(player, buff);
#endif /* PUEBLO_SUPPORT */
free_lbuf(buff);
} else if (def) {
notify(player, def);
}
free_lbuf(d);
} else if ((what < 0) && def) {
notify(player, def);
}
/* message to neighbors */
if ((owhat > 0) && Has_location(player) &&
Good_obj(loc = Location(player))) {
d = atr_pget(thing, owhat, &aowner, &aflags);
if (*d) {
if (!need_pres) {
need_pres = 1;
save_global_regs("did_it_save", preserve);
}
buff = exec(thing, player, EV_EVAL | EV_FIGNORE | EV_TOP,
d, args, nargs);
notify_except2(loc, player, player, thing,
tprintf("%s %s", Name(player), buff));
free_lbuf(buff);
} else if (odef) {
notify_except2(loc, player, player, thing,
tprintf("%s %s", Name(player), odef));
}
free_lbuf(d);
} else if ((owhat < 0) && def && Has_location(player) &&
Good_obj(loc = Location(player))) {
if (odef) {
notify_except2(loc, player, player, thing,
tprintf("%s %s", Name(player), odef));
}
}
/* If we preserved the state of the global registers, restore them. */
if (need_pres)
restore_global_regs("did_it_restore", preserve);
/* do the action attribute */
if (awhat > 0) {
if (*(act = atr_pget(thing, awhat, &aowner, &aflags))) {
charges = atr_pget(thing, A_CHARGES, &aowner, &aflags);
if (*charges) {
num = atoi(charges);
if (num > 0) {
buff = alloc_sbuf("did_it.charges");
sprintf(buff, "%d", num - 1);
atr_add_raw(thing, A_CHARGES, buff);
free_sbuf(buff);
} else if (*(buff = atr_pget(thing, A_RUNOUT, &aowner, &aflags))) {
free_lbuf(act);
act = buff;
} else {
free_lbuf(act);
free_lbuf(buff);
free_lbuf(charges);
return;
}
}
free_lbuf(charges);
wait_que(thing, player, 0, NOTHING, act, args, nargs,
mudstate.global_regs);
}
free_lbuf(act);
}
}
/* ---------------------------------------------------------------------------
* do_verb: Command interface to did_it.
*/
void
do_verb(player, cause, key, victim_str, args, nargs)
dbref player, cause;
int key, nargs;
char *victim_str, *args[];
{
dbref actor, victim, aowner;
int what, owhat, awhat, nxargs, should_restrict, aflags, i;
ATTR *ap;
const char *whatd, *owhatd;
char *xargs[10];
/* Look for the victim */
if (!victim_str || !*victim_str) {
notify(player, "Nothing to do.");
return;
}
/* Get the victim */
init_match(player, victim_str, NOTYPE);
match_everything(MAT_EXIT_PARENTS);
victim = noisy_match_result();
if (!Good_obj(victim))
return;
/* Get the actor. Default is my cause */
if ((nargs >= 1) && args[0] && *args[0]) {
init_match(player, args[0], NOTYPE);
match_everything(MAT_EXIT_PARENTS);
actor = noisy_match_result();
if (!Good_obj(actor))
return;
} else {
actor = cause;
}
/* Check permissions. There are two possibilities
* 1: Player controls both victim and actor. In this case victim runs
* his action list.
* 2: Player controls actor. In this case victim does not run his
* action list and any attributes that player cannot read from
* victim are defaulted.
*/
if (!controls(player, actor)) {
notify_quiet(player, "Permission denied,");
return;
}
should_restrict = !controls(player, victim);
what = -1;
owhat = -1;
awhat = -1;
whatd = NULL;
owhatd = NULL;
nxargs = 0;
/* Get invoker message attribute */
if (nargs >= 2) {
ap = atr_str(args[1]);
if (ap && (ap->number > 0))
what = ap->number;
}
/* Get invoker message default */
if ((nargs >= 3) && args[2] && *args[2]) {
whatd = args[2];
}
/* Get others message attribute */
if (nargs >= 4) {
ap = atr_str(args[3]);
if (ap && (ap->number > 0))
owhat = ap->number;
}
/* Get others message default */
if ((nargs >= 5) && args[4] && *args[4]) {
owhatd = args[4];
}
/* Get action attribute */
if (nargs >= 6) {
ap = atr_str(args[5]);
if (ap)
awhat = ap->number;
}
/* Get arguments */
if (nargs >= 7) {
parse_arglist(victim, actor, args[6], '\0',
EV_STRIP_LS | EV_STRIP_TS, xargs, 10, (char **) NULL, 0);
for (nxargs = 0; (nxargs < 10) && xargs[nxargs]; nxargs++);
}
/* If player doesn't control both, enforce visibility restrictions.
* Regardless of control, we still check if the player can read the
* attribute, since we don't want him getting wiz-readable-only attrs.
*/
atr_get_info(victim, what, &aowner, &aflags);
if (what != -1) {
ap = atr_num(what);
if (!ap || !Read_attr(player, victim, ap, aowner, aflags) ||
(should_restrict &&
((ap->number == A_DESC) && !mudconf.read_rem_desc &&
!controls(player, victim) && !nearby(player, victim))))
what = -1;
}
atr_get_info(victim, owhat, &aowner, &aflags);
if (owhat != -1) {
ap = atr_num(owhat);
if (!ap || !Read_attr(player, victim, ap, aowner, aflags) ||
(should_restrict &&
((ap->number == A_DESC) && !mudconf.read_rem_desc &&
!controls(player, victim) && !nearby(player, victim))))
owhat = -1;
}
if (should_restrict)
awhat = 0;
/* Go do it */
did_it(actor, victim, what, whatd, owhat, owhatd, awhat,
xargs, nxargs);
/* Free user args */
for (i = 0; i < nxargs; i++)
free_lbuf(xargs[i]);
}
#endif /* STANDALONE */