/* $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;
}