void CHSInterface::LinkExitToRoom(HS_DBREF dbExit, HS_DBREF dbRoom) { #ifdef PENNMUSH // No change in code between versions Exits(dbExit) = dbRoom; PUSH(dbExit, Exits(dbRoom)); #endif #if defined(TM3) || defined(MUX) s_Exits(dbRoom, insert_first(Exits(dbRoom), dbExit)); s_Exits(dbExit, dbRoom); s_Location(dbExit, NOTHING); link_exit(GOD, dbExit, dbRoom); #endif }
static void open_exit(dbref player, dbref loc, char *direction, char *linkto, int key) { dbref exit; char *tpr_buff, *tprp_buff; if (!Good_obj(loc)) return; if (!direction || !*direction) { notify_quiet(player, "Open where?"); return; } else if (!controls(player, loc) && !could_doit(player, loc, A_LOPEN, 0, 0)) { notify_quiet(player, "Permission denied."); return; } exit = create_obj(player, TYPE_EXIT, direction, 0); if (exit == NOTHING) return; /* Initialize everything and link it in. */ s_Exits(exit, loc); s_Next(exit, Exits(loc)); s_Exits(loc, exit); /* and we're done */ if ( !(key & SIDEEFFECT ) ) notify_quiet(player, "Opened."); /* See if we should do a link */ if (!linkto || !*linkto) return; loc = parse_linkable_room(player, linkto); if (loc != NOTHING) { /* Make sure the player passes the link lock */ if ((loc != HOME) && !could_doit(player, loc, A_LLINK, 1, 0)) { notify_quiet(player, "You can't link to there."); return; } /* Link it if the player can pay for it */ if (!payfor(player, mudconf.linkcost)) { tprp_buff = tpr_buff = alloc_lbuf("open_exit"); notify_quiet(player, safe_tprintf(tpr_buff, &tprp_buff, "You don't have enough %s to link.", mudconf.many_coins)); free_lbuf(tpr_buff); } else { s_Location(exit, loc); if ( !(key & SIDEEFFECT ) ) notify_quiet(player, "Linked."); } } }
/* Show the 'Obvious Exits' list for a room. Used in 'look' and 'examine'. * \param player The player looking * \param loc room whose exits we're showing * \param exit_name "Obvious Exits" string * \param pe_info the pe_info to use for evaluating EXITFORMAT and interact locks */ static void look_exits(dbref player, dbref loc, const char *exit_name, NEW_PE_INFO *pe_info) { dbref thing; char *tbuf1, *tbuf2, *nbuf; char *s1, *s2; char *p; int exit_count, this_exit, total_count; int texits; ufun_attrib ufun; PUEBLOBUFF; /* make sure location is a room */ if (!IsRoom(loc)) return; tbuf1 = (char *) mush_malloc(BUFFER_LEN, "string"); tbuf2 = (char *) mush_malloc(BUFFER_LEN, "string"); nbuf = (char *) mush_malloc(BUFFER_LEN, "string"); if (!tbuf1 || !tbuf2 || !nbuf) mush_panic("Unable to allocate memory in look_exits"); s1 = tbuf1; s2 = tbuf2; texits = exit_count = total_count = 0; this_exit = 1; if (fetch_ufun_attrib ("EXITFORMAT", loc, &ufun, UFUN_IGNORE_PERMS | UFUN_REQUIRE_ATTR)) { char *arg, *buff, *bp; PE_REGS *pe_regs = pe_regs_create(PE_REGS_ARG, "look_exits"); arg = (char *) mush_malloc(BUFFER_LEN, "string"); buff = (char *) mush_malloc(BUFFER_LEN, "string"); if (!arg || !buff) mush_panic("Unable to allocate memory in look_exits"); bp = arg; DOLIST(thing, Exits(loc)) { if (((Light(loc) || Light(thing)) || !(Dark(loc) || Dark(thing))) && can_interact(thing, player, INTERACT_SEE, pe_info)) { if (bp != arg) safe_chr(' ', arg, &bp); safe_dbref(thing, arg, &bp); } } *bp = '\0'; pe_regs_setenv_nocopy(pe_regs, 0, arg); call_ufun(&ufun, buff, player, player, pe_info, pe_regs); pe_regs_free(pe_regs); notify_by(loc, player, buff); mush_free(tbuf1, "string"); mush_free(tbuf2, "string"); mush_free(nbuf, "string"); mush_free(arg, "string"); mush_free(buff, "string"); return; }
/* Clone an object. The new object is owned by the cloning player */ static dbref clone_object(dbref player, dbref thing, const char *newname, int preserve) { dbref clone; clone = new_object(); Owner(clone) = Owner(player); Name(clone) = NULL; if (newname && *newname) set_name(clone, newname); else set_name(clone, Name(thing)); s_Pennies(clone, Pennies(thing)); AttrCount(clone) = 0; List(clone) = NULL; Locks(clone) = NULL; clone_locks(player, thing, clone); Zone(clone) = Zone(thing); Parent(clone) = Parent(thing); Flags(clone) = clone_flag_bitmask("FLAG", Flags(thing)); if (!preserve) { clear_flag_internal(clone, "WIZARD"); clear_flag_internal(clone, "ROYALTY"); Warnings(clone) = 0; /* zap warnings */ Powers(clone) = new_flag_bitmask("POWER"); /* zap powers */ } else { Powers(clone) = clone_flag_bitmask("POWER", Powers(thing)); Warnings(clone) = Warnings(thing); if (Wizard(clone) || Royalty(clone) || Warnings(clone) || !null_flagmask("POWER", Powers(clone))) notify(player, T ("Warning: @CLONE/PRESERVE on an object with WIZ, ROY, @powers, or @warnings.")); } /* We give the clone the same modification time that its * other clone has, but update the creation time */ ModTime(clone) = ModTime(thing); CreTime(clone) = mudtime; Type(clone) = Type(thing); Contents(clone) = Location(clone) = Next(clone) = NOTHING; if (IsRoom(thing)) Exits(clone) = NOTHING; else Home(clone) = Home(thing); atr_cpy(clone, thing); queue_event(player, "OBJECT`CREATE", "%s,%s", unparse_objid(clone), unparse_objid(thing)); return clone; }
/** Given an exit, find the room that is its source through brute force. * This is used in pathological cases where the exit's own source * element is invalid. * \param door dbref of exit to find source of. * \return dbref of exit's source room, or NOTHING. */ dbref find_entrance(dbref door) { dbref room; dbref thing; for (room = 0; room < db_top; room++) { if (IsRoom(room)) { thing = Exits(room); while (thing != NOTHING) { if (thing == door) return room; thing = Next(thing); } } } return NOTHING; }
static bool match_exit_internal(dbref loc, dbref baseloc, int local) { if ( !Good_obj(loc) || !Has_exits(loc)) { return true; } dbref exit; bool result = false; int key; DOLIST(exit, Exits(loc)) { if (exit == md.absolute_form) { key = 0; if (Examinable(md.player, loc)) { key |= VE_LOC_XAM; } if (Dark(loc)) { key |= VE_LOC_DARK; } if (Dark(baseloc)) { key |= VE_BASE_DARK; } if (exit_visible(exit, md.player, key)) { promote_match(exit, CON_DBREF | local); return true; } } if (matches_exit_from_list(md.string, PureName(exit))) { promote_match(exit, CON_COMPLETE | local); result = true; } } return result; }
HS_DBREF CHSInterface::GetFirstExit(HS_DBREF dbRoom) { return Exits(dbRoom); }
void CR_ModuleEx::CreateFlowGraph64(CR_Addr64 entrance) { auto cf = Info64()->CodeFuncFromAddr(entrance); assert(cf); CR_Addr64Set leaders; leaders.insert(entrance); // insert jumpees auto& jumpees = cf->Jumpees(); leaders.insert(jumpees.begin(), jumpees.end()); // insert exits' next auto& exits = cf->Exits(); for (auto addr : exits) { auto op_code = Info64()->OpCodeFromAddr(addr); auto size = op_code->Codes().size(); auto next_addr = static_cast<CR_Addr64>(addr + size); leaders.insert(next_addr); } // insert jumpers' next auto& jumpers = cf->Jumpers(); for (auto addr : jumpers) { auto op_code = Info64()->OpCodeFromAddr(addr); auto size = op_code->Codes().size(); auto next_addr = static_cast<CR_Addr64>(addr + size); leaders.insert(next_addr); } // sort std::vector<CR_Addr64> vecLeaders(leaders.begin(), leaders.end()); std::sort(vecLeaders.begin(), vecLeaders.end()); // store leaders cf->Leaders() = std::move(leaders); const size_t size = vecLeaders.size() - 1; for (size_t i = 0; i < size; ++i) { // for every pair of two adjacent leaders auto addr1 = vecLeaders[i], addr2 = vecLeaders[i + 1]; // prepare a basic block CR_BasicBlock64 block; block.m_addr = addr1; CR_Addr64 next_addr = cr_invalid_addr64; for (auto addr = addr1; addr < addr2; ) { if (cf->Leaders().count(addr)) { // set label at each leader block.AddLeaderLabel(addr); } // op.code from addr auto op_code = Info64()->OpCodeFromAddr(addr); if (op_code == NULL) { break; } auto type = op_code->OpCodeType(); if (type == cr_OCT_JMP) { // jump auto oper = op_code->Operand(0); if (oper->GetOperandType() == cr_DF_IMM) { block.m_jump_to = oper->Value64(); // jump to } next_addr = cr_invalid_addr64; } else if (type == cr_OCT_RETURN) { next_addr = cr_invalid_addr64; } else if (type == cr_OCT_JCC || type == cr_OCT_LOOP) { // conditional jump or loop auto oper = op_code->Operand(0); if (oper->GetOperandType() == cr_DF_IMM) { block.m_jump_to = oper->Value64(); // jump to } block.m_cond_code = op_code->CondCode(); next_addr = static_cast<CR_Addr64>(addr + op_code->Codes().size()); } else { next_addr = static_cast<CR_Addr64>(addr + op_code->Codes().size()); } // add op.code block.m_stmts.emplace_back(*op_code); // go to next addr addr += static_cast<CR_Addr64>(op_code->Codes().size()); } // add label at last block.AddLeaderLabel(addr2); // set next addr block.m_next_addr = next_addr; // add block cf->BasicBlocks().emplace_back(block); } } // CR_ModuleEx::CreateFlowGraph64
BOOL CR_ModuleEx::_DisAsmAddr64(CR_Addr64 func, CR_Addr64 va) { if (!IsModuleLoaded() || !Is64Bit()) return FALSE; // calculate int len; char outbuf[256]; CR_Addr64 addr; // add or retrieve the code function auto cf = Info64()->CodeFuncFromAddr(func); if (cf == NULL) { Info64()->MapAddrToCodeFunc().emplace(func, make_shared<CR_CodeFunc64>()); cf = Info64()->CodeFuncFromAddr(func); } assert(cf); if (func == va) { cf->Addr() = func; } auto pCode = CodeSectionHeader(); assert(pCode); DWORD rva = RVAFromVA64(va); LPBYTE input = m_pLoadedImage + rva; LPBYTE iend = m_pLoadedImage + pCode->RVA + pCode->SizeOfRawData; while (input < iend) { // add or retrieve op.code auto oc = Info64()->OpCodeFromAddr(va); if (oc == NULL) { Info64()->MapAddrToOpCode().emplace(va, make_shared<CR_OpCode64>()); oc = Info64()->OpCodeFromAddr(va); // set op.code address oc->Addr() = va; } assert(oc); if (oc->FuncAddrs().count(func) > 0) break; // add function address for this op.code oc->FuncAddrs().emplace(func); if (oc->FuncAddrs().size() > 1) { cf->FuncFlags() |= cr_FF_FUNCINFUNC; // function in function } if (oc->Codes().empty()) { // disassemble len = disasm(input, outbuf, sizeof(outbuf), 64, va, false, 0); // parse insn if (!len || input + len > iend) { len = 1; oc->Name() = "???"; oc->OpCodeType() = cr_OCT_UNKNOWN; // don't decompile if any unknown instruction. cf->FuncFlags() |= cr_FF_INVALID; } else { oc->Parse(outbuf); } // complement operand size oc->DeductOperandSizes(); // add asm codes to op.code oc->Codes().insert(oc->Codes().end(), input, &input[len]); } else { len = int(oc->Codes().size()); } BOOL bBreak = FALSE; switch (oc->OpCodeType()) { case cr_OCT_JCC: // conditional jump switch (oc->Operand(0)->GetOperandType()) { case cr_DF_IMM: addr = oc->Operand(0)->Value64(); cf->Jumpers().emplace(va); cf->Jumpees().emplace(addr); break; default: break; } break; case cr_OCT_JMP: // jump switch (oc->Operand(0)->GetOperandType()) { case cr_DF_IMM: if (func == va) { // func is jumper cf->FuncFlags() |= cr_FF_JUMPERFUNC; addr = oc->Operand(0)->Value64(); Info64()->Entrances().emplace(addr); cf->Callers().emplace(addr); auto newcf = Info64()->CodeFuncFromAddr(addr); if (newcf == NULL) { Info64()->MapAddrToCodeFunc().emplace( addr, make_shared<CR_CodeFunc64>()); newcf = Info64()->CodeFuncFromAddr(addr); } newcf->Addr() = addr; newcf->Callees().emplace(func); } else { addr = oc->Operand(0)->Value64(); cf->Jumpers().emplace(va); cf->Jumpees().emplace(addr); } break; case cr_DF_MEMIMM: if (func == va) { // func is jumper cf->FuncFlags() |= cr_FF_JUMPERFUNC; bBreak = TRUE; } break; default: break; } bBreak = TRUE; break; case cr_OCT_CALL: // call switch (oc->Operand(0)->GetOperandType()) { case cr_DF_IMM: // function call addr = oc->Operand(0)->Value64(); Info64()->Entrances().emplace(addr); cf->Callees().emplace(addr); { auto newcf = Info64()->CodeFuncFromAddr(addr); if (newcf == NULL) { Info64()->MapAddrToCodeFunc().emplace( addr, make_shared<CR_CodeFunc64>()); newcf = Info64()->CodeFuncFromAddr(addr); } newcf->Addr() = addr; newcf->Callers().emplace(func); } break; default: break; } break; case cr_OCT_RETURN: // return if (oc->Operands().size() && oc->Operand(0)->GetOperandType() == cr_DF_IMM) { cf->StackArgSizeRange().Set(oc->Operand(0)->Value64()); } else { if (func == va) { cf->FuncFlags() |= cr_FF_RETURNONLY; } } cf->Exits().insert(va); bBreak = TRUE; break; default: break; } if (bBreak) break; // move to next position input += len; va += len; } return TRUE; } // CR_ModuleEx::_DisAsmAddr64
static void ct_exit(dbref player, dbref i, warn_type flags) { dbref j, src, dst; int count = 0; int lt; /* i must be an exit, must be in a valid room, and must lead to a * different room * Remember, for exit i, Exits(i) = source room * and Location(i) = destination room */ dst = Destination(i); if ((flags & W_EXIT_UNLINKED) && (dst == NOTHING)) complain(player, i, "exit-unlinked", T("exit is unlinked; anyone can steal it")); if ((flags & W_EXIT_UNLINKED) && dst == AMBIGUOUS) { ATTR *a; const char *var = "DESTINATION"; a = atr_get(i, "DESTINATION"); if (!a) a = atr_get(i, "EXITTO"); if (a) var = "EXITTO"; if (!a) complain(player, i, "exit-unlinked", T("Variable exit has no %s attribute"), var); else { const char *x = atr_value(a); if (!x || !*x) complain(player, i, "exit-unlinked", T("Variable exit has empty %s attribute"), var); } } if (!Dark(i)) { if (flags & W_EXIT_MSGS) { lt = warning_lock_type(getlock(i, Basic_Lock)); if ((lt & W_UNLOCKED) && (!atr_get(i, "OSUCCESS") || !atr_get(i, "ODROP") || !atr_get(i, "SUCCESS"))) complain(player, i, "exit-msgs", T("possibly unlocked exit missing succ/osucc/odrop")); if ((lt & W_LOCKED) && !atr_get(i, "FAILURE")) complain(player, i, "exit-msgs", T("possibly locked exit missing fail")); } if (flags & W_EXIT_DESC) { if (!atr_get(i, "DESCRIBE")) complain(player, i, "exit-desc", T("exit is missing description")); } } src = Source(i); if (!GoodObject(src) || !IsRoom(src)) return; if (src == dst) return; /* Don't complain about exits linked to HOME or variable exits. */ if (!GoodObject(dst)) return; for (j = Exits(dst); GoodObject(j); j = Next(j)) if (Location(j) == src) { if (!(flags & W_EXIT_MULTIPLE)) return; else count++; } if ((count == 0) && (flags & W_EXIT_ONEWAY)) complain(player, i, "exit-oneway", T("exit has no return exit")); else if ((count > 1) && (flags & W_EXIT_MULTIPLE)) complain(player, i, "exit-multiple", T("exit has multiple (%d) return exits"), count); }
static int db_write_object(FILE * f, dbref i, int db_format, int flags) { ATTR *a; char *got, *as; dbref aowner; int ca, aflags, save, j; BOOLEXP *tempbool; if(!(flags & V_ATRNAME)) putstring(f, Name(i)); putref(f, Location(i)); if(flags & V_ZONE) putref(f, Zone(i)); putref(f, Contents(i)); putref(f, Exits(i)); if(flags & V_LINK) putref(f, Link(i)); putref(f, Next(i)); if(!(flags & V_ATRKEY)) { got = atr_get(i, A_LOCK, &aowner, &aflags); tempbool = parse_boolexp(GOD, got, 1); free_lbuf(got); putboolexp(f, tempbool); if(tempbool) free_bool(tempbool); } putref(f, Owner(i)); if(flags & V_PARENT) putref(f, Parent(i)); if(!(flags & V_ATRMONEY)) putref(f, Pennies(i)); putref(f, Flags(i)); if(flags & V_XFLAGS) putref(f, Flags2(i)); if(flags & V_3FLAGS) putref(f, Flags3(i)); if(flags & V_POWERS) { putref(f, Powers(i)); putref(f, Powers2(i)); } /* * write the attribute list */ if((!(flags & V_GDBM)) || (mudstate.panicking == 1)) { for(ca = atr_head(i, &as); ca; ca = atr_next(&as)) { save = 0; a = atr_num(ca); if(a) j = a->number; else j = -1; if(j > 0) { switch (j) { case A_NAME: if(flags & V_ATRNAME) save = 1; break; case A_LOCK: if(flags & V_ATRKEY) save = 1; break; case A_LIST: case A_MONEY: break; default: save = 1; } } if(save) { got = atr_get_raw(i, j); fprintf(f, ">%d\n", j); putstring(f, got); } } fprintf(f, "<\n"); } return 0; }
/** Create an exit. * This function opens an exit and optionally links it. * \param player the enactor. * \param direction the name of the exit. * \param linkto the room to link to, as a string. * \param pseudo a phony location for player if a back exit is needed. This is bpass by do_open() as the source room of the back exit. * \return dbref of the new exit, or NOTHING. */ dbref do_real_open(dbref player, const char *direction, const char *linkto, dbref pseudo) { dbref loc = (pseudo != NOTHING) ? pseudo : (IsExit(player) ? Source(player) : (IsRoom(player) ? player : Location(player))); dbref new_exit; char *flaglist, *flagname; char flagbuff[BUFFER_LEN]; char *name = NULL; char *alias = NULL; if (!command_check_byname(player, "@dig")) { notify(player, T("Permission denied.")); return NOTHING; } if ((loc == NOTHING) || (!IsRoom(loc))) { notify(player, T("Sorry, you can only make exits out of rooms.")); return NOTHING; } if (Going(loc)) { notify(player, T("You can't make an exit in a place that's crumbling.")); return NOTHING; } if (!*direction) { notify(player, T("Open where?")); return NOTHING; } else if (ok_object_name ((char *) direction, player, NOTHING, TYPE_EXIT, &name, &alias) < 1) { notify(player, T("That's a strange name for an exit!")); if (name) mush_free(name, "name.newname"); if (alias) mush_free(alias, "name.newname"); return NOTHING; } if (!Open_Anywhere(player) && !controls(player, loc)) { notify(player, T("Permission denied.")); } else if (can_pay_fees(player, EXIT_COST)) { /* create the exit */ new_exit = new_object(); /* initialize everything */ set_name(new_exit, name); if (alias && *alias != ALIAS_DELIMITER) atr_add(new_exit, "ALIAS", alias, player, 0); Owner(new_exit) = Owner(player); Zone(new_exit) = Zone(player); Source(new_exit) = loc; Type(new_exit) = TYPE_EXIT; Flags(new_exit) = new_flag_bitmask("FLAG"); strcpy(flagbuff, options.exit_flags); flaglist = trim_space_sep(flagbuff, ' '); if (*flaglist != '\0') { while (flaglist) { flagname = split_token(&flaglist, ' '); twiddle_flag_internal("FLAG", new_exit, flagname, 0); } } mush_free(name, "name.newname"); if (alias) mush_free(alias, "name.newname"); /* link it in */ PUSH(new_exit, Exits(loc)); /* and we're done */ notify_format(player, T("Opened exit %s"), unparse_dbref(new_exit)); /* check second arg to see if we should do a link */ if (linkto && *linkto != '\0') { notify(player, T("Trying to link...")); if ((loc = check_var_link(linkto)) == NOTHING) loc = parse_linkable_room(player, linkto); if (loc != NOTHING) { if (!payfor(player, LINK_COST)) { notify_format(player, T("You don't have enough %s to link."), MONIES); } else { /* it's ok, link it */ Location(new_exit) = loc; notify_format(player, T("Linked exit #%d to #%d"), new_exit, loc); } } } current_state.exits++; local_data_create(new_exit); queue_event(player, "OBJECT`CREATE", "%s", unparse_objid(new_exit)); return new_exit; } if (name) mush_free(name, "name.newname"); if (alias) mush_free(alias, "name.newname"); return NOTHING; }
/** Clone an object. * \verbatim * This is the top-level function for @clone, which creates a duplicate * of a (non-player) object. * \endverbatim * \param player the enactor. * \param name the name of the object to clone. * \param newname the name to give the duplicate. * \param preserve if 1, preserve ownership and privileges on duplicate. * \paran newdbref the (unparsed) dbref to give the object, or NULL to use the next free * \return dbref of the duplicate, or NOTHING. */ dbref do_clone(dbref player, char *name, char *newname, int preserve, char *newdbref) { dbref clone, thing; char dbnum[BUFFER_LEN]; thing = noisy_match_result(player, name, NOTYPE, MAT_EVERYTHING); if ((thing == NOTHING)) return NOTHING; if (newname && *newname && !ok_name(newname, IsExit(thing))) { notify(player, T("That is not a reasonable name.")); return NOTHING; } if (!controls(player, thing) || IsPlayer(thing) || (IsRoom(thing) && !command_check_byname(player, "@dig")) || (IsExit(thing) && !command_check_byname(player, "@open")) || (IsThing(thing) && !command_check_byname(player, "@create"))) { notify(player, T("Permission denied.")); return NOTHING; } /* don't allow cloning of destructed things */ if (IsGarbage(thing)) { notify(player, T("There's nothing left of it to clone!")); return NOTHING; } if (preserve && !Wizard(player)) { notify(player, T("You cannot @CLONE/PRESERVE. Use normal @CLONE instead.")); return NOTHING; } if (!make_first_free_wrapper(player, newdbref)) { return NOTHING; } /* make sure owner can afford it */ switch (Typeof(thing)) { case TYPE_THING: if (can_pay_fees(player, Pennies(thing))) { clone = clone_object(player, thing, newname, preserve); notify_format(player, T("Cloned: Object %s."), unparse_dbref(clone)); if (IsRoom(player)) moveto(clone, player, player, "cloned"); else moveto(clone, Location(player), player, "cloned"); current_state.things++; local_data_clone(clone, thing); real_did_it(player, clone, NULL, NULL, NULL, NULL, "ACLONE", NOTHING, global_eval_context.wenv, 0); return clone; } return NOTHING; break; case TYPE_ROOM: if (can_pay_fees(player, ROOM_COST)) { clone = clone_object(player, thing, newname, preserve); Exits(clone) = NOTHING; notify_format(player, T("Cloned: Room #%d."), clone); current_state.rooms++; local_data_clone(clone, thing); real_did_it(player, clone, NULL, NULL, NULL, NULL, "ACLONE", NOTHING, global_eval_context.wenv, 0); return clone; } return NOTHING; break; case TYPE_EXIT: /* For exits, we don't want people to be able to link it to a location they can't with @open. So, all this stuff. */ switch (Location(thing)) { case NOTHING: strcpy(dbnum, "#-1"); break; case HOME: strcpy(dbnum, "home"); break; case AMBIGUOUS: strcpy(dbnum, "variable"); break; default: strcpy(dbnum, unparse_dbref(Location(thing))); } if (newname && *newname) clone = do_real_open(player, newname, dbnum, NOTHING); else clone = do_real_open(player, Name(thing), dbnum, NOTHING); if (!GoodObject(clone)) { return NOTHING; } else { atr_cpy(clone, thing); clone_locks(player, thing, clone); Zone(clone) = Zone(thing); Parent(clone) = Parent(thing); Flags(clone) = clone_flag_bitmask("FLAG", Flags(thing)); if (!preserve) { clear_flag_internal(clone, "WIZARD"); clear_flag_internal(clone, "ROYALTY"); Warnings(clone) = 0; /* zap warnings */ Powers(clone) = new_flag_bitmask("POWER"); /* zap powers */ } else { Warnings(clone) = Warnings(thing); Powers(clone) = clone_flag_bitmask("POWER", Powers(thing)); } if (Wizard(clone) || Royalty(clone) || Warnings(clone) || !null_flagmask("POWER", Powers(clone))) notify(player, T ("Warning: @CLONE/PRESERVE on an object with WIZ, ROY, @powers, or @warnings.")); notify_format(player, T("Cloned: Exit #%d."), clone); local_data_clone(clone, thing); return clone; } } return NOTHING; }