/* $Header: /belch_a/users/rearl/tinymuck/src/RCS/move.c,v 1.11 90/09/16 04:42:35 rearl Exp $ */ /* * $Log: move.c,v $ * Revision 1.11 90/09/16 04:42:35 rearl * Preparation code added for disk-based MUCK. * * Revision 1.10 90/09/15 22:26:57 rearl * Fixed moveto bug from HOME. * * Revision 1.9 90/09/13 06:28:04 rearl * send_contents changed to be usable for do_toad() in wiz.c * * Revision 1.8 90/09/10 02:20:29 rearl * Put exec_or_notify in the drop messages of exits. * * Revision 1.7 90/09/04 18:39:43 rearl * Added some prototypes for externs. * * Revision 1.6 90/09/01 05:59:21 rearl * Took out drop code for rooms, setting parent depends on @tel now. * * Revision 1.5 90/08/27 03:31:58 rearl * Added environment support. * * Revision 1.4 90/08/15 03:06:01 rearl * Fixed 0 PENNY_RATE bug. Took out #ifdef GENDER. * * Revision 1.3 90/08/05 03:19:47 rearl * Redid matching routines. * * Revision 1.2 90/07/29 17:41:43 rearl * Fixed moveto problems relating to ROOM programs, also programs * go to their owner rather than "home" -- programs have no home by * definition. * * Revision 1.1 90/07/19 23:03:55 casie * Initial revision * * */ #include "copyright.h" #include "config.h" #include "db.h" #include "params.h" #include "interface.h" #include "match.h" #include "externs.h" #ifdef RECYCLE void recycle(dbref, dbref); #endif /* RECYCLE */ void moveto_internal(dbref, dbref); void enter_room_internal(dbref, dbref); void moveto(dbref what, dbref where) { if(can_hear(what)) { enter_room_internal(what, where); } else { moveto_internal(what, where); } } void moveto_internal(dbref what, dbref where) { dbref loc; /* remove what from old loc */ if((loc = DBFETCH(what)->location) != NOTHING) { DBSTORE(loc, contents, remove_first(DBFETCH(loc)->contents, what)); } /* test for special cases */ switch(where) { case NOTHING: DBSTORE(what, location, NOTHING); return; /* NOTHING doesn't have contents */ case HOME: switch(Typeof(what)) { case TYPE_PLAYER: where = DBFETCH(what)->link; break; case TYPE_THING: where = DBFETCH(what)->link; break; case TYPE_ROOM: where = GLOBAL_ENVIRONMENT; break; case TYPE_PROGRAM: where = OWNER(what); break; default: where = GLOBAL_ENVIRONMENT; } } if(parent_loop_check(what, where)) what = PLAYER_START; if(parent_loop_check(what, where)) what = GLOBAL_ENVIRONMENT; if(parent_loop_check(what, where)) return; if(!is_ok(what)) { printf("Bad what: %d\n",what); what = PLAYER_START; } /* now put what in where */ PUSH(what, DBFETCH(where)->contents); DBDIRTY(where); DBSTORE(what, location, where); } dbref reverse(dbref); void send_contents(dbref loc, dbref dest) { dbref first; dbref rest; puts("starting send_contents"); first = DBFETCH(loc)->contents; DBSTORE(loc, contents, NOTHING); /* blast locations of everything in list */ DOLIST(rest, first) { DBSTORE(rest, location, NOTHING); } while(first != NOTHING) { rest = DBFETCH(first)->next; if((Typeof(first) != TYPE_THING) && (Typeof(first) != TYPE_PROGRAM)) { moveto(first, loc); } else { moveto(first, FLAGS(first) & STICKY ? HOME : dest); } first = rest; } DBSTORE(loc, contents, reverse(DBFETCH(loc)->contents)); puts("done send_contents"); } void maybe_dropto(dbref loc, dbref dropto) { dbref thing; if(loc == dropto) return; /* bizarre special case */ /* check for players */ DOLIST(thing, DBFETCH(loc)->contents) { if(Typeof(thing) == TYPE_PLAYER) return; } /* no players, send everything to the dropto */ send_contents(loc, dropto); } int parent_loop_check_internal(dbref source, dbref dest, int depth) { if(depth > 20) return 1; if(dest == NOTHING) return 0; return(parent_loop_check_internal(source, DBFETCH(dest)->location, depth + 1)); } int parent_loop_check(dbref source, dbref dest) { if(source == NOTHING) return 1; if(dest == HOME) dest = DBFETCH(source)->link; if (dest == NOTHING) return 0; if(Typeof(source) != TYPE_ROOM || Typeof(dest) != TYPE_ROOM) return 0; if (source == dest) return 1; /* That's an easy one! */ if(Typeof(dest) == TYPE_GARBAGE) return 1; return parent_loop_check_internal(source, DBFETCH(dest)->location, 0); } void enter_room(dbref player, dbref loc, dbref exit) { /*we ignore exit. */ moveto(player,loc); } void enter_room_internal(dbref player, dbref loc) { dbref old; dbref dropto; char buf[BUFFER_LEN]; /* check for room == HOME */ if(loc == HOME) loc = DBFETCH(player)->link; /* home */ /* get old location */ old = DBFETCH(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.", NAME(player)); notify_except(DBFETCH(old)->contents, player, buf); } } /* go there */ moveto_internal(player, loc); /* if old location has STICKY dropto, send stuff through it */ if(old != NOTHING && (dropto = DBFETCH(old)->link) != NOTHING && (FLAGS(old) & STICKY)) { maybe_dropto(old, dropto); } /* tell other folks in new location if not DARK */ if(loc != NOTHING && !Dark(loc) && !Dark(player)) { sprintf(buf, "%s has arrived.", NAME(player)); notify_except(DBFETCH(loc)->contents, player, buf); } } /* autolook */ if(loc != NOTHING) look_room(player, loc); #if (PENNY_RATE) != 0 /* check for pennies */ if(!controls(player, loc) && DBFETCH(player)->sp.player.pennies <= MAX_PENNIES && (random() % PENNY_RATE) == 0) { notify(player, "You found a penny!"); DBFETCH(player)->sp.player.pennies++; DBDIRTY(player); } #endif /* PENNY_RATE */ } 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, DBFETCH(thing)->link, DBFETCH(thing)->location); break; case TYPE_THING: send_contents(thing,HOME); moveto(thing, DBFETCH(thing)->link); /* home */ break; case TYPE_PROGRAM: moveto(thing, OWNER(thing)); break; default: /* no effect */ break; } return; } int can_move(dbref player, const char *direction) { struct match_data md; if(!string_compare(direction, "home")) return 1; /* otherwise match on exits */ init_match(player, direction, TYPE_EXIT, &md); match_all_exits(&md); return(last_match_result(&md) != NOTHING); } /* * trigger() * * This procedure 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. * */ 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 < DBFETCH(exit)->sp.exit.ndest; i++) { dest = (DBFETCH(exit)->sp.exit.dest)[i]; if (dest == HOME) dest = DBFETCH(player)->link; switch(Typeof(dest)) { case TYPE_ROOM: if (pflag) { if (get_attr(exit, "Drop")) exec_or_notify(player, exit, get_attr(exit, "Drop")); if (get_attr(exit, "Odrop") && !Dark(player)) { sprintf(buf, "%s %s", NAME(player), pronoun_substitute(player, get_attr(exit, "Odrop"), exit)); notify_except(DBFETCH(dest)->contents, player, buf); } enter_room(player, dest, exit); succ = 1; } break; case TYPE_THING: if (Typeof(DBFETCH(exit)->location) == TYPE_THING) { moveto (dest, DBFETCH(DBFETCH(exit)->location)->location); if (!(FLAGS(exit) & STICKY)) { /* send home source object */ sobjact = 1; } } else { moveto (dest, DBFETCH(exit)->location); } if (get_attr(exit, "Succ")) succ = 1; break; case TYPE_EXIT: /* It's a meta-link(tm)! */ trigger(player, (DBFETCH(exit)->sp.exit.dest)[i], 0); if (get_attr(exit, "Succ")) succ = 1; break; case TYPE_PLAYER: if (pflag && DBFETCH(dest)->location != NOTHING) { succ = 1; if (FLAGS(dest) & JUMP_OK) { if (get_attr(exit, "Drop")) exec_or_notify(player, exit, get_attr(exit, "Drop")); if (get_attr(exit, "Odrop") && !Dark(player)) { sprintf(buf, "%s %s", NAME(player), pronoun_substitute(player, get_attr(exit, "Odrop"), exit)); notify_except(DBFETCH(DBFETCH(dest)->location)->contents, player, buf); } enter_room(player, DBFETCH(dest)->location, exit); } else { notify(player, "That player does not wish to be disturbed."); } } break; case TYPE_PROGRAM: if (DBFETCH(player)->run) return; (void) interp(player, dest, exit); if (!(DBFETCH(player)->run -> pc)) { free((void *) DBFETCH(player)->run); DBFETCH(player)->run = 0; DBDIRTY(player); } return; } } if (sobjact) send_home(DBFETCH(exit)->location); if (!succ && pflag) notify(player, "Done."); } void do_move(dbref player, const char *direction) { dbref exit; dbref loc; struct match_data md; if(!string_compare(direction, "home")) { /* send him home */ /* but steal all his possessions */ if((loc = DBFETCH(player)->location) != NOTHING) { /* tell everybody else */ sprintf(buf, "%s goes home.", NAME(player)); notify_except(DBFETCH(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, &md); match_all_exits(&md); switch(exit = match_result(&md)) { 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 = DBFETCH(player)->location; 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; struct match_data md; init_match_check_keys(player, what, TYPE_THING, &md); match_neighbor(&md); if(Arch(player)) match_absolute(&md); /* the wizard has long fingers */ if((thing = noisy_match_result(&md)) != NOTHING) { if(DBFETCH(thing)->location == player) { notify(player, "You already have that!"); return; } switch(Typeof(thing)) { case TYPE_THING: case TYPE_PROGRAM: case TYPE_PLAYER: if(parent_loop_check(thing,player)) { notify(player,"You can't take that!"); return; } if(can_doit(player, thing, "You can't pick that up.")) { moveto(thing, player); notify(player, "Taken."); } 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]; struct match_data md; if((loc = getloc(player)) == NOTHING) return; init_match(player, name, NOTYPE, &md); match_possession(&md); if ((thing = noisy_match_result(&md)) == NOTHING || thing == AMBIGUOUS) return; switch (Typeof(thing)) { case TYPE_THING: case TYPE_PROGRAM: case TYPE_PLAYER: if(DBFETCH(thing)->location != player) { /* Shouldn't ever happen. */ notify(player, "You can't drop that."); break; } if ((FLAGS(thing) & STICKY) && Typeof(thing) == TYPE_THING) { send_home(thing); } else { int immediate_dropto = (DBFETCH(loc)->link != NOTHING && !(FLAGS(loc) & STICKY)); moveto(thing, (immediate_dropto && (Typeof(loc)==TYPE_ROOM)) ? DBFETCH(loc)->link : loc); } if (get_attr(thing, "Drop")) exec_or_notify(player, thing, get_attr(thing, "Drop")); else notify(player, "Dropped."); if (get_attr(loc, "Drop") && Typeof(loc)==TYPE_ROOM) exec_or_notify(thing, loc, get_attr(loc, "Drop")); if (get_attr(thing, "Odrop")) { sprintf(buf, "%s %s", NAME(player), pronoun_substitute(player, get_attr(thing, "Odrop"), thing)); } else { sprintf(buf, "%s drops %s.", NAME(player), NAME(thing)); } notify_except(DBFETCH(loc)->contents, player, buf); if(get_attr(thing, "Adrop")) { trigobj(thing, get_attr(thing, "Adrop"),player); } if(get_attr(loc, "Adrop") && Typeof(loc)==TYPE_ROOM) { trigobj(loc, get_attr(loc, "Adrop"),thing); } if (get_attr(loc, "Odrop") && Typeof(loc)==TYPE_ROOM) { sprintf(buf, "%s %s", NAME(thing), pronoun_substitute(thing, get_attr(loc, "Odrop"), loc)); notify_except(DBFETCH(loc)->contents, player, buf); } break; default: notify(player, "You can't drop that."); break; } } #ifdef RECYCLE void do_recycle(dbref player, const char *name) { dbref thing; struct match_data md; init_match(player, name, TYPE_THING, &md); match_all_exits(&md); match_neighbor(&md); match_possession(&md); match_here(&md); match_absolute(&md); if((thing = noisy_match_result(&md)) != NOTHING) { if(!controls(player, thing)) { notify(player, "Permission denied."); } else { switch(Typeof(thing)) { case TYPE_ROOM: if(OWNER(thing) != player) { notify(player, "Permission denied."); return; } if(thing == PLAYER_START || thing == GLOBAL_ENVIRONMENT) { notify(player, "This room may not be recycled."); return; } break; case TYPE_THING: if(OWNER(thing) != player) { notify(player, "Permission denied."); return; } break; case TYPE_EXIT: if(OWNER(thing) != player) { notify(player, "Permission denied."); return; } if(!unset_source(player, DBFETCH(player)->location, thing)) { return; } break; case TYPE_PLAYER: notify(player, "You can't recycle a player!"); return; /*NOTREACHED*/ break; case TYPE_PROGRAM: if(OWNER(thing) != player) { notify(player, "Permission denied."); return; } break; case TYPE_DAEMON: if(OWNER(thing) != player) { notify(player, "Permission denied."); return; } break; case TYPE_GARBAGE: notify(player, "That's already garbage!"); return; /*NOTREACHED*/ break; } recycle(player, thing); notify(player,"Thank you for recycling."); } } } void recycle(dbref player, dbref thing) { extern dbref recyclable; static int depth = 0; dbref first; dbref rest; char buf[2048]; depth++; switch(Typeof(thing)) { case TYPE_ROOM: DBFETCH(OWNER(thing))->sp.player.pennies += ROOM_COST; DBDIRTY(OWNER(thing)); for(first = DBFETCH(thing)->exits; first != NOTHING; first = rest) { rest = DBFETCH(first)->next; if(DBFETCH(first)->location == NOTHING || DBFETCH(first)->location == thing) recycle(player, first); } notify_except(DBFETCH(thing)->contents, NOTHING, "The room shakes and crumbles..."); break; case TYPE_THING: halt_long(thing); DBFETCH(OWNER(thing))->sp.player.pennies += DBFETCH(thing)->sp.thing.value; DBDIRTY(OWNER(thing)); for(first = DBFETCH(thing)->exits; first != NOTHING; first = rest) { rest = DBFETCH(first)->next; if(DBFETCH(first)->location == NOTHING || DBFETCH(first)->location == thing) recycle(player, first); } notify_except(DBFETCH(thing)->contents, NOTHING, "This place shakes and crumbles..."); break; case TYPE_EXIT: DBFETCH(OWNER(thing))->sp.player.pennies += EXIT_COST; if(DBFETCH(thing)->sp.exit.ndest != 0) DBFETCH(OWNER(thing))->sp.player.pennies += LINK_COST; DBDIRTY(OWNER(thing)); break; case TYPE_PROGRAM: sprintf(buf, "muf/%d.m", (int) thing); unlink(buf); break; } for(rest = 0; rest < db_top; rest++) { switch(Typeof(rest)) { case TYPE_ROOM: if(DBFETCH(rest)->link == thing) DBFETCH(rest)->link = NOTHING; if(DBFETCH(rest)->exits == thing) DBFETCH(rest)->exits = DBFETCH(thing)->next; if(OWNER(rest) == thing) OWNER(rest) = GOD; break; case TYPE_THING: if(DBFETCH(rest)->link == thing) { if(DBFETCH(OWNER(rest))->link == thing) DBSTORE(OWNER(rest), link, PLAYER_START); DBFETCH(rest)->link = DBFETCH(OWNER(rest))->link; } if(DBFETCH(rest)->exits == thing) DBFETCH(rest)->exits = DBFETCH(thing)->next; if(OWNER(rest) == thing) OWNER(rest) = GOD; break; case TYPE_EXIT: { int i, j; for (i = j = 0; i < DBFETCH(rest)->sp.exit.ndest; i++) { if((DBFETCH(rest)->sp.exit.dest)[i] != thing) (DBFETCH(rest)->sp.exit.dest)[j++] = (DBFETCH(rest)->sp.exit.dest)[i]; } if(j < DBFETCH(rest)->sp.exit.ndest) { DBFETCH(OWNER(rest))->sp.player.pennies += LINK_COST; DBDIRTY(OWNER(rest)); DBFETCH(rest)->sp.exit.ndest = j; } } if(OWNER(rest) == thing) OWNER(rest) = GOD; break; case TYPE_PLAYER: if(DBFETCH(rest)->link == thing) DBFETCH(rest)->link = PLAYER_START; if(DBFETCH(rest)->exits == thing) DBFETCH(rest)->exits = DBFETCH(thing)->next; if(DBFETCH(rest)->curr_prog == thing) DBFETCH(rest)->curr_prog = 0; break; case TYPE_PROGRAM: if(OWNER(rest) == thing) OWNER(rest) = GOD; break; case TYPE_DAEMON: if(OWNER(rest) == thing) OWNER(rest) = GOD; } if(DBFETCH(rest)->location == thing) DBFETCH(rest)->location = NOTHING; if(DBFETCH(rest)->contents == thing) DBFETCH(rest)->contents = DBFETCH(thing)->next; if(DBFETCH(rest)->next == thing) DBFETCH(rest)->next = DBFETCH(thing)->next; DBDIRTY(rest); } for(first = DBFETCH(thing)->contents; first != NOTHING; first = rest) { rest = DBFETCH(first)->next; if(Typeof(first) == TYPE_PLAYER) enter_room(first, HOME, DBFETCH(thing)->location); else moveto(first, HOME); } moveto(thing, NOTHING); --depth; db_free_object(thing); db_clear_object(thing); NAME(thing) = "<garbage>"; add_attr(thing, "Desc", "<recyclable>"); DBFETCH(thing)->key = TRUE_BOOLEXP; FLAGS(thing) = TYPE_GARBAGE; DBFETCH(thing)->next = recyclable; recyclable = thing; DBDIRTY(thing); } #endif /* RECYCLE */ void do_enter(dbref player, const char *thing) { dbref what; dbref where = DBFETCH(player) -> location; struct match_data md; init_match(player,thing,TYPE_THING,&md); match_neighbor(&md); if(Arch(player)) { match_absolute(&md); match_player(&md); } what = noisy_match_result(&md); if(!is_ok(what)) return; if(((FLAGS(what)&JUMP_OK) || controls(player,what)) && (Typeof(what)==TYPE_PLAYER || Typeof(what)==TYPE_THING)) { if(get_attr(what,"Enter")) notify(player, get_attr(what,"Enter")); if(get_attr(what,"Oenter") && !Dark(player)) { sprintf(buf,"%%n %s", get_attr(what,"Oenter")); notify_except(DBFETCH(where)->contents, player, pronoun_substitute(player, buf, what)); } moveto(player, what); if(get_attr(what, "Aenter")) trigobj(what, get_attr(what, "Aenter"), player); } else { notify(player,"Permission denied."); } } void do_leave(dbref player) { dbref where = DBFETCH(player)->location; if(Typeof(where) == TYPE_ROOM) notify(player, "You can't leave."); else { if(get_attr(where, "Leave")) notify(player, get_attr(where, "Enter")); if(get_attr(where, "Oleave") && !Dark(player)) { sprintf(buf, "%%n %s", get_attr(where, "Oleave")); notify_except(DBFETCH(where)->contents, player, pronoun_substitute(player, buf, where)); } moveto(player, DBFETCH(where)->location); if(get_attr(where, "Aleave")) trigobj(where, get_attr(where, "Aleave"), player); } }