/* This software is Copyright 1989, 1990, 1992, 1993 by various individuals. Please see the accompanying file COPYRIGHT for details. */ #include #include #include "db.h" #include "defs.h" #include "match.h" #include "money.h" #include "params.h" #include "config.h" #include "random.h" #include "externs.h" #include "strings.h" #include "property.h" #include "interface.h" void moveto(dbref what, dbref where) { dbref loc; /* remove what from old loc */ loc = DBFETCH(what)->location; if (loc != NOTHING) { DBSTORE(loc,contents,remove_first(DBFETCH(loc)->contents,what)); } /* test for special cases */ switch(where) { case NOTHING: DBSTORE(what,location,NOTHING); return; break; case HOME: switch(Typeof(what)) { case TYPE_PLAYER: where = DBFETCH(what)->sp.player.home; break; case TYPE_THING: where = DBFETCH(what)->sp.thing.home; break; case TYPE_ROOM: where = GLOBAL_ENVIRONMENT; break; case TYPE_PROGRAM: case TYPE_DAEMON: where = OWNER(what); break; } break; } /* now put what in where */ PUSH(what,DBFETCH(where)->contents); DBDIRTY(where); DBSTORE(what,location,where); } void send_contents(dbref loc, dbref dest) { dbref first; dbref rest; 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) && (Typeof(first) != TYPE_DAEMON)) { moveto(first, loc); } else { moveto(first, (FLAGS(first) & STICKY) ? HOME : dest); } first = rest; } DBSTORE(loc, contents, reverse(DBFETCH(loc)->contents)); } static void maybe_dropto(dbref loc, dbref dropto) { dbref thing; if (loc == dropto) return; /* Is anyone left in the room? */ DOLIST(thing,DBFETCH(loc)->contents) if (Typeof(thing) == TYPE_PLAYER) return; /* no players, send everything to the dropto */ switch (Typeof(dropto)) { case TYPE_PROGRAM: run_dropto(loc,dropto,NOTHING); break; case TYPE_ROOM: case TYPE_PLAYER: send_contents(loc,dropto); break; } } int parent_loop_check(dbref source, dbref dest) { if (source == dest) return 1; /* That's an easy one! */ if (dest == NOTHING) return 0; if (Typeof(dest) != TYPE_ROOM) return 0; return parent_loop_check(source, DBFETCH(dest)->location); } static double useval_scale(unsigned long int seconds) { return(pow(USEVAL_SCALE,seconds)); } static char *exit_primary_name(dbref xit) { char *xn; char *dp; char *rv; xn = NAME(xit); if (FLAGS(xit) & FULLNAME) return(dup_string(xn)); dp = index(xn,EXIT_DELIMITER); if (! dp) return(dup_string(xn)); rv = malloc((dp-xn)+1); bcopy(xn,rv,dp-xn); rv[dp-xn] = '\0'; return(rv); } const char *find_default_msg(const char *def, dbref xit, const char *proppart) { int ppl; char *xn; int xnl; dbref room; static void *tmp = 0; static int tmplen = -1; if (Typeof(xit) != TYPE_EXIT) return(def?dup_string(def):0); if (Dark(xit)) return(0); xn = exit_primary_name(xit); ppl = strlen(proppart); xnl = strlen(xn); if (tmplen < ppl+xnl) { if (tmp == 0) register_free(&tmp); tmplen = ppl + xnl; free(tmp); tmp = malloc(6+ppl+1+xnl+1); } sprintf(tmp,"_auto-%s-%s",proppart,xn); free(xn); room = DBFETCH(xit)->location; while (room != NOTHING) { struct propref *pr; pr = lookup_property(room,tmp,0); if (pr) { if ((propref_get_attr(pr) & PATTR_TYPE) == PTYPE_STRING) { char *p; p = propref_get_string(pr); propref_done(pr); return(p); } propref_done(pr); } room = DBFETCH(room)->location; } return(dup_string(def)); } int no_autolook(dbref loc, dbref player) { #ifdef ENABLE_NO_AUTOLOOK char tmp[64]; struct propref *pr; PROPATTR mask; dbref own; PROPATTR attr; own = OWNER(loc); mask = PPERM_PICKWHO(God(loc),Wizard(loc),Royalty(loc),1) & PPERM_HOW_C & PPERM_BIT_X; pr = lookup_property(loc,"_no-autolook",0); if (pr) { attr = propref_get_attr(pr); propref_done(pr); if (attr & mask) { pr = lookup_property(loc,"_no-autolook-*",0); if (pr) { attr = propref_get_attr(pr); propref_done(pr); if (attr & mask) return(1); } sprintf(&tmp[0],"_no-autolook-%d",(int)player); pr = lookup_property(loc,&tmp[0],0); if (pr) { attr = propref_get_attr(pr); propref_done(pr); if (attr & mask) return(1); } } } #endif return(0); } 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)->sp.player.home,DBFETCH(thing)->location); break; case TYPE_THING: moveto(thing,DBFETCH(thing)->sp.thing.home); break; case TYPE_PROGRAM: case TYPE_DAEMON: moveto(thing,OWNER(thing)); break; } } int can_move(dbref player, const char *direction) { struct match_data md; init_match(player,direction,TYPE_EXIT,&md); match_all_exits(&md); return(last_match_result(&md)!=NOTHING); } static void generate_drop_message(dbref player, dbref xit) { if (DBFETCH(xit)->drop_message != NOSTR) { exec_or_notify_str(player,xit,DBFETCH(xit)->drop_message); } else { const char *def; def = find_default_msg(0,xit,"drop"); if (def) { exec_or_notify(player,xit,def); cfree(def); } } } /* * trigger() * * This procedure triggers a series of actions, or meta-actions * which are contained in the 'dest' field of the exit. * Locks are ignored; meta-links' locks are defined to be ignored, * and locks on directly-triggered exits must be checked before * calling this routine. * * `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. * `arg' is the string after the exit name on the command line. * `cmd' is the exit name from the command line. */ void trigger(dbref player, dbref xit, int pflag, const char *arg, const char *cmd) { int i; dbref dest; dbref sobjact; SERIAL sobjser; int succ; SERIAL xser; SERIAL pser; char buf[BUFFER_LEN]; sobjact = NOTHING; succ = 0; if (Typeof(xit) != TYPE_EXIT) return; xser = DBFETCH(xit)->serial; pser = DBFETCH(player)->serial; if ((FLAGS(xit) & INDIVIDUAL) && (DBFETCH(xit)->sp.exit.ndest > 1)) { i = rnd(DBFETCH(xit)->sp.exit.ndest); } else { i = 0; } while (i < DBFETCH(xit)->sp.exit.ndest) { dest = DBFETCH(xit)->sp.exit.dest[i]; if (dest == HOME) dest = DBFETCH(player)->sp.player.home; if (dest == BUILTIN) { strcpy(&buf[0],arg); /* XXX */ /* ignore errors (eg, unknown command names) */ do_builtin_command(player,DBFETCH(xit)->name,&buf[0]); continue; } switch (Typeof(dest)) { case TYPE_ROOM: if (pflag) { generate_drop_message(player,xit); if ( (DBFETCH(xit)->serial != xser) || (DBFETCH(player)->serial != pser) ) return; if ((DBFETCH(xit)->odrop != NOSTR) && !Dark(player)) { sprintf(buf,"%s %s",NAME(player),pronoun_substitute_str(player,DBFETCH(xit)->odrop)); notify_except(player,dest,player,buf); } enter_room(player,dest,xit); if ( (DBFETCH(xit)->serial != xser) || (DBFETCH(player)->serial != pser) ) return; succ = 1; } break; case TYPE_THING: if (Typeof(DBFETCH(xit)->location) == TYPE_THING) { moveto(dest,DBFETCH(DBFETCH(xit)->location)->location); if (! (FLAGS(xit) & STICKY)) { /* send home source object */ sobjact = DBFETCH(xit)->location; sobjser = DBFETCH(sobjact)->serial; } } else { moveto(dest,DBFETCH(xit)->location); } if (DBFETCH(xit)->succ_message != NOSTR) succ = 1; break; case TYPE_EXIT: /* It's a meta-link(tm)! */ trigger(player,DBFETCH(xit)->sp.exit.dest[i],0,arg,cmd); if ( (DBFETCH(xit)->serial != xser) || (DBFETCH(player)->serial != pser) ) return; if (DBFETCH(xit)->succ_message != NOSTR) succ = 1; break; case TYPE_PLAYER: if (pflag && (DBFETCH(dest)->location != NOTHING)) { succ = 1; if (! (FLAGS(dest) & JUMP_OK)) { notify(player,"That player does not wish to be disturbed."); } else if (no_teleport_zone(DBFETCH(player)->location)) { notify(player,"You are in a no-teleport zone."); } else if (no_teleport_zone(DBFETCH(dest)->location)) { notify(player,"That would enter a no-teleport zone."); } else { generate_drop_message(player,xit); if ( (DBFETCH(xit)->serial != xser) || (DBFETCH(player)->serial != pser) ) return; if ((DBFETCH(xit)->odrop != NOSTR) && !Dark(player)) { sprintf(buf,"%s %s",NAME(player),pronoun_substitute_str(player,DBFETCH(xit)->odrop)); notify_except(player,DBFETCH(dest)->location,player,buf); } enter_room(player,DBFETCH(dest)->location,xit); if ( (DBFETCH(xit)->serial != xser) || (DBFETCH(player)->serial != pser) ) return; } } break; case TYPE_PROGRAM: interp(player,dest,xit,arg,makeinst(PROG_STRING,dup_string(cmd))); if ( (DBFETCH(xit)->serial != xser) || (DBFETCH(player)->serial != pser) ) return; succ = 1; break; } if (FLAGS(xit) & INDIVIDUAL) break; i ++; } if ((sobjact != NOTHING) && (DBFETCH(sobjact)->serial == sobjser)) send_home(sobjact); if (succ && !pflag && (FLAGS(xit) & NOISY) && DBFETCH(xit)->succ_message) { exec_or_notify_str(player,xit,DBFETCH(xit)->succ_message); } if (!succ && pflag) notify(player,"Done."); } static void least_common_parent(dbref a, dbref b, void (*afn)(dbref, void *), void (*bfn)(dbref, void *), void * arg) { dbref d; int na; int nb; int n; int x; int i; static dbref *v = 0; static int vn = 0; if (a == b) return; if (DBFETCH(a)->location == DBFETCH(b)->location) /* common case */ { (*afn)(a,arg); (*bfn)(b,arg); return; } for (d=a,na=0;d!=NOTHING;na++,d=DBFETCH(d)->location) ; for (d=b,nb=0;d!=NOTHING;nb++,d=DBFETCH(d)->location) ; n = na + nb; if (n > vn) { free(v); vn = n; v = malloc(vn*sizeof(dbref)); } for (d=a,x=0;d!=NOTHING;x++,d=DBFETCH(d)->location) v[x] = d; if (x != na) abort(); for (d=b;d!=NOTHING;x++,d=DBFETCH(d)->location) v[x] = d; if (x != n) abort(); for (x=1;(x<=na)&&(x<=nb)&&(v[na-x]==v[n-x]);x++) ; for (i=0;i<=na-x;i++) (*afn)(v[i],arg); for (i=nb-x;i>=0;i--) (*bfn)(v[na+i],arg); } static void trigger_enterleave(dbref rm, dbref who, const char *name) { dbref x; if (Typeof(rm) != TYPE_ROOM) return; DOLIST(x,DBFETCH(rm)->sp.room.exits) { if (! strcmp(name,DBFETCH(x)->name)) { trigger(who,x,0,"",""); } } } static void trigger_leave(dbref rm, void *vv) { trigger_enterleave(rm,*(dbref *)(((void **)vv)[0]),".enterleave-leave"); } static void trigger_enter(dbref rm, void *vv) { trigger_enterleave(rm,*(dbref *)(((void **)vv)[0]),".enterleave-enter"); } void enter_room(dbref player, dbref loc, dbref xit) { dbref old; dbref dropto; char buf[BUFFER_LEN]; if (loc == HOME) loc = DBFETCH(player)->sp.player.home; 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) { /* maybe notify others in old room */ if ( !Dark(old) && !Dark(player) && ( (DBFETCH(xit)->osuccess == NOSTR) || (FLAGS(xit) & NOISY) ) ) { const char *msg; msg = find_default_msg("has left.",xit,"osucc"); if (msg) { sprintf(buf,"%s %s",NAME(player),pronoun_substitute(player,msg)); cfree(msg); notify_except(player,old,player,buf); } } /* handle enterleave triggers */ { void *vv[1]; dbref pcopy; pcopy = player; vv[0] = &pcopy; least_common_parent(old,loc,&trigger_leave,&trigger_enter,&vv[0]); } } /* go there */ moveto(player,loc); /* if old location has STICKY dropto, send stuff through it */ if (old != NOTHING) { dropto = DBFETCH(old)->sp.room.dropto; if ((dropto != NOTHING) && (FLAGS(old) & STICKY)) { maybe_dropto(old,dropto); } } /* maybe tell other folks in new location */ if ( !Dark(loc) && !Dark(player) && ( (DBFETCH(xit)->odrop == NOSTR) || (FLAGS(xit) & NOISY) ) ) { const char *msg; msg = find_default_msg("has arrived.",xit,"odrop"); if (msg) { sprintf(buf,"%s %s",NAME(player),pronoun_substitute(player,msg)); cfree(msg); notify_except(player,loc,player,buf); } } } if (! no_autolook(loc,player)) { SERIAL locser; SERIAL playser; locser = DBFETCH(loc)->serial; playser = DBFETCH(player)->serial; look_room(player,loc); if ( (DBFETCH(loc)->serial != locser) || (DBFETCH(player)->serial != playser) ) return; } /* penny check */ { struct object *l; unsigned long int now; l = DBFETCH(loc); now = curtm(); l->sp.room.useval *= useval_scale(now-l->sp.room.stamp); #if defined(PENNY_RATE) && (PENNY_RATE != 0) if ( !controls(player,loc) && #ifdef MAX_PENNIES (DBFETCH(player)->sp.player.pennies <= MAX_PENNIES) && #endif (rnd(PENNY_RATE) == 0) ) { if (frnd() < exp(-l->sp.room.useval)) { notify(player,FOUND); add_pennies(player,1,"found"); } } #endif l->sp.room.useval += USEVAL_INC; l->sp.room.stamp = now; DBDIRTY(loc); } } void do_move(dbref player, const char *direction) { dbref xit; dbref loc; struct match_data md; /* find the exit */ init_match_check_keys(player,direction,TYPE_EXIT,&md); match_all_exits(&md); xit = match_result(&md); switch (xit) { 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; #ifdef TIMESTAMPS DBFETCH(xit)->time_used = curtm(); #endif { SERIAL xitser; SERIAL playser; const char *deffail; xitser = DBFETCH(xit)->serial; playser = DBFETCH(player)->serial; deffail = (DBFETCH(xit)->fail_message == NOSTR) ? find_default_msg("You can't go that way.",xit,"fail") : 0; if ( (can_doit(player,xit,deffail)) && (DBFETCH(player)->serial == playser) && (DBFETCH(xit)->serial == xitser) ) { char *tmp; tmp = malloc(md.match_cmdlen+1); bcopy(direction,tmp,md.match_cmdlen); tmp[md.match_cmdlen] = '\0'; trigger(player,xit,1,md.match_args,tmp); free(tmp); } if (deffail) cfree(deffail); } break; } } void do_get(dbref player, const char *what) { dbref thing; char buf[BUFFER_LEN]; struct match_data md; init_match_check_keys(player,what,TYPE_THING,&md); match_neighbor(&md); if (Royalty(player)) match_absolute(&md); thing = noisy_match_result(&md); if (thing != NOTHING) { #ifdef TIMESTAMPS DBFETCH(thing)->time_used = curtm(); #endif if (DBFETCH(thing)->location == player) { notify(player,"You already have that!"); return; } switch (Typeof(thing)) { case TYPE_THING: case TYPE_PROGRAM: case TYPE_DAEMON: { SERIAL thingser; SERIAL playser; thingser = DBFETCH(thing)->serial; playser = DBFETCH(player)->serial; if ( (can_doit(player,thing,"You can't pick that up.")) && (DBFETCH(thing)->serial == thingser) && (DBFETCH(player)->serial == playser) ) { notify(player,"Taken."); if (DBFETCH(thing)->osuccess == NOSTR) { sprintf(&buf[0],"%s picks up %s.",NAME(player),NAME(thing)); notify_except(player,getloc(player),player,&buf[0]); } moveto(thing,player); } } 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; loc = getloc(player); if (loc == NOTHING) return; init_match(player,name,NOTYPE,&md); match_possession(&md); thing = noisy_match_result(&md); if ((thing == NOTHING) || (thing == AMBIGUOUS)) return; #ifdef TIMESTAMPS DBFETCH(thing)->time_used = curtm(); #endif switch (Typeof(thing)) { case TYPE_THING: case TYPE_PROGRAM: case TYPE_DAEMON: 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 { if ( (DBFETCH(loc)->sp.room.dropto != NOTHING) && !(FLAGS(loc) & STICKY) ) { switch (Typeof(DBFETCH(loc)->sp.room.dropto)) { case TYPE_PROGRAM: moveto(thing,loc); run_dropto(loc,DBFETCH(loc)->sp.room.dropto,thing); break; case TYPE_ROOM: case TYPE_PLAYER: moveto(thing,DBFETCH(loc)->sp.room.dropto); break; } } else { moveto(thing,loc); } } if (DBFETCH(thing)->drop_message != NOSTR) { SERIAL thingser; SERIAL playser; thingser = DBFETCH(thing)->serial; playser = DBFETCH(player)->serial; exec_or_notify_str(player,thing,DBFETCH(thing)->drop_message); if ( (DBFETCH(thing)->serial != thingser) || (DBFETCH(player)->serial != playser) ) return; } else { notify(player,"Dropped."); } if (DBFETCH(loc)->drop_message != NOSTR) { SERIAL locser; SERIAL playser; locser = DBFETCH(loc)->serial; playser = DBFETCH(player)->serial; exec_or_notify_str(player,loc,DBFETCH(loc)->drop_message); if ( (DBFETCH(loc)->serial != locser) || (DBFETCH(player)->serial != playser) ) return; } if (DBFETCH(thing)->odrop != NOSTR) { sprintf(buf,"%s %s",NAME(player),pronoun_substitute_str(player,DBFETCH(thing)->odrop)); } else { sprintf(buf,"%s drops %s.",NAME(player),NAME(thing)); } notify_except(player,loc,player,buf); if (DBFETCH(loc)->odrop != NOSTR) { sprintf(buf,"%s %s",NAME(thing),pronoun_substitute_str(thing,DBFETCH(loc)->odrop)); notify_except(player,loc,player,buf); } break; default: notify(player,"You can't drop that."); break; } } void do_recycle(dbref player, const char *name) { dbref thing; struct match_data md; init_match(player,name,NOTYPE,&md); match_all_exits(&md); match_neighbor(&md); match_possession(&md); match_here(&md); match_absolute(&md); thing = noisy_match_result(&md); if (thing != NOTHING) { if (!controls(player,thing)) { notify(player,"Permission denied."); } else { if (OWNER(thing) != player) { notify( player, Royalty(player) ? "You must @chown it to yourself first." : "Permission denied." ); return; } switch (Typeof(thing)) { case TYPE_EXIT: case TYPE_ROOM: case TYPE_THING: case TYPE_PROGRAM: if (FLAGS(thing) & KEEP) { notify(player,"That may not be recycled."); return; } break; } switch (Typeof(thing)) { case TYPE_ROOM: if ((thing == PLAYER_START) || (thing == GLOBAL_ENVIRONMENT)) { notify(player,"This room may not be recycled."); return; } break; case TYPE_EXIT: if (!unset_source(player,DBFETCH(player)->location,thing)) return; break; case TYPE_THING: break; case TYPE_PLAYER: notify(player,"You can't recycle a player!"); return; break; case TYPE_PROGRAM: break; case TYPE_DAEMON: notify(player,"Use @kill to recycle a daemon"); return; break; case TYPE_GARBAGE: notify(player,"That's already garbage!"); return; } recycle(player,thing,0); notify(player,"Thank you for recycling."); } } }