/* $Header: move.c,v 2.0 90/05/05 12:45:38 lachesis Exp $ * $Log: move.c,v $ * Revision 2.0 90/05/05 12:45:38 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.3 90/04/24 14:46:32 lachesis * Fixed the @odrop on rooms to links. * * Revision 1.2 90/04/20 14:06:47 lachesis * Added @odrop && @drop. * * Revision 1.1 90/04/14 14:56:47 lachesis * Initial revision * */ #include "copyright.h" #include "os.h" #include "db.h" #include "config.h" #include "interface.h" #include "match.h" #include "externs.h" void moveto (dbref what, dbref where) { dbref loc; /* remove what from old loc */ if ((loc = db[what].location) != NOTHING) { db[loc].contents = remove_first (db[loc].contents, what); } /* test for special cases */ switch (where) { case NOTHING: db[what].location = NOTHING; return; /* NOTHING doesn't have contents */ case HOME: switch (Typeof (what)) { case TYPE_PLAYER: where = db[what].sp.player.home; break; case TYPE_THING: where = db[what].sp.thing.home; break; } } /* now put what in where */ PUSH (what, db[where].contents); db[what].location = where; } void send_contents (dbref loc, dbref dest) { dbref first; dbref rest; first = db[loc].contents; db[loc].contents = NOTHING; /* blast locations of everything in list */ DOLIST (rest, first) { db[rest].location = NOTHING; } while (first != NOTHING) { rest = db[first].next; if (Typeof (first) != TYPE_THING) { moveto (first, loc); } else { moveto (first, db[first].flags & STICKY ? HOME : dest); } first = rest; } db[loc].contents = reverse (db[loc].contents); } static void maybe_dropto (dbref loc, dbref dropto) { dbref thing; if (loc == dropto) return; /* bizarre special case */ /* check for players */ DOLIST (thing, db[loc].contents) { if (Typeof (thing) == TYPE_PLAYER) return; } /* no players, send everything to the dropto */ send_contents (loc, dropto); } void enter_room (dbref player, dbref loc) { dbref old; dbref dropto; char buf[BUFFER_LEN]; /* check for room == HOME */ if (loc == HOME) loc = db[player].sp.player.home; /* home */ /* get old location */ old = db[player].location; /* check for self-loop */ /* self-loops don't do move or other player notification */ /* but you still get autolook and penny check */ if (loc != old) { if (old != NOTHING) { /* notify others unless DARK */ if (!Dark (old) && !Dark (player)) { sprintf (buf, "%s has left.", db[player].name); notify_except (db[old].contents, player, buf); } } /* go there */ moveto (player, loc); /* if old location has STICKY dropto, send stuff through it */ if (old != NOTHING && (dropto = db[old].sp.room.dropto) != NOTHING && (db[old].flags & STICKY)) { maybe_dropto (old, dropto); } /* tell other folks in new location if not DARK */ if (!Dark (loc) && !Dark (player)) { sprintf (buf, "%s has arrived.", db[player].name); notify_except (db[loc].contents, player, buf); } } /* autolook */ look_room (player, loc); /* check for pennies */ if (!controls (player, loc) && db[player].sp.player.pennies <= MAX_PENNIES && OS_RAND () % PENNY_RATE == 0) { notify (player, "You found a cookie!"); db[player].sp.player.pennies++; } } void send_home (dbref thing) { switch (Typeof (thing)) { case TYPE_PLAYER: /* send his possessions home first! */ /* that way he sees them when he arrives */ send_contents (thing, HOME); enter_room (thing, db[thing].sp.player.home); /* home */ break; case TYPE_THING: moveto (thing, db[thing].sp.thing.home); /* home */ break; default: /* no effect */ break; } } int can_move (dbref player, const char *direction) { if (!string_compare (direction, "home")) return 1; /* otherwise match on exits */ init_match (player, direction, TYPE_EXIT); match_all_exits (); return (last_match_result () != NOTHING); } /* * trigger() * * This procudure triggers a series of actions, or meta-actions * which are contained in the 'dest' field of the exit. * Locks other than the first one are over-ridden. * * `player' is the player who triggered the exit * `exit' is the exit triggered * `pflag' is a flag which indicates whether player and room exits * are to be used (non-zero) or ignored (zero). Note that * player/room destinations triggered via a meta-link are * ignored. * */ static void trigger (dbref player, dbref exit, int pflag) { int i; dbref dest; int sobjact; /* sticky object action flag, sends home source obj */ int succ; char buf[BUFFER_LEN]; sobjact = 0; succ = 0; for (i = 0; i < db[exit].sp.exit.ndest; i++) { dest = (db[exit].sp.exit.dest)[i]; if (dest == HOME) dest = db[player].sp.player.home; switch (Typeof (dest)) { case TYPE_ROOM: if (pflag) { if (db[exit].drop_message) notify (player, db[exit].drop_message); if (db[exit].odrop) { #ifdef GENDER pronoun_substitute (buf, player, db[exit].odrop, 1); #else /* GENDER */ sprintf (buf, "%s %s", db[player].name, db[exit].odrop); #endif /* GENDER */ notify_except (db[dest].contents, player, buf); } enter_room (player, dest); succ = 1; } break; case TYPE_THING: if (Typeof (db[exit].location) == TYPE_THING) { moveto (dest, db[db[exit].location].location); if (!(db[exit].flags & STICKY)) { /* send home source object */ sobjact = 1; } } else { moveto (dest, db[exit].location); } if (db[exit].succ_message) succ = 1; break; case TYPE_EXIT: /* It's a meta-link(tm)! */ trigger (player, (db[exit].sp.exit.dest)[i], 0); if (db[exit].succ_message) succ = 1; break; case TYPE_PLAYER: if (pflag && db[dest].location != NOTHING) { succ = 1; if (db[dest].flags & JUMP_OK) { if (db[exit].drop_message) notify (player, db[exit].drop_message); if (db[exit].odrop) { #ifdef GENDER char buf[BUFFER_LEN]; pronoun_substitute (buf, player, db[exit].odrop, 1); notify_except (db[db[player].location].contents, player, buf); #else /* GENDER */ notify_except (db[db[player].location].contents, player, db[exit].odrop); #endif } enter_room (player, db[dest].location); } else { notify (player, "That player does not wish to be disturbed."); } } break; } } if (sobjact) send_home (db[exit].location); if (!succ && pflag) notify (player, "Done."); } void do_move (dbref player, const char *direction) { dbref exit; dbref loc; char buf[BUFFER_LEN]; if (!string_compare (direction, "home")) { /* send him home */ /* but steal all his possessions */ if ((loc = db[player].location) != NOTHING) { /* tell everybody else */ sprintf (buf, "%s goes home.", db[player].name); notify_except (db[loc].contents, player, buf); } /* give the player the messages */ notify (player, "There's no place like home..."); notify (player, "There's no place like home..."); notify (player, "There's no place like home..."); notify (player, "You wake up back home, without your possessions."); send_home (player); } else { /* find the exit */ init_match_check_keys (player, direction, TYPE_EXIT); match_all_exits (); switch (exit = match_result ()) { case NOTHING: notify (player, "You can't go that way."); break; case AMBIGUOUS: notify (player, "I don't know which way you mean!"); break; default: /* we got one */ /* check to see if we got through */ loc = db[player].location; if (!(db[loc].flags & JUMP_OK) && (db[exit].location != NOTHING) && (Typeof (db[exit].location) == TYPE_PLAYER || Typeof (db[exit].location) == TYPE_THING) && db[exit].sp.exit.owner != db[loc].sp.room.owner && db[exit].location != loc) { notify (player, "You can't do that here."); } else if (can_doit (player, exit, "You can't go that way.")) { trigger (player, exit, 1); } break; } } } void do_get (dbref player, const char *what) { dbref thing; init_match_check_keys (player, what, TYPE_THING); match_all_exits (); match_neighbor (); if (Wizard (player)) match_absolute (); /* the wizard has long fingers */ if ((thing = noisy_match_result ()) != NOTHING) { if (db[thing].location == player) { notify (player, "You already have that!"); return; } switch (Typeof (thing)) { case TYPE_THING: if (can_doit (player, thing, "You can't pick that up.")) { moveto (thing, player); notify (player, "Taken."); } break; case TYPE_EXIT: if (controls (player, thing)) { notify (player, "Use @attach exit=me to move an exit."); } else { notify (player, "I don't see that here."); } break; default: notify (player, "You can't take that!"); break; } } } void do_drop (dbref player, const char *name) { dbref loc; dbref thing; char buf[BUFFER_LEN]; int reward; if ((loc = getloc (player)) == NOTHING) return; init_match (player, name, TYPE_THING); match_player_actions (); match_possession (); switch (thing = match_result ()) { case NOTHING: notify (player, "You don't have that!"); break; case AMBIGUOUS: notify (player, "I don't know which you mean!"); break; default: if (db[thing].location != player) { /* Shouldn't ever happen. */ notify (player, "You can't drop that."); } else if (Typeof (thing) == TYPE_EXIT) { notify (player, "Use @attach exit=here to drop an exit."); break; } else if (db[loc].flags & TEMPLE) { /* sacrifice time */ send_home (thing); sprintf (buf, "A kindly nurse takes %s from you and puts it out of your view.", db[thing].name); notify (player, buf); sprintf (buf, "%s hands %s to the nurse.", db[player].name, db[thing].name); notify_except (db[loc].contents, player, buf); /* check for reward */ if (!controls (player, thing)) { reward = db[thing].sp.thing.value; if (reward < 1 || db[player].sp.player.pennies > MAX_PENNIES) { reward = 1; } else if (reward > MAX_OBJECT_ENDOWMENT) { reward = MAX_OBJECT_ENDOWMENT; } db[player].sp.player.pennies += reward; sprintf (buf, "The nurse gives you %d %s for returning this item.", reward, reward == 1 ? "cookie" : "cookies"); notify (player, buf); } } else if (db[thing].flags & STICKY) { send_home (thing); if (db[thing].drop_message) notify (player, db[thing].drop_message); else notify (player, "Dropped."); if (db[loc].drop_message) notify (player, db[loc].drop_message); } else if (db[loc].sp.room.dropto != NOTHING && !(db[loc].flags & STICKY)) { /* location has immediate dropto */ moveto (thing, db[loc].sp.room.dropto); if (db[thing].drop_message) notify (player, db[thing].drop_message); else notify (player, "Dropped."); if (db[loc].drop_message) notify (player, db[loc].drop_message); } else { moveto (thing, loc); if (db[thing].drop_message) notify (player, db[thing].drop_message); else notify (player, "Dropped."); if (db[loc].drop_message) notify (player, db[loc].drop_message); if (db[thing].odrop) { #ifdef GENDER pronoun_substitute (buf, player, db[thing].odrop, 1); #else /* GENDER */ sprintf (buf, "%s %s", db[player].name, db[thing].odrop); #endif /* GENDER */ } else sprintf (buf, "%s dropped %s.", db[player].name, db[thing].name); notify_except (db[loc].contents, player, buf); if (db[loc].odrop) { #ifdef GENDER pronoun_substitute (buf, thing, db[loc].odrop, 1); #else /* !GENDER */ sprintf (buf, "%s %s", db[thing].name, db[loc].odrop); #endif /* GENDER */ notify_except (db[loc].contents, player, buf); } } break; } } #ifdef RECYCLE void recycle (dbref player, dbref thing) { extern dbref recyclable; struct object *o = db + thing; dbref first; dbref rest; switch (Typeof (thing)) { case TYPE_ROOM: for (first = db[thing].sp.room.exits; first != NOTHING; first = rest) { rest = db[first].next; if (db[first].location == NOTHING || db[first].location == thing) recycle (player, first); } notify_except (db[thing].contents, NOTHING, "You feel a wrenching sensation..."); break; case TYPE_THING: for (first = db[thing].sp.thing.actions; first != NOTHING; first = rest) { rest = db[first].next; if (db[first].location == NOTHING || db[first].location == thing) recycle (player, first); } break; } for (rest = 0; rest < db_top; rest++) { switch (Typeof (rest)) { case TYPE_ROOM: if (db[rest].sp.room.dropto == thing) db[rest].sp.room.dropto = NOTHING; if (db[rest].sp.room.exits == thing) db[rest].sp.room.exits = db[thing].next; if (db[rest].sp.room.owner == thing) db[rest].sp.room.owner = GOD; break; case TYPE_THING: if (db[rest].sp.thing.home == thing) { if (db[db[rest].sp.thing.owner].sp.player.home == thing) db[db[rest].sp.thing.owner].sp.player.home = PLAYER_START; db[rest].sp.thing.home = db[db[rest].sp.thing.owner].sp.player.home; } if (db[rest].sp.thing.actions == thing) db[rest].sp.thing.actions = db[thing].next; if (db[rest].sp.thing.owner == thing) db[rest].sp.thing.owner = GOD; break; case TYPE_EXIT: { int i, j; for (i = j = 0; i < db[rest].sp.exit.ndest; i++) { if ((db[rest].sp.exit.dest)[i] != thing) (db[rest].sp.exit.dest)[j++] = (db[rest].sp.exit.dest)[i]; } db[rest].sp.exit.ndest = j; } if (db[rest].sp.exit.owner == thing) db[rest].sp.exit.owner = GOD; break; case TYPE_PLAYER: if (db[rest].sp.player.home == thing) db[rest].sp.player.home = PLAYER_START; if (db[rest].sp.player.actions == thing) db[rest].sp.player.actions = db[thing].next; break; } if (db[rest].location == thing) db[rest].location = NOTHING; if (db[rest].contents == thing) db[rest].contents = db[thing].next; if (db[rest].next == thing) db[rest].next = db[thing].next; } for (first = db[thing].contents; first != NOTHING; first = rest) { rest = db[first].next; if (Typeof (first) == TYPE_PLAYER) enter_room (first, HOME); else moveto (first, HOME); } moveto (thing, NOTHING); db_free_object (thing); db_clear_object (thing); o->name = "<garbage>"; o->description = "<recyclable>"; o->key = TRUE_BOOLEXP; o->flags = TYPE_GARBAGE; o->next = recyclable; recyclable = thing; } void do_recycle (dbref player, const char *name) { dbref thing; init_match (player, name, TYPE_THING); match_all_exits (); match_neighbor (); match_possession (); match_here (); if (Wizard (player)) { match_absolute (); } if ((thing = noisy_match_result ()) != NOTHING) { if (!controls (player, thing)) { notify (player, "Permission denied."); } else { switch (Typeof (thing)) { case TYPE_ROOM: if (db[thing].sp.room.owner != player) { notify (player, "Permission denied."); return; } if (thing == PLAYER_START) { notify (player, "This room may not be recycled."); return; } break; case TYPE_THING: if (db[thing].sp.thing.owner != player) { notify (player, "Permission denied."); return; } break; case TYPE_EXIT: if (db[thing].sp.exit.owner != player) { notify (player, "Permission denied."); return; } if (!unset_source (player, db[player].location, thing, "You can't recycle an exit in another room.")) return; break; case TYPE_PLAYER: notify (player, "You can't recycle a player!"); return; case TYPE_GARBAGE: notify (player, "That's already garbage!"); return; } recycle (player, thing); notify (player, "Thank you for recycling."); } } } #endif /* RECYCLE */