#include #include #include #include #include #include "ai.h" #include "attack.h" #include "defs.h" #include "flag.h" #include "io.h" #include "lf.h" #include "map.h" #include "move.h" #include "nexus.h" #include "objects.h" #include "spell.h" #include "text.h" extern lifeform_t *player; extern int statdirty; extern int needredraw; extern enum GAMEMODE gamemode; extern void (*precalclos)(lifeform_t *); extern enum ERROR reason; extern void *rdata; extern long curtime; extern WINDOW *gamewin, *msgwin; int canandwillmove(lifeform_t *lf, int dir, enum ERROR *error) { if (isplayer(lf)) { if (ispossiblemove(lf, dir)) { return B_TRUE; } } else { if (ispossiblemove(lf, dir) && willmove(lf, dir, error)) { return B_TRUE; } } return B_FALSE; } int isorthogonal(int dir) { switch (dir) { case D_N: case D_E: case D_S: case D_W: case DC_N: case DC_E: case DC_S: case DC_W: return B_TRUE; } return B_FALSE; } // ie. moving into a wall isn't possible // moving into a lf/door IS possible since you will attack/open it // moving while grabbed IS possible since you'll try to break free int ispossiblemove(lifeform_t *lf, int dir) { enum ERROR error; if (moveclear(lf, dir, &error)) { return B_TRUE; } else { object_t *inway = NULL; switch (error) { case E_OFFMAP: if (lf->cell->map->region == RG_WORLDMAP) { return B_TRUE; } break; case E_CANTMOVE: case E_GRABBEDBY: case E_TOOHEAVY: case E_LFINWAY: return B_TRUE; case E_DOORINWAY: if (canopendoors(lf)) { return B_TRUE; } break; case E_OBINWAY: inway = (object_t *)rdata; if (inway && ispushable(inway)) { // player can always try pushing... if (isplayer(lf)) { return B_TRUE; } else { // for ai, move is only 'possible' if // we are strong enough to push the object. if (canpush(lf, inway, dir)) { return B_TRUE; } } } break; default: break; } } return B_FALSE; } // lf is the one moving, lf2 is the one who is being forced to swap int canswapwith(lifeform_t *lf, lifeform_t *lf2) { // player can never be forced to swap if (isplayer(lf2)) { return B_FALSE; } // mosnters don't swap with people who have F_NOSWAP. if (!isplayer(lf) && lfhasflag(lf2, F_NOSWAP)) { return B_FALSE; } // cannot swap if lf's cell is dangerous to lf2 if (celldangerous(lf2, lf->cell, B_FALSE, NULL)) { return B_FALSE; } // cannot swap with sleeping lfs if (lfhasflag(lf2, F_ASLEEP)) { return B_FALSE; } // allies can always swap if (areallies(lf, lf2)) { return B_TRUE; } if (isplayer(lf) && !areenemies(lf, lf2)) { // player can swap with peaceful lgs // if they are a lot smaller if (getlfsize(lf) - getlfsize(lf2) >= 2) { return B_TRUE; } } return B_FALSE; } // will populate rdata // onlyifknown = true means "check for _known_ dangerous objects" // onlyifknown = false means "check for _any dangerous objects, doesn't matter if we know about them" int celldangerous(lifeform_t *lf, cell_t *cell, int onlyifknown, enum ERROR *error) { enum ATTRBRACKET wis; int include_nonobvious = B_FALSE; flag_t *f; object_t *o; // default if (error) { *error = E_OK; rdata = NULL; } // never dangerous if there's someone there, since we'll // attack them instead of moving! if (cell->lf) { return B_FALSE; } // obvious things that you can see if (!onlyifknown || (haslos(lf, cell) && !lfhasflag(lf, F_UNDEAD))) { // water needing creature out of water? if (lfhasflag(lf, F_NEEDSWATER)) { if (!hasobwithflag(cell->obpile, F_DEEPWATER)) { if (error) { *error = E_DANGEROUS; } return B_TRUE; } } for (o = cell->obpile->first ; o ; o = o->next) { if (onlyifknown && !canseeob(lf, o)) continue; if (hasflag(o->flags, F_TRAP)) { if (error) { *error = E_AVOIDOB; rdata = o; } return B_TRUE; } f = hasflag(o->flags, F_PIT); if (f && (f->val[0] == D_DOWN)) { if (!isairborne(lf)) { if (error) { *error = E_AVOIDOB; rdata = o; } return B_TRUE; } } f = hasflag(o->flags, F_DEEPWATER); if (f) { // non swimming creature in water? if (!isairborne(lf) && (getobdepth(o, lf) >= DP_HEAD)) { if (getskill(lf, SK_SWIMMING) - isburdened(lf) <= 0) { if (error) { *error = E_AVOIDOB; rdata = o; } return B_TRUE; } } } f = obrestrictsmovement(o, lf); if (f) { // always avoid if possible if (error) { *error = E_AVOIDOB; rdata = o; } return B_TRUE; } f = hasflag(o->flags, F_WALKDAM); if (f) { if ((f->val[0] != DT_WATER) || isvulnto(lf->flags, DT_WATER)) { // are we immune to this? if (!lfhasflagval(lf, F_DTIMMUNE, f->val[0], NA, NA, NULL)) { if (error) { *error = E_AVOIDOB; rdata = o; } return B_TRUE; } } } } } // non-obvious things - only include these if we are smart enough if (!onlyifknown) { include_nonobvious = B_TRUE; } else { wis = getattrbracket(getattr(lf, A_WIS), A_WIS, NULL); if ((wis >= AT_AVERAGE) && haslos(lf, cell)) { if (!lfhasflag(lf, F_UNDEAD)) { include_nonobvious = B_TRUE; } } } if (include_nonobvious) { for (o = cell->obpile->first ; o ; o = o->next) { // don't walk on sharp objects without boots if (hasflag(o->flags, F_SHARP)) { if (!getequippedob(lf->pack, BP_FEET)) { if (error) { *error = E_AVOIDOB; rdata = o; } return B_TRUE; } } } } return B_FALSE; } int cellwalkable(lifeform_t *lf, cell_t *cell, enum ERROR *error) { object_t *o; // default if (error) { *error = E_OK; rdata = NULL; } if (!cell) { if (error) *error = E_OFFMAP; return B_FALSE; } if (cell->type->solid) { // mover is noncorporeal? if (lf && lfhasflag(lf, F_NONCORPOREAL)) { if (error) *error = E_WALLINWAY; // ok but still set reason // ok } else if (cell->lf && (cell->lf != lf)) { // someone (else) in the wall? if (error) *error = E_LFINWAY; // ok but still set reason //return B_FALSE; // ok } else { if (error) *error = E_WALLINWAY; return B_FALSE; } } // must check for lf before checking for impassable objects, // so that we are able to attack monsters embedded in walls. if (cell->lf && (cell->lf != lf)) { // usually can't attack while swimming if (lf && isswimming(lf) && (getskill(lf, SK_SWIMMING) <= PR_EXPERT)) { if (!lfhasflag(lf, F_AQUATIC)) { if (error) *error = E_SWIMMING; return B_FALSE; } } if (error) *error = E_LFINWAY; return B_FALSE; } for (o = cell->obpile->first ; o ; o = o->next) { if (isimpassableob(o, lf)) { if (lf) { enum MATERIAL mid; mid = getlfmaterial(lf); if ((mid == MT_GAS) || (mid == MT_SLIME)) { // ok } else if (lfhasflag(lf, F_NONCORPOREAL) && !hasflag(o->flags, F_REALLYIMPASSABLE)) { // ok but still set error *error = E_OBINWAY; } else { // not ok rdata = o; if (error) { if (isdoor(o, NULL)) { if (hasflag(o->flags, F_SECRET)) { *error = E_WALLINWAY; } else { *error = E_DOORINWAY; } } else { *error = E_OBINWAY; } } return B_FALSE; } } else { rdata = o; if (error) { if (isdoor(o, NULL)) { *error = E_DOORINWAY; } else { *error = E_OBINWAY; } } return B_FALSE; } } } return B_TRUE; } int diropposite(int dir) { switch (dir) { case D_N: return D_S; case D_E: return D_W; case D_S: return D_N; case D_W: return D_E; case DC_N: return DC_S; case DC_NE: return DC_SW; case DC_E: return DC_W; case DC_SE: return DC_NW; case DC_S: return DC_N; case DC_SW: return DC_NE; case DC_W: return DC_E; case DC_NW: return DC_SE; case D_UP: return D_DOWN; case D_DOWN: return D_UP; } // should never happen! return dir; } // restonfail means "if we can't find any valid moves, just rest" // returns true on error int dorandommove(lifeform_t *lf, int badmovesok, int restonfail) { int dir; int tries = 0; int moveok; enum ERROR why; // find a valid direction dir = getrandomdir(DT_COMPASS); moveok = canandwillmove(lf, dir, &why); if (!moveok && badmovesok) { switch (why) { // actually okay to move into someone case E_WALLINWAY: case E_LFINWAY: moveok = B_TRUE; break; default: break; } } while (!moveok) { // try next direction... if (++dir > DC_NW) dir = DC_N; if (++tries >= MAXDIR_COMPASS) { if (restonfail) { rest(lf, B_TRUE); } return B_TRUE; } // check this direction... moveok = canandwillmove(lf, dir, &why); if (!moveok && badmovesok) { switch (why) { case E_WALLINWAY: case E_LFINWAY: moveok = B_TRUE; break; default: break; } } } return trymove(lf, dir, B_TRUE); } // src is where something is // dst is what we are going away from // wantcheck is whether to check for dangerous things before considering a direction valid int getdiraway(cell_t *src, cell_t *dst, lifeform_t *srclf, int wantcheck, int dirtype, int keepinlof) { int d; cell_t *c; int maxdist=-1,bestdir=D_NONE; int dist[MAXDIR_COMPASS]; int poss[MAXDIR_COMPASS]; int nposs; enum ERROR error; if (dirtype == DT_ORTH) { maxdist = getcelldistorth(src, dst); } else { maxdist = getcelldist(src, dst); } for (d = DC_N; d <= DC_NW; d++) { dist[d - DC_N] = -1; } for (d = DC_N; d <= DC_NW; d++) { int thisdist = -1; int ok = B_FALSE; c = getcellindir(src, d); if (!c) continue; if (c == dst) { // destination is the thing we're fleeing from! thisdist = 0; } else { if (wantcheck) { if (srclf) { if (canandwillmove(srclf, d, &error)) { ok = B_TRUE; } else if (error == E_DOORINWAY) { ok = B_TRUE; } } else { ok = B_TRUE; } } else { if (srclf) { if (cellwalkable(srclf, c, &error)) { ok = B_TRUE; } else if (error == E_DOORINWAY) { ok = B_TRUE; } } else { ok = B_TRUE; } } if (keepinlof) { if (!haslof(c, dst, LOF_NEED, NULL)) { ok = B_FALSE; } } if (ok) { if (dirtype == DT_ORTH) { thisdist = getcelldistorth(c, dst); } else { thisdist = getcelldist(c, dst); } } else { thisdist = -1; } } dist[d - DC_N] = thisdist; if (thisdist >= maxdist) { maxdist = thisdist; bestdir = d; } } nposs = 0; for (d = DC_N; d <= DC_NW; d++) { if (dist[d - DC_N] != -1) { if (dist[d - DC_N] == maxdist) { poss[nposs] = d; nposs++; } } } if (nposs <= 0) { return D_NONE; } bestdir = poss[rnd(0,nposs-1)]; reason = E_OK; return bestdir; } int getdirtowards(cell_t *src, cell_t *dst, lifeform_t *srclf, int wantcheck, int dirtype) { int d; cell_t *c; int mindist=9999,bestdir=D_NONE; int dist[MAXDIR_COMPASS]; int poss[MAXDIR_COMPASS]; int nposs; enum ERROR error; if (dirtype == DT_ORTH) { mindist = getcelldistorth(src, dst); } else { mindist = getcelldist(src, dst); } for (d = DC_N; d <= DC_NW; d++) { dist[d - DC_N] = -1; } for (d = DC_N; d <= DC_NW; d++) { int ok = B_FALSE; int thisdist; c = getcellindir(src, d); if (!c) continue; if (c == dst) { dist[d - DC_N] = 0; mindist = 0; break; } if (wantcheck) { if (srclf) { if (canandwillmove(srclf, d, &error)) { ok = B_TRUE; } else if (error == E_DOORINWAY) { ok = B_TRUE; } } else { ok = B_TRUE; } } else { if (srclf) { if (cellwalkable(srclf, c, &error)) { ok = B_TRUE; } else if (error == E_DOORINWAY) { ok = B_TRUE; } } else { ok = B_TRUE; } } // get distance if (ok) { if (dirtype == DT_ORTH) { thisdist = getcelldistorth(c, dst); } else { thisdist = getcelldist(c, dst); } if (thisdist <= mindist) { dist[d - DC_N] = thisdist; mindist = thisdist; } else { // don't move AWAY from them. dist[d - DC_N] = -1; } } else { dist[d - DC_N] = -1; // ie. invalid } } // handle ties nposs = 0; for (d = DC_N; d <= DC_NW; d++) { if (dist[d - DC_N] != -1) { if (dist[d - DC_N] == mindist) { poss[nposs] = d; nposs++; } } } if (nposs <= 0) { return D_NONE; } bestdir = poss[rnd(0,nposs-1)]; reason = E_OK; return bestdir; } int getwalkoffdir(lifeform_t *lf, int dir) { switch (dir) { case DC_NE: if (lf->cell->y == 0) { return D_N; } else if ( lf->cell->x == (lf->cell->map->w-1)) { return D_E; } break; case DC_SE: if (lf->cell->y == (lf->cell->map->h-1)) { return D_S; } else if ( lf->cell->x == (lf->cell->map->w-1)) { return D_E; } break; case DC_SW: if (lf->cell->y == (lf->cell->map->h-1)) { return D_S; } else if ( lf->cell->x == 0) { return D_W; } break; case DC_NW: if (lf->cell->y == 0) { return D_N; } else if ( lf->cell->x == 0) { return D_W; } break; } return D_NONE; } // use 'n/a' for zero chance of falling. 0 means 'calculate based on distance' int knockback(lifeform_t *lf, int dir, int howfar, lifeform_t *pusher, int fallcheckdiff) { int i; char lfname[BUFLEN]; char newlfname[BUFLEN]; int seen; int mightfall = B_TRUE; lifeform_t *newlf; if (lfhasflag(lf, F_GRAVLESSENED)) { howfar *= 2; } else if (lfhasflag(lf, F_GRAVBOOSTED)) { howfar /= 2; if (howfar < 0) howfar = 0; } // calculate chance of falling if (fallcheckdiff == 0) { // chance based on distance fallcheckdiff = howfar*10; } getlfname(lf,lfname); if (cansee(player, lf)) { seen = B_TRUE; } else { seen = B_FALSE; } if (dir == D_NONE) { // failed! return B_TRUE; } // if levitating (not flying), knocked back further. if (lfhasflag(lf, F_LEVITATING)) { howfar *= 2; } breakgrabs(lf, B_TRUE, B_TRUE); for (i = 0; i < howfar; i++) { if (moveclear(lf, dir, &reason)) { if ((i == 0) && seen) { msg("%s %s knocked backwards!",lfname,is(lf)); } trymove(lf, dir, B_FALSE); } if (reason != E_OK) { char buf[BUFLEN]; char thing[BUFLEN]; cell_t *newcell; newcell = getcellindir(lf->cell, dir); // failed to move switch (reason) { case E_OFFMAP: if (lf->cell->map->region == RG_WORLDMAP) { if (!walkoffmap(lf, dir, B_FALSE)) { // successful break; } } // fall through case E_WALLINWAY: case E_OBINWAY: case E_DOORINWAY: if (reason == E_WALLINWAY) { if (newcell) strcpy(thing, newcell->type->name); else strcpy(thing, "wall"); } else { // ie door or object getobname(rdata, thing, 1); } if (seen) msg("%s slam%s into %s!",lfname,isplayer(lf) ? "" : "s", thing); snprintf(buf, BUFLEN, "slamming into %s", thing); losehp(lf, rnd(1,6), DT_BASH, pusher, buf); // stop moving i = howfar; // don't fall mightfall = B_FALSE; if (onein(3)) criticalhit(NULL, lf, getrandomcorebp(lf), DT_BASH); break; case E_SWIMMING: case E_LFINWAY: newcell = getcellindir(lf->cell, dir); newlf = newcell->lf; if (newlf) { // should always be true int momentumleft; getlfname(newlf, newlfname); if (seen) msg("%s slam%s into %s!",lfname,isplayer(lf) ? "" : "s",newlfname); // remember our momentum momentumleft = howfar - i; // stop moving i = howfar; // higher chance of both falling if (fallcheckdiff != NA) { fallcheckdiff += (momentumleft*5); } // confer our remaining momentum on to them knockback(newcell->lf, dir, momentumleft, lf, fallcheckdiff); // NOTE: recursive call } break; default: break; } } } if (fallcheckdiff == NA) { mightfall = B_FALSE; } if (mightfall) { // save to avoid falling if (!skillcheck(lf, SC_FALL, fallcheckdiff, 0)) { fall(lf, NULL, B_TRUE); } } return B_FALSE; } int makeorthogonal(int dir) { switch (dir) { case DC_N: case D_N: return D_N; case DC_E: case D_E: return D_E; case DC_S: case D_S: return D_S; case DC_W: case D_W: return D_W; } return D_NONE; } // see 'movetowards' for description of dirtype int moveawayfrom(lifeform_t *lf, cell_t *dst, int dirtype, int keepinlof ) { int dir; int rv = B_TRUE; if (isblind(lf)) { dorandommove(lf, B_TRUE, B_TRUE); return B_FALSE; } // move away from them dir = getdiraway(lf->cell, dst, lf, B_TRUE, dirtype, keepinlof); if (dir == D_NONE) { rv = B_TRUE; } else { rv = trymove(lf, dir, B_TRUE); } return rv; } // ie. is the destination cell free? int moveclear(lifeform_t *lf, int dir, enum ERROR *error) { cell_t *cell; flag_t *f; // default if (error) { *error = E_OK; rdata = NULL; } if (isburdened(lf) >= BR_OVERLOADED) { if (error) *error = E_TOOHEAVY; return B_FALSE; } // check if we are paralyzed, frozen, etc if (isimmobile(lf)) { if (error) *error = E_CANTMOVE; return B_FALSE; } cell = getcellindir(lf->cell, dir); if (!cell) { if (error) *error = E_OFFMAP; return B_FALSE; } f = lfhasflag(lf, F_GRABBEDBY); if (f) { lifeform_t *lf2; lf2 = findlf(NULL, f->val[0]); if (lf2 && (lf2 != cell->lf)) { if (error) { rdata = lf2; *error = E_GRABBEDBY; } return B_FALSE; } } if ((lf->race->raceclass->id == RC_DEMON) && hasob(cell->obpile, OT_PENTAGRAM)) { *error = E_PENTAGRAM; return B_FALSE; } // not attacking if (lfhasflag(lf, F_DOESNTMOVE) && !cell->lf) { *error = E_CANTMOVE; return B_FALSE; } return cellwalkable(lf, cell, error); } // effects which happen after player moves. // IMPORTANT: don't modify lf's flagpile during this code! // particularly don't remove flags... // returns TRUE if we displayed a message int moveeffects(lifeform_t *lf) { flag_t *f; int didmsg = B_FALSE; if (lfhasflagval(lf, F_INJURY, IJ_HAMSTRUNG, NA, NA, NULL)) { if (!skillcheck(lf, SC_FALL, 20, 0)) fall(lf, NULL, B_TRUE); } if (isbleeding(lf)) { if (lfhasflagval(lf, F_INJURY, NA, BP_LEGS, DT_SLASH, NULL)) { bleed(lf, B_FALSE); losehp(lf, 1, DT_DIRECT, NULL, "blood loss"); } else { if (rnd(1,2) == 1) { bleed(lf, B_FALSE); } } } f = lfhasflag(lf, F_PAIN); if (f) { if (!isdrunk(lf)) { int dam; if (isplayer(lf)) { msg("Your body is wracked with pain!"); didmsg = B_TRUE; } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s convulses in pain!",lfname); didmsg = B_TRUE; } if (strlen(f->text)) { dam = roll(f->text); } else { dam = roll("1d2"); } losehp(lf, dam, f->val[0], NULL, "extreme pain"); if (isdead(lf)) return didmsg; } } return didmsg; } // returns TRUE if something happened int movelf(lifeform_t *lf, cell_t *newcell) { object_t *o,*nexto; char obname[BUFLEN],lfname[BUFLEN],buf[BUFLEN]; lifeform_t *l; int didmsg = B_FALSE; flag_t *f; int changedlev = B_FALSE; int preroom = -1, postroom = -1; int preshop = -1; int prespeed = B_FALSE, postspeed = B_FALSE; int prewater = B_FALSE; vault_t *v; assert(newcell); getlfname(lf, lfname); if (isplayer(lf) || cansee(player, lf)) { needredraw = B_TRUE; } if (newcell->map != lf->cell->map) { changedlev = B_TRUE; if (isplayer(lf)) { // remember the time which we exitted this map. lf->cell->map->lastplayervisit = curtime; } } // remember current cell + room id prespeed = getmovespeed(lf); preroom = getroomid(lf->cell); v = getcellvault(lf->cell); if (v && hasflag(v->flags, F_VAULTISSHOP)) { preshop = getroomid(lf->cell); } // getting out of water? if (hasobwithflag(lf->cell->obpile, F_DEEPWATER)) { prewater = B_TRUE; } // special effects when the player moves to a new map if (changedlev && isplayer(lf)) { object_t *o; long barrierid = -1; // mapentereffects will give all monster on the new map // a bunch of turns to simulate time passing while the player // was away. to prevent them from blocking off the staircase cell // where the player is about to arrive, place a magic barrier over it. o = addobfast(newcell->obpile, OT_MAGICBARRIER); if (o) barrierid = o->id; mapentereffects(newcell->map); // now remove the barrier o = hasobid(newcell->obpile, barrierid); if (o) killob(o); } // move out... lf->cell->lf = NULL; // if required, relink lifeform to new map if (newcell->map != lf->cell->map) { if (isplayer(lf)) { statdirty = B_TRUE; } relinklf(lf, newcell->map); if (isplayer(lf)) { // clear map to force redraw. wclear(gamewin); } } // remember previous cells lf->prevcell[1] = lf->prevcell[0]; lf->prevcell[0] = lf->cell; // update lifeform lf->cell = newcell; // nothing should be in new cell.. assert(!newcell->lf); // remember new room... postroom = getroomid(lf->cell); postspeed = getmovespeed(lf); // update new cell newcell->lf = lf; // update light if (lfproduceslight(lf)) { calclight(lf->cell->map); } precalclos(lf); if (isplayer(lf) || cansee(player, lf)) { needredraw = B_TRUE; } didmsg = moveeffects(lf); killflagsofid(lf->flags, F_HIDING); // remove grabs (but not attached things) // Note: only remove this from the person _being grabbed_. // if the grabb_er_ moves away, they'll drag the grabee with them. f = lfhasflag(lf, F_GRABBEDBY); if (f) { lifeform_t *grabber; grabber = findlf(NULL, f->val[0]); assert(grabber); if (getcelldist(lf->cell, grabber->cell) > 1) { killflagsofid(grabber->flags, F_GRABBING); killflagsofid(lf->flags, F_GRABBEDBY); } } // passwall ends when you walk onto a non-solid cell. f = lfhasflag(lf, F_NONCORPOREAL); if (f && (f->obfrom == OT_S_PASSWALL)) { enum ERROR err; cellwalkable(lf, lf->cell, &err); if (err == E_OK) { stopspell(lf, OT_S_PASSWALL); killflag(f); if (isplayer(lf)) { didmsg = B_TRUE; } } } if (isplayer(lf)) { if (prewater && !hasobwithflag(newcell->obpile, F_DEEPWATER)) { // getitng out of water? statdirty = B_TRUE; } } // check ground objects if (!isairborne(lf)) { for (o = newcell->obpile->first ; o ; o = nexto ) { nexto = o->next; f = hasflag(o->flags, F_DEEPWATER); if (f) { if (checkfordrowning(lf, o)) { didmsg = B_TRUE; if (isdead(lf)) return B_TRUE; } // did you just enter the water? if (!prewater) { if ((getobdepth(o, lf) >= DP_WAIST) && getskill(lf, SK_SWIMMING)) { if (isplayer(lf)) { msg("You start swimming."); didmsg = B_TRUE; statdirty = B_TRUE; } else if (cansee(player, lf)) { msg("%s starts swimming.", lfname); didmsg = B_TRUE; } // put out fires extinguishlf(lf); // stop sprinting stopsprinting(lf); } } } f = hasflag(o->flags, F_SHARP); if (f && hasbp(lf, BP_FEET) && !lfhasflag(lf, F_SNEAK)) { object_t *boots; // has boots on? boots = getequippedob(lf->pack, BP_FEET); if (!boots) { // take damage getobname(o, obname, 1); if (isplayer(lf)) { msg("Ow - you step on %s!",obname); didmsg = B_TRUE; } else if (haslos(player, newcell)) { msg("%s steps on %s!",lfname, obname); didmsg = B_TRUE; } snprintf(buf, BUFLEN, "stepping on %s", obname); losehp(lf, rnd(f->val[0],f->val[1]), DT_SLASH, NULL, buf); } } f = hasflag(o->flags, F_CRUSHABLE); if (f && !lfhasflag(lf, F_SNEAK)) { enum LFSIZE crushsize; crushsize = f->val[0]; if (getlfsize(lf) >= crushsize) { // crunch it broken glass getobname(o, obname, 1); // special case if (o->type->id == OT_BROKENGLASS) { if (o->amt > 1) { char *newname; // we want 'xx steps on some pieces of broken glass' // not 'xx steps on 5 pieces of broken glass' newname = makeplural(obname); newname = strrep(newname, "a ", "some ", NULL); strcpy(obname, newname); free(newname); } } if (isplayer(lf)) { msg("You crush %s underfoot.",obname); didmsg = B_TRUE; } else if (haslos(player, newcell)) { msg("%s crushes %s.",lfname, obname); didmsg = B_TRUE; } // kill object removeob(o, o->amt); continue; } } // end if crushable if ((o->type->id == OT_VINE) && !hasjob(lf, J_DRUID)) { char obname[BUFLEN]; getobname(o,obname,o->amt); if (isplayer(lf)) { msg("%s grab%s you!",obname,(o->amt == 1) ? "s" : ""); } else if (cansee(player, lf)) { msg("%s grab%s %s!",obname, (o->amt == 1) ? "s" : "", lfname); } } } // end foreach object in cell } // end if !flying // update where player knows // (but without a map you will then slowly forget it) if (isplayer(lf)) { updateknowncells(); // TODO: not sure about this next bit yet... // it definitely won't work for non-square rooms // or rooms with pillars. would be better to fix // haslos() code to handle looking along walls // instead. // if you walked into a new fully lit room, which // ISNT a vault, reveal it. // /* if ((getskill(lf, SK_CARTOGRAPHY) >= PR_NOVICE) && (!lf->cell->vault)) { if ((postroom > 0) && (postroom != preroom)) { cell_t *c[MAX_MAPW*MAX_MAPH]; int ncells; int i,alllit = B_TRUE,allknown = B_TRUE; // is the whole room lit? getroomcells(lf->cell->map, postroom, c, &ncells); for (i = 0; i < ncells; i++) { if (!islit(c[i])) { alllit = B_FALSE; } if (!c[i]->known) { allknown = B_FALSE; } } if (alllit && !allknown) { // make the all known for (i = 0; i < ncells; i++) { setcellknown(c[i], B_FALSE); } } } } */ } // does anyone else see you? if (gamemode == GM_GAMESTARTED) { for (l = newcell->map->lf ; l ; l = l->next) { if (l != lf) { flag_t *alarm; //if (haslos(l, newcell)) { if (cansee(l, lf)) { int dointerrupt = B_FALSE; // much larger creatures moving will cause our los to be recalculated if (getlfsize(lf) - getlfsize(l) >= 2) { precalclos(l); } if (isplayer(l)) { if (areenemies(lf, l)) { if (lfhasflag(l, F_RUNNING) || lfhasflag(l, F_TRAINING)) { // TODO: also check for isresting(l), if we have allies standing watch getlfnamea(lf, lfname); msg("%s comes into view.", lfname); } dointerrupt = B_TRUE; } } else if (isplayer(lf)) { if (areallies(lf, l)) { // remember player's last known loc f = lfhasflag(l, F_PETOF); if (f) { f->val[1] = player->cell->x; f->val[2] = player->cell->y; } } //dointerrupt = B_TRUE; } if (dointerrupt) { interrupt(l); } } alarm = hasactivespell(l, OT_S_ALARM); if (alarm && areenemies(lf, l) && haslof(lf->cell, l->cell, LOF_WALLSTOP, NULL) ) { // in range of alarm? range is 3 * spell power cells. if (getcelldist(lf->cell, l->cell) <= (alarm->val[2]*3)) { // alarm goes off noise(l->cell, NULL, NC_OTHER, 50, "a blaring siren!", NULL); killflag(alarm); } } } } // leaving a shop if (preshop) { // are you about to go outside a shop with stolen goods? if ((getroomid(lf->cell) == preshop) && (lf->cell->type->id != CT_FLOORSHOP)) { lifeform_t *shk; int nitems = 0; shk = findshopkeeper(lf->cell->map, preshop); // do you have any unpaid items from that shop? if (shk && getowing(lf, preshop, &nitems)) { // warning... sayphrase(shk, SP_PAYWARN, SV_SHOUT, NA, (nitems == 1) ? "that" : "those" ); didmsg = B_TRUE; } } else if (getroomid(lf->cell) != preshop) { // you've left the shop lifeform_t *shk; shk = findshopkeeper(lf->cell->map, preshop); if (shk && getowing(lf, preshop, NULL)) { // call the guards sayphrase(shk, SP_PAYTHREAT, SV_ROAR, NA, NULL); didmsg = B_TRUE; fightback(shk, lf); // shopkeeper attacks callguards(shk, lf); // guards come running } } } } // status bar if ((prespeed != postspeed) && isplayer(lf)) { statdirty = B_TRUE; } return didmsg; } // basically this is a warpper for 'movelf' which // does other game things like telling the player // what is here. int moveto(lifeform_t *lf, cell_t *newcell, int onpurpose, int dontclearmsg) { object_t *o; char lfname[BUFLEN]; int didmsg; int predark = B_FALSE,postdark = B_FALSE; if (!onpurpose || !isplayer(lf)) { dontclearmsg = B_TRUE; } assert(!newcell->lf); getlfname(lf, lfname); // is current cell dark? if (isplayer(lf)) { if (!haslos(lf, lf->cell) && !isblind(lf)) { predark = B_TRUE; } } if (lfhasflag(lf, F_HIDING)) { dontclearmsg = B_TRUE; } // actually do the move didmsg = movelf(lf, newcell); if (isplayer(lf)) { // is new cell dark? if (!haslos(lf, lf->cell) && !isblind(lf)) { postdark = B_TRUE; } else { killflagsofid(lf->flags, F_DONEDARKMSG); } // just moved into a dark area - announce it. if (postdark && !predark && !lfhasflag(lf, F_DONEDARKMSG)) { msg("It is %s!", (lf->cell->lit == L_PERMDARK) ? "unnaturally dark" : "pitch black"); addflag(lf->flags, F_DONEDARKMSG, B_TRUE, NA, NA, NULL); dontclearmsg = B_TRUE; } } if (dontclearmsg) { didmsg = B_TRUE; } // tell player about things if (!isdead(lf)) { // some lifeforms can go through things if (getlfmaterial(lf) == MT_GAS) { char obname[BUFLEN]; for (o = newcell->obpile->first ; o ; o = o->next) { if (isimpassableob(o, lf) && !hasflag(o->flags, F_REALLYIMPASSABLE)) { getobname(o, obname, o->amt); if (isplayer(lf)) { msg("You seep around %s.", obname); } else if (haslos(player, newcell)) { msg("%s seeps around %s.", lfname, obname); } } } } else if (getlfmaterial(lf) == MT_SLIME) { char obname[BUFLEN]; for (o = newcell->obpile->first ; o ; o = o->next) { if (isimpassableob(o, lf)) { getobname(o, obname, o->amt); if (isplayer(lf)) { msg("You seep under %s.", obname); } else if (haslos(player, newcell)) { msg("%s seeps under %s.", lfname, obname); } } } } // see objects on ground if (isplayer(lf)) { int numobs; numobs = countnoncosmeticobs(newcell->obpile, B_TRUE); if ((numobs == 0) && !newcell->writing) { // just clear the message buffer if (!didmsg) clearmsg(); } else { // tell player what is here if (onpurpose) { dolook(newcell, B_FALSE); } } } } // make some noise // (stealth check to avoid this) if (!lfhasflag(lf, F_SILENTMOVE) && !lfhasflag(lf, F_SNEAK)) { if (!skillcheck(lf, SC_STEALTH, 20, 0)) { if (isairborne(lf)) { makenoise(lf, N_FLY); } else { makenoise(lf, N_WALK); } } } // slip on blood in new cell? if (!isairborne(lf)) { int slip; object_t *o,*nexto; object_t *slipob; if (!lfhasflag(lf, F_SNEAK)) { slip = getslipperyness(newcell, &slipob); if (slip && !skillcheck(lf, SC_SLIP, slip, 0)) { slipon(lf, slipob); } } // activate traps for (o = newcell->obpile->first ; o ; o = nexto ) { nexto = o->next; if (hasflag(o->flags, F_TRAP)) { triggertrap(lf, NULL, o, lf->cell); interrupt(lf); } } } return B_FALSE; } // dirtype etermines whether to use compass or orthogonal direction to // measure distance when determining which way is "towards" // // in general: // use orthogonal/dt_orth for voluntary movement (eg. monster moving // towards player), compass // // use compass/dt_compass for involuntary movement (eg. being knocked back by // an explosion) // int movetowards(lifeform_t *lf, cell_t *dst, int dirtype) { int dir; int rv = B_TRUE; if (isblind(lf)) { dorandommove(lf, B_TRUE, B_TRUE); return B_FALSE; } // move towards them dir = getdirtowards(lf->cell, dst, lf, B_TRUE, dirtype); if (dir != D_NONE) { rv = trymove(lf, dir, B_TRUE); } return rv; } int move_will_hurt(lifeform_t *lf) { flag_t *retflag[MAXCANDIDATES]; int nretflags,i; if (lfhasflag(lf, F_PAIN)) { return B_TRUE; } getflags(lf->flags, retflag, &nretflags, F_INJURY, F_PAIN, F_NONE); for (i = 0; i < nretflags; i++) { flag_t *f; f = retflag[i]; if (f->id == F_PAIN) return B_TRUE; if (f->id == F_INJURY) { switch (f->val[0]) { case IJ_LEGBLEED: return B_TRUE; default: break; } } } return B_FALSE; } int opendoorat(lifeform_t *lf, cell_t *c) { object_t *o; int rv; o = hasobwithflag(c->obpile, F_DOOR); if (o) { rv = opendoor(lf, o); } else { rv = B_TRUE; } return rv; } int opendoor(lifeform_t *lf, object_t *o) { cell_t *doorcell; char buf[BUFLEN]; char obname[BUFLEN]; flag_t *f; doorcell = o->pile->where; assert(doorcell); getobname(o, obname, 1); if (!isdoor(o, NULL)) { return B_TRUE; } if (!canopendoors(lf)) { if (isplayer(lf)) { msg("You have no hands with which to open the door!"); } return B_TRUE; } f = hasflag(o->flags, F_OPEN); if (f) { if (lf && isplayer(lf)) { msg("It is already open!"); } return B_TRUE; } else { if (lf) { if (isplayer(lf)) { // has known trap? if (hasflagval(o->flags, F_TRAPPED, NA, NA, B_TRUE, NULL)) { if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_AVERAGE) { char ch; snprintf(buf, BUFLEN,"Really open %s?", obname); ch = askchar(buf,"yn","n", B_TRUE); if (ch != 'y') { msg("Cancelled."); return B_TRUE; } } } // hear water behind it? if (getskill(lf, SK_LISTEN)) { int dir; cell_t *pastdoorcell; dir = getdirtowards(doorcell, lf->cell, NULL, B_FALSE, DT_ORTH); pastdoorcell = getcellindir(doorcell, dir); if (pastdoorcell && getcellwaterdepth(pastdoorcell, NULL)) { if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_AVERAGE) { char ch; snprintf(buf, BUFLEN,"Your hear running water behind %s. Really open it?", obname); ch = askchar(buf,"yn","n", B_TRUE); if (ch != 'y') { msg("Cancelled."); return B_TRUE; } } } } } taketime(lf, getactspeed(lf)); touch(lf, o); // stop sprinting stopsprinting(lf); /* sf = lfhasflag(lf, F_SPRINTING); if (sf && sf->val[0]) { killflag(sf); } */ } // trapped? if (lf && hasflag(o->flags, F_TRAPPED)) { if (doobtraps(o, lf)) { return B_TRUE; } } // locked? if (hasflag(o->flags, F_LOCKED)) { if (lf && isplayer(lf)) { msg("The %s is locked.", noprefix(obname)); } return B_TRUE; } else { int openit = B_TRUE; f = hasflag(o->flags, F_JAMMED); if (f) { int amt; amt = getattr(lf, A_STR) - 10; if (amt < 0) amt = 0; if (lf && isplayer(lf)) { if (amt > 0) { msg("The %s moves slightly but seems jammed.", noprefix(obname)); } else { msg("The %s is jammed.", noprefix(obname)); } } // loosen a bit if (amt) { f->val[0] -= amt; if (f->val[0] <= 0) { killflag(f); } } openit = B_FALSE; // don't open the door } if (openit) { cell_t *where; // open it addflag(o->flags, F_OPEN, B_TRUE, NA, NA, NULL); killflagsofid(o->flags, F_IMPASSABLE); killflagsofid(o->flags, F_BLOCKSVIEW); killflagsofid(o->flags, F_SECRET); killflagsofid(o->flags, F_TRAPPED); if (lf) { if (isplayer(lf)) { msg("You open %s.",obname); } else { if (cansee(player, lf) && isadjacent(lf->cell, doorcell)) { getlfname(lf, buf); capitalise(buf); msg("%s opens %s.",buf, obname); } else if (haslos(player, doorcell)) { capitalise(obname); msg("%s opens.",obname); } } } where = getoblocation(o); if (player) { if (haslos(player, where)) { needredraw = B_TRUE; drawscreen(); } else { // don't anonuce this if we can see it. // normally 'noise()' takes care of this by // checking if we have LOS to the lifeform making // sound, but in this case it's the door making // the sound, not the lf. noise(where, NULL, NC_OTHER, 2, "a door opening.", NULL); } } } return B_FALSE; } } return B_FALSE; } int closedoorat(lifeform_t *lf, cell_t *c) { object_t *o; int rv; o = hasobwithflag(c->obpile, F_DOOR); if (o) { rv = closedoor(lf, o); } else { rv = B_TRUE; } return rv; } int closedoor(lifeform_t *lf, object_t *o) { cell_t *cell; char buf[BUFLEN]; char obname[BUFLEN]; object_t *oo; flag_t *f; cell = getoblocation(o); getobname(o, obname, 1); if (!isdoor(o, NULL)) { if (isplayer(lf)) { msg("There is nothing to close there!"); } return B_TRUE; } if (lf && lfhasflagval(lf, F_NOBODYPART, BP_HANDS, NA, NA, NULL)) { if (isplayer(lf)) { msg("You have no hands with which to close the door!"); } return B_TRUE; } if (cell->lf) { if (lf && isplayer(lf)) { char inwayname[BUFLEN]; getlfname(cell->lf, inwayname); msg("%s is in the way!", haslos(lf, cell) ? inwayname : "Something"); } return B_TRUE; } // any solid object other than the door? for (oo = cell->obpile->first ; oo ; oo = oo->next) { if ((oo != o) && (getmaterialstate(oo->material->id) == MS_SOLID)) { if (lf && isplayer(lf)) { char inwayname[BUFLEN]; getobname(oo, inwayname, oo->amt); msg("%s %s in the way!", haslos(lf, cell) ? inwayname : "Something", (haslos(lf,cell) && (oo->amt > 1)) ? "are" : "is" ); } return B_TRUE; } } f = hasflag(o->flags, F_OPEN); if (!f) { if (lf && (isplayer(lf))) { msg("It is already closed!"); } return B_TRUE; } else { // close it killflag(f); f = hasflag(o->flags, F_DOOR); addflag(o->flags, F_IMPASSABLE, f->val[0], f->val[1], f->val[2], f->text); addflag(o->flags, F_BLOCKSVIEW, B_TRUE, NA, NA, NULL); if (lf) { // stop sprinting stopsprinting(lf); if (isplayer(lf)) { msg("You close %s.", obname); } else { if (cansee(player, lf) && isadjacent(lf->cell, cell)) { getlfname(lf, buf); capitalise(buf); msg("%s closes %s.",buf, obname); } else if (haslos(player, cell)) { capitalise(obname); msg("%s closes.",obname); } } taketime(lf, getactspeed(lf)); touch(lf, o); } if (player && haslos(player, cell)) { needredraw = B_TRUE; drawscreen(); } } return B_FALSE; } int tryrun(lifeform_t *lf, int dir) { if (!trymove(lf, dir, B_TRUE)) { // success! addflag(lf->flags, F_RUNNING, dir, B_FALSE, NA, NULL); } return B_FALSE; } int trysneak(lifeform_t *lf, int dir) { if (dir == D_NONE) { if (isplayer(lf)) { char ques[BUFLEN]; char ch; snprintf(ques, BUFLEN, "Carefully %s in which direction (- to cancel)", getmoveverb(lf)); ch = askchar(ques, "yuhjklbn.-","-", B_FALSE); dir = chartodir(ch); if (dir == D_NONE) return B_TRUE; } else { return B_TRUE; } } addflag(lf->flags, F_SNEAK, NA, NA, NA, NULL); trymove(lf, dir, B_TRUE); killflagsofid(lf->flags, F_SNEAK); return B_FALSE; } // try to pull lifeform towards cell c (or next to it) int pullnextto(lifeform_t *lf, cell_t *c) { int dir; cell_t *dst = NULL; cell_t *newdst = NULL; dst = c; while (dst->lf) { dir = getdirtowards(dst, lf->cell, lf, B_FALSE, DT_COMPASS); if (dir == D_NONE) { return B_TRUE; } else { dst = getcellindir(dst, dir); if (dst == lf->cell) { return B_TRUE; } } } // is the path clear? if (!dst) { return B_TRUE; } if (!haslof(lf->cell, dst, B_FALSE, &newdst)) { if (newdst) { // update destination dst = newdst; } else { return B_TRUE; } } if (isplayer(lf) || cansee(player, lf)) { char buf[BUFLEN]; getlfname(lf, buf); msg("%s %s pulled %s!", buf, is(lf), isairborne(lf) ? "through the air" : "along the ground"); } movelf(lf, dst); return B_FALSE; } // do pre-move checks like slipping on stuff in your current cell, // webs, etc. // cell can be null if you're using stairs. // return true if something happened int initiatemove(lifeform_t *lf, cell_t *cell, int *didmsg) { object_t *o, *nexto; char buf[BUFLEN]; flag_t *f; // demon in pentagram if ((getraceclass(lf) == RC_DEMON) && hasob(lf->cell->obpile, OT_PENTAGRAM)) { if (isplayer(lf)) { msg("You cannot escape the pentagram!"); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s struggles within a pentagram!", lfname); } reason = E_OK; taketime(lf, getmovespeed(lf)); return B_TRUE; } // gravboosted if (lfhasflag(lf, F_GRAVBOOSTED)) { // make a saving throw to move if (!skillcheck(lf, SC_STR, 25, 0)) { if (isplayer(lf)) { msg("You try to %s but are unable to %s!", isprone(lf) ? "stand" : "move", isprone(lf) ? "lift your arms" : "lift your feet"); if (didmsg) *didmsg = B_TRUE; } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s tries to %s but is unable to %s!", lfname, isprone(lf) ? "stand" : "move", isprone(lf) ? "get up" : "lift its feet"); if (didmsg) *didmsg = B_TRUE; } reason = E_OK; taketime(lf, getmovespeed(lf)); return B_TRUE; } } // sticky objects in current cell? for (o = lf->cell->obpile->first ; o ; o = nexto) { nexto = o->next; f = obrestrictsmovement(o, lf); if (f) { char lfname[BUFLEN]; int diff; int checkmod = 0; int getsweaker; if ((o->type->id == OT_WEB) && isairborne(lf)) { checkmod -= 5; } getlfname(lf,lfname); getobname(o, buf, o->amt); // for stacks of sticky objects, each one after the first adds // quarter its difficuly. ie: // 1 x object with f_sticky:20, difficult is 20 // 2 x object with f_sticky:20, difficult is 25 // 3 x object with f_sticky:20, difficult is 30 // etc // can you break free? diff = f->val[0]; if (o->amt > 1) { diff = (o->amt - 1) * ((float)f->val[0] / 4.0); } getsweaker = f->val[1]; if (skillcheck(lf, SC_STR, diff, checkmod)) { if (isplayer(lf)) { msg("You tear free from %s!", buf); if (didmsg) *didmsg = B_TRUE; } else if (cansee(player, lf)) { msg("%s tears free from %s!", lfname, buf); if (didmsg) *didmsg = B_TRUE; } killob(o); continue; } else { // failed - object gets a little less sticky if (isplayer(lf)) { msg("You struggle in %s!", buf); if (didmsg) *didmsg = B_TRUE; } else if (cansee(player, lf)) { msg("%s struggles in %s!", lfname, buf); if (didmsg) *didmsg = B_TRUE; } if (getsweaker) { takedamage(o, 1, DT_DIRECT); } taketime(lf, getmovespeed(lf)); reason = E_OK; return B_TRUE; } } } // are we on the ground? if (isprone(lf)) { int howlong; float quartermax; int units; if (isplayer(lf)) { msg("You stand up."); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s stands up.",lfname); } killflagsofid(lf->flags, F_PRONE); killflagsofid(lf->flags, F_FEIGNINGDEATH); // time to get up depends on armour // 1*movespeed for every 1/4 of maxcarryweight being worn. quartermax = getmaxcarryweight(lf) / 4; units = (getequippedweight(lf) / quartermax)+1; howlong = getmovespeed(lf)*units; taketime(lf, howlong); reason = E_OK; return B_TRUE; } // slipping on something before moving? if (!isairborne(lf)) { int slip; object_t *slipob; if (!lfhasflag(lf, F_SNEAK)) { slip = getslipperyness(lf->cell, &slipob); if (slip && !skillcheck(lf, SC_SLIP, slip, 0)) { if (!slipon(lf, slipob)) { if (didmsg) *didmsg = B_TRUE; // don't move reason = E_OK; return B_TRUE; } } } } // check for cursed objects in new cell + animals // do this AFTER checking if we will move, so that // they will actually try the move and fail (this lets // the player find out about the cursed object). // // note however that if a monster is chasing a player (ie // has F_TARGET,player) then they will simply avoid the cursed // object rather than failing the movement. if (cell) { for (o = cell->obpile->first ; o ; o = nexto) { nexto = o->next; if (!isplayer(lf)) { if ((o->blessed == B_CURSED) && (getraceclass(lf) == RC_ANIMAL) && !isairborne(lf)) { if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf,lfname); getobname(o, buf, o->amt); msg("%s %s away from %s!", lfname, isplayer(lf) ? "shy" : "shies", buf); o->blessknown = B_TRUE; if (didmsg) *didmsg = B_TRUE; } taketime(lf, getmovespeed(lf)); reason = E_OK; // avoid this object in future snprintf(buf, BUFLEN, "%ld",o->id); addflag(lf->flags, F_AVOIDOB, B_CURSED, NA, NA, buf); return B_TRUE; } else if (lfhasflagval(lf, F_AVOIDOBTYPE, o->type->id, NA, NA, NULL)) { if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf,lfname); getobname(o, buf, o->amt); msg("%s %s away from %s!", lfname, isplayer(lf) ? "shy" : "shies", buf); o->blessknown = B_TRUE; if (didmsg) *didmsg = B_TRUE; } taketime(lf, getmovespeed(lf)); reason = E_OK; // avoid this object in future snprintf(buf, BUFLEN, "%ld",o->id); addflag(lf->flags, F_AVOIDOB, NA, NA, NA, buf); return B_TRUE; } } } } return B_FALSE; } void swapplaces(lifeform_t *lf1, lifeform_t *lf2, int onpurpose) { cell_t *cell1,*cell2; cell1 = lf1->cell; cell2 = lf2->cell; // make lf who is there vanish temporarily... cell2->lf = NULL; lf2->cell = NULL; // move you.. moveto(lf1, cell2, onpurpose, B_FALSE); // move them... lf2->cell = cell1; cell1->lf = lf2; // remember that we just swapped if (!isplayer(lf1)) { addflag(lf1->flags, F_NOSWAP, B_TRUE, NA, NA, NULL); } } // teleport somewhere, along with puffs of smoke etc int teleportto(lifeform_t *lf, cell_t *c, int wantsmoke) { char buf[BUFLEN]; // can't teleport on top of something else if (c->lf) { // go somewhere nearby c = getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND); if (!c) { if (isplayer(lf)) { msg("You feel a wrenching sensation."); } return B_TRUE; } } if (!isplayer(lf) && cansee(player, lf)) { getlfname(lf, buf); if (wantsmoke) { msg("%s disappears in a cloud of smoke!", buf); } else { msg("%s vanishes!", buf); } } if (wantsmoke) { addob(lf->cell->obpile, "cloud of smoke"); } movelf(lf, c); if (cansee(player, lf)) { redraw(); // redraw screen } if (isplayer(lf)) { msg("Suddenly, your surroundings appear different!"); } else if (cansee(player, lf)) { getlfname(lf, buf); msg("%s appears!", buf); } // show any objects here, just like if we moved. // BUT don't let dolook() clear the msg bar if there are // no objects here. if (isplayer(lf) && countnoncosmeticobs(lf->cell->obpile, B_TRUE)) { dolook(lf->cell, B_FALSE); } return B_FALSE; } void triggertrap(lifeform_t *lf, object_t *o, object_t *trapob, cell_t *where) { char triggerer[BUFLEN]; char trapname[BUFLEN]; int wants; getobname(trapob, trapname, 1); if (lf) { getlfname(lf, triggerer); if (isplayer(lf)) wants = B_FALSE; else wants = B_TRUE; } else { getobname(o, triggerer, o->amt); if (o->amt == 1) wants = B_TRUE; else wants = B_FALSE; } if (haslos(player, where)) { msg("%s trigger%s %s!", triggerer, (wants) ? "s" : "", trapname); if (lf && isplayer(lf)) more(); // no longer hidden killflagsofid(trapob->flags, F_SECRET); } // NOTE: after trapeffects(), oo might be killed. trapeffects(trapob, trapob->type->id, where); if (lf) interrupt(lf); } int trymove(lifeform_t *lf, int dir, int onpurpose) { cell_t *cell; enum ERROR errcode; char buf[BUFLEN]; int dontclearmsg = B_FALSE; int moveok; int drunk = B_FALSE; flag_t *f; f = isdrunk(lf); if (f) { if (!hasjob(lf, J_PIRATE)) { if (rnd(1,6) <= ((f->lifetime/TM_DRUNKTIME)+1)) { // randomize move dir = rnd(DC_N, DC_NW); drunk = B_TRUE; // ie. you can walk into walls now. } } } cell = getcellindir(lf->cell, dir); if (isplayer(lf) && !lfhasflag(lf, F_SNEAK)) { if (cell && celldangerous(lf, cell, B_TRUE, &errcode)) { char ques[BUFLEN]; char ch; if ((errcode == E_AVOIDOB) && rdata) { char obname[BUFLEN]; object_t *avoidob; avoidob = (object_t *)rdata; getobname(avoidob, obname, avoidob->amt); snprintf(ques, BUFLEN, "Really %s into %s?", getmoveverb(lf), obname); } else { snprintf(ques, BUFLEN, "Really %s there?", getmoveverb(lf)); } ch = askchar(ques, "yn","n", B_TRUE); if (ch != 'y') { return B_TRUE; } } } moveok = B_FALSE; if (moveclear(lf, dir, &errcode)) { if (onpurpose) { if (canandwillmove(lf, dir, &errcode)) { moveok = B_TRUE; } } else { moveok = B_TRUE; } } if (moveok) { lifeform_t *alf; if (initiatemove(lf, cell, &dontclearmsg)) { // failed? return B_TRUE; } reason = E_OK; // remember last dir we walked killflagsofid(lf->flags, F_LASTDIR); addflag(lf->flags, F_LASTDIR, dir, NA, NA, NULL); // add footprints in our current cell. addtrail(lf, dir); // do your pets see you move? if (isplayer(lf) && (gamemode == GM_GAMESTARTED)) { lifeform_t *l; for (l = lf->cell->map->lf ; l ; l = l->next) { flag_t *tf; if (isplayer(lf)) { tf = ispetortarget(l, lf); if (tf) { if (cansee(l, lf)) { // update text field free(tf->text); asprintf(&(tf->text), "%d", dir); } } } } } // now move to new cell moveto(lf, cell, drunk ? B_FALSE : onpurpose, dontclearmsg); if (onpurpose) { taketime(lf, getmovespeed(lf)); } // attached lfs or lfs you have grabbed will move the same direction if they can for (alf = cell->map->lf ; alf ; alf = alf->next) { int willdrag = B_FALSE; int attached = B_FALSE; int grabbed = B_FALSE; if (lfhasflagval(alf, F_ATTACHEDTO, lf->id, NA, NA, NULL)) { willdrag = B_TRUE; attached = B_TRUE; grabbed = B_FALSE; } else if (lfhasflagval(lf, F_GRABBING, alf->id, NA, NA, NULL)) { willdrag = B_TRUE; attached = B_FALSE; grabbed = B_TRUE; } if (willdrag) { // if the lifeform we were attached to just moved away from us, // try to stay with them. if (getcelldist(alf->cell,lf->cell) > 1) { int newdir; newdir = getdirtowards(alf->cell, lf->cell, alf, B_FALSE, DT_ORTH); // do a manual canmove check here first, to avoid 'the stirge flies into a wall' // if the move fails. // // but DONT do the check if you're dragging someone you grabbed. otherwise // moveclear() will say "can't move because someone is holding you" if (newdir != D_NONE) { cell_t *nc; nc = getcellindir(alf->cell, newdir); if (nc && cellwalkable(alf, nc, NULL)) { if (isplayer(lf)) { char alfname[BUFLEN]; getlfname(alf, alfname); msg("You drag %s along.", alfname); } else if (isplayer(alf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s drags you along.", lfname); } else if (cansee(player, lf) || cansee(player, alf)) { char lfname[BUFLEN],alfname[BUFLEN]; getlfname(lf, lfname); getlfname(alf, alfname); msg("%s drags %s along.", lfname, alfname); } movelf(alf, nc); } } } } } // train if (isswimming(lf)) { practice(lf, SK_SWIMMING, 1); } } else { // ie !moveok object_t *inway = NULL; int door, dooropen; reason = errcode; switch (errcode) { case E_OFFMAP: if (lf->cell->map->region->rtype->id == RG_WORLDMAP) { // cope with nonorthogonal! // ie. ne counts as n if we are at the top. if (!isorthogonal(dir)) { dir = getwalkoffdir(lf, dir); } if (dir != D_NONE) { // we are allowed to walk off the edge return walkoffmap(lf, dir, B_TRUE); } } // otherwise fall through... case E_WALLINWAY: if (isplayer(lf)) { msg("Ouch! You %s into a %s.", getmoveverb(lf), cell ? cell->type->name : "wall"); } else if (cansee(player, lf)) { getlfname(lf, buf); msg("%s %ss into a %s.", buf, getmoveverb(lf), cell ? cell->type->name : "wall"); } //if (isblind(lf) || !haslos(lf, cell)) { if (!cell || !haslos(lf, cell)) { if (isplayer(lf)) { // only take damage if we didn't know about this if ((cell && !cell->known) || isdrunk(lf)) { snprintf(buf, BUFLEN, "%sing into a %s", getmoveverb(lf), cell ? cell->type->name : "wall"); losehp(lf, 1, DT_BASH, NULL, buf); if (cell) { // we now know there is a wall there. setcellknown(cell, B_FALSE); } if (onpurpose) taketime(lf, getmovespeed(lf)); } } else { snprintf(buf, BUFLEN, "%sing into a %s", getmoveverb(lf), cell ? cell->type->name : "wall"); losehp(lf, 1, DT_BASH, NULL, buf); if (onpurpose) taketime(lf, getmovespeed(lf)); } } break; case E_DOORINWAY: inway = (object_t *)rdata; door = isdoor(inway, &dooropen); if (door && !dooropen) { if (isblind(lf)) { // run into it if (isplayer(lf)) { if (cell->known) { // try to open it opendoor(lf, inway); } else { msg("Ouch! You %s into a door.", getmoveverb(lf)); setcellknown(cell, B_FALSE); snprintf(buf, BUFLEN, "%sing into a door", getmoveverb(lf)); losehp(lf, 1, DT_BASH, NULL, buf); if (onpurpose) taketime(lf, getmovespeed(lf)); } } else { if (cansee(player, lf)) { getlfname(lf, buf); msg("%s %ss into a door.", buf, getmoveverb(lf)); } snprintf(buf, BUFLEN, "%sing into a door", getmoveverb(lf)); losehp(lf, 1, DT_BASH, NULL, buf); if (onpurpose) taketime(lf, getmovespeed(lf)); } } else { // try to open it if (!opendoor(lf, inway)) { // opening a door counts as a successful move. reason = E_OK; } } } break; case E_OBINWAY: inway = (object_t *)rdata; // can we push this? if (ispushable(inway)) { if (canpush(lf, inway, dir)) { // push it! push(lf, inway, dir); } else { if (isplayer(lf)) { char obname[BUFLEN]; getobname(inway, obname, 1); switch (reason) { case E_TOOHEAVY: msg("The %s is too heavy to move.",strchr(obname, ' ')+1); break; case E_INSUBSTANTIAL: msg("You cannot push %s without a physical body!",strchr(obname, ' ')+1); break; default: msg("The %s won't move for some reason.",strchr(obname, ' ')+1); break; } } } } else { // somethign random is in the way if (isplayer(lf) && inway) { char obname[BUFLEN]; if (haslos(lf, cell)) { getobname(inway, obname, 1); } else { strcpy(obname, "something"); } msg("There is %s in your way.",obname); } } break; case E_SWIMMING: if (isplayer(lf)) { msg("You can't attack while swimming!"); } break; case E_LFINWAY: if (canswapwith(lf, cell->lf)) { lifeform_t *lfinway; // otherwise swap locations. lfinway = cell->lf; swapplaces(lf, lfinway, onpurpose); if (onpurpose) taketime(lf, getmovespeed(lf)); if (isplayer(lf)) { char lfname[BUFLEN]; getlfname(lfinway, lfname); msg("You swap places with %s.", lfname); } } else { if (!onpurpose || canandwillmove(lf, dir, &errcode)) { return attackcell(lf, cell, B_FALSE); } else { // won't attack for some reason. return B_TRUE; } } break; case E_CANTMOVE: if (isplayer(lf)) { msg("You cannot move!"); } if (onpurpose) taketime(lf, getmovespeed(lf)); break; case E_GRABBEDBY: if (rdata) { lifeform_t *grabbedby; char gbname[BUFLEN]; // skill check to escape. grabbedby = (lifeform_t *)rdata; getlfname(grabbedby, gbname); if (!cell->lf && skillcheckvs(lf, SC_STR, 0, grabbedby, SC_STR, 0)) { // broke free killflagsofid(lf->flags, F_GRABBEDBY); killflagsofid(grabbedby->flags, F_GRABBING); // move - don't clear the 'you break free from' msg // NOW is the move possible? if (moveclear(lf, dir, &errcode)) { moveto(lf, cell, B_TRUE, B_TRUE); } } else { if (isplayer(lf)) { msg("You cannot get away from %s!",gbname); } } } else { if (isplayer(lf)) { msg("You cannot get away from whatever is holding you!"); } } if (onpurpose) taketime(lf, getmovespeed(lf)); break; case E_PENTAGRAM: if (isplayer(lf)) { msg("You cannot seem to enter the pentagram."); } if (onpurpose) taketime(lf, getmovespeed(lf)); break; case E_TOOHEAVY: if (isplayer(lf)) { msg("Your load is too heavy to move with!"); } if (onpurpose) taketime(lf, getmovespeed(lf)); break; default: break; } if (reason == E_OK) { return B_FALSE; } else { return B_TRUE; } } return B_FALSE; } int walkoffmap(lifeform_t *lf, int dir, int onpurpose) { map_t *adjmap = NULL, *thismap; cell_t *dst = NULL; lifeform_t *adjally[8]; int nadjallies = 0; int n; // make sure dircetion is orthogonal dir = makeorthogonal(dir); assert(dir != D_NONE); // announce if (isplayer(lf)) { char dirname[BUFLEN]; curs_set(1); strcpy(dirname, getdirname(dir)); dirname[0] = tolower(dirname[0]); msg("You %s to the %s...", getmoveverb(lf), getdirname(dir)); // move cursor to msgwindow while we create the new level... wrefresh(msgwin); } else if (cansee(player, lf)) { char dirname[BUFLEN]; char lfname[BUFLEN]; getlfname(lf, lfname); curs_set(1); strcpy(dirname, getdirname(dir)); dirname[0] = tolower(dirname[0]); msg("%s %s to the %s...", lfname, getmoveverbother(lf), getdirname(dir)); wrefresh(msgwin); } // is there a map in that direction ? thismap = lf->cell->map; adjmap = findmap(lf->cell->map->nextmap[dir]); if (!adjmap) { // make one adjmap = addmap(); createmap(adjmap, thismap->depth, thismap->region, thismap, dir, NULL); } if (adjmap) { // find an empty cell in the next map dst = findmapentrypoint(adjmap, diropposite(dir), lf); } if (!dst) { // failed if (isplayer(lf)) { msg("Your path seems to be blocked."); } return B_TRUE; } if (onpurpose) { // get list of adjacent allies if (isplayer(lf)) { getwhowillfollow(lf, NULL, adjally, &nadjallies); } } // announce announcearrival(lf, dst->map); // move there moveto(lf, dst, onpurpose, B_TRUE); if (onpurpose) { taketime(lf, getmovespeed(lf)); } // move adjacent allies if (onpurpose) { for (n = 0; n < nadjallies; n++) { cell_t *c; c = getrandomadjcell(dst, WE_WALKABLE, B_ALLOWEXPAND); if (c) { if (!initiatemove(adjally[n], NULL, NULL)) { movelf(adjally[n], c); taketime(adjally[n], getmovespeed(adjally[n])); } } } } if (isplayer(lf)) { statdirty = B_TRUE; needredraw = B_TRUE; calclight(player->cell->map); precalclos(lf); drawscreen(); } return B_FALSE; } int willmove(lifeform_t *lf, int dir, enum ERROR *error) { cell_t *cell; enum ATTRBRACKET iq; object_t *o; char buf[BUFLEN]; flag_t *f; //object_t *o; iq = getattrbracket(getattr(lf, A_IQ), A_IQ, NULL); // default if (error) { *error = E_OK; rdata = NULL; } if (isdrunk(lf)) { return B_TRUE; } cell = getcellindir(lf->cell, dir); if (!cell) { // won't walk off map return B_FALSE; } if (celldangerous(lf, cell, B_TRUE, error)) { if (error) *error = E_WONT; return B_FALSE; } // some lfs will only leave their rooms if they have a target // ie. shopkeepers f = lfhasflag(lf, F_STAYINROOM); if (f) { int roomid; roomid = f->val[0]; if (roomid == NA) { // don't move out of ANY room. if ((getroomid(lf->cell) != getroomid(cell))) { if ((f->val[1] != NA) && aihastarget(lf)) { // exception! } else { if (error) *error = E_WONT; return B_FALSE; } } } else { // don't move out of the given room. if ((getroomid(lf->cell) == roomid) && (getroomid(lf->cell) != getroomid(cell))) { if ((f->val[1] != NA) && aihastarget(lf)) { // exception! } else { if (error) *error = E_WONT; return B_FALSE; } } } } if (lfhasflag(lf, F_STAYINHABITAT) && (cell->habitat->id != lf->cell->habitat->id)) { if (error) *error = E_WONT; return B_FALSE; } // don't attack other monsters if (cell->lf) { // if someone is in the way object_t *defenderwep = NULL; if (lf->race->raceclass->id == RC_INSECT) { if (hasactivespell(cell->lf, OT_S_REPELINSECTS)) { if (error) *error = E_WONT; return B_FALSE; } } defenderwep = getweapon(cell->lf); if (defenderwep) { if (lfhasflagval(lf, F_AVOIDOBTYPE, defenderwep->type->id, B_TRUE, NA, NULL)) { if (error) *error = E_WONT; return B_FALSE; } } if (!isplayer(lf)) { // if we are a monster // if the person in the way isn't our enemy... if (!areenemies(lf, cell->lf)) { // if they are an ally... if (canswapwith(lf, cell->lf)) { return B_TRUE; } if (error) *error = E_WONT; return B_FALSE; } } } // for at least average iq things... if (iq >= AT_AVERAGE) { // don't move if in pain if (move_will_hurt(lf)) { if (error) *error = E_WONT; return B_FALSE; } } // look for avoided objects (because they are cursed). for (o = cell->obpile->first ; o ; o = o->next) { flag_t *f; snprintf(buf, BUFLEN, "%ld",o->id); f = lfhasflagval(lf, F_AVOIDOB, NA, NA, NA, buf); if (f) { // still cursed? if ((f->val[0] != NA) && (o->blessed == f->val[0])) { if (error) *error = E_WONT; return B_FALSE; } else { // remove the flag. killflag(f); } } if (hasflag(o->flags, F_TRAP)) { if (hasflag(o->flags, F_SECRET)) { // hidden traps? if (iq >= AT_GTAVERAGE) { if (error) *error = E_WONT; return B_FALSE; } } else { // non-hidden traps? if (iq >= AT_AVERAGE) { if (error) *error = E_WONT; return B_FALSE; } } } } return B_TRUE; }