/* $Header: create.c,v 2.0 90/05/05 12:44:12 lachesis Exp $ * $Log: create.c,v $ * Revision 2.0 90/05/05 12:44:12 lachesis * Incorporated ABODE and HAVEN flags (remembering to convert FireFoot's * usage of those flags to ours). * Added Examine of objects that don't belong to you, added GOD_PRIV. * * Revision 1.1 90/04/14 14:56:40 lachesis * Initial revision * */ #include "copyright.h" /* Commands that create new objects */ #include "db.h" #include "config.h" #include "interface.h" #include "externs.h" #include "match.h" #include <strings.h> #include <ctype.h> /* utility for open and link */ static dbref parse_linkable_room(dbref player, const char *room_name) { dbref room; /* skip leading NUMBER_TOKEN if any */ if(*room_name == NUMBER_TOKEN) room_name++; /* parse room */ if(!string_compare(room_name, "here")) { room = db[player].location; } else if(!string_compare(room_name, "home")) { return HOME; /* HOME is always linkable */ } else { room = parse_dbref(room_name); } /* check room */ if(room < 0 || room >= db_top || Typeof(room) != TYPE_ROOM) { notify(player, "That's not a room!"); return NOTHING; } else if(!can_link_to(player, room)) { notify(player, "You can't link to that."); return NOTHING; } else { return room; } } #ifdef ABODE /* utility for open and link for objects/players */ static dbref parse_abode_room(dbref player, const char *room_name) { dbref room; /* skip leading NUMBER_TOKEN if any */ if(*room_name == NUMBER_TOKEN) room_name++; /* parse room */ if(!string_compare(room_name, "here")) { room = db[player].location; } else if(!string_compare(room_name, "home")) { return HOME; /* HOME is always linkable */ } else { room = parse_dbref(room_name); } /* check room */ if(room < 0 || room >= db_top || Typeof(room) != TYPE_ROOM) { notify(player, "That's not a room!"); return NOTHING; } else if(! (controls(player, room) || (db[room].flags & ABODE))) { notify(player, "You can't link to that."); return NOTHING; } else { return room; } } #endif /* ABODE */ /* 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, const char *dest_name) { dbref dobj; /* destination room/player/thing/link */ static char buf[BUFFER_LEN]; init_match(player, dest_name, NOTYPE); match_absolute(); match_everything(); match_home(); if ((dobj = match_result()) == NOTHING) { sprintf(buf, "I couldn't find \"%s\".", dest_name); notify(player, buf); return NOTHING; } if (dobj == AMBIGUOUS) { notify(player, "I don't know which one you mean!"); 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, buf); return NOTHING; #endif /* TELEPORT_TO_PLAYER */ } if (!can_link_to(player, dobj)) { sprintf(buf, "You can't link to %s.", unparse_object(player, dobj)); notify(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 < db[dest].sp.exit.ndest; i++) { if ( (db[dest].sp.exit.dest)[i] == source) { return 1; /* Found a loop! */ } if (Typeof((db[dest].sp.exit.dest)[i]) == TYPE_EXIT) { if (exit_loop_check(source, (db[dest].sp.exit.dest)[i])) { return 1; /* Found one recursively */ } } } return 0; /* No loops found */ } /* use this to create an exit */ void do_open(dbref player, const char *direction, const char *linkto) { dbref loc, exit; dbref good_dest[MAX_LINKS]; int i, ndest; #ifdef RESTRICTED_BUILDING if(!Builder(player)) { notify(player, "That command is restricted to authorized builders."); return; } #endif /* RESTRICTED_BUILDING */ if((loc = getloc(player)) == NOTHING) return; if(!*direction) { notify(player, "You must specify a direction or action name to open."); return; } else if(!ok_name(direction)) { notify(player, "That's a strange name for an exit!"); return; } if(!controls(player, loc)) { notify(player, "Permission denied."); } else if(!payfor(player, EXIT_COST)) { notify(player, "Sorry, you don't have enough cookies to open an exit."); } else { /* create the exit */ exit = new_object(); /* initialize everything */ db[exit].name = alloc_string(direction); db[exit].location = loc; db[exit].sp.exit.owner = player; db[exit].flags = TYPE_EXIT; db[exit].sp.exit.ndest = 0; db[exit].sp.exit.dest = 0; /* link it in */ PUSH(exit, db[loc].sp.room.exits); /* and we're done */ notify(player, "Opened."); /* check second arg to see if we should do a link */ if(*linkto != '\0') { notify(player, "Trying to link..."); if(!payfor(player, LINK_COST)) { notify(player, "You don't have enough cookies to link."); return; } ndest = link_exit(player, exit, (char *) linkto, good_dest); db[exit].sp.exit.ndest = ndest; db[exit].sp.exit.dest = (dbref *) malloc(sizeof(dbref)*ndest); for (i = 0; i < ndest; i++) { (db[exit].sp.exit.dest)[i] = good_dest[i]; } } } } /* * 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. * */ #ifdef mips int link_exit(player, exit, dest_name, dest_list) dbref player, exit; char *dest_name; dbref dest_list[]; #else /* !mips */ int link_exit(dbref player, dbref exit, char *dest_name, dbref *dest_list) #endif /* mips */ { char *p, *q; int prdest; dbref dest; int ndest; char buf[BUFFER_LEN], 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++; /* go to space or end */ 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++) /*EMPTY*/ ; if ( (dest = parse_linkable_dest(player, q)) == NOTHING) continue; switch (Typeof(dest)) { case TYPE_PLAYER: case TYPE_ROOM: if (prdest) { sprintf (buf, "Only one player or room destination allowed. Destination %d ignored.", dest); notify(player, buf); continue; } dest_list[ndest++] = dest; prdest = 1; break; case TYPE_THING: dest_list[ndest++] = dest; break; case TYPE_EXIT: if (exit_loop_check(exit, dest)) { sprintf(buf, "Destination %d would create a loop, ignored.", dest); notify(player, buf); continue; } dest_list[ndest++] = dest; break; default: notify(player, "Internal error: weird object type."); fprintf(stderr, "PANIC weird object: Typeof(%d) = %d\n", dest, Typeof(dest)); break; } if (dest == HOME) { notify(player, "Linked to HOME."); } else { sprintf(buf, "Linked to %s.", unparse_object(player, dest)); notify(player, buf); } if (ndest >= MAX_LINKS) { notify(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(dbref player, const char *thing_name, const char *dest_name) { dbref thing; dbref dest; dbref good_dest[MAX_LINKS]; int ndest, i; init_match(player, thing_name, TYPE_EXIT); match_all_exits(); match_neighbor(); match_possession(); match_me(); match_here(); if(Wizard(player)) { match_absolute(); match_player(); } if((thing = noisy_match_result()) == NOTHING) return; switch(Typeof(thing)) { case TYPE_EXIT: /* we're ok, check the usual stuff */ if(db[thing].sp.exit.ndest != 0) { if(controls(player, thing)) { notify(player, "That exit is already linked."); return; } else { notify(player, "Permission denied."); return; } } /* handle costs */ if(db[thing].sp.exit.owner == player) { if(!payfor(player, LINK_COST)) { notify(player, "It costs a cookie to link this exit."); return; } } else { if(!payfor(player, LINK_COST + EXIT_COST)) { notify(player, "It costs two cookies to link this exit."); return; #ifdef RESTRICTED_BUILDING } else if(!Builder(player)) { notify(player, "Only authorized builders may seize exits."); return; #endif /* RESTRICTED_BUILDING */ } else { /* pay the owner for his loss */ db[db[thing].sp.exit.owner].sp.player.pennies += EXIT_COST; } } /* link has been validated and paid for; do it */ db[thing].sp.exit.owner = player; ndest = link_exit(player, thing, (char *) dest_name, good_dest); if (ndest == 0) { notify(player, "No destinations linked."); db[player].sp.player.pennies += LINK_COST; /* Refund! */ break; } db[thing].sp.exit.ndest = ndest; db[thing].sp.exit.dest = (dbref *) malloc(sizeof(dbref)*ndest); for (i = 0; i < ndest; i++) { (db[thing].sp.exit.dest)[i] = good_dest[i]; } break; case TYPE_THING: case TYPE_PLAYER: #ifndef ABODE if ( (dest = parse_linkable_room(player, dest_name) ) == NOTHING) return; #else /* ABODE */ dest = parse_abode_room(player, dest_name); if (dest == NOTHING) return; #endif /* ABODE */ if (Typeof(dest) != TYPE_ROOM) { notify(player, "You can only set home to a room."); break; } if(!controls(player, thing)) { notify(player, "Permission denied."); } else if(dest == HOME) { notify(player, "Can't set home to 'home'."); } else { /* do the link */ if (Typeof(thing) == TYPE_THING) db[thing].sp.thing.home = dest; else db[thing].sp.player.home = dest; notify(player, "Home set."); } break; case TYPE_ROOM: /* room dropto's */ dest = parse_linkable_room(player, dest_name); if (dest == NOTHING) { notify(player, "You can't link to that."); return; } if(!controls(player, thing)) { notify(player, "Permission denied."); } else { /* do the link, in location */ db[thing].sp.room.dropto = dest; /* dropto */ notify(player, "Dropto set."); } break; default: notify(player, "Internal error: weird object type."); fprintf(stderr, "PANIC weird object: Typeof(%d) = %d\n", thing, Typeof(thing)); break; } } /* * do_dig * * Use this to create a room. */ void do_dig(dbref player, const char *name) { dbref room; char buf[BUFFER_LEN]; #ifdef RESTRICTED_BUILDING if(!Builder(player)) { notify(player, "That command is restricted to authorized builders."); return; } #endif /* RESTRICTED_BUILDING */ /* we don't need to know player's location! hooray! */ if(*name == '\0') { notify(player, "You must specify a name for the room."); } else if(!ok_name(name)) { notify(player, "That's a silly name for a room!"); } else if(!payfor(player, ROOM_COST)) { notify(player, "Sorry, you don't have enough cookies to dig a room."); } else { room = new_object(); /* Initialize everything */ db[room].name = alloc_string(name); db[room].sp.room.owner = player; db[room].sp.room.exits = NOTHING; db[room].sp.room.dropto = NOTHING; db[room].flags = TYPE_ROOM | (db[player].flags & JUMP_OK); sprintf(buf, "%s created with room number %d.", name, room); notify(player, buf); } } /* * do_create * * Use this to create an object. */ void do_create(dbref player, const char *name, int cost) { dbref loc; dbref thing; static char buf[BUFFER_LEN]; #ifdef RESTRICTED_BUILDING if(!Builder(player)) { notify(player, "That command is restricted to authorized builders."); return; } #endif /* RESTRICTED_BUILDING */ if(*name == '\0') { notify(player, "Create what?"); return; } else if(!ok_name(name)) { notify(player, "That's a silly name for a thing!"); return; } else if(cost < 0) { notify(player, "You can't create an object for less than nothing!"); return; } else if(cost < OBJECT_COST) { cost = OBJECT_COST; } if(!payfor(player, cost)) { notify(player, "Sorry, you don't have enough cookies."); } else { /* create the object */ thing = new_object(); /* initialize everything */ db[thing].name = alloc_string(name); db[thing].location = player; db[thing].sp.thing.owner = player; db[thing].sp.thing.value = OBJECT_ENDOWMENT(cost); db[thing].sp.thing.actions = NOTHING; db[thing].flags = TYPE_THING; /* endow the object */ if(db[thing].sp.thing.value > MAX_OBJECT_ENDOWMENT) { db[thing].sp.thing.value = MAX_OBJECT_ENDOWMENT; } /* home is here (if we can link to it) or player's home */ if((loc = db[player].location) != NOTHING && controls(player, loc)) { db[thing].sp.thing.home = loc; /* home */ } else { db[thing].sp.thing.home = db[player].sp.player.home; /* set to player's home instead */ } /* link it in */ PUSH(thing, db[player].contents); /* and we're done */ sprintf(buf, "%s created with number %d.", name, thing); notify(player, buf); } } /* * 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, const char *source_name) { dbref source; init_match(player, source_name, NOTHING); /* source type can be any */ match_neighbor(); match_me(); match_here(); match_possession(); if (Wizard(player)) { match_absolute(); } source = noisy_match_result(); if (source == NOTHING) return NOTHING; /* You can only attach actions to things you control */ if(!controls(player, source)) { notify(player, "Permission denied."); return NOTHING; } if(Typeof(source) == TYPE_EXIT) { notify(player, "You can't attach an action to an action."); return NOTHING; } return source; } /* * set_source() * * This routine sets the source of an action to the specified source. * It is called by do_action and do_attach. * */ void set_source(dbref player, dbref action, dbref source) { switch(Typeof(source)) { case TYPE_ROOM: PUSH(action, db[source].sp.room.exits); break; case TYPE_THING: PUSH(action, db[source].sp.thing.actions); break; case TYPE_PLAYER: PUSH(action, db[source].sp.player.actions); break; default: notify(player, "Internal error: weird object type."); fprintf(stderr, "PANIC weird object: Typeof(%d) = %d\n", source, Typeof(source)); return; /*NOTREACHED*/ break; } db[action].location = source; return; } /* * 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(dbref player, const char *action_name, const char *source_name) { dbref action, source; static char buf[BUFFER_LEN]; #ifdef RESTRICTED_BUILDING if(!Builder(player)) { notify(player, "That command is restricted to authorized builders."); return; } #endif /* RESTRICTED_BUILDING */ if (!*action_name || !*source_name) { notify(player, "You must specify an action name and a source object."); return; } else if (!ok_name(action_name)) { notify(player, "That's a strange name for an action!"); return; } else if(!payfor(player, EXIT_COST)) { notify(player, "Sorry, you don't have enough cookies to make an action."); return; } if ((source = parse_source(player, source_name)) == NOTHING) return; action = new_object(); db[action].name = alloc_string(action_name); db[action].location = NOTHING; db[action].sp.exit.owner = player; db[action].sp.exit.ndest = 0; db[action].sp.exit.dest = NULL; db[action].flags = TYPE_EXIT; set_source(player, action, source); sprintf(buf, "Action created with number %d and attached.", action); notify(player, buf); } /* * 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(dbref player, const char *action_name, const char *source_name) { dbref action, source; dbref oldsrc; /* action's old source */ dbref loc; /* player's current location */ if ( (loc = db[player].location) == NOTHING) return; #ifdef RESTRICTED_BUILDING if(!Builder(player)) { notify(player, "That command is restricted to authorized builders."); return; } #endif /* RESTRICTED_BUILDING */ if (!*action_name || !*source_name) { notify(player, "You must specify an action name and a source object."); return; } init_match(player, action_name, TYPE_EXIT); match_all_exits(); if (Wizard(player)) match_absolute(); if ( (action = noisy_match_result() ) == NOTHING) return; if (Typeof(action) != TYPE_EXIT) { notify(player, "That's not an action!"); return; } else if (!controls(player, action)) { notify(player, "Permission denied."); return; } if ((source = parse_source(player, source_name)) == NOTHING) return; if ( (oldsrc = db[action].location) == NOTHING) { /* old-style, sourceless exit */ if(!member(action, db[loc].sp.room.exits)) { notify(player, "You can't re-attach an exit in another room."); return; } db[db[player].location].sp.room.exits = remove_first(db[db[player].location].sp.room.exits, action); } else { switch(Typeof(oldsrc)) { case TYPE_PLAYER: db[oldsrc].sp.player.actions = remove_first(db[oldsrc].sp.player.actions, action); break; case TYPE_ROOM: db[oldsrc].sp.room.exits = remove_first(db[oldsrc].sp.room.exits, action); break; case TYPE_THING: db[oldsrc].sp.thing.actions = remove_first(db[oldsrc].sp.thing.actions, action); break; case TYPE_EXIT: fprintf(stderr, "PANIC: Action #%d was sourced to an exit.", action); return; /*NOTREACHED*/ break; } } set_source(player, action, source); notify(player, "Action re-attached."); } int unset_source(dbref player, dbref loc, dbref action, const char *other_room_msg) { dbref oldsrc; if ( (oldsrc = db[action].location) == NOTHING) { /* old-style, sourceless exit */ if(!member(action, db[loc].sp.room.exits)) { notify(player, other_room_msg); return 0; } db[db[player].location].sp.room.exits = remove_first(db[db[player].location].sp.room.exits, action); } else { switch(Typeof(oldsrc)) { case TYPE_PLAYER: db[oldsrc].sp.player.actions = remove_first(db[oldsrc].sp.player.actions, action); break; case TYPE_ROOM: db[oldsrc].sp.room.exits = remove_first(db[oldsrc].sp.room.exits, action); break; case TYPE_THING: db[oldsrc].sp.thing.actions = remove_first(db[oldsrc].sp.thing.actions, action); break; case TYPE_EXIT: fprintf(stderr, "PANIC: Action #%d was sourced to an exit.", action); return 0; /*NOTREACHED*/ break; } } return 1; }