/* funobj.c - object functions */ /* $Id: funobj.c,v 1.73 2004/02/23 04:35:14 rmg Exp $ */ #include "copyright.h" #include "autoconf.h" #include "config.h" #include "alloc.h" /* required by mudconf */ #include "flags.h" /* required by mudconf */ #include "htab.h" /* required by mudconf */ #include "mudconf.h" /* required by code */ #include "db.h" /* required by externs */ #include "externs.h" /* required by code */ #include "functions.h" /* required by code */ #include "match.h" /* required by code */ #include "attrs.h" /* required by code */ #include "powers.h" /* required by code */ #include "walkdb.h" /* required by code */ extern char *FDECL(upcasestr, (char *)); extern dbref FDECL(find_connected_ambiguous, (dbref, char *)); extern NAMETAB attraccess_nametab[]; extern NAMETAB indiv_attraccess_nametab[]; /* --------------------------------------------------------------------------- * nearby_or_control: Check if player is near or controls thing */ #define nearby_or_control(p,t) \ (Good_obj(p) && Good_obj(t) && (Controls(p,t) || nearby(p,t))) /* --------------------------------------------------------------------------- * fun_con: Returns first item in contents list of object/room */ FUNCTION(fun_con) { dbref it; it = match_thing(player, fargs[0]); if (Good_loc(it) && (Examinable(player, it) || (where_is(player) == it) || (it == cause))) { safe_dbref(buff, bufc, Contents(it)); return; } safe_nothing(buff, bufc); return; } /* --------------------------------------------------------------------------- * fun_exit: Returns first exit in exits list of room. */ FUNCTION(fun_exit) { dbref it, exit; int key; it = match_thing(player, fargs[0]); if (Good_obj(it) && Has_exits(it) && Good_obj(Exits(it))) { key = 0; if (Examinable(player, it)) key |= VE_LOC_XAM; if (Dark(it)) key |= VE_LOC_DARK; DOLIST(exit, Exits(it)) { if (Exit_Visible(exit, player, key)) { safe_dbref(buff, bufc, exit); return; } } } safe_nothing(buff, bufc); return; } /* --------------------------------------------------------------------------- * fun_next: return next thing in contents or exits chain */ FUNCTION(fun_next) { dbref it, loc, exit, ex_here; int key; it = match_thing(player, fargs[0]); if (Good_obj(it) && Has_siblings(it)) { loc = where_is(it); ex_here = Good_obj(loc) ? Examinable(player, loc) : 0; if (ex_here || (loc == player) || (loc == where_is(player))) { if (!isExit(it)) { safe_dbref(buff, bufc, Next(it)); return; } else { key = 0; if (ex_here) key |= VE_LOC_XAM; if (Dark(loc)) key |= VE_LOC_DARK; DOLIST(exit, it) { if ((exit != it) && Exit_Visible(exit, player, key)) { safe_dbref(buff, bufc, exit); return; } } } } } safe_nothing(buff, bufc); return; } /* --------------------------------------------------------------------------- * handle_loc: Locate an object (LOC, WHERE). * loc(): Returns the location of something. * where(): Returns the "true" location of something */ FUNCTION(handle_loc) { dbref it; it = match_thing(player, fargs[0]); if (locatable(player, it, cause)) { safe_dbref(buff, bufc, Is_Func(LOCFN_WHERE) ? where_is(it) : Location(it)); } else { safe_nothing(buff, bufc); } } /* --------------------------------------------------------------------------- * fun_rloc: Returns the recursed location of something (specifying #levels) */ FUNCTION(fun_rloc) { int i, levels; dbref it; levels = atoi(fargs[1]); if (levels > mudconf.ntfy_nest_lim) levels = mudconf.ntfy_nest_lim; it = match_thing(player, fargs[0]); if (locatable(player, it, cause)) { for (i = 0; i < levels; i++) { if (Good_obj(it) && (Has_location(it) || isExit(it))) { it = Location(it); } else { break; } } safe_dbref(buff, bufc, it); return; } safe_nothing(buff, bufc); } /* --------------------------------------------------------------------------- * fun_room: Find the room an object is ultimately in. */ FUNCTION(fun_room) { dbref it; int count; it = match_thing(player, fargs[0]); if (locatable(player, it, cause)) { for (count = mudconf.ntfy_nest_lim; count > 0; count--) { it = Location(it); if (!Good_obj(it)) break; if (isRoom(it)) { safe_dbref(buff, bufc, it); return; } } safe_nothing(buff, bufc); } else if (isRoom(it)) { safe_dbref(buff, bufc, it); } else { safe_nothing(buff, bufc); } return; } /* --------------------------------------------------------------------------- * fun_owner: Return the owner of an object. */ FUNCTION(fun_owner) { dbref it, aowner; int atr, aflags; if (parse_attrib(player, fargs[0], &it, &atr, 1)) { if (atr == NOTHING) { it = NOTHING; } else { atr_pget_info(it, atr, &aowner, &aflags); it = aowner; } } else { it = match_thing(player, fargs[0]); if (Good_obj(it)) it = Owner(it); } safe_dbref(buff, bufc, it); } /* --------------------------------------------------------------------------- * fun_controls: Does x control y? */ FUNCTION(fun_controls) { dbref x, y; x = match_thing(player, fargs[0]); if (!Good_obj(x)) { safe_str("#-1 ARG1 NOT FOUND", buff, bufc); return; } y = match_thing(player, fargs[1]); if (!Good_obj(y)) { safe_str("#-1 ARG2 NOT FOUND", buff, bufc); return; } safe_bool(buff, bufc, Controls(x, y)); } /* --------------------------------------------------------------------------- * fun_sees: Can X see Y in the normal Contents list of the room. If X * or Y do not exist, 0 is returned. */ FUNCTION(fun_sees) { dbref it, thing; it = match_thing(player, fargs[0]); thing = match_thing(player, fargs[1]); if (!Good_obj(it) || !Good_obj(thing)) { safe_chr('0', buff, bufc); return; } safe_bool(buff, bufc, (isExit(thing) ? Can_See_Exit(it, thing, Darkened(it, Location(thing))) : Can_See(it, thing, Sees_Always(it, Location(thing))))); } /* --------------------------------------------------------------------------- * fun_nearby: Return whether or not obj1 is near obj2. */ FUNCTION(fun_nearby) { dbref obj1, obj2; obj1 = match_thing(player, fargs[0]); obj2 = match_thing(player, fargs[1]); if (!(nearby_or_control(player, obj1) || nearby_or_control(player, obj2))) { safe_chr('0', buff, bufc); } else { safe_bool(buff, bufc, nearby(obj1, obj2)); } } /* --------------------------------------------------------------------------- * Presence functions. * hears(<object>, <speaker>): Can <object> hear <speaker> speak? * knows(<object>, <target>): Can <object> know about <target>? * moves(<object>, <mover>): Can <object> see <mover> move? */ FUNCTION(handle_okpres) { int oper; dbref object, actor; object = match_thing(player, fargs[0]); actor = match_thing(player, fargs[1]); if (!Good_obj(object) || !Good_obj(actor)) { safe_chr('0', buff, bufc); return; } oper = Func_Mask(PRESFN_OPER); if (oper == PRESFN_HEARS) { safe_bool(buff, bufc, ((Unreal(actor) && !Check_Heard(object, actor)) || (Unreal(object) && !Check_Hears(actor, object))) ? 0 : 1); } else if (oper == PRESFN_MOVES) { safe_bool(buff, bufc, ((Unreal(actor) && !Check_Noticed(object, actor)) || (Unreal(object) && !Check_Notices(actor, object))) ? 0 : 1); } else if (oper == PRESFN_KNOWS) { safe_bool(buff, bufc, ((Unreal(actor) && !Check_Known(object, actor)) || (Unreal(object) && !Check_Knows(actor, object))) ? 0 : 1); } else { safe_chr('0', buff, bufc); } } /* --------------------------------------------------------------------------- * handle_name: Get object name (NAME, FULLNAME). * name(): Return the name of an object. * fullname(): Return the fullname of an object (good for exits). */ FUNCTION(handle_name) { dbref it; it = match_thing(player, fargs[0]); if (!Good_obj(it)) { return; } if (!mudconf.read_rem_name) { if (!nearby_or_control(player, it) && !isPlayer(it) && !Long_Fingers(player)) { safe_str("#-1 TOO FAR AWAY TO SEE", buff, bufc); return; } } if (!Is_Func(NAMEFN_FULLNAME) && isExit(it)) safe_exit_name(it, buff, bufc); else safe_name(it, buff, bufc); } /* --------------------------------------------------------------------------- * handle_pronoun: perform pronoun sub for object (OBJ, POSS, SUBJ, APOSS). */ FUNCTION(handle_pronoun) { dbref it; char *str; char *pronouns[4] = { "%o", "%p", "%s", "%a" }; it = match_thing(player, fargs[0]); if (!Good_obj(it) || (!isPlayer(it) && !nearby_or_control(player, it))) { safe_nomatch(buff, bufc); } else { str = pronouns[Func_Flags(fargs)]; exec(buff, bufc, it, it, it, 0, &str, (char **)NULL, 0); } } /* --------------------------------------------------------------------------- * Locks. */ FUNCTION(fun_lock) { dbref it, aowner; int aflags, alen; char *tbuf; ATTR *attr; struct boolexp *bool; /* Parse the argument into obj + lock */ if (!get_obj_and_lock(player, fargs[0], &it, &attr, buff, bufc)) return; /* Get the attribute and decode it if we can read it */ tbuf = atr_get(it, attr->number, &aowner, &aflags, &alen); if (Read_attr(player, it, attr, aowner, aflags)) { bool = parse_boolexp(player, tbuf, 1); free_lbuf(tbuf); tbuf = (char *)unparse_boolexp_function(player, bool); free_boolexp(bool); safe_str(tbuf, buff, bufc); } else free_lbuf(tbuf); } FUNCTION(fun_elock) { dbref it, victim, aowner; int aflags, alen; char *tbuf; ATTR *attr; struct boolexp *bool; /* Parse lock supplier into obj + lock */ if (!get_obj_and_lock(player, fargs[0], &it, &attr, buff, bufc)) return; /* Get the victim and ensure we can do it */ victim = match_thing(player, fargs[1]); if (!Good_obj(victim)) { safe_nomatch(buff, bufc); } else if (!nearby_or_control(player, victim) && !nearby_or_control(player, it)) { safe_str("#-1 TOO FAR AWAY", buff, bufc); } else { tbuf = atr_get(it, attr->number, &aowner, &aflags, &alen); if ((attr->flags & AF_IS_LOCK) || Read_attr(player, it, attr, aowner, aflags)) { if (Pass_Locks(victim)) { safe_chr('1', buff, bufc); } else { bool = parse_boolexp(player, tbuf, 1); safe_bool(buff, bufc, eval_boolexp(victim, it, it, bool)); free_boolexp(bool); } } else { safe_chr('0', buff, bufc); } free_lbuf(tbuf); } } FUNCTION(fun_elockstr) { dbref locked_obj, actor_obj; BOOLEXP *okey; locked_obj = match_thing(player, fargs[0]); actor_obj = match_thing(player, fargs[1]); if (!Good_obj(locked_obj) || !Good_obj(actor_obj)) { safe_nomatch(buff, bufc); } else if (!nearby_or_control(player, actor_obj)) { safe_str("#-1 TOO FAR AWAY", buff, bufc); } else if (!Controls(player, locked_obj)) { safe_noperm(buff, bufc); } else { okey = parse_boolexp(player, fargs[2], 0); if (okey == TRUE_BOOLEXP) { safe_str("#-1 INVALID KEY", buff, bufc); } else if (Pass_Locks(actor_obj)) { safe_chr('1', buff, bufc); } else { safe_ltos(buff, bufc, eval_boolexp(actor_obj, locked_obj, locked_obj, okey)); } free_boolexp(okey); } } /* --------------------------------------------------------------------------- * fun_xcon: Return a partial list of contents of an object, starting from * a specified element in the list and copying a specified number * of elements. */ FUNCTION(fun_xcon) { dbref thing, it; char *bb_p; int i, first, last; Delim osep; VaChk_Only_Out(4); it = match_thing(player, fargs[0]); bb_p = *bufc; if (Good_loc(it) && (Examinable(player, it) || (Location(player) == it) || (it == cause))) { first = atoi(fargs[1]); last = atoi(fargs[2]); if ((first > 0) && (last > 0)) { /* Move to the first object that we want */ for (thing = Contents(it), i = 1; (i < first) && (thing != NOTHING) && (Next(thing) != thing); thing = Next(thing), i++) ; /* Grab objects until we reach the last one we want */ for (i = 0; (i < last) && (thing != NOTHING) && (Next(thing) != thing); thing = Next(thing), i++) { if (*bufc != bb_p) { print_sep(&osep, buff, bufc); } safe_dbref(buff, bufc, thing); } } } else safe_nothing(buff, bufc); } /* --------------------------------------------------------------------------- * fun_lcon: Return a list of contents. */ FUNCTION(fun_lcon) { dbref thing, it; char *bb_p; Delim osep; VaChk_Only_Out(2); it = match_thing(player, fargs[0]); bb_p = *bufc; if (Good_loc(it) && (Examinable(player, it) || (Location(player) == it) || (it == cause))) { DOLIST(thing, Contents(it)) { if (*bufc != bb_p) { print_sep(&osep, buff, bufc); } safe_dbref(buff, bufc, thing); } } else safe_nothing(buff, bufc); } /* --------------------------------------------------------------------------- * fun_lexits: Return a list of exits. */ FUNCTION(fun_lexits) { dbref thing, it, parent; char *bb_p; int exam, lev, key; Delim osep; VaChk_Only_Out(2); it = match_thing(player, fargs[0]); if (!Good_obj(it) || !Has_exits(it)) { safe_nothing(buff, bufc); return; } exam = Examinable(player, it); if (!exam && (where_is(player) != it) && (it != cause)) { safe_nothing(buff, bufc); return; } /* Return info for all parent levels */ bb_p = *bufc; ITER_PARENTS(it, parent, lev) { /* Look for exits at each level */ if (!Has_exits(parent)) continue; key = 0; if (Examinable(player, parent)) key |= VE_LOC_XAM; if (Dark(parent)) key |= VE_LOC_DARK; if (Dark(it)) key |= VE_BASE_DARK; DOLIST(thing, Exits(parent)) { if (Exit_Visible(thing, player, key)) { if (*bufc != bb_p) { print_sep(&osep, buff, bufc); } safe_dbref(buff, bufc, thing); } } } return; } /* --------------------------------------------------------------------------- * fun_entrances: approximate equivalent of @entrances command. * * borrowed in part from PennMUSH. */ FUNCTION(fun_entrances) { dbref thing, i; char *bb_p; int low_bound, high_bound, control_thing; int find_ex, find_th, find_pl, find_rm; VaChk_Range(0, 4); if (nfargs >= 3) { low_bound = atoi(fargs[2] + (fargs[2][0] == NUMBER_TOKEN)); if (!Good_dbref(low_bound)) low_bound = 0; } else { low_bound = 0; } if (nfargs == 4) { high_bound = atoi(fargs[3] + (fargs[3][0] == NUMBER_TOKEN)); if (!Good_dbref(high_bound)) high_bound = mudstate.db_top - 1; } else { high_bound = mudstate.db_top - 1; } find_ex = find_th = find_pl = find_rm = 0; if (nfargs >= 2) { for (bb_p = fargs[1]; *bb_p; ++bb_p) { switch(*bb_p) { case 'a': case 'A': find_ex = find_th = find_pl = find_rm = 1; break; case 'e': case 'E': find_ex = 1; break; case 't': case 'T': find_th = 1; break; case 'p': case 'P': find_pl = 1; break; case 'r': case 'R': find_rm = 1; break; default: safe_str("#-1 INVALID TYPE", buff, bufc); return; } } } if (!find_ex && !find_th && !find_pl && !find_rm) { find_ex = find_th = find_pl = find_rm = 1; } if (!fargs[0] || !*fargs[0]) { if (Has_location(player)) thing = Location(player); else thing = player; if (!Good_obj(thing)) { safe_nothing(buff, bufc); return; } } else { init_match(player, fargs[0], NOTYPE); match_everything(MAT_EXIT_PARENTS); thing = noisy_match_result(); if (!Good_obj(thing)) { safe_nothing(buff, bufc); return; } } if (!payfor(player, mudconf.searchcost)) { notify(player, tprintf("You don't have enough %s.", mudconf.many_coins)); safe_nothing(buff, bufc); return; } control_thing = Examinable(player, thing); bb_p = *bufc; for (i = low_bound; i <= high_bound; i++) { if (control_thing || Examinable(player, i)) { if ((find_ex && isExit(i) && (Location(i) == thing)) || (find_rm && isRoom(i) && (Dropto(i) == thing)) || (find_th && isThing(i) && (Home(i) == thing)) || (find_pl && isPlayer(i) && (Home(i) == thing))) { if (*bufc != bb_p) safe_chr(' ', buff, bufc); safe_dbref(buff, bufc, i); } } } } /* -------------------------------------------------------------------------- * fun_home: Return an object's home */ FUNCTION(fun_home) { dbref it; it = match_thing(player, fargs[0]); if (!Good_obj(it) || !Examinable(player, it)) { safe_nothing(buff, bufc); } else if (Has_home(it)) { safe_dbref(buff, bufc, Home(it)); } else if (Has_dropto(it)) { safe_dbref(buff, bufc, Dropto(it)); } else if (isExit(it)) { safe_dbref(buff, bufc, where_is(it)); } else { safe_nothing(buff, bufc); } return; } /* --------------------------------------------------------------------------- * fun_money: Return an object's value */ FUNCTION(fun_money) { dbref it; it = match_thing(player, fargs[0]); if (!Good_obj(it) || !Examinable(player, it)) safe_nothing(buff, bufc); else safe_ltos(buff, bufc, Pennies(it)); } /* --------------------------------------------------------------------------- * fun_findable: can X locate Y */ /* Borrowed from PennMUSH 1.50 */ FUNCTION(fun_findable) { dbref obj = match_thing(player, fargs[0]); dbref victim = match_thing(player, fargs[1]); if (!Good_obj(obj)) safe_str("#-1 ARG1 NOT FOUND", buff, bufc); else if (!Good_obj(victim)) safe_str("#-1 ARG2 NOT FOUND", buff, bufc); else safe_bool(buff, bufc, locatable(obj, victim, obj)); } /* --------------------------------------------------------------------------- * fun_visible: Can X examine Y. If X does not exist, 0 is returned. * If Y, the object, does not exist, 0 is returned. If * Y the object exists, but the optional attribute does * not, X's ability to return Y the object is returned. */ /* Borrowed from PennMUSH 1.50 */ FUNCTION(fun_visible) { dbref it, thing, aowner; int aflags, atr; ATTR *ap; it = match_thing(player, fargs[0]); if (!Good_obj(it)) { safe_chr('0', buff, bufc); return; } if (parse_attrib(player, fargs[1], &thing, &atr, 1)) { if (atr == NOTHING) { safe_bool(buff, bufc, Examinable(it, thing)); return; } ap = atr_num(atr); atr_pget_info(thing, atr, &aowner, &aflags); safe_bool(buff, bufc, See_attr_all(it, thing, ap, aowner, aflags, 1)); return; } thing = match_thing(player, fargs[1]); if (!Good_obj(thing)) { safe_chr('0', buff, bufc); return; } safe_bool(buff, bufc, Examinable(it, thing)); } /* ------------------------------------------------------------------------ * fun_writable: Returns 1 if player could set <obj>/<attr>. */ FUNCTION(fun_writable) { dbref it, thing, aowner; int aflags, atr, retval; ATTR *ap; it = match_thing(player, fargs[0]); if (!Good_obj(it)) { safe_chr('0', buff, bufc); return; } retval = parse_attrib(player, fargs[1], &thing, &atr, 1); /* Possibilities: * retval is 0, which means we didn't match a thing. * retval is NOTHING, which means we matched a thing but have a * non-existent attribute. * retval is 1; atr is either NOTHING (no permission to see) * or a valid attr number. */ if (retval == 0) { safe_chr('0', buff, bufc); return; } if (retval == 1) { if (atr == NOTHING) { safe_chr('0', buff, bufc); return; } else { ap = atr_num(atr); atr_pget_info(thing, atr, &aowner, &aflags); safe_bool(buff, bufc, Set_attr(it, thing, ap, aflags)); return; } } /* fargs[1] should now contain the attribute name. */ if (!fargs[1] || !*fargs[1]) { safe_chr('0', buff, bufc); return; } atr = mkattr(fargs[1]); if ((atr <= 0) || ((ap = atr_num(atr)) == NULL)) { safe_chr('0', buff, bufc); return; } atr_pget_info(thing, atr, &aowner, &aflags); safe_bool(buff, bufc, Set_attr(it, thing, ap, aflags)); } /* ------------------------------------------------------------------------ * fun_flags: Returns the flags on an object. * Because @switch is case-insensitive, not quite as useful as it could be. */ FUNCTION(fun_flags) { dbref it, aowner; int atr, aflags; char *buff2, xbuf[16], *xbufp; if (parse_attrib(player, fargs[0], &it, &atr, 1)) { if (atr == NOTHING) { safe_nothing(buff, bufc); } else { atr_pget_info(it, atr, &aowner, &aflags); Print_Attr_Flags(aflags, xbuf, xbufp); safe_str(xbuf, buff, bufc); } } else { it = match_thing(player, fargs[0]); if (Good_obj(it) && (mudconf.pub_flags || Examinable(player, it) || (it == cause))) { buff2 = unparse_flags(player, it); safe_str(buff2, buff, bufc); free_sbuf(buff2); } else safe_nothing(buff, bufc); } } /* --------------------------------------------------------------------------- * andflags, orflags: Check a list of flags. */ FUNCTION(handle_flaglists) { char *s; char flagletter[2]; FLAGSET fset; FLAG p_type; int negate, temp, type; dbref it = match_thing(player, fargs[0]); type = Is_Func(LOGIC_OR); negate = temp = 0; if (!Good_obj(it) || (!(mudconf.pub_flags || Examinable(player, it) || (it == cause)))) { safe_chr('0', buff, bufc); return; } for (s = fargs[1]; *s; s++) { /* Check for a negation sign. If we find it, we note it and * increment the pointer to the next character. */ if (*s == '!') { negate = 1; s++; } else { negate = 0; } if (!*s) { safe_chr('0', buff, bufc); return; } flagletter[0] = *s; flagletter[1] = '\0'; if (!convert_flags(player, flagletter, &fset, &p_type)) { /* Either we got a '!' that wasn't followed by a * letter, or we couldn't find that flag. For * AND, since we've failed a check, we can * return false. Otherwise we just go on. */ if (!type) { safe_chr('0', buff, bufc); return; } else continue; } else { /* does the object have this flag? */ if ((Flags(it) & fset.word1) || (Flags2(it) & fset.word2) || (Flags3(it) & fset.word3) || (Typeof(it) == p_type)) { if ((p_type == TYPE_PLAYER) && (fset.word2 == CONNECTED) && Can_Hide(it) && Hidden(it) && !See_Hidden(player)) temp = 0; else temp = 1; } else { temp = 0; } if (!(type ^ negate ^ temp)) { /* Four ways to satisfy that test: * AND, don't want flag but we have it; * AND, do want flag but don't have it; * OR, don't want flag and don't have it; * OR, do want flag and do have it. */ safe_bool(buff, bufc, type); return; } /* Otherwise, move on to check the next flag. */ } } safe_bool(buff, bufc, !type); } /*--------------------------------------------------------------------------- * fun_hasflag: plus auxiliary function atr_has_flag. */ static int atr_has_flag(player, thing, attr, aowner, aflags, flagname) dbref player, thing; ATTR *attr; int aowner, aflags; char *flagname; { int flagval; if (!See_attr(player, thing, attr, aowner, aflags)) return 0; flagval = search_nametab(player, indiv_attraccess_nametab, flagname); if (flagval < 0) flagval = search_nametab(player, attraccess_nametab, flagname); if (flagval < 0) return 0; return (aflags & flagval); } FUNCTION(fun_hasflag) { dbref it, aowner; int atr, aflags; ATTR *ap; if (parse_attrib(player, fargs[0], &it, &atr, 1)) { if (atr == NOTHING) { safe_str("#-1 NOT FOUND", buff, bufc); } else { ap = atr_num(atr); atr_pget_info(it, atr, &aowner, &aflags); safe_bool(buff, bufc, atr_has_flag(player, it, ap, aowner, aflags, fargs[1])); } } else { it = match_thing(player, fargs[0]); if (!Good_obj(it)) { safe_nomatch(buff, bufc); return; } if (mudconf.pub_flags || Examinable(player, it) || (it == cause)) { safe_bool(buff, bufc, has_flag(player, it, fargs[1])); } else { safe_noperm(buff, bufc); } } } FUNCTION(fun_haspower) { dbref it; it = match_thing(player, fargs[0]); if (!Good_obj(it)) { safe_nomatch(buff, bufc); return; } if (mudconf.pub_flags || Examinable(player, it) || (it == cause)) { safe_bool(buff, bufc, has_power(player, it, fargs[1])); } else { safe_noperm(buff, bufc); } } /* --------------------------------------------------------------------------- * hasflags(<object>, <flag list to AND>, <OR flag list to AND>, <etc.>) */ FUNCTION(fun_hasflags) { dbref it; char **elems; int n_elems, i, j, result; if (nfargs < 2) { safe_tprintf_str(buff, bufc, "#-1 FUNCTION (HASFLAGS) EXPECTS AT LEAST 2 ARGUMENTS BUT GOT %d", nfargs); return; } it = match_thing(player, fargs[0]); if (!Good_obj(it)) { safe_nomatch(buff, bufc); return; } /* Walk through each of the lists we've been passed. We need to have * all the flags in a particular list (AND) in order to consider that * list true. We return 1 if any of the lists are true. (i.e., we * OR the list results). */ result = 0; for (i = 1; !result && (i < nfargs); i++) { n_elems = list2arr(&elems, LBUF_SIZE / 2, fargs[i], &SPACE_DELIM); if (n_elems > 0) { result = 1; for (j = 0; result && (j < n_elems); j++) { if (*elems[j] == '!') result = (has_flag(player, it, elems[j] + 1)) ? 0 : 1; else result = has_flag(player, it, elems[j]); } } XFREE(elems, "fun_hasflags.elems"); } safe_bool(buff, bufc, result); } /* --------------------------------------------------------------------------- * handle_timestamp: Get timestamps (LASTACCESS, LASTMOD). */ FUNCTION(handle_timestamp) { dbref it = match_thing(player, fargs[0]); if (!Good_obj(it) || !Examinable(player, it)) { safe_known_str("-1", 2, buff, bufc); } else { safe_ltos(buff, bufc, Is_Func(TIMESTAMP_MOD) ? ModTime(it) : AccessTime(it)); } } /* --------------------------------------------------------------------------- * Parent-child relationships. */ FUNCTION(fun_parent) { dbref it; it = match_thing(player, fargs[0]); if (Good_obj(it) && (Examinable(player, it) || (it == cause))) { safe_dbref(buff, bufc, Parent(it)); } else { safe_nothing(buff, bufc); } return; } FUNCTION(fun_lparent) { dbref it, par; int i; Delim osep; VaChk_Only_Out(2); it = match_thing(player, fargs[0]); if (!Good_obj(it)) { safe_nomatch(buff, bufc); return; } else if (!(Examinable(player, it))) { safe_noperm(buff, bufc); return; } safe_dbref(buff, bufc, it); par = Parent(it); i = 1; while (Good_obj(par) && Examinable(player, it) && (i < mudconf.parent_nest_lim)) { print_sep(&osep, buff, bufc); safe_dbref(buff, bufc, par); it = par; par = Parent(par); i++; } } FUNCTION(fun_children) { dbref i, it; char *bb_p; Delim osep; VaChk_Only_Out(2); if (!strcmp(fargs[0], "#-1")) { it = NOTHING; } else { it = match_thing(player, fargs[0]); if (!Good_obj(it)) { safe_nomatch(buff, bufc); return; } } if (!Controls(player, it) && !See_All(player)) { safe_noperm(buff, bufc); return; } bb_p = *bufc; DO_WHOLE_DB(i) { if (Parent(i) == it) { if (*bufc != bb_p) { print_sep(&osep, buff, bufc); } safe_dbref(buff, bufc, i); } } } /* --------------------------------------------------------------------------- * Zones. */ FUNCTION(fun_zone) { dbref it; if (!mudconf.have_zones) { safe_str("#-1 ZONES DISABLED", buff, bufc); return; } it = match_thing(player, fargs[0]); if (!Good_obj(it) || !Examinable(player, it)) { safe_nothing(buff, bufc); return; } safe_dbref(buff, bufc, Zone(it)); } FUNCTION(scan_zone) { dbref i, it; int type; char *bb_p; type = Func_Mask(TYPE_MASK); if (!mudconf.have_zones) { safe_str("#-1 ZONES DISABLED", buff, bufc); return; } if (!strcmp(fargs[0], "#-1")) { it = NOTHING; } else { it = match_thing(player, fargs[0]); if (!Good_obj(it)) { safe_nomatch(buff, bufc); return; } } if (!Controls(player, it) && !WizRoy(player)) { safe_noperm(buff, bufc); return; } bb_p = *bufc; DO_WHOLE_DB(i) { if (Typeof(i) == type) { if (Zone(i) == it) { if (*bufc != bb_p) { safe_chr(' ', buff, bufc); } safe_dbref(buff, bufc, i); } } } } FUNCTION(fun_zfun) { dbref aowner; int aflags, alen; ATTR *ap; char *tbuf1, *str; dbref zone = Zone(player); if (!mudconf.have_zones) { safe_str("#-1 ZONES DISABLED", buff, bufc); return; } if (zone == NOTHING) { safe_str("#-1 INVALID ZONE", buff, bufc); return; } if (!fargs[0] || !*fargs[0]) return; /* find the user function attribute */ ap = atr_str(upcasestr(fargs[0])); if (!ap) { safe_str("#-1 NO SUCH USER FUNCTION", buff, bufc); return; } tbuf1 = atr_pget(zone, ap->number, &aowner, &aflags, &alen); if (!See_attr(player, zone, ap, aowner, aflags)) { safe_str("#-1 NO PERMISSION TO GET ATTRIBUTE", buff, bufc); free_lbuf(tbuf1); return; } str = tbuf1; /* Behavior here is a little wacky. The enactor was always the player, * not the cause. You can still get the caller, though. */ exec(buff, bufc, zone, caller, player, EV_EVAL | EV_STRIP | EV_FCHECK, &str, &(fargs[1]), nfargs - 1); free_lbuf(tbuf1); } /* --------------------------------------------------------------------------- * fun_hasattr: does object X have attribute Y. */ FUNCTION(fun_hasattr) { dbref thing, aowner; int aflags, alen, check_parents; ATTR *attr; char *tbuf; check_parents = Is_Func(CHECK_PARENTS); thing = match_thing(player, fargs[0]); if (!Good_obj(thing)) { safe_nomatch(buff, bufc); return; } else if (!Examinable(player, thing)) { safe_noperm(buff, bufc); return; } attr = atr_str(fargs[1]); if (!attr) { safe_chr('0', buff, bufc); return; } if (check_parents) { atr_pget_info(thing, attr->number, &aowner, &aflags); } else { atr_get_info(thing, attr->number, &aowner, &aflags); } if (!See_attr(player, thing, attr, aowner, aflags)) { safe_chr('0', buff, bufc); } else { if (check_parents) { tbuf = atr_pget(thing, attr->number, &aowner, &aflags, &alen); } else { tbuf = atr_get(thing, attr->number, &aowner, &aflags, &alen); } if (*tbuf) { safe_chr('1', buff, bufc); } else { safe_chr('0', buff, bufc); } free_lbuf(tbuf); } } /* --------------------------------------------------------------------------- * fun_v: Function form of %-substitution */ FUNCTION(fun_v) { dbref aowner; int aflags, alen; char *sbuf, *sbufc, *tbuf, *str; ATTR *ap; tbuf = fargs[0]; if (isalpha(tbuf[0]) && tbuf[1]) { /* Fetch an attribute from me. First see if it exists, * returning a null string if it does not. */ ap = atr_str(fargs[0]); if (!ap) { return; } /* If we can access it, return it, otherwise return a null * string */ tbuf = atr_pget(player, ap->number, &aowner, &aflags, &alen); if (See_attr(player, player, ap, aowner, aflags)) safe_known_str(tbuf, alen, buff, bufc); free_lbuf(tbuf); return; } /* Not an attribute, process as %<arg> */ sbuf = alloc_sbuf("fun_v"); sbufc = sbuf; safe_sb_chr('%', sbuf, &sbufc); safe_sb_str(fargs[0], sbuf, &sbufc); *sbufc = '\0'; str = sbuf; exec(buff, bufc, player, caller, cause, EV_FIGNORE, &str, cargs, ncargs); free_sbuf(sbuf); } /* --------------------------------------------------------------------------- * perform_get: Get attribute from object: GET, XGET, GET_EVAL, EVAL(obj,atr) */ FUNCTION(perform_get) { dbref thing, aowner; int attrib, aflags, alen, eval_it; char *atr_gotten, *str; eval_it = Is_Func(GET_EVAL); if (Is_Func(GET_XARGS)) { if (!*fargs[0] || !*fargs[1]) return; str = tprintf("%s/%s", fargs[0], fargs[1]); } else { str = fargs[0]; } if (!parse_attrib(player, str, &thing, &attrib, 0)) { safe_nomatch(buff, bufc); return; } if (attrib == NOTHING) { return; } /* There used to be code here to handle AF_IS_LOCK attributes, * but parse_attrib can never return one of those. Use fun_lock * instead. */ atr_gotten = atr_pget(thing, attrib, &aowner, &aflags, &alen); if (eval_it) { str = atr_gotten; exec(buff, bufc, thing, player, player, EV_FIGNORE | EV_EVAL, &str, (char **)NULL, 0); } else { safe_known_str(atr_gotten, alen, buff, bufc); } free_lbuf(atr_gotten); } FUNCTION(fun_eval) { char *str; VaChk_Range(1, 2); if (nfargs == 1) { str = fargs[0]; exec(buff, bufc, player, caller, cause, EV_EVAL|EV_FCHECK, &str, (char **)NULL, 0); return; } perform_get( FUNCTION_ARGLIST ); } /* --------------------------------------------------------------------------- * do_ufun: Call a user-defined function: U, ULOCAL */ FUNCTION(do_ufun) { dbref aowner, thing; int is_local, aflags, alen, anum; ATTR *ap; char *atext, *str; GDATA *preserve; is_local = Is_Func(U_LOCAL); /* We need at least one argument */ if (nfargs < 1) { safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc); return; } /* Two possibilities for the first arg: <obj>/<attr> and <attr>. */ Parse_Uattr(player, fargs[0], thing, anum, ap); Get_Uattr(player, thing, ap, atext, aowner, aflags, alen); /* If we're evaluating locally, preserve the global registers. */ if (is_local) { preserve = save_global_regs("fun_ulocal.save"); } /* Evaluate it using the rest of the passed function args */ str = atext; exec(buff, bufc, thing, player, cause, EV_FCHECK | EV_EVAL, &str, &(fargs[1]), nfargs - 1); free_lbuf(atext); /* If we're evaluating locally, restore the preserved registers. */ if (is_local) { restore_global_regs("fun_ulocal.restore", preserve); } } /* --------------------------------------------------------------------------- * objcall: Call the text of a u-function from a specific object's * perspective. (i.e., get the text as the player, but execute * it as the specified object. */ FUNCTION(fun_objcall) { dbref obj, aowner, thing; int aflags, alen, anum; ATTR *ap; char *atext, *str; if (nfargs < 2) { safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc); return; } /* Two possibilities for the first arg: <obj>/<attr> and <attr>. */ Parse_Uattr(player, fargs[1], thing, anum, ap); Get_Uattr(player, thing, ap, atext, aowner, aflags, alen); /* Find our perspective. */ obj = match_thing(player, fargs[0]); if (Cannot_Objeval(player, obj)) obj = player; /* Evaluate using the rest of the passed function args. */ str = atext; exec(buff, bufc, obj, player, cause, EV_FCHECK | EV_EVAL, &str, &(fargs[2]), nfargs - 2); free_lbuf(atext); } /* --------------------------------------------------------------------------- * fun_localize: Evaluate a function with local scope (i.e., preserve and * restore the r-registers). Essentially like calling ulocal() but with the * function string directly. */ FUNCTION(fun_localize) { char *str; GDATA *preserve; preserve = save_global_regs("fun_localize_save"); str = fargs[0]; exec(buff, bufc, player, caller, cause, EV_FCHECK | EV_STRIP | EV_EVAL, &str, cargs, ncargs); restore_global_regs("fun_localize_restore", preserve); } /* --------------------------------------------------------------------------- * fun_default, fun_edefault, and fun_udefault: * These check for the presence of an attribute. If it exists, then it * is gotten, via the equivalent of get(), get_eval(), or u(), respectively. * Otherwise, the default message is used. * In the case of udefault(), the remaining arguments to the function * are used as arguments to the u(). */ FUNCTION(fun_default) { dbref thing, aowner; int attrib, aflags, alen; ATTR *attr; char *objname, *atr_gotten, *bp, *str; objname = bp = alloc_lbuf("fun_default"); str = fargs[0]; exec(objname, &bp, player, caller, cause, EV_EVAL | EV_STRIP | EV_FCHECK, &str, cargs, ncargs); /* First we check to see that the attribute exists on the object. * If so, we grab it and use it. */ if (objname != NULL) { if (parse_attrib(player, objname, &thing, &attrib, 0) && (attrib != NOTHING)) { attr = atr_num(attrib); if (attr && !(attr->flags & AF_IS_LOCK)) { atr_gotten = atr_pget(thing, attrib, &aowner, &aflags, &alen); if (*atr_gotten) { safe_known_str(atr_gotten, alen, buff, bufc); free_lbuf(atr_gotten); free_lbuf(objname); return; } free_lbuf(atr_gotten); } } free_lbuf(objname); } /* If we've hit this point, we've not gotten anything useful, so we * go and evaluate the default. */ str = fargs[1]; exec(buff, bufc, player, caller, cause, EV_EVAL | EV_STRIP | EV_FCHECK, &str, cargs, ncargs); } FUNCTION(fun_edefault) { dbref thing, aowner; int attrib, aflags, alen; ATTR *attr; char *objname, *atr_gotten, *bp, *str; objname = bp = alloc_lbuf("fun_edefault"); str = fargs[0]; exec(objname, &bp, player, caller, cause, EV_EVAL | EV_STRIP | EV_FCHECK, &str, cargs, ncargs); /* First we check to see that the attribute exists on the object. * If so, we grab it and use it. */ if (objname != NULL) { if (parse_attrib(player, objname, &thing, &attrib, 0) && (attrib != NOTHING)) { attr = atr_num(attrib); if (attr && !(attr->flags & AF_IS_LOCK)) { atr_gotten = atr_pget(thing, attrib, &aowner, &aflags, &alen); if (*atr_gotten) { str = atr_gotten; exec(buff, bufc, thing, player, player, EV_FIGNORE | EV_EVAL, &str, (char **)NULL, 0); free_lbuf(atr_gotten); free_lbuf(objname); return; } free_lbuf(atr_gotten); } } free_lbuf(objname); } /* If we've hit this point, we've not gotten anything useful, so we * go and evaluate the default. */ str = fargs[1]; exec(buff, bufc, player, caller, cause, EV_EVAL | EV_STRIP | EV_FCHECK, &str, cargs, ncargs); } FUNCTION(fun_udefault) { dbref thing, aowner; int aflags, alen, anum, i, j; ATTR *ap; char *objname, *atext, *str, *bp, *xargs[NUM_ENV_VARS]; if (nfargs < 2) /* must have at least two arguments */ return; str = fargs[0]; objname = bp = alloc_lbuf("fun_udefault"); exec(objname, &bp, player, caller, cause, EV_EVAL | EV_STRIP | EV_FCHECK, &str, cargs, ncargs); /* First we check to see that the attribute exists on the object. * If so, we grab it and use it. */ if (objname != NULL) { Parse_Uattr(player, objname, thing, anum, ap); if (ap) { atext = atr_pget(thing, ap->number, &aowner, &aflags, &alen); if (*atext) { /* Now we have a problem -- we've got to go eval * all of those arguments to the function. */ for (i = 2, j = 0; j < NUM_ENV_VARS; i++, j++) { if ((i < nfargs) && fargs[i]) { bp = xargs[j] = alloc_lbuf("fun_udefault.args"); str = fargs[i]; exec(xargs[j], &bp, player, caller, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str, cargs, ncargs); } else { xargs[j] = NULL; } } str = atext; exec(buff, bufc, thing, player, cause, EV_FCHECK | EV_EVAL, &str, xargs, nfargs - 2); /* Then clean up after ourselves. */ for (j = 0; j < NUM_ENV_VARS; j++) if (xargs[j]) free_lbuf(xargs[j]); free_lbuf(atext); free_lbuf(objname); return; } free_lbuf(atext); } free_lbuf(objname); } /* If we've hit this point, we've not gotten anything useful, so we * go and evaluate the default. */ str = fargs[1]; exec(buff, bufc, player, caller, cause, EV_EVAL | EV_STRIP | EV_FCHECK, &str, cargs, ncargs); } /*--------------------------------------------------------------------------- * Evaluate from a specific object's perspective. */ FUNCTION(fun_objeval) { dbref obj; char *name, *bp, *str; if (!*fargs[0]) { return; } name = bp = alloc_lbuf("fun_objeval"); str = fargs[0]; exec(name, &bp, player, caller, cause, EV_FCHECK | EV_STRIP | EV_EVAL, &str, cargs, ncargs); obj = match_thing(player, name); /* In order to evaluate from something else's viewpoint, you must * have the same owner as it, or be a wizard (unless * objeval_requires_control is turned on, in which case you * must control it, period). Otherwise, we default to evaluating * from our own viewpoint. Also, you cannot evaluate things from * the point of view of God. */ if (Cannot_Objeval(player, obj)) obj = player; str = fargs[1]; exec(buff, bufc, obj, player, cause, EV_FCHECK | EV_STRIP | EV_EVAL, &str, cargs, ncargs); free_lbuf(name); } /* --------------------------------------------------------------------------- * Matching functions. */ FUNCTION(fun_num) { safe_dbref(buff, bufc, match_thing(player, fargs[0])); } FUNCTION(fun_pmatch) { char *name, *temp, *tp; dbref thing; dbref *p_ptr; /* If we have a valid dbref, it's okay if it's a player. */ if ((*fargs[0] == NUMBER_TOKEN) && fargs[0][1]) { thing = parse_dbref(fargs[0] + 1); if (Good_obj(thing) && isPlayer(thing)) { safe_dbref(buff, bufc, thing); } else { safe_nothing(buff, bufc); } return; } /* If we have *name, just advance past the *; it doesn't matter */ name = fargs[0]; if (*fargs[0] == LOOKUP_TOKEN) { do { name++; } while (isspace(*name)); } /* Look up the full name */ tp = temp = alloc_lbuf("fun_pmatch"); safe_str(name, temp, &tp); for (tp = temp; *tp; tp++) *tp = tolower(*tp); p_ptr = (int *) hashfind(temp, &mudstate.player_htab); free_lbuf(temp); if (p_ptr) { /* We've got it. Check to make sure it's a good object. */ if (Good_obj(*p_ptr) && isPlayer(*p_ptr)) { safe_dbref(buff, bufc, (int) *p_ptr); } else { safe_nothing(buff, bufc); } return; } /* We haven't found anything. Now we try a partial match. */ thing = find_connected_ambiguous(player, name); if (thing == AMBIGUOUS) { safe_known_str("#-2", 3, buff, bufc); } else if (Good_obj(thing) && isPlayer(thing)) { safe_dbref(buff, bufc, thing); } else { safe_nothing(buff, bufc); } } FUNCTION(fun_pfind) { dbref thing; if (*fargs[0] == '#') { safe_dbref(buff, bufc, match_thing(player, fargs[0])); return; } if (!((thing = lookup_player(player, fargs[0], 1)) == NOTHING)) { safe_dbref(buff, bufc, thing); return; } else safe_nomatch(buff, bufc); } /* --------------------------------------------------------------------------- * fun_locate: Search for things with the perspective of another obj. */ FUNCTION(fun_locate) { int pref_type, check_locks, verbose, multiple; dbref thing, what; char *cp; pref_type = NOTYPE; check_locks = verbose = multiple = 0; /* Find the thing to do the looking, make sure we control it. */ if (See_All(player)) thing = match_thing(player, fargs[0]); else thing = match_controlled(player, fargs[0]); if (!Good_obj(thing)) { safe_noperm(buff, bufc); return; } /* Get pre- and post-conditions and modifiers */ for (cp = fargs[2]; *cp; cp++) { switch (*cp) { case 'E': pref_type = TYPE_EXIT; break; case 'L': check_locks = 1; break; case 'P': pref_type = TYPE_PLAYER; break; case 'R': pref_type = TYPE_ROOM; break; case 'T': pref_type = TYPE_THING; break; case 'V': verbose = 1; break; case 'X': multiple = 1; break; } } /* Set up for the search */ if (check_locks) init_match_check_keys(thing, fargs[1], pref_type); else init_match(thing, fargs[1], pref_type); /* Search for each requested thing */ for (cp = fargs[2]; *cp; cp++) { switch (*cp) { case 'a': match_absolute(); break; case 'c': match_carried_exit_with_parents(); break; case 'e': match_exit_with_parents(); break; case 'h': match_here(); break; case 'i': match_possession(); break; case 'm': match_me(); break; case 'n': match_neighbor(); break; case 'p': match_player(); break; case '*': match_everything(MAT_EXIT_PARENTS); break; } } /* Get the result and return it to the caller */ if (multiple) what = last_match_result(); else what = match_result(); if (verbose) (void)match_status(player, what); safe_dbref(buff, bufc, what); } /* --------------------------------------------------------------------------- * handle_lattr: * lattr: Return list of attributes I can see on the object. * nattr: Ditto, but just count 'em up. */ FUNCTION(handle_lattr) { dbref thing; ATTR *attr; char *bb_p; int ca, total = 0, count_only; Delim osep; count_only = Is_Func(LATTR_COUNT); if (!count_only) { VaChk_Only_Out(2); } /* Check for wildcard matching. parse_attrib_wild checks for read * permission, so we don't have to. Have p_a_w assume the * slash-star if it is missing. */ olist_push(); if (parse_attrib_wild(player, fargs[0], &thing, 0, 0, 1, 1)) { bb_p = *bufc; for (ca = olist_first(); ca != NOTHING; ca = olist_next()) { attr = atr_num(ca); if (attr) { if (count_only) { total++; } else { if (*bufc != bb_p) { print_sep(&osep, buff, bufc); } safe_str((char *)attr->name, buff, bufc); } } } if (count_only) safe_ltos(buff, bufc, total); } else { if (!mudconf.lattr_oldstyle) { safe_nomatch(buff, bufc); } else if (count_only) { safe_chr('0', buff, bufc); } } olist_pop(); } /* --------------------------------------------------------------------------- * fun_search: Search the db for things, returning a list of what matches */ FUNCTION(fun_search) { dbref thing; char *bp, *nbuf; SEARCH searchparm; /* Set up for the search. If any errors, abort. */ if (!search_setup(player, fargs[0], &searchparm)) { safe_str("#-1 ERROR DURING SEARCH", buff, bufc); return; } /* Do the search and report the results */ olist_push(); search_perform(player, cause, &searchparm); bp = *bufc; nbuf = alloc_sbuf("fun_search"); for (thing = olist_first(); thing != NOTHING; thing = olist_next()) { if (bp == *bufc) sprintf(nbuf, "#%d", thing); else sprintf(nbuf, " #%d", thing); safe_str(nbuf, buff, bufc); } free_sbuf(nbuf); olist_pop(); } /* --------------------------------------------------------------------------- * fun_stats: Get database size statistics. */ FUNCTION(fun_stats) { dbref who; STATS statinfo; if ((!fargs[0]) || !*fargs[0] || !string_compare(fargs[0], "all")) { who = NOTHING; } else { who = lookup_player(player, fargs[0], 1); if (who == NOTHING) { safe_str("#-1 NOT FOUND", buff, bufc); return; } } if (!get_stats(player, who, &statinfo)) { safe_str("#-1 ERROR GETTING STATS", buff, bufc); return; } safe_tprintf_str(buff, bufc, "%d %d %d %d %d %d %d %d", statinfo.s_total, statinfo.s_rooms, statinfo.s_exits, statinfo.s_things, statinfo.s_players, statinfo.s_unknown, statinfo.s_going, statinfo.s_garbage); } /* --------------------------------------------------------------------------- * Memory usage. */ static int mem_usage(thing) dbref thing; { int k; int ca; char *as, *str; ATTR *attr; k = sizeof(OBJ); k += strlen(Name(thing)) + 1; for (ca = atr_head(thing, &as); ca; ca = atr_next(&as)) { str = atr_get_raw(thing, ca); if (str && *str) k += strlen(str); attr = atr_num(ca); if (attr) { str = (char *)attr->name; if (str && *str) k += strlen(((ATTR *) atr_num(ca))->name); } } return k; } static int mem_usage_attr(player, str) dbref player; char *str; { dbref thing, aowner; int atr, aflags, alen; char *abuf; ATTR *ap; int bytes_atext = 0; abuf = alloc_lbuf("mem_usage_attr"); olist_push(); if (parse_attrib_wild(player, str, &thing, 0, 0, 1, 1)) { for (atr = olist_first(); atr != NOTHING; atr = olist_next()) { ap = atr_num(atr); if (!ap) continue; abuf = atr_get_str(abuf, thing, atr, &aowner, &aflags, &alen); /* Player must be able to read attribute with 'examine' */ if (Examinable(player, thing) && Read_attr(player, thing, ap, aowner, aflags)) bytes_atext += alen; } } olist_pop(); free_lbuf(abuf); return bytes_atext; } FUNCTION(fun_objmem) { dbref thing; if (strchr(fargs[0], '/')) { safe_ltos(buff, bufc, mem_usage_attr(player, fargs[0])); return; } thing = match_thing(player, fargs[0]); if (!Good_obj(thing) || !Examinable(player, thing)) { safe_noperm(buff, bufc); return; } safe_ltos(buff, bufc, mem_usage(thing)); } FUNCTION(fun_playmem) { int tot = 0; dbref thing; dbref j; thing = match_thing(player, fargs[0]); if (!Good_obj(thing) || !Examinable(player, thing)) { safe_noperm(buff, bufc); return; } DO_WHOLE_DB(j) if (Owner(j) == thing) tot += mem_usage(j); safe_ltos(buff, bufc, tot); } /* --------------------------------------------------------------------------- * Type functions. */ FUNCTION(fun_type) { dbref it; it = match_thing(player, fargs[0]); if (!Good_obj(it)) { safe_nomatch(buff, bufc); return; } switch (Typeof(it)) { case TYPE_ROOM: safe_known_str("ROOM", 4, buff, bufc); break; case TYPE_EXIT: safe_known_str("EXIT", 4, buff, bufc); break; case TYPE_PLAYER: safe_known_str("PLAYER", 6, buff, bufc); break; case TYPE_THING: safe_known_str("THING", 5, buff, bufc); break; default: safe_str("#-1 ILLEGAL TYPE", buff, bufc); } return; } FUNCTION(fun_hastype) { dbref it = match_thing(player, fargs[0]); if (!Good_obj(it)) { safe_nomatch(buff, bufc); return; } if (!fargs[1] || !*fargs[1]) { safe_str("#-1 NO SUCH TYPE", buff, bufc); return; } switch (*fargs[1]) { case 'r': case 'R': safe_bool(buff, bufc, isRoom(it)); break; case 'e': case 'E': safe_bool(buff, bufc, isExit(it)); break; case 'p': case 'P': safe_bool(buff, bufc, isPlayer(it)); break; case 't': case 'T': safe_bool(buff, bufc, isThing(it)); break; default: safe_str("#-1 NO SUCH TYPE", buff, bufc); break; }; } /* --------------------------------------------------------------------------- * fun_lastcreate: Return the last object of type Y that X created. */ FUNCTION(fun_lastcreate) { int i, aowner, aflags, alen, obj_list[4], obj_type; char *obj_str, *p, *tokst; dbref obj = match_thing(player, fargs[0]); if (!controls(player, obj)) { /* Automatically checks for GoodObj */ safe_nothing(buff, bufc); return; } switch (*fargs[1]) { case 'R': case 'r': obj_type = 0; break; case 'E': case 'e': obj_type = 1;; break; case 'T': case 't': obj_type = 2; break; case 'P': case 'p': obj_type = 3; break; default: notify_quiet(player, "Invalid object type."); safe_nothing(buff, bufc); return; } obj_str = atr_get(obj, A_NEWOBJS, &aowner, &aflags, &alen); if (!*obj_str) { free_lbuf(obj_str); safe_nothing(buff, bufc); return; } for (p = strtok_r(obj_str, " ", &tokst), i = 0; p && (i < 4); p = strtok_r(NULL, " ", &tokst), i++) { obj_list[i] = atoi(p); } free_lbuf(obj_str); safe_dbref(buff, bufc, obj_list[obj_type]); } /* --------------------------------------------------------------------------- * fun_speak: Complex say-format-processing function. * * speak(<object>, <string>[, <substitute for "says,"> * [, <transform>[, <empty>[, <open>[, <close>]]]]]) * * <string> can be a plain string (treated like say), :<foo> (pose), * : <foo> (pose/nospace), ;<foo> (pose/nospace), |<foo> (emit), or * "<foo> (also treated like say). * * If we are given <transform>, we parse through the string, pulling out * the things that fall between <open in> and <close in> (which default to * double-quotes). We pass the speech between those things to the * u-function (or everything, if a plain say string), with the speech * as %0, <object>'s resolved dbref as %1, and the speech part number * (plain say begins numbering at 0, others at 1) as %2. * * We rely on the u-function to do any necessary formatting of the * return string, including putting quotes (or whatever) back around it * if necessary. * * If the return string is null, we call <empty>, with <object>'s resolved * dbref as %0, and the speech part number as %1. */ static void transform_say(speaker, str, key, say_str, trans_str, empty_str, open_sep, close_sep, player, caller, cause, buff, bufc) dbref speaker, player, caller, cause; char *str, *say_str, *trans_str, *empty_str, *buff, **bufc; const Delim *open_sep, *close_sep; int key; { char *sp, *ep, *save, *tp, *bp; char *result, *tstack[3], *estack[2], tbuf[LBUF_SIZE]; int spos, trans_len, empty_len; int done = 0; tstack[1] = alloc_sbuf("transform_say.dbref1"); tstack[2] = alloc_sbuf("transform_say.pos1"); if (trans_str && *trans_str) trans_len = strlen(trans_str); else return; /* should never happen; caller should check */ if (empty_str && *empty_str) { empty_len = strlen(empty_str); estack[0] = alloc_sbuf("transform_say.dbref2"); estack[1] = alloc_sbuf("transform_say.pos2"); } else { empty_len = 0; } /* Find the start of the speech string. Copy up to it. */ sp = str; if (key == SAY_SAY) { spos = 0; } else { save = split_token(&sp, open_sep); safe_str(save, buff, bufc); if (!sp) return; spos = 1; } result = alloc_lbuf("transform_say.out"); while (!done) { /* Find the end of the speech string. */ ep = sp; sp = split_token(&ep, close_sep); /* Pass the stuff in-between through the u-functions. */ tstack[0] = sp; sprintf(tstack[1], "#%d", speaker); sprintf(tstack[2], "%d", spos); StrCopyKnown(tbuf, trans_str, trans_len); tp = tbuf; bp = result; exec(result, &bp, player, caller, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &tp, tstack, 3); if (result && *result) { if ((key == SAY_SAY) && (spos == 0)) { safe_tprintf_str(buff, bufc, "%s %s %s", Name(speaker), say_str, result); } else { safe_str(result, buff, bufc); } } else if (empty_len > 0) { sprintf(estack[0], "#%d", speaker); sprintf(estack[1], "%d", spos); StrCopyKnown(tbuf, empty_str, empty_len); tp = tbuf; bp = result; exec(result, &bp, player, caller, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &tp, estack, 2); if (result && *result) { safe_str(result, buff, bufc); } } /* If there's more, find it and copy it. sp will point to the * beginning of the next speech string. */ if (ep && *ep) { sp = ep; save = split_token(&sp, open_sep); safe_str(save, buff, bufc); if (!sp) done = 1; } else { done = 1; } spos++; } free_lbuf(result); free_sbuf(tstack[1]); free_sbuf(tstack[2]); if (empty_len > 0) { free_sbuf(estack[0]); free_sbuf(estack[1]); } } FUNCTION(fun_speak) { dbref thing, obj1, obj2; Delim isep, osep; /* really open and close separators */ dbref aowner1, aowner2; int aflags1, alen1, anum1, aflags2, alen2, anum2; ATTR *ap1, *ap2; char *atext1, *atext2, *str, *say_str; int is_transform, key; /* Delimiter processing here is different. We have to do some * funky stuff to make sure that a space delimiter is really an * intended space, not delim_check() defaulting. */ VaChk_Range(2, 7); VaChk_InSep(6, 0); if ((isep.len == 1) && (isep.str[0] == ' ')) { if ((nfargs < 6) || !fargs[5] || !*fargs[5]) { isep.str[0] = '"'; } } VaChk_DefaultOut(7) { VaChk_OutSep(7, 0); } /* Just need a match. Don't need to control it or anything. */ thing = match_thing(player, fargs[0]); if (!Good_obj(thing)) { safe_nomatch(buff, bufc); return; } /* Must have an input string. Otherwise silent fail. */ if (!fargs[1] || !*fargs[1]) return; /* Check if there's a string substituting for "says,". */ if ((nfargs >= 3) && fargs[2] && *fargs[2]) say_str = fargs[2]; else say_str = (char *) (mudconf.comma_say ? "says," : "says"); /* Find the u-function. If we have a problem with it, we just default * to no transformation. */ atext1 = atext2 = NULL; is_transform = 0; if (nfargs >= 4) { Parse_Uattr(player, fargs[3], obj1, anum1, ap1); if (ap1) { atext1 = atr_pget(obj1, ap1->number, &aowner1, &aflags1, &alen1); if (!*atext1 || !(See_attr(player, obj1, ap1, aowner1, aflags1))) { free_lbuf(atext1); atext1 = NULL; } else { is_transform = 1; } } else { atext1 = NULL; } } /* Do some up-front work on the empty-case u-function, too. */ if (nfargs >= 5) { Parse_Uattr(player, fargs[4], obj2, anum2, ap2); if (ap2) { atext2 = atr_pget(obj2, ap2->number, &aowner2, &aflags2, &alen2); if (!*atext2 || !(See_attr(player, obj2, ap2, aowner2, aflags2))) { free_lbuf(atext2); atext2 = NULL; } } else { atext2 = NULL; } } /* Take care of the easy case, no u-function. */ if (!is_transform) { switch (*fargs[1]) { case ':': if (*(fargs[1] + 1) == ' ') { safe_tprintf_str(buff, bufc, "%s%s", Name(thing), fargs[1] + 2); } else { safe_tprintf_str(buff, bufc, "%s %s", Name(thing), fargs[1] + 1); } break; case ';': safe_tprintf_str(buff, bufc, "%s%s", Name(thing), fargs[1] + 1); break; case '|': safe_tprintf_str(buff, bufc, "%s", fargs[1] + 1); break; case '"': safe_tprintf_str(buff, bufc, "%s %s \"%s\"", Name(thing), say_str, fargs[1] + 1); break; default: safe_tprintf_str(buff, bufc, "%s %s \"%s\"", Name(thing), say_str, fargs[1]); break; } return; } /* Now for the nasty stuff. */ switch (*fargs[1]) { case ':': safe_name(thing, buff, bufc); if (*(fargs[1] + 1) != ' ') { safe_chr(' ', buff, bufc); str = fargs[1] + 1; key = SAY_POSE; } else { str = fargs[1] + 2; key = SAY_POSE_NOSPC; } break; case ';': safe_name(thing, buff, bufc); str = fargs[1] + 1; key = SAY_POSE_NOSPC; break; case '|': str = fargs[1] + 1; key = SAY_EMIT; break; case '"': str = fargs[1] + 1; key = SAY_SAY; break; default: str = fargs[1]; key = SAY_SAY; } transform_say(thing, str, key, say_str, atext1, atext2, &isep, &osep, player, caller, cause, buff, bufc); }