/*! \file set.cpp * \brief Commands which modify objects. * * These functions primarily implement commands like \@set and * \@name that modify attributes or basic properties of an object, * but this file also includes \@trigger and the use command. */ #include "copyright.h" #include "autoconf.h" #include "config.h" #include "externs.h" #include "routing.h" void set_modified(dbref thing) { CLinearTimeAbsolute ltaNow; ltaNow.GetLocal(); atr_add_raw(thing, A_MODIFIED, ltaNow.ReturnDateString(7)); } dbref match_controlled_handler(dbref executor, const UTF8 *name, bool bQuiet) { dbref mat; init_match(executor, name, NOTYPE); match_everything(MAT_EXIT_PARENTS); if (bQuiet) { mat = match_result(); } else { mat = noisy_match_result(); } if (!Good_obj(mat)) { return mat; } if (Controls(executor, mat)) { if (NoModify(mat) && !WizRoy(executor)) { if (!bQuiet) { notify_quiet(executor, NOPERM_MESSAGE); } return NOTHING; } return mat; } if (!bQuiet) { notify_quiet(executor, NOPERM_MESSAGE); } return NOTHING; } void do_chzone ( dbref executor, dbref caller, dbref enactor, int eval, int key, int nargs, UTF8 *name, UTF8 *newobj, const UTF8 *cargs[], int ncargs ) { UNUSED_PARAMETER(caller); UNUSED_PARAMETER(enactor); UNUSED_PARAMETER(eval); UNUSED_PARAMETER(key); UNUSED_PARAMETER(nargs); UNUSED_PARAMETER(cargs); UNUSED_PARAMETER(ncargs); if (!mudconf.have_zones) { notify(executor, M_("Zones disabled.")); return; } init_match(executor, name, NOTYPE); match_everything(0); dbref thing = noisy_match_result(); if (thing == NOTHING) { return; } dbref zone; if ( newobj[0] == '\0' || !mux_stricmp(newobj, T("none"))) { zone = NOTHING; } else { init_match(executor, newobj, NOTYPE); match_everything(0); zone = noisy_match_result(); if (zone == NOTHING) { return; } if ( !isThing(zone) && !isRoom(zone)) { notify(executor, M_("Invalid zone object type.")); return; } } if ( !Wizard(executor) && !Controls(executor, thing) && !check_zone_handler(executor, thing, true) && db[executor].owner != db[thing].owner) { notify(executor, M_("You don’t have the power to shift reality.")); return; } if (NoModify(thing) && !WizRoy(executor)) { notify(executor, NOPERM_MESSAGE); return; } // A player may change an object's zone to NOTHING or to an object he owns. // if ( zone != NOTHING && !Wizard(executor) && !Controls(executor, zone) && db[executor].owner != db[zone].owner) { notify(executor, M_("You cannot move that object to that zone.")); return; } // Only rooms may be zoned to other rooms. // if ( zone != NOTHING && isRoom(zone) && !isRoom(thing)) { notify(executor, M_("Only rooms may be zoned to other rooms.")); return; } // Everything is okay, do the change. // dbref old_zone = Zone(thing); s_Zone(thing, zone); // If a room changed zones, invalidate both the old and new zone // routing tables plus the inter-zone meta-table. // if (isRoom(thing) && Navigable(thing) && old_zone != zone) { route_invalidate_zone(old_zone); route_invalidate_zone(zone); route_invalidate_meta(); } if (!isPlayer(thing)) { // If the object is a player, resetting these flags is rather // inconvenient -- although this may pose a bit of a security risk. Be // careful when @chzone'ing wizard or royal players. // SetClearFlags(thing, mudconf.stripped_flags.word, nullptr); // Wipe out all powers. // Powers(thing) = 0; Powers2(thing) = 0; } notify(executor, M_("Zone changed.")); } void do_name ( dbref executor, dbref caller, dbref enactor, int eval, int key, int nargs, UTF8 *name, UTF8 *newname, const UTF8 *cargs[], int ncargs ) { UNUSED_PARAMETER(caller); UNUSED_PARAMETER(enactor); UNUSED_PARAMETER(eval); UNUSED_PARAMETER(key); UNUSED_PARAMETER(cargs); UNUSED_PARAMETER(ncargs); dbref thing = match_controlled(executor, name); if (NOTHING == thing) { return; } // Check for bad name. // if ( nargs < 2 || '\0' == newname[0]) { notify_quiet(executor, M_("Give it what new name?")); return; } // Check for renaming a player. // if (isPlayer(thing)) { UTF8 *buff = trim_spaces(newname); if ( !ValidatePlayerName(buff) || !badname_check(buff)) { notify_quiet(executor, M_("You can’t use that name.")); free_lbuf(buff); return; } if (!protectname_check(buff, thing)) { notify_quiet(executor, M_("That name is protected by another player.")); free_lbuf(buff); return; } else { bool bAlias = false; dbref jwho = lookup_player_name(buff, bAlias); if ( NOTHING != jwho && ( jwho != thing || bAlias)) { notify_quiet(executor, M_("That name is already in use.")); free_lbuf(buff); return; } } // Everything ok, notify. // STARTLOG(LOG_SECURITY, "SEC", "CNAME"); log_name(thing), log_text(T(" renamed to ")); log_text(buff); ENDLOG; if (Suspect(thing)) { raw_broadcast(WIZARD, M_("[Suspect] %s renamed to %s"), Name(thing), buff); } delete_player_name(thing, Name(thing), false); s_Name(thing, buff); set_modified(thing); add_player_name(thing, Name(thing), false); if (!Quiet(executor) && !Quiet(thing)) { notify_quiet(executor, M_("Name set.")); } free_lbuf(buff); return; } else { size_t nValidName; bool bValid; UTF8 *pValidName; if (isExit(thing)) { pValidName = MakeCanonicalExitName(newname, &nValidName, &bValid); } else if (isThing(thing)) { pValidName = MakeCanonicalObjectName(newname, &nValidName, &bValid, mudconf.thing_name_charset); } else { pValidName = MakeCanonicalObjectName(newname, &nValidName, &bValid, mudconf.room_name_charset); } if (!bValid) { notify_quiet(executor, M_("That is not a reasonable name.")); return; } // Everything ok, change the name. // s_Name(thing, pValidName); set_modified(thing); if (!Quiet(executor) && !Quiet(thing)) { notify_quiet(executor, M_("Name set.")); } } } /* * --------------------------------------------------------------------------- * * do_alias: Make an alias for a player or object. */ void do_alias ( dbref executor, dbref caller, dbref enactor, int eval, int key, int nargs, UTF8 *name, UTF8 *alias, const UTF8 *cargs[], int ncargs ) { UNUSED_PARAMETER(caller); UNUSED_PARAMETER(enactor); UNUSED_PARAMETER(eval); UNUSED_PARAMETER(key); UNUSED_PARAMETER(nargs); UNUSED_PARAMETER(cargs); UNUSED_PARAMETER(ncargs); dbref thing = match_controlled(executor, name); if (NOTHING == thing) { return; } // Check for renaming a player. // dbref aowner; int aflags; ATTR *ap = atr_num(A_ALIAS); if (isPlayer(thing)) { // Fetch the old alias. // LBuf oldalias = LBuf_Adopt(atr_pget(thing, A_ALIAS, &aowner, &aflags)); UTF8 *trimalias = trim_spaces(alias); dbref nPlayer; bool bAlias = false; if (!Controls(executor, thing)) { // Make sure we have rights to do it. We can't do the // normal Set_attr check because ALIAS is only // writable by GOD and we want to keep people from // doing &ALIAS and bypassing the player name checks. // notify_quiet(executor, NOPERM_MESSAGE); } else if (!*trimalias) { // New alias is null, just clear it. // delete_player_name(thing, oldalias, true); atr_clr(thing, A_ALIAS); if (!Quiet(executor)) { notify_quiet(executor, M_("Alias removed.")); } } else if ( (nPlayer = lookup_player_name(trimalias, bAlias)) != NOTHING && ( nPlayer != thing || !bAlias)) { // Make sure new alias isn't already in use. // notify_quiet(executor, M_("That name is already in use.")); } else if ( !( badname_check(trimalias) && ValidatePlayerName(trimalias))) { notify_quiet(executor, M_("That’s a silly name for a player!")); } else { // Remove the old name and add the new name. // delete_player_name(thing, oldalias, true); atr_add(thing, A_ALIAS, trimalias, Owner(executor), aflags); if (add_player_name(thing, trimalias, true)) { if (!Quiet(executor)) { notify_quiet(executor, M_("Alias set.")); } } else { notify_quiet(executor, T("That name is already in use or is illegal, alias cleared.")); atr_clr(thing, A_ALIAS); } } free_lbuf(trimalias); } else { atr_pget_info(thing, A_ALIAS, &aowner, &aflags); // Make sure we have rights to do it. // if (!bCanSetAttr(executor, thing, ap)) { notify_quiet(executor, NOPERM_MESSAGE); } else { atr_add(thing, A_ALIAS, alias, Owner(executor), aflags); if (!Quiet(executor)) { notify_quiet(executor, M_("Set.")); } } } } // --------------------------------------------------------------------------- // do_forwardlist: Set a forwardlist. // --------------------------------------------------------------------------- void do_forwardlist ( dbref executor, dbref caller, dbref enactor, int eval, int key, int nargs, UTF8 *target, UTF8 *newlist, const UTF8 *cargs[], int ncargs ) { UNUSED_PARAMETER(caller); UNUSED_PARAMETER(enactor); UNUSED_PARAMETER(eval); UNUSED_PARAMETER(key); UNUSED_PARAMETER(nargs); UNUSED_PARAMETER(cargs); UNUSED_PARAMETER(ncargs); dbref thing = match_controlled(executor, target); if (thing == NOTHING) { return; } dbref aowner, aflags; atr_pget_info(thing, A_FORWARDLIST, &aowner, &aflags); if (!Controls(executor, thing)) { notify_quiet(executor, NOPERM_MESSAGE); return; } else if (!*newlist) { // New forwardlist is null, just clear it. // atr_clr(thing, A_FORWARDLIST); set_modified(thing); if (!Quiet(executor)) { notify_quiet(executor, M_("Forwardlist removed.")); } } else if (!fwdlist_ck(executor, thing, A_FORWARDLIST, newlist)) { notify_quiet(executor, M_("Invalid forwardlist.")); return; } else { atr_add(thing, A_FORWARDLIST, newlist, Owner(executor), aflags); if (!Quiet(executor)) { notify_quiet(executor, M_("Set.")); } } } /* * --------------------------------------------------------------------------- * * do_lock: Set a lock on an object or attribute. */ void do_lock ( dbref executor, dbref caller, dbref enactor, int eval, int key, int nargs, UTF8 *name, UTF8 *keytext, const UTF8 *cargs[], int ncargs ) { UNUSED_PARAMETER(caller); UNUSED_PARAMETER(enactor); UNUSED_PARAMETER(eval); UNUSED_PARAMETER(nargs); UNUSED_PARAMETER(cargs); UNUSED_PARAMETER(ncargs); dbref thing; ATTR *ap; if ( parse_attrib(executor, name, &thing, &ap) && ap) { dbref aowner; int aflags; if (!atr_get_info(thing, ap->number, &aowner, &aflags)) { notify_quiet(executor, M_("Attribute not present on object.")); return; } if (bCanLockAttr(executor, thing, ap)) { aflags |= AF_LOCK; atr_set_flags(thing, ap->number, aflags); if ( !Quiet(executor) && !Quiet(thing)) { notify_quiet(executor, M_("Attribute locked.")); } } else { notify_quiet(executor, NOPERM_MESSAGE); } return; } init_match(executor, name, NOTYPE); match_everything(MAT_EXIT_PARENTS); thing = match_result(); switch (thing) { case NOTHING: notify_quiet(executor, M_("I don’t see what you want to lock!")); return; case AMBIGUOUS: notify_quiet(executor, M_("I don’t know which one you want to lock!")); return; default: if (!Controls(executor, thing)) { notify_quiet(executor, M_("You can’t lock that!")); return; } if (NoModify(thing) && !WizRoy(executor)) { notify_quiet(executor, NOPERM_MESSAGE); return; } } thread_local UTF8 pRestrictedKeyText[LBUF_SIZE]; StripTabsAndTruncate(keytext, pRestrictedKeyText, LBUF_SIZE-1, LBUF_SIZE-1); struct boolexp *okey = parse_boolexp(executor, pRestrictedKeyText, false); if (okey == TRUE_BOOLEXP) { notify_quiet(executor, M_("I don’t understand that key.")); } else { // Everything ok, do it. // if (!key) { key = A_LOCK; } atr_add_raw(thing, key, unparse_boolexp_quiet(executor, okey)); if ( !Quiet(executor) && !Quiet(thing)) { notify_quiet(executor, M_("Locked.")); } } free_boolexp(okey); } /* * --------------------------------------------------------------------------- * * Remove a lock from an object of attribute. */ void do_unlock(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *name, const UTF8 *cargs[], int ncargs) { UNUSED_PARAMETER(caller); UNUSED_PARAMETER(enactor); UNUSED_PARAMETER(eval); UNUSED_PARAMETER(cargs); UNUSED_PARAMETER(ncargs); dbref thing; ATTR *ap; if ( parse_attrib(executor, name, &thing, &ap) && ap) { // We have been asked to unlock an attribute. // dbref aowner; int aflags; if (!atr_get_info(thing, ap->number, &aowner, &aflags)) { notify_quiet(executor, M_("Attribute not present on object.")); return; } if (bCanLockAttr(executor, thing, ap)) { aflags &= ~AF_LOCK; atr_set_flags(thing, ap->number, aflags); if ( !Quiet(executor) && !Quiet(thing)) { notify_quiet(executor, M_("Attribute unlocked.")); } } else { notify_quiet(executor, NOPERM_MESSAGE); } return; } // We have been asked to change the ownership of an object. // if (!key) { key = A_LOCK; } thing = match_controlled(executor, name); if (thing != NOTHING) { atr_clr(thing, key); set_modified(thing); if (!Quiet(executor) && !Quiet(thing)) { notify_quiet(executor, M_("Unlocked.")); } } } /* * --------------------------------------------------------------------------- * * do_unlink: Unlink an exit from its destination or remove a dropto. */ void do_unlink(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *name, const UTF8 *cargs[], int ncargs) { UNUSED_PARAMETER(caller); UNUSED_PARAMETER(enactor); UNUSED_PARAMETER(key); UNUSED_PARAMETER(eval); UNUSED_PARAMETER(cargs); UNUSED_PARAMETER(ncargs); dbref exit; init_match(executor, name, TYPE_EXIT); match_everything(0); exit = match_result(); switch (exit) { case NOTHING: notify_quiet(executor, M_("Unlink what?")); break; case AMBIGUOUS: notify_quiet(executor, M_("I don’t know which one you mean!")); break; default: if (!Controls(executor, exit)) { notify_quiet(executor, NOPERM_MESSAGE); } else if (NoModify(exit) && !WizRoy(executor)) { notify_quiet(executor, NOPERM_MESSAGE); } else { switch (Typeof(exit)) { case TYPE_EXIT: s_Location(exit, NOTHING); if (!Quiet(executor)) { notify_quiet(executor, M_("Unlinked.")); } giveto(Owner(exit), mudconf.linkcost); route_invalidate(); break; case TYPE_ROOM: s_Dropto(exit, NOTHING); if (!Quiet(executor)) { notify_quiet(executor, M_("Dropto removed.")); } break; default: notify_quiet(executor, M_("You can’t unlink that!")); break; } } } } void TranslateFlags_Clone ( FLAG aClearFlags[3], dbref executor, int key ) { if (key & CLONE_NOSTRIP) { if (God(executor)) { // #1 using /nostrip causes nothing to be stripped. // aClearFlags[FLAG_WORD1] = 0; } else { // With #1 powers, /nostrip still strips the WIZARD bit. // aClearFlags[FLAG_WORD1] = WIZARD; } aClearFlags[FLAG_WORD2] = 0; aClearFlags[FLAG_WORD3] = 0; } else { if ( (key & CLONE_INHERIT) && Inherits(executor)) { // The /inherit switch specifically allows INHERIT powers through. // aClearFlags[FLAG_WORD1] = (WIZARD | mudconf.stripped_flags.word[FLAG_WORD1]) & ~(INHERIT); } else { // Normally WIZARD and the other stripped_flags are removed. // aClearFlags[FLAG_WORD1] = WIZARD | mudconf.stripped_flags.word[FLAG_WORD1]; } aClearFlags[FLAG_WORD2] = mudconf.stripped_flags.word[FLAG_WORD2]; aClearFlags[FLAG_WORD3] = mudconf.stripped_flags.word[FLAG_WORD3]; } } void TranslateFlags_Chown ( FLAG aClearFlags[3], FLAG aSetFlags[3], bool *bClearPowers, dbref executor, int key ) { if (key & CHOWN_NOSTRIP) { if (God(executor)) { // #1 using /nostrip only clears CHOWN_OK and sets HALT. // aClearFlags[FLAG_WORD1] = CHOWN_OK; *bClearPowers = false; } else { // With /nostrip, CHOWN_OK and WIZARD are cleared, HALT is // set, and powers are cleared. // aClearFlags[FLAG_WORD1] = CHOWN_OK | WIZARD; *bClearPowers = true; } aClearFlags[FLAG_WORD2] = 0; aClearFlags[FLAG_WORD3] = 0; } else { // Without /nostrip, CHOWN_OK, WIZARD, and stripped_flags are // cleared, HALT is set, powers are cleared. // aClearFlags[FLAG_WORD1] = CHOWN_OK | WIZARD | mudconf.stripped_flags.word[FLAG_WORD1]; aClearFlags[FLAG_WORD2] = mudconf.stripped_flags.word[FLAG_WORD2]; aClearFlags[FLAG_WORD3] = mudconf.stripped_flags.word[FLAG_WORD3]; *bClearPowers = true; } aSetFlags[FLAG_WORD1] = HALT; aSetFlags[FLAG_WORD2] = 0; aSetFlags[FLAG_WORD3] = 0; } void SetClearFlags ( dbref thing, FLAG aClearFlags[3], FLAG aSetFlags[3] ) { int j; for (j = FLAG_WORD1; j <= FLAG_WORD3; j++) { FLAG newval = db[thing].fs.word[j]; if (nullptr != aClearFlags) { newval &= ~aClearFlags[j]; } if (nullptr != aSetFlags) { newval |= aSetFlags[j]; } s_Flags(thing, j, newval); } } /* * --------------------------------------------------------------------------- * * do_chown: Change ownership of an object or attribute. */ void do_chown ( dbref executor, dbref caller, dbref enactor, int eval, int key, int nargs, UTF8 *name, UTF8 *newown, const UTF8 *cargs[], int ncargs ) { UNUSED_PARAMETER(caller); UNUSED_PARAMETER(enactor); UNUSED_PARAMETER(eval); UNUSED_PARAMETER(nargs); UNUSED_PARAMETER(cargs); UNUSED_PARAMETER(ncargs); dbref nOwnerOrig, nOwnerNew, thing; bool bDoit; ATTR *ap; if ( parse_attrib(executor, name, &thing, &ap) && ap && See_attr(executor, thing, ap)) { // An attribute was given, so we worry about changing the owner of the // attribute. // nOwnerOrig = Owner(thing); if (!*newown) { nOwnerNew = nOwnerOrig; } else if (!string_compare(newown, T("me"))) { nOwnerNew = Owner(executor); } else { nOwnerNew = lookup_player(executor, newown, true); } // You may chown an attr to yourself if you own the object and the attr // is not locked. You may chown an attr to the owner of the object if // you own the attribute. To do anything else you must be a wizard. // Only #1 can chown attributes on #1. // dbref aowner; int aflags; if (!atr_get_info(thing, ap->number, &aowner, &aflags)) { notify_quiet(executor, M_("Attribute not present on object.")); return; } bDoit = false; if (nOwnerNew == NOTHING) { notify_quiet(executor, M_("I couldn’t find that player.")); } else if ( God(thing) && !God(executor)) { notify_quiet(executor, NOPERM_MESSAGE); } else if (Wizard(executor)) { bDoit = true; } else if (nOwnerNew == Owner(executor)) { // Chown to me: only if I own the obj and !locked // if ( !Controls(executor, thing) || (aflags & AF_LOCK)) { notify_quiet(executor, NOPERM_MESSAGE); } else { bDoit = true; } } else if (nOwnerNew == nOwnerOrig) { // chown to obj owner: only if I own attr and !locked // if ( Owner(executor) != aowner || (aflags & AF_LOCK)) { notify_quiet(executor, NOPERM_MESSAGE); } else { bDoit = true; } } else { notify_quiet(executor, NOPERM_MESSAGE); } if (!bDoit) { return; } if (!bCanSetAttr(executor, executor, ap)) { notify_quiet(executor, NOPERM_MESSAGE); return; } LBuf buff = LBuf_Adopt(atr_get("do_chown.887", thing, ap->number, &aowner, &aflags)); atr_add(thing, ap->number, buff, nOwnerNew, aflags); if (!Quiet(executor)) { notify_quiet(executor, M_("Attribute owner changed.")); } return; } // An attribute was not specified, so we are being asked to change the // owner of the object. // init_match(executor, name, TYPE_THING); match_possession(); match_here(); match_exit(); match_me(); if (Chown_Any(executor)) { match_player(); match_absolute(); } switch (thing = match_result()) { case NOTHING: notify_quiet(executor, M_("You don’t have that!")); return; case AMBIGUOUS: notify_quiet(executor, M_("I don’t know which you mean!")); return; } nOwnerOrig = Owner(thing); if (NoModify(thing) && !WizRoy(executor)) { notify_quiet(executor, NOPERM_MESSAGE); return; } if (!*newown || !(string_compare(newown, T("me")))) { nOwnerNew = Owner(executor); } else { nOwnerNew = lookup_player(executor, newown, true); } int cost = 1, quota = 1; switch (Typeof(thing)) { case TYPE_ROOM: cost = mudconf.digcost; quota = mudconf.room_quota; break; case TYPE_THING: cost = OBJECT_DEPOSIT(Pennies(thing)); quota = mudconf.thing_quota; break; case TYPE_EXIT: cost = mudconf.opencost; quota = mudconf.exit_quota; break; case TYPE_PLAYER: cost = mudconf.robotcost; quota = mudconf.player_quota; break; } bool bPlayerControlsThing = Controls(executor, thing); if ( isGarbage(thing) && bPlayerControlsThing) { notify_quiet(executor, M_("You shouldn’t be rummaging through the garbage.")); } else if (nOwnerNew == NOTHING) { notify_quiet(executor, M_("I couldn’t find that player.")); } else if ( isPlayer(thing) && !God(executor)) { notify_quiet(executor, M_("Players always own themselves.")); } else if ( ( !bPlayerControlsThing && !Chown_Any(executor) && !Chown_ok(thing)) || ( isThing(thing) && Location(thing) != executor && !Chown_Any(executor)) || !Controls(executor, nOwnerNew) || God(thing)) { notify_quiet(executor, NOPERM_MESSAGE); } else if (canpayfees(executor, nOwnerNew, cost, quota)) { giveto(nOwnerOrig, cost); if (mudconf.quotas) { add_quota(nOwnerOrig, quota); } if (!God(executor)) { nOwnerNew = Owner(nOwnerNew); } s_Owner(thing, nOwnerNew); atr_chown(thing); FLAGSET clearflags; FLAGSET setflags; bool bClearPowers; TranslateFlags_Chown(clearflags.word, setflags.word, &bClearPowers, executor, key); SetClearFlags(thing, clearflags.word, setflags.word); if (bClearPowers) { s_Powers(thing, 0); s_Powers2(thing, 0); } // Always halt the queue. // halt_que(NOTHING, thing); // Warn if ROYALTY is left set. // if (ROYALTY & db[thing].fs.word[FLAG_WORD1]) { notify_quiet(executor, tprintf(M_("Warning: @chown/nostrip on %s(#%d) leaves ROYALTY privilege intact."), Moniker(thing), thing)); } // Warn if INHERIT is left set. // if (INHERIT & db[thing].fs.word[FLAG_WORD1]) { notify_quiet(executor, tprintf(M_("Warning: @chown/nostrip on %s(#%d) leaves INHERIT privilege intact."), Moniker(thing), thing)); } if (!Quiet(executor)) { LBuf buff = LBuf_Src("do_chown.notify"); UTF8 *bp = buff; safe_tprintf_str(buff, &bp, T("Owner of %s(#%d) changed from "), Moniker(thing), thing); safe_tprintf_str(buff, &bp, T("%s(#%d) to "), Moniker(nOwnerOrig), nOwnerOrig); safe_tprintf_str(buff, &bp, T("%s(#%d)."), Moniker(nOwnerNew), nOwnerNew); *bp = '\0'; notify_quiet(executor, buff); } } } /* * --------------------------------------------------------------------------- * * do_set: Set flags or attributes on objects, or flags on attributes. */ static void set_attr_internal(dbref player, dbref thing, int attrnum, const UTF8 *attrtext, int key) { dbref aowner; int aflags; ATTR *pattr = atr_num(attrnum); atr_pget_info(thing, attrnum, &aowner, &aflags); if ( pattr && bCanSetAttr(player, thing, pattr)) { bool could_hear = Hearer(thing); atr_add(thing, attrnum, attrtext, Owner(player), aflags); handle_ears(thing, could_hear, Hearer(thing)); if ( !(key & SET_QUIET) && !Quiet(player) && !Quiet(thing)) { notify_quiet(player, M_("Set.")); } } else { notify_quiet(player, NOPERM_MESSAGE); } } bool copy_attr(dbref executor, attr_info &src, attr_info &dest, int key) { if ( !Good_obj(src.m_object) || !Good_obj(dest.m_object) || nullptr == src.m_attr) { notify_quiet(executor, M_("No match.")); return false; } if ( nullptr == dest.m_attr || !See_attr(executor, src.m_object, src.m_attr) || !bCanSetAttr(executor, dest.m_object, dest.m_attr)) { notify_quiet(executor, NOPERM_MESSAGE); return false; } LBuf buff = LBuf_Src("copy_attr"); atr_pget_str(buff, src.m_object, src.m_attr->number, &src.m_aowner, &src.m_aflags); src.m_bHaveInfo = true; set_attr_internal(executor, dest.m_object, dest.m_attr->number, buff, key); return true; } void do_set ( dbref executor, dbref caller, dbref enactor, int eval, int key, int nargs, UTF8 *name, UTF8 *flagname, const UTF8 *cargs[], int ncargs ) { UNUSED_PARAMETER(caller); UNUSED_PARAMETER(enactor); UNUSED_PARAMETER(eval); UNUSED_PARAMETER(nargs); UNUSED_PARAMETER(cargs); UNUSED_PARAMETER(ncargs); dbref thing, aowner; int aflags; ATTR *pattr; // See if we have the / form, which is how you set // attribute flags. // if (parse_attrib(executor, name, &thing, &pattr)) { if ( pattr && See_attr(executor, thing, pattr)) { // You must specify a flag name. // if ( !flagname || flagname[0] == '\0') { notify_quiet(executor, M_("I don’t know what you want to set!")); return; } // Check for clearing. // bool clear = false; if (flagname[0] == NOT_TOKEN) { flagname++; clear = true; } // Make sure player specified a valid attribute flag. // int flagvalue; if (!search_nametab(executor, indiv_attraccess_nametab, flagname, &flagvalue)) { notify_quiet(executor, M_("You can’t set that!")); return; } // Make sure the object has the attribute present. // if (!atr_get_info(thing, pattr->number, &aowner, &aflags)) { notify_quiet(executor, M_("Attribute not present on object.")); return; } // Make sure we can write to the attribute. // if (!bCanSetAttr(executor, thing, pattr)) { notify_quiet(executor, NOPERM_MESSAGE); return; } // Check if the attribute flag is already in the desired // state. // if (clear ? ((aflags & flagvalue) == 0) : ((aflags & flagvalue) == flagvalue)) { if ( !(key & SET_QUIET) && !Quiet(executor) && !Quiet(thing)) { if (clear) { notify_quiet(executor, M_("Already cleared.")); } else { notify_quiet(executor, M_("Already set.")); } } return; } // Go do it. // if (clear) { aflags &= ~flagvalue; } else { aflags |= flagvalue; } bool could_hear = Hearer(thing); atr_set_flags(thing, pattr->number, aflags); // Tell the player about it. // handle_ears(thing, could_hear, Hearer(thing)); if ( !(key & SET_QUIET) && !Quiet(executor) && !Quiet(thing)) { if (clear) { notify_quiet(executor, M_("Cleared.")); } else { notify_quiet(executor, M_("Set.")); } } return; } } // Find thing. // thing = match_controlled(executor, name); if (!Good_obj(thing)) { return; } // Check for attribute set first. // UTF8 *p; for (p = flagname; *p && (*p != ':'); p++) { ; // Nothing. } if (*p) { *p++ = '\0'; int atr = mkattr(executor, flagname); if (atr <= 0) { notify_quiet(executor, M_("Couldn’t create attribute.")); return; } pattr = atr_num(atr); if (!pattr) { notify_quiet(executor, NOPERM_MESSAGE); return; } if (!bCanSetAttr(executor, thing, pattr)) { notify_quiet(executor, NOPERM_MESSAGE); return; } // Check for _ // if (*p == '_') { p++; attr_info src(executor, p, false, false); attr_info dest(thing, pattr); copy_attr(executor, src, dest, key); return; } // Go set it. // set_attr_internal(executor, thing, atr, p, key); return; } // Set or clear a flag. // flag_set(thing, executor, flagname, key); } void do_power ( dbref executor, dbref caller, dbref enactor, int eval, int key, int nargs, UTF8 *name, UTF8 *flag, const UTF8 *cargs[], int ncargs ) { UNUSED_PARAMETER(caller); UNUSED_PARAMETER(enactor); UNUSED_PARAMETER(eval); UNUSED_PARAMETER(nargs); UNUSED_PARAMETER(cargs); UNUSED_PARAMETER(ncargs); if ( !flag || !*flag) { notify_quiet(executor, M_("I don’t know what you want to set!")); return; } // Find thing. // dbref thing = match_controlled(executor, name); if (thing == NOTHING) { return; } power_set(thing, executor, flag, key); } void do_setattr ( dbref executor, dbref caller, dbref enactor, int eval, int attrnum, int nargs, UTF8 *name, UTF8 *attrtext, const UTF8 *cargs[], int ncargs ) { UNUSED_PARAMETER(caller); UNUSED_PARAMETER(enactor); UNUSED_PARAMETER(eval); UNUSED_PARAMETER(nargs); UNUSED_PARAMETER(cargs); UNUSED_PARAMETER(ncargs); init_match(executor, name, NOTYPE); match_everything(MAT_EXIT_PARENTS); dbref thing = noisy_match_result(); if (!Good_obj(thing)) { return; } set_attr_internal(executor, thing, attrnum, attrtext, 0); } void do_cpattr(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *oldpair, UTF8 *newpair[], int nargs, const UTF8 *cargs[], int ncargs) { UNUSED_PARAMETER(enactor); UNUSED_PARAMETER(caller); UNUSED_PARAMETER(eval); UNUSED_PARAMETER(cargs); UNUSED_PARAMETER(ncargs); if ( isEmpty(oldpair) || isEmpty(newpair[0]) || nargs < 1) { return; } // Check if source has wildcard in attribute portion. // bool bWild = false; const UTF8 *p = oldpair; while (*p && *p != '/') { p++; } if (*p == '/') { p++; while (*p) { if (*p == '*' || *p == '?') { bWild = true; break; } p++; } } if (bWild) { // Wildcard path: iterate all matching attributes on source object. // dbref thing; olist_push(); if (!parse_attrib_wild(executor, oldpair, &thing, false, false, false)) { olist_pop(); notify_quiet(executor, M_("No match.")); return; } int atr; for (atr = olist_first(); atr != NOTHING; atr = olist_next()) { ATTR *ap = atr_num(atr); if (ap) { attr_info src(thing, ap); for (int i = 0; i < nargs; i++) { attr_info dest(executor, newpair[i], true, false); if (!dest.m_bValid) { if (Good_obj(dest.m_object)) { dest.m_attr = src.m_attr; dest.m_bValid = true; } } if (dest.m_bValid) { copy_attr(executor, src, dest, key); } } } } olist_pop(); } else { // Single-attribute path. // attr_info src(executor, oldpair, false, true); if (src.m_bValid) { for (int i = 0; i < nargs; i++) { attr_info dest(executor, newpair[i], true, false); if (!dest.m_bValid) { if (Good_obj(dest.m_object)) { dest.m_attr = src.m_attr; dest.m_bValid = true; } } if (dest.m_bValid) { copy_attr(executor, src, dest, key); } } } else { notify_quiet(executor, M_("No match.")); } } } void do_mvattr(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *what, UTF8 *args[], int nargs, const UTF8 *cargs[], int ncargs) { UNUSED_PARAMETER(eval); UNUSED_PARAMETER(caller); UNUSED_PARAMETER(enactor); UNUSED_PARAMETER(key); UNUSED_PARAMETER(cargs); UNUSED_PARAMETER(ncargs); // Make sure we have something to do. // if (nargs < 2) { notify_quiet(executor, M_("Nothing to do.")); return; } // Find and make sure we control the target object. // dbref thing = match_controlled(executor, what); if (thing == NOTHING) { return; } // Look up the source attribute. If it either doesn't exist or isn't // readable, use an empty string. // int in_anum = -1; LBuf astr = LBuf_Src("do_mvattr"); ATTR *in_attr = atr_str(args[0]); int aflags = 0; if (in_attr == nullptr) { *astr = '\0'; } else { dbref aowner; atr_get_str(astr, thing, in_attr->number, &aowner, &aflags); if (See_attr(executor, thing, in_attr)) { in_anum = in_attr->number; } else { *astr = '\0'; } } // Copy the attribute to each target in turn. // bool bCanDelete = true; int nCopied = 0; for (int i = 1; i < nargs; i++) { int anum = mkattr(executor, args[i]); if (anum <= 0) { notify_quiet(executor, tprintf(M_("%s: That’s not a good name for an attribute."), args[i])); continue; } ATTR *out_attr = atr_num(anum); if (!out_attr) { notify_quiet(executor, tprintf(M_("%s: Permission denied."), args[i])); } else if (out_attr->number == in_anum) { // It doesn't make sense to delete a source attribute if it's also // included as a destination. // bCanDelete = false; } else { if (!bCanSetAttr(executor, thing, out_attr)) { notify_quiet(executor, tprintf(M_("%s: Permission denied."), args[i])); } else { nCopied++; atr_add(thing, out_attr->number, astr, Owner(executor), aflags); if (!Quiet(executor)) { notify_quiet(executor, tprintf(M_("%s: Set."), out_attr->name)); } } } } // Remove the source attribute if we were able to copy it successfully to // even one destination object. // if (nCopied <= 0) { if (in_attr) { notify_quiet(executor, tprintf(M_("%s: Not copied anywhere. Not cleared."), in_attr->name)); } else { notify_quiet(executor, M_("Not copied anywhere. Non-existent attribute.")); } } else if ( in_anum > 0 && bCanDelete) { in_attr = atr_num(in_anum); if (in_attr) { if (bCanSetAttr(executor, thing, in_attr)) { atr_clr(thing, in_attr->number); if (!Quiet(executor)) { notify_quiet(executor, tprintf(M_("%s: Cleared."), in_attr->name)); } } else { notify_quiet(executor, tprintf(M_("%s: Could not remove old attribute. Permission denied."), in_attr->name)); } } else { notify_quiet(executor, M_("Could not remove old attribute. Non-existent attribute.")); } } } /* * --------------------------------------------------------------------------- * * parse_attrib, parse_attrib_wild: parse / tokens. */ bool parse_attrib(dbref player, const UTF8 *str, dbref *thing, ATTR **attr) { ATTR *tattr = nullptr; *thing = NOTHING; if (!str) { *attr = tattr; return false; } // Break apart string into obj and attr. // const UTF8 *AttrName; bool retval = parse_thing_slash(player, str, &AttrName, thing); // Get the named attribute from the object if we can. // if (retval) { tattr = atr_str(AttrName); } *attr = tattr; return retval; } void find_wild_attrs(dbref player, dbref thing, const UTF8 *str, bool check_exclude, bool hash_insert, bool get_locks) { ATTR *pattr; dbref aowner; int ca, ok, aflags; // Walk the attribute list of the object. // atr_push(); unsigned char *as; for (ca = atr_head(thing, &as); ca; ca = atr_next(&as)) { pattr = atr_num(ca); // Discard bad attributes and ones we've seen before. // if (!pattr) { continue; } if ( check_exclude && ( (pattr->flags & AF_PRIVATE) || mudstate.parent_htab.find(ca) != mudstate.parent_htab.end())) { continue; } // If we aren't the top level remember this attr so we exclude it in // any parents. // atr_get_info(thing, ca, &aowner, &aflags); if ( check_exclude && (aflags & AF_PRIVATE)) { continue; } if (get_locks) { ok = bCanReadAttr(player, thing, pattr, false); } else { ok = See_attr(player, thing, pattr); } mudstate.wild_invk_ctr = 0; if ( ok && quick_wild(str, pattr->name)) { olist_add(ca); if (hash_insert) { mudstate.parent_htab.emplace(ca, pattr); } } } atr_pop(); } bool parse_attrib_wild(dbref player, const UTF8 *str, dbref *thing, bool check_parents, bool get_locks, bool df_star) { if (!str) { return false; } dbref parent; int lev; bool check_exclude, hash_insert; const UTF8 *after = str; // Separate name and attr portions at the first /. // if (!parse_thing_slash(player, str, &after, thing)) { // Not in obj/attr format, return if not defaulting to. // if (!df_star) { return false; } // Look for the object, return failure if not found. // init_match(player, str, NOTYPE); match_everything(MAT_EXIT_PARENTS); *thing = match_result(); if (!Good_obj(*thing)) { return false; } after = T("*"); } // Check the object (and optionally all parents) for attributes. // if (check_parents) { check_exclude = false; hash_insert = check_parents; mudstate.parent_htab.clear(); ITER_PARENTS(*thing, parent, lev) { if (!Good_obj(Parent(parent))) { hash_insert = false; } find_wild_attrs(player, parent, after, check_exclude, hash_insert, get_locks); check_exclude = true; } } else { find_wild_attrs(player, *thing, after, false, false, get_locks); } return true; } /* * --------------------------------------------------------------------------- * * edit_string, edit_string_ansi, do_edit: Modify attributes. */ void edit_string(UTF8 *src, UTF8 **dst, UTF8 *from, UTF8 *to) { UTF8 *cp; // Do the substitution. Idea for prefix/suffix from R'nice@TinyTIM. // if (!strcmp(reinterpret_cast(from), "^")) { // Prepend 'to' to string. // *dst = alloc_lbuf("edit_string.^"); cp = *dst; safe_str(to, *dst, &cp); safe_str(src, *dst, &cp); *cp = '\0'; } else if (!strcmp(reinterpret_cast(from), "$")) { // Append 'to' to string. // *dst = alloc_lbuf("edit_string.$"); cp = *dst; safe_str(src, *dst, &cp); safe_str(to, *dst, &cp); *cp = '\0'; } else { // Replace all occurances of 'from' with 'to'. Handle the special // cases of from = \$ and \^. // if ( ( from[0] == '\\' || from[0] == '%') && ( from[1] == '$' || from[1] == '^') && from[2] == '\0') { from++; } *dst = replace_string(from, to, src); } } static void edit_string_ansi(UTF8 *src, UTF8 **dst, UTF8 **returnstr, UTF8 *from, UTF8 *to) { UTF8 *cp, *rp; // Do the substitution. Idea for prefix/suffix from R'nice@TinyTIM // if (!strcmp(reinterpret_cast(from), "^")) { // Prepend 'to' to string. // *dst = alloc_lbuf("edit_string.^"); cp = *dst; safe_str(to, *dst, &cp); safe_str(src, *dst, &cp); *cp = '\0'; // Do the ansi string used to notify. // *returnstr = alloc_lbuf("edit_string_ansi.^"); rp = *returnstr; safe_str(reinterpret_cast(COLOR_INTENSE), *returnstr, &rp); safe_str(to, *returnstr, &rp); safe_str(reinterpret_cast(COLOR_RESET), *returnstr, &rp); safe_str(src, *returnstr, &rp); *rp = '\0'; } else if (!strcmp(reinterpret_cast(from), "$")) { // Append 'to' to string // *dst = alloc_lbuf("edit_string.$"); cp = *dst; safe_str(src, *dst, &cp); safe_str(to, *dst, &cp); *cp = '\0'; // Do the ansi string used to notify. // *returnstr = alloc_lbuf("edit_string_ansi.$"); rp = *returnstr; safe_str(src, *returnstr, &rp); safe_str(reinterpret_cast(COLOR_INTENSE), *returnstr, &rp); safe_str(to, *returnstr, &rp); safe_str(reinterpret_cast(COLOR_RESET), *returnstr, &rp); *rp = '\0'; } else { // Replace all occurances of 'from' with 'to'. Handle the special // cases of from = \$ and \^. // if ( ((from[0] == '\\') || (from[0] == '%')) && ((from[1] == '$') || (from[1] == '^')) && ( from[2] == '\0')) { from++; } *dst = replace_string(from, to, src); *returnstr = replace_string(from, tprintf(T("%s%s%s"), COLOR_INTENSE, to, COLOR_RESET), src); } } void do_edit(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *it, UTF8 *args[], int nargs, const UTF8 *cargs[], int ncargs) { UNUSED_PARAMETER(eval); UNUSED_PARAMETER(caller); UNUSED_PARAMETER(enactor); UNUSED_PARAMETER(key); UNUSED_PARAMETER(cargs); UNUSED_PARAMETER(ncargs); dbref thing, aowner; int atr, aflags; bool bGotOne; UTF8 *from, *to, *result, *returnstr, *atext; ATTR *ap; // Make sure we have something to do. // if ( nargs < 1 || !*args[0]) { notify_quiet(executor, M_("Nothing to do.")); return; } from = args[0]; to = (nargs >= 2) ? args[1] : const_cast(T("")); // Look for the object and get the attribute (possibly wildcarded) // olist_push(); if ( !it || !*it || !parse_attrib_wild(executor, it, &thing, false, false, false)) { notify_quiet(executor, M_("No match.")); return; } // Iterate through matching attributes, performing edit. // bGotOne = 0; atext = alloc_lbuf("do_edit.atext"); bool could_hear = Hearer(thing); for (atr = olist_first(); atr != NOTHING; atr = olist_next()) { ap = atr_num(atr); if (ap) { // Get the attr and make sure we can modify it. // atr_get_str(atext, thing, ap->number, &aowner, &aflags); if (bCanSetAttr(executor, thing, ap)) { // Do the edit and save the result // bGotOne = true; edit_string_ansi(atext, &result, &returnstr, from, to); atr_add(thing, ap->number, result, Owner(executor), aflags); if (!Quiet(executor)) { notify_quiet(executor, tprintf(M_("Set - %s: %s"), ap->name, returnstr)); } free_lbuf(result); free_lbuf(returnstr); } else { // No rights to change the attr. // if (!Quiet(executor)) { notify_quiet(executor, tprintf(M_("%s: Permission denied."), ap->name)); } } } } // Clean up. // free_lbuf(atext); olist_pop(); if (!bGotOne) { notify_quiet(executor, M_("No matching attributes.")); } else { handle_ears(thing, could_hear, Hearer(thing)); } } void do_wipe(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *it, const UTF8 *cargs[], int ncargs) { UNUSED_PARAMETER(caller); UNUSED_PARAMETER(enactor); UNUSED_PARAMETER(eval); UNUSED_PARAMETER(key); UNUSED_PARAMETER(cargs); UNUSED_PARAMETER(ncargs); dbref thing; olist_push(); if ( !it || !*it || !parse_attrib_wild(executor, it, &thing, false, false, true)) { notify_quiet(executor, M_("No match.")); return; } if ( mudconf.safe_wipe && has_flag(NOTHING, thing, T("SAFE"))) { notify_quiet(executor, M_("SAFE objects may not be @wiped.")); return; } // Iterate through matching attributes, zapping the writable ones // int atr; ATTR *ap; bool bGotOne = false, could_hear = Hearer(thing); for (atr = olist_first(); atr != NOTHING; atr = olist_next()) { ap = atr_num(atr); if (ap) { // Get the attr and make sure we can modify it. // if (bCanSetAttr(executor, thing, ap)) { atr_clr(thing, ap->number); bGotOne = true; } } } // Clean up // olist_pop(); if (!bGotOne) { notify_quiet(executor, M_("No matching attributes.")); } else { set_modified(thing); handle_ears(thing, could_hear, Hearer(thing)); if (!Quiet(executor)) { notify_quiet(executor, M_("Wiped.")); } } } void do_trigger(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *object, UTF8 *argv[], int nargs, const UTF8 *cargs[], int ncargs) { UNUSED_PARAMETER(cargs); UNUSED_PARAMETER(ncargs); dbref thing; ATTR *pattr; if (!( parse_attrib(executor, object, &thing, &pattr) && pattr)) { notify_quiet(executor, M_("No match.")); return; } if (!Controls(executor, thing)) { notify_quiet(executor, NOPERM_MESSAGE); return; } if (key & TRIG_NOW) { // Execute the attribute inline instead of enqueuing. // dbref aowner; int aflags; UTF8 *act = atr_pget(thing, pattr->number, &aowner, &aflags); if (*act) { bool bRun = true; dbref aowner2; int aflags2; UTF8 *charges = atr_pget(thing, A_CHARGES, &aowner2, &aflags2); if (*charges) { // int64_t, not int (#1402): large charges must not wrap // to a small positive and skip RUNOUT. // int64_t num = mux_atoi64(charges); if (num > 0) { UTF8 *buff = alloc_sbuf("trigger.now.charges"); mux_i64toa(num - 1, buff); atr_add_raw(thing, A_CHARGES, buff); free_sbuf(buff); } else { UTF8 *buff = atr_pget(thing, A_RUNOUT, &aowner2, &aflags2); if (*buff) { free_lbuf(act); act = buff; } else { free_lbuf(buff); bRun = false; } } } free_lbuf(charges); if (bRun) { if ((aflags & AF_NOEVAL) || NoEval(thing)) { // NOEVAL content is data, not code -- skip execution. } else { process_command(thing, executor, executor, AttrTrace(aflags, 0), false, act, (const UTF8 **)argv, nargs); } } } free_lbuf(act); } else { did_it(executor, thing, 0, nullptr, 0, nullptr, pattr->number, 0, (const UTF8 **)argv, nargs); } if (key & TRIG_NOTIFY) { LBuf tbuf = LBuf_Src("trigger.notify_cmd"); mux_strncpy(tbuf, T("@notify/quiet me"), LBUF_SIZE-1); CLinearTimeAbsolute lta; wait_que(executor, caller, enactor, eval, false, lta, NOTHING, A_SEMAPHORE, tbuf, 0, nullptr, mudstate.global_regs); } if ( !(key & TRIG_QUIET) && !Quiet(executor)) { notify_quiet(executor, M_("Triggered.")); } } // --------------------------------------------------------------------------- // do_include: Inline-expand the contents of an attribute into the current // action list. Unlike @trigger, this does not create a new queue entry -- // the included commands execute immediately in the caller's context, and // @break/@assert propagate to the parent action list (unless /nobreak). // // Syntax: @include[/nobreak][/localize][/clearregs] obj/attr[=arg0,arg1,...] // // Without arguments, the parent's %0-%9 pass through unchanged. // With arguments, %0-%9 are replaced for the duration of the include. // void do_include(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *object, UTF8 *argv[], int nargs, const UTF8 *cargs[], int ncargs) { dbref thing; ATTR *pattr; if (!( parse_attrib(executor, object, &thing, &pattr) && pattr)) { notify_quiet(executor, M_("No match.")); return; } // @include requires read access, not control: the attribute text is // expanded into the *includer's* action list (executor stays the caller). // Running as the source object with only See_attr would let a player // execute with foreign privileges by including any VISUAL attr (#1279). // if (!See_attr(executor, thing, pattr)) { notify_quiet(executor, NOPERM_MESSAGE); return; } dbref aowner; int aflags; LBuf act = LBuf_Adopt(atr_pget(thing, pattr->number, &aowner, &aflags)); if (!*act.get()) { return; } if ((aflags & AF_NOEVAL) || NoEval(thing)) { // NOEVAL content is data, not code -- skip execution. // return; } // Strip $command: or ^listen: prefix if present, so that the same // attribute that serves as a $-command can be @included as a // subroutine. // UTF8 *body = act; if ( '$' == act[0] || '^' == act[0]) { UTF8 *colon = (UTF8 *)strchr((char *)act.get(), ':'); if (colon) { body = colon + 1; } } // Determine effective environment: if caller supplies args, use them; // otherwise pass through the parent's environment. // const UTF8 **env; int nenv; if ( nargs > 0 && argv[0] && argv[0][0] != '\0') { env = (const UTF8 **)argv; nenv = nargs; } else { env = cargs; nenv = ncargs; } // Save/clear %q-registers if requested. // reg_ref *preserve[MAX_GLOBAL_REGS]; bool bLocalize = (key & INCLUDE_LOCALIZE) != 0; if (bLocalize) { for (int i = 0; i < MAX_GLOBAL_REGS; i++) { preserve[i] = mudstate.global_regs[i]; if (preserve[i]) { RegAddRef(preserve[i]); } } } if (key & INCLUDE_CLEARREGS) { for (int i = 0; i < MAX_GLOBAL_REGS; i++) { if (mudstate.global_regs[i]) { RegRelease(mudstate.global_regs[i]); mudstate.global_regs[i] = nullptr; } } } // Save break state so /nobreak can isolate it. // bool save_break = break_called; if (key & INCLUDE_NOBREAK) { break_called = false; } // Walk the included text, splitting on semicolons and executing // each command inline as the includer (not the source object — // #1279), with ;| piping honored like the queued runner (#788). // process_command_list_inline(executor, caller, enactor, AttrTrace(aflags, 0), body, env, nenv); // Restore break state for /nobreak. // if (key & INCLUDE_NOBREAK) { break_called = save_break; } // Restore %q-registers if /localize. // if (bLocalize) { for (int i = 0; i < MAX_GLOBAL_REGS; i++) { if (mudstate.global_regs[i]) { RegRelease(mudstate.global_regs[i]); } mudstate.global_regs[i] = preserve[i]; } } } void do_use(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *object, const UTF8 *cargs[], int ncargs) { UNUSED_PARAMETER(caller); UNUSED_PARAMETER(enactor); UNUSED_PARAMETER(eval); UNUSED_PARAMETER(key); UNUSED_PARAMETER(cargs); UNUSED_PARAMETER(ncargs); dbref thing, aowner; int aflags; init_match(executor, object, NOTYPE); match_neighbor(); match_possession(); if (Wizard(executor)) { match_absolute(); match_player(); } match_me(); match_here(); thing = noisy_match_result(); if (thing == NOTHING) return; // Make sure player can use it. // if (!could_doit(executor, thing, A_LUSE)) { did_it(executor, thing, A_UFAIL, M_("You can’t figure out how to use that."), A_OUFAIL, nullptr, A_AUFAIL, 0, nullptr, 0); return; } LBuf temp = LBuf_Src("do_use"); bool doit = false; if (*atr_pget_str(temp, thing, A_USE, &aowner, &aflags)) { doit = true; } else if (*atr_pget_str(temp, thing, A_OUSE, &aowner, &aflags)) { doit = true; } else if (*atr_pget_str(temp, thing, A_AUSE, &aowner, &aflags)) { doit = true; } if (doit) { LBuf df_use = LBuf_Src("do_use.use"); LBuf df_ouse = LBuf_Src("do_use.ouse"); mux_sprintf(df_use, LBUF_SIZE, M_("You use %s"), Moniker(thing)); mux_sprintf(df_ouse, LBUF_SIZE, M_("uses %s"), Moniker(thing)); did_it(executor, thing, A_USE, df_use, A_OUSE, df_ouse, A_AUSE, 0, nullptr, 0); } else { notify_quiet(executor, M_("You can’t figure out how to use that.")); } } /* * --------------------------------------------------------------------------- * * do_setvattr: Set a user-named (or possibly a predefined) attribute. */ void do_setvattr ( dbref executor, dbref caller, dbref enactor, int eval, int key, int nargs, UTF8 *arg1, UTF8 *arg2, const UTF8 *cargs[], int ncargs ) { UNUSED_PARAMETER(eval); UNUSED_PARAMETER(key); UNUSED_PARAMETER(nargs); UNUSED_PARAMETER(cargs); UNUSED_PARAMETER(ncargs); UTF8 *s; int anum; // Skip the '&' // arg1++; // Take to the space // for (s = arg1; *s && !mux_isspace(*s); s++) { ; // Nothing. } // Split it // if (*s) { *s++ = '\0'; } // Get or make attribute // anum = mkattr(executor, reinterpret_cast(arg1)); if (anum <= 0) { notify_quiet(executor, M_("That’s not a good name for an attribute.")); return; } do_setattr(executor, caller, enactor, 0, anum, 2, s, arg2, nullptr, 0); }