/* $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 "os.h"
#include "db.h"
#include "config.h"
#include "interface.h"
#include "externs.h"
#include "match.h"
static int link_exit (dbref player, dbref exit, char *dest_name, dbref * dest_list);
/* 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.
*/
static 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.
*
*/
static int link_exit (dbref player, dbref exit, char *dest_name, dbref * dest_list)
{
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.
*
*/
static 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.
*
*/
static 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;
}