#include "copyright.h" #include "config.h" /* Commands that create new objects */ #include "db.h" #include "params.h" #include "interface.h" #include "externs.h" #include "match.h" #include "money.h" #include <ctype.h> line *read_program(dbref i); static char buf[BUFFER_LEN]; /* parse_linkable_dest() * * A utility for open and link which checks whether a given destination * string is valid. It returns a parsed dbref on success, and NOTHING * on failure. */ static dbref parse_linkable_dest(dbref player, dbref exit1, char *dest_name) { dbref dobj; /* destination room/player/thing/link */ match_data md; init_match(player, dest_name, NOTYPE, &md); match_absolute(&md); match_everything(&md); match_home(&md); if ((dobj = match_result(&md)) == NOTHING || dobj == AMBIGUOUS) { sprintf(buf, "I couldn't find '%s'.", dest_name); notify(player, player, buf); return NOTHING; #ifndef TELEPORT_TO_PLAYER } if (Typeof(dobj) == TYPE_PLAYER) { sprintf(buf, "You can't link to players. Destination %s ignored.", unparse_object(player, dobj)); notify(player, player, buf); return NOTHING; #endif /* TELEPORT_TO_PLAYER */ } if (!can_link(player, exit1)) { notify(player, player, "You can't link that."); return NOTHING; } if (!can_link_to(player, Typeof(exit1), dobj)) { sprintf(buf, "You can't link to %s.", unparse_object(player, dobj)); notify(player, player, buf); return NOTHING; } else return dobj; } /* exit_loop_check() * * Recursive check for loops in destinations of exits. Checks to see * if any circular references are present in the destination chain. * Returns 1 if circular reference found, 0 if not. */ int exit_loop_check(dbref source, dbref dest) { int i; if (source == dest) return 1; /* That's an easy one! */ if (Typeof(dest) != TYPE_EXIT) return 0; for (i = 0; i < DBFETCH(dest)->sp.exit.ndest; i++) { if ((DBFETCH(dest)->sp.exit.dest)[i] == source) return 1; /* Found a loop! */ if (Typeof((DBFETCH(dest)->sp.exit.dest)[i]) == TYPE_EXIT) { if (exit_loop_check(source, (DBFETCH(dest)->sp.exit.dest)[i])) return 1; /* Found one recursively */ } } return 0; /* No loops found */ } /* use this to create an exit */ void do_open(__DO_PROTO) { dbref loc, ext; dbref good_dest[MAX_LINKS]; int i, ndest; if(!Builder(OWNER(player))) { notify(player, player, "That command is restricted to authorized builders."); return; } if((loc = getloc(player)) == NOTHING) return; if(!*arg1) { notify(player, player, "You must specify a direction or action name to open."); return; } else if(!ok_name(arg1)) { notify(player, player, "That's a strange name for an exit!"); return; } if(!controls(player, loc)) notify(player, player, "Permission denied."); else if(!payfor(OWNER(player), EXIT_COST)) notify(player, player, tprintf("Sorry, you don't have enough %s to open an exit.", PL_MONEY)); else { /* create the exit */ ext = new_object(); /* initialize everything */ DBSTORE(ext, name, dup_string(arg1)); DBSTORE(ext, location, loc); DBSTORE(ext, owner, OWNER(player)); add_ownerlist(ext); FLAGS(ext) = TYPE_EXIT; DBFETCH(ext)->sp.exit.ndest = 0; DBFETCH(ext)->sp.exit.dest = NULL; /* link it in */ PUSH(ext, DBFETCH(loc)->exits); DBDIRTY(loc); /* and we're done */ sprintf(buf, "Exit opened with number %ld.", ext); notify(player, player, buf); /* check second arg to see if we should do a link */ if(*arg2 != '\0') { notify(player, player, "Trying to link..."); ndest = link_exit(player, ext, arg2, good_dest); DBFETCH(ext)->sp.exit.ndest = ndest; DBFETCH(ext)->sp.exit.dest = (dbref *)malloc(sizeof(dbref) * ndest); for (i = 0; i < ndest; i++) (DBFETCH(ext)->sp.exit.dest)[i] = good_dest[i]; add_backlinks(ext); DBDIRTY(ext); } } } /* * link_exit() * * This routine connects an exit to a bunch of destinations. * * 'player' contains the player's name. * 'exit' is the the exit whose destinations are to be linked. * 'dest_name' is a character string containing the list of exits. * * 'dest_list' is an array of dbref's where the valid destinations are * stored. * */ int link_exit(dbref player, dbref ext, char *dest_name, dbref *dest_list) { char *p, *q; int prdest; dbref dest; int ndest; char qbuf[BUFFER_LEN]; prdest = 0; ndest = 0; while (*dest_name) { while (isspace(*dest_name)) dest_name++; /* skip white space */ p = dest_name; while (*dest_name && (*dest_name != EXIT_DELIMITER)) dest_name++; q = strncpy(qbuf, p, BUFFER_LEN); /* copy word */ q[(dest_name - p)] = '\0'; /* terminate it */ if (*dest_name) for (dest_name++; *dest_name && isspace(*dest_name); dest_name++); if ((dest = parse_linkable_dest(player, ext, q)) == NOTHING) continue; switch (Typeof(dest)) { case TYPE_PLAYER: case TYPE_ROOM: case TYPE_PROGRAM: if (prdest) { sprintf (buf, "Only one player, room, or program destination allowed. Destination %s ignored.", unparse_object(player, dest)); notify(player, player, buf); continue; } prdest = 1; case TYPE_THING: dest_list[ndest++] = dest; break; case TYPE_EXIT: if (exit_loop_check(ext, dest)) { sprintf(buf, "Destination %s would create a loop, ignored.", unparse_object(player, dest)); notify(player, player, buf); continue; } dest_list[ndest++] = dest; } if (dest == HOME) notify(player, player, "Linked to HOME."); else { sprintf(buf, "Linked to %s.", unparse_object(player, dest)); notify(player, player, buf); } if (ndest >= MAX_LINKS) { notify(player, player, "Too many destinations, rest ignored."); break; } } return ndest; } /* do_link * * Use this to link to a room that you own. It also sets home for * objects and things, and drop-to's for rooms. * It seizes ownership of an unlinked exit, and costs 1 penny * plus a penny transferred to the exit owner if they aren't you * * All destinations must either be owned by you, or be LINK_OK. */ void do_link(__DO_PROTO) { dbref thing, dest, good_dest[MAX_LINKS]; match_data md; int ndest, i; init_match(player, arg1, TYPE_EXIT, &md); match_all_exits(&md); match_neighbor(&md); match_possession(&md); match_me(&md); match_here(&md); if(Wizard(player)) { match_absolute(&md); match_player(&md); } if((thing = noisy_match_result(&md)) == NOTHING) return; switch(Typeof(thing)) { case TYPE_EXIT: /* we're ok, check the usual stuff */ if(!controls(player, thing)) { notify(player, player, "Permission denied."); return; } if (DBFETCH(thing)->sp.exit.ndest != 0) { notify(player, player, "Exit already linked."); return; } /* handle costs */ /* link has been validated and paid for; do it */ ndest = link_exit(player, thing, arg2, good_dest); if (ndest == 0) { notify(player, player, "No destinations linked."); if (!Wizard(player)) DBFETCH(OWNER(player))->pennies += LINK_COST; /* Refund! */ DBDIRTY(OWNER(player)); break; } DBSTORE(thing, sp.exit.ndest, ndest); DBSTORE(thing, sp.exit.dest, (dbref *)malloc(sizeof(dbref)*ndest)); for (i = 0; i < ndest; i++) (DBFETCH(thing)->sp.exit.dest)[i] = good_dest[i]; add_backlinks(thing); break; case TYPE_THING: case TYPE_PROGRAM: case TYPE_PLAYER: init_match(player, arg2, TYPE_ROOM, &md); match_neighbor(&md); match_absolute(&md); match_here(&md); match_me(&md); if ((dest = noisy_match_result(&md)) == NOTHING) return; if (!controls(player, thing) || !can_link_to(player, Typeof(thing), dest)) { notify(player, player, "Permission denied."); return; } remove_backlinks(thing); DBSTORE(thing, link, dest); add_backlinks(thing); notify(player, player, "Home set."); break; case TYPE_ROOM: /* room dropto's */ init_match(player, arg2, TYPE_ROOM, &md); match_neighbor(&md); match_absolute(&md); match_home(&md); if ((dest = noisy_match_result(&md)) == NOTHING) break; if (!controls(player, thing) || !can_link_to(player, Typeof(thing), dest) || (thing == dest)) notify(player, player, "Permission denied."); else { remove_backlinks(thing); DBSTORE(thing, link, dest); add_backlinks(thing); notify(player, player, "Dropto set."); } break; } DBDIRTY(thing); return; } /* * do_dig * * Use this to create a room. */ void do_dig(__DO_PROTO) { dbref room; dbref parent; match_data md; if(!Builder(OWNER(player))) { notify(player, player, "That command is restricted to authorized builders."); return; } if(*arg1 == '\0') { notify(player, player, "You must specify a name for the room."); return; } if(!ok_name(arg1)) { notify(player, player, "That's a silly name for a room!"); return; } if(!payfor(OWNER(player), ROOM_COST)) { notify(player, player, tprintf( "Sorry, you don't have enough %s to dig a room.", PL_MONEY)); return; } room = new_object(); FLAGS(room) = TYPE_ROOM | (FLAGS(player) & JUMP_OK); parent = DBFETCH(DBFETCH(player)->location)->location; if (*arg2) { init_match(player, arg2, TYPE_ROOM, &md); match_absolute(&md); match_here(&md); parent = noisy_match_result(&md); } if ((parent == NOTHING) || (parent == AMBIGUOUS) || (!can_link_to(player, Typeof(room), parent)) || (room == parent)) parent = GLOBAL_ENVIRONMENT; /* Initialize everything */ DBSTORE(room, name, dup_string(arg1)); DBSTORE(room, location, parent); DBSTORE(room, owner, OWNER(player)); add_ownerlist(room); DBSTORE(room, exits, NOTHING); DBSTORE(room, link, NOTHING); PUSH(room, DBFETCH(parent)->contents); #ifdef MUSH FLAGS(room) |= NOCOMMAND; #endif DBDIRTY(room); DBDIRTY(parent); sprintf(buf, "%s created with room number %ld, parent %ld.", arg1, room, parent); notify(player, player, buf); } /* Use this to create a program. First, find a program that matches that name. If there's one, then we put him into edit mode and do it. Otherwise, we create a new object for him, and call it a program. */ void do_program(__DO_PROTO) { dbref i; match_data md; if (!Mucker(player) || Typeof(player) != TYPE_PLAYER) { notify(player, player, "You're no programmer!"); return; } if (!*arg1) { notify(player, player, "No program name given."); return; } init_match(player, arg1, TYPE_PROGRAM, &md); match_possession(&md); match_neighbor(&md); match_absolute(&md); if ((i = match_result(&md)) == NOTHING) { i = new_object(); DBSTORE(i, name, dup_string(arg1)); sprintf(buf, "A scroll containing a spell called %s", arg1); #ifndef USE_DBP_STR DBSTORE(i, desc, dup_string(buf)); #else add_property(i, "desc", buf, PERMS_COREAD | PERMS_COWRITE | PERMS_OTREAD, ACCESS_CO); #endif DBSTORE(i, location, player); DBSTORE(i, link, player); add_backlinks(i); DBSTORE(i, owner, player); add_ownerlist(i); FLAGS(i) = TYPE_PROGRAM; DBSTORE(i, sp.program.first, 0); DBSTORE(i, sp.program.curr_line, 0); DBSTORE(i, sp.program.siz, 0); DBSTORE(i, sp.program.code, 0); DBSTORE(i, sp.program.start, 0); DBSTORE(i, sp.program.editlocks, NULL); DBSTORE(player, curr_prog, i); PUSH(i, DBFETCH(player)->contents); DBDIRTY(i); DBDIRTY(player); sprintf(buf, "Program %s created with number %ld.", arg1, i); notify(player, player, buf); notify(player, player, "Entering editor."); } else if (i == AMBIGUOUS) { notify(player, player, "I don't know which one you mean!"); return; } else { if ((Typeof(i) != TYPE_PROGRAM) || !controls(player, i)) { notify(player, player, "Permission denied!"); return; } DBSTORE(i, sp.program.first, read_program(i)); DBSTORE(player, curr_prog, i); notify(player, player, "Entering editor."); /* list current line */ do_list(player, i, 0, 0); DBDIRTY(i); } DBSTORE(i, sp.program.editlocks, dbreflist_add(DBFETCH(i)->sp.program.editlocks, player)); FLAGS(player) |= INTERACTIVE; DBDIRTY(player); } void do_mlist(__DO_PROTO) { match_and_list(player, arg1, arg2); } void do_edit(__DO_PROTO) { dbref i; match_data md; if (!Mucker(player) || Typeof(player) != TYPE_PLAYER) { notify(player, player, "You're no programmer!"); return; } if (!*arg1) { notify(player, player, "No program name given."); return; } init_match(player, arg1, TYPE_PROGRAM, &md); match_possession(&md); match_neighbor(&md); match_absolute(&md); if ((i = noisy_match_result(&md)) == NOTHING || i == AMBIGUOUS) return; if ((Typeof(i) != TYPE_PROGRAM) || !controls(player, i)) { notify(player, player, "Permission denied!"); return; } DBSTORE(i, sp.program.first, read_program(i)); DBSTORE(player, curr_prog, i); notify(player, player, "Entering editor."); /* list current line */ do_list(player, i, 0, 0); FLAGS(player) |= INTERACTIVE; DBSTORE(i, sp.program.editlocks, dbreflist_add(DBFETCH(i)->sp.program.editlocks, player)); } /* * do_create * * Use this to create an object. */ void do_create(__DO_PROTO) { dbref thing; int cost; if(!Builder(OWNER(player))) { notify(player, player, "That command is restricted to authorized builders."); return; } if(*arg1 == '\0') { notify(player, player, "Create what?"); return; } else if(!ok_name(arg1)) { notify(player, player, "That's a silly name for a thing!"); return; } cost = atol(arg2); if(cost < OBJECT_COST) cost = OBJECT_COST; if(!payfor(OWNER(player), cost)) notify(player, player, tprintf( "Sorry, you don't have enough %s.", PL_MONEY)); else { /* create the object */ thing = new_object(); /* initialize everything */ DBSTORE(thing, name, dup_string(arg1)); DBSTORE(thing, location, player); DBSTORE(thing, owner, OWNER(player)); add_ownerlist(thing); DBSTORE(thing, pennies, OBJECT_ENDOWMENT(cost)); DBSTORE(thing, exits, NOTHING); DBSTORE(thing, link, player); add_backlinks(thing); FLAGS(thing) = TYPE_THING; /* endow the object */ if(DBFETCH(thing)->pennies > MAX_OBJECT_ENDOWMENT) { DBSTORE(thing, pennies, MAX_OBJECT_ENDOWMENT); } DBSTORE(thing, pennies, cost); /* link it in */ PUSH(thing, DBFETCH(player)->contents); DBDIRTY(player); /* and we're done */ sprintf(buf, "%s created with number %ld.", arg1, thing); notify(player, player, buf); #ifdef MUSH FLAGS(thing) |= NOCOMMAND; #endif DBDIRTY(thing); } } /* * parse_source() * * This is a utility used by do_action and do_attach. It parses * the source string into a dbref, and checks to see that it * exists. * * The return value is the dbref of the source, or NOTHING if an * error occurs. * */ dbref parse_source(dbref player, char *source_name) { dbref source; match_data md; init_match(player, source_name, NOTYPE, &md); /* source type can be any */ match_neighbor(&md); match_me(&md); match_here(&md); match_possession(&md); if (Wizard(player)) match_absolute(&md); source = noisy_match_result(&md); if (source == NOTHING) return NOTHING; /* You can only attach actions to things you control */ if(!controls(player, source)) { notify(player, player, "Permission denied."); return NOTHING; } return source; } /* * do_action() * * This routine attaches a new existing action to a source object, * where possible. * The action will not do anything until it is LINKed. * */ void do_action(__DO_PROTO) { dbref action, source; if(!Builder(OWNER(player))) { notify(player, player, "That command is restricted to authorized builders."); return; } if (!*arg1 || !*arg2) { notify(player, player, "You must specify an action name and a source object."); return; } else if (!ok_name(arg1)) { notify(player, player, "That's a strange name for an action!"); return; } if (((source = parse_source(player, arg2)) == NOTHING)) return; action = new_object(); DBSTORE(action, name, dup_string(arg1)); DBSTORE(action, location, NOTHING); DBSTORE(action, owner, OWNER(player)); add_ownerlist(action); DBSTORE(action, sp.exit.ndest, 0); DBSTORE(action, sp.exit.dest, NULL); FLAGS(action) = TYPE_EXIT; moveto(action, source); sprintf(buf, "Action created with number %ld and attached.", action); notify(player, player, buf); DBDIRTY(action); } /* * do_attach() * * This routine attaches a previously existing action to a source object. * The action will not do anything unless it is LINKed. * */ void do_attach(__DO_PROTO) { dbref action, source; dbref loc; /* player's current location */ match_data md; if ( (loc = DBFETCH(player)->location) == NOTHING) return; if(!Builder(OWNER(player))) { notify(player, player, "That command is restricted to authorized builders."); return; } if (!*arg1 || !*arg2) { notify(player, player, "You must specify an action name and a source object."); return; } init_match(player, arg1, TYPE_EXIT, &md); match_all_exits(&md); if (Wizard(player)) match_absolute(&md); if ((action = noisy_match_result(&md)) == NOTHING) return; if (Typeof(action) != TYPE_EXIT) { notify(player, player, "That's not an action!"); return; } else if (!controls(player, action)) { notify(player, player, "Permission denied."); return; } if (((source = parse_source(player, arg2)) == NOTHING) || Typeof(source) == TYPE_PROGRAM) return; moveto(action, source); notify(player, player, "Action re-attached."); }