#include #include #include #include #include #include "ai.h" #include "attack.h" #include "defs.h" #include "flag.h" #include "god.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 enum ERROR reason; extern void *rdata; extern long curtime; extern condset_t ccwalkable; 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->id == BH_WORLDMAP) { return B_TRUE; } break; case E_CANTMOVE: case E_GRABBEDBY: case E_TOOHEAVY: case E_LFINWAY: return B_TRUE; case E_DOORINWAY: // if ai lifeforms CANT open doors, they will attack them if (canopendoors(lf) || !isplayer(lf) || lfhasflag(lf, F_RAGE)) { 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) { // can't swap places when enraged if (lfhasflag(lf, F_RAGE)) { return B_FALSE; } // 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; } // allies can always swap if (areallies(lf, lf2)) { return B_TRUE; } if (isplayer(lf)) { if (isunconscious(lf2)) { if (getlfsize(lf2) <= getlfsize(lf)) { return B_TRUE; } } else if (!areenemies(lf, lf2) && isknownpeaceful(lf2)) { // player can swap with peaceful lfs // if they are a lot smaller //if (getlfsize(lf) - getlfsize(lf2) >= 2) { // return B_TRUE; //} 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; rdata = NULL; // default if (error) { *error = E_OK; } // never dangerous if there's someone there, since we'll // attack them instead of moving! if (cell->lf && (cell->lf != lf)) { return B_FALSE; } if (lf && isplayer(lf) && godprayedto(R_GODMERCY)) { wis = AT_VHIGH; } else { wis = getattrbracket(getattr(lf, A_WIS), A_WIS, NULL); } // 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; } if (o->type->id == OT_PENTAGRAM) { // this is only dangerous for the player, and only if they have // above average wisdom if (isplayer(lf) && (wis >= AT_GTAVERAGE)) { object_t *oo; // any blessed objects in your pack? for (oo = lf->pack->first ; oo ; oo = oo->next) { if (oo->blessknown && isblessed(oo)) { if (error) { *error = E_AVOIDOB; rdata = o; } return B_TRUE; } } } } else if (o->type->id == OT_HOLYCIRCLE) { if (isundead(lf) || lfhasflag(lf, F_LYCANTHROPE)) { 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, NULL)) { 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, NULL) && !lfhasflag(lf, F_AQUATIC)) { if ((getobdepth(o, lf) >= DP_HEAD)) { if (getskill(lf, SK_SWIMMING) - isburdened(lf) <= 0) { if (error) { *error = E_AVOIDOB; rdata = o; } return B_TRUE; } } if (isvulnto(lf->flags, DT_WATER, B_FALSE)) { 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, B_FALSE)) { // 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 wise enough if (!onlyifknown) { include_nonobvious = B_TRUE; } else { if (haslos(lf, cell)) { if (!lfhasflag(lf, F_UNDEAD)) { include_nonobvious = B_TRUE; } } } if (include_nonobvious) { for (o = cell->obpile->first ; o ; o = o->next) { if (wis >= AT_GTAVERAGE) { // don't walk onto crushable objects if you worship ekrub if (isplayer(lf) && godprayedto(R_GODNATURE) && cancrush(lf, o) && !lfhasflag(lf, F_CAREFULMOVE)) { if (error) { *error = E_AVOIDOB; rdata = o; } return B_TRUE; } } else if (wis >= AT_AVERAGE) { // don't walk on sharp objects without boots if (hasflag(o->flags, F_SHARP)) { if (!isairborne(lf, NULL) && !getequippedob(lf->pack, BP_FEET)) { if (error) { *error = E_AVOIDOB; rdata = o; } return B_TRUE; } } } else if (wis >= AT_LOW) { // avoid objects which produce light if we have sensitive eyes if (obproduceslight(o) && !isblind(lf) && (lighthurtseyes(lf) || isvulnto(lf->flags, DT_LIGHT, B_FALSE)) ) { if (error) { *error = E_AVOIDOB; rdata = o; } return B_TRUE; } } // other checks - ie ones where the condition for knowing // about the danger is based on more than just wisdom. // avoid active landmines f = hasflag(o->flags, F_EXPLODEONMOTION); if (f) { if ((wis >= AT_GTAVERAGE) || (getskill(lf, SK_TECHUSAGE) >= gettechlevel(o->type->id))) { int avoidit = B_FALSE; if (f->val[2] == B_IFACTIVATED) { if (isactivated(o)) { avoidit = B_TRUE; } } else { avoidit = B_TRUE; } if (avoidit) { 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 (lf && isclimbing(lf)) { cell_t *rightcell,*leftcell,*frontcell; int rightdir,leftdir,i; int gotlf = B_FALSE; // climbing rules: you can climb into a cell if: // - it contains a lifeform and is in front of you // OR // - it is directly to your side // - the target cell (beside you) is a wall // - the cell in FRONT (ie. dir you are facing) of the target cell // is NOT a wall // OR // - it is straight forward (ie. off the wall) if (cell->lf && (cell->lf != lf)) { if (getrelativedir(lf, getdirtowards(lf->cell, cell, lf, B_FALSE, DT_ORTH)) == RD_FORWARDS) { if (error) *error = E_LFINWAY; // ok but still set reason gotlf = B_TRUE; } } if (!gotlf) { rightdir = lf->facing; leftdir = lf->facing; for (i = 0; i < 2; i++) { if (++rightdir > DC_NW) rightdir = DC_N; if (--leftdir < DC_N) leftdir = DC_NW; } rightcell = getcellindir(lf->cell, rightdir); leftcell = getcellindir(lf->cell, leftdir); frontcell = getcellindir(lf->cell, lf->facing); if ((cell != rightcell) && (cell != leftcell) && (cell != frontcell)) { // not left/right/front if (error) *error = E_BADCLIMBDIR; return B_FALSE; } if (cell == frontcell) { // in front if (cell->type->solid) { if (error) *error = E_BADCLIMBDIR; return B_FALSE; } else if (!cell->lf) { // in front and not solid, and no lf there if (error) *error = E_STOPCLIMBING; return B_TRUE; } } else { // to the side if (cell->type->solid) { cell_t *sidefrontcell; sidefrontcell = getcellindir(cell, lf->facing); if (!sidefrontcell || sidefrontcell->type->solid) { // cell in front of target cell is solid if (error) *error = E_BADCLIMBDIR; return B_FALSE; } } else { // to the side and not solid if (error) *error = E_BADCLIMBDIR; return B_FALSE; } } } // if okay, now drop through to below code which checks // whether there are any lfs or objects in the way } else { // not climbing 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)) { rdata = cell->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, SZ_ANY)) { 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; } enum RELATIVEDIR getrelativedir(lifeform_t *lf, int dir) { int tempdir; if (lfhasflag(lf, F_AWARENESS)) return RD_FORWARDS; // facing N, dir N == forwards if (lf->facing == dir) return RD_FORWARDS; // facing N, dir NE == forwards tempdir = lf->facing + 1; if (tempdir > DC_NW) tempdir -= MAXDIR_COMPASS; if (tempdir == dir) return RD_FORWARDS; // facing N, dir NW == forwards tempdir = lf->facing - 1; if (tempdir < DC_N) tempdir += MAXDIR_COMPASS; if (tempdir == dir) return RD_FORWARDS; // facing N, dir E == sideways tempdir = lf->facing + 2; if (tempdir > DC_NW) tempdir -= MAXDIR_COMPASS; if (tempdir == dir) return RD_SIDEWAYS; // facing N, dir W == sideways tempdir = lf->facing - 2; if (tempdir < DC_N) tempdir += MAXDIR_COMPASS; if (tempdir == dir) return RD_SIDEWAYS; // anything else is backwards return RD_BACKWARDS; } 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 strafe) { int origtimespent; int dir; int tries = 0; int moveok,rv; enum ERROR why; if (lfhasflag(lf, F_DOESNTMOVE)) { rest(lf, B_TRUE); return B_TRUE; } // 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; } } } origtimespent = lf->timespent; rv = trymove(lf, dir, B_TRUE, strafe); if (restonfail && (rv || (lf->timespent == origtimespent))) { // ie move failed rest(lf, B_TRUE); } return rv; } // src is where something is // dst is what we are going away from // if srclf is set, it is used for cellwalkable() checks. ie. if move will be involuntary, don't set this! // 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 = E_OK; 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_real(c, dst, LOF_NEED, NULL, srclf, B_TRUE)) { 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 = E_OK; 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; } cell_t *getdodgecell(lifeform_t *lf) { int dir; cell_t *c,*poss[8]; int nposs = 0; for (dir = DC_N; dir <= DC_NW; dir++) { c = getcellindir(lf->cell, dir); if (c && cellwalkable(lf, c, NULL) && !celldangerous(lf, c, B_TRUE, NULL)) { poss[nposs++] = c; } } if (nposs) { return poss[rnd(0,nposs-1)]; } return NULL; } 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 wantannounce, int dodam) { int i,dam; char lfname[BUFLEN]; char newlfname[BUFLEN]; int seen; int mightfall = B_TRUE; lifeform_t *newlf; int preventchance = 0; enum LFSIZE lfsize; // can't knock non-solid things back if (getmaterialstate(getlfmaterial(lf)) != MS_SOLID) { return B_TRUE; } 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*45; } 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; preventchance -= 30; } // avoid being moved? lfsize = getlfsize(lf); if (lfhasflag(lf, F_GRAVBOOSTED)) { preventchance += 100; } if (lfsize > SZ_HUMAN ){ // 15% chance of avoidance per size > human. preventchance += ((lfsize - SZ_HUMAN) * 15); } if (pctchance(preventchance)) { if (cansee(player, lf)) { msg("%s stagger%s %s but hold%s %s %s.",lfname,isplayer(lf) ? "" : "s", getreldirname(getrelativedir(lf, dir)), isplayer(lf) ? "" : "s", isplayer(lf) ? "your" : "its", isairborne(lf, NULL) ? "position" : "ground"); } return B_TRUE; } breakgrabs(lf, B_TRUE, B_TRUE, B_TRUE); for (i = 0; i < howfar; i++) { if (moveclear(lf, dir, &reason)) { if ((i == 0) && seen && wantannounce) { msg("%s %s knocked %s!",lfname,is(lf), getreldirname(getrelativedir(lf, dir))); } trymove(lf, dir, B_FALSE, 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->id == BH_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) { snprintf(thing, BUFLEN, "%s %s", needan(newcell->type->name) ? "an" : "a",newcell->type->name); } else { strcpy(thing, "a wall"); } } else { // ie door or object getobname(rdata, thing, 1); } if (seen) { if (dodam) { msg("^%c%s slam%s into %s!^n",getlfcol(lf, CC_BAD), lfname,isplayer(lf) ? "" : "s", thing); } else { msg("%s stop%s %s on %s.^n", lfname, isplayer(lf) ? "" : "s", isplayer(lf) ? "yourself" : "itself", thing); } } if (dodam) { // 1d6 dam per cell pushed snprintf(buf, BUFLEN, "slamming into %s", thing); dam = rolldie(howfar, 6); losehp(lf, dam, DT_BASH, pusher, buf); } // stop moving i = howfar; // don't fall mightfall = B_FALSE; if (dodam) { // 20% chance per cell pushed if (pctchance(howfar*20)) criticalhit(NULL, lf, getrandomcorebp(lf, NULL), NULL, dam, 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*45); } // confer our remaining momentum on to them knockback(newcell->lf, dir, momentumleft, lf, fallcheckdiff, B_DOANNOUNCE, dodam); // 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 strafe, int onpurpose ) { int dir; int rv = B_TRUE; if (isblind(lf)) { dorandommove(lf, B_TRUE, B_TRUE, B_FALSE); 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, onpurpose, strafe); } 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 or attacks. // 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, int moved) { flag_t *f; int didmsg = B_FALSE; // effects which only happen if you actually moved (not attacked) if (moved) { if (!isairborne(lf, NULL) && lfhasflagval(lf, F_INJURY, IJ_HAMSTRUNG, NA, NA, NULL)) { if (!skillcheck(lf, SC_FALL, 80, 0)) { fall(lf, NULL, B_TRUE); if (isplayer(lf)) didmsg = B_TRUE; } } if (onein(10) && lfhasflagval(lf, F_INJURY, IJ_TAILLACERATED, NA, NA, NULL)) { fall(lf, NULL, B_TRUE); if (isplayer(lf)) didmsg = B_TRUE; } if (isbleeding(lf)) { if (hasbleedinginjury(lf, BP_LEGS)) { if (!bleedfrom(lf, BP_LEGS, B_FALSE)) { if (isplayer(lf)) msg("^BYou bleed!"); losehp(lf, 1, DT_DIRECT, NULL, "blood loss"); } } else { if (rnd(1,2) == 1) { bleed(lf, B_NOSPLATTER); } } } if (lfhasflag(lf, F_GERMS)) { object_t *o; for (o = lf->cell->obpile->first; o ; o = o->next) { if (isedible(o) && !hasflag(o->flags, F_TAINTED)) { addflag(o->flags, F_TAINTED, B_TRUE, NA, NA, NULL); } } } } if (lfhasflagval(lf, F_INJURY, IJ_WINGBLEED, NA, NA, NULL)) { flag_t *retflag[MAXCANDIDATES]; int nretflags,i; getflags(lf->flags, retflag, &nretflags, F_FLYING, F_NONE); for (i = 0; i < nretflags; i++) { if (retflag[i]->lifetime == FROMRACE) { if (!bleedfrom(lf, BP_WINGS, B_FALSE)) { if (isplayer(lf)) msg("^BYou bleed!"); losehp(lf, 1, DT_DIRECT, NULL, "blood loss"); } } } } f = lfhasflag(lf, F_PAIN); if (f) { if (!lfhasflag(lf, F_DRUNK)) { 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, int onpurpose) { object_t *o,*nexto; cell_t *precell; enum TEMPERATURE pretemp,posttemp; char obname[BUFLEN],lfname[BUFLEN],buf[BUFLEN]; lifeform_t *l; int didmsg = B_FALSE; flag_t *f; enum FLAG flying; int changedlev = B_FALSE; room_t *preroom = NULL, *postroom = NULL; //int preshop = -1; int prespeed = B_FALSE, postspeed = B_FALSE; int prewater = B_FALSE; int preseenbyplayer = B_FALSE; //vault_t *v; flag_t *retflag[MAXCANDIDATES]; int nretflags,i; int autoangry = B_FALSE; assert(newcell); getlfname(lf, lfname); if (gamemode == GM_GAMESTARTED) { if (isplayer(lf) || cansee(player, lf)) { needredraw = B_TRUE; } pretemp = getlftemp(lf); if (newcell->map != lf->cell->map) { changedlev = B_TRUE; lf->changinglev = B_TRUE; setlosdirty(lf); if (isplayer(lf)) { // remember the time which we exitted this map. lf->cell->map->lastplayervisit = curtime; } } if (changedlev) { // used for one of Yumi's effects addtempflag(lf->flags, F_JUSTENTERED, B_TRUE, NA, NA, NULL, isplayer(lf) ? 1 : 2); } // 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); if (isplayer(lf)) redrawresume(); } // remember current cell + room id prespeed = getmovespeed(lf); preroom = lf->cell->room; /* 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; } if (!isplayer(lf) && cansee(player, lf)) { preseenbyplayer = B_TRUE; } } else { prespeed = SP_NORMAL; preroom = NULL; //preshop = B_FALSE; prewater = B_FALSE; preseenbyplayer = B_FALSE; } precell = lf->cell; // move out... lf->cell->lf = NULL; // if required, relink lifeform to new map if (newcell->map != lf->cell->map) { if (gamemode == GM_GAMESTARTED) { if (isplayer(lf)) { statdirty = B_TRUE; } } relinklf(lf, newcell->map); if (gamemode == GM_GAMESTARTED) { if (isplayer(lf)) { // clear map to force redraw. wclear(gamewin); } } } if (gamemode == GM_GAMESTARTED) { // 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... if (gamemode == GM_GAMESTARTED) { postroom = lf->cell->room; postspeed = getmovespeed(lf); posttemp = getlftemp(lf); } // update new cell newcell->lf = lf; setlosdirty(lf); if (gamemode == GM_GAMESTARTED) { // update light /* if ((isplayer(lf) && changedlev) || lfproduceslight(lf, NULL)) { calclight(lf->cell->map); setlosdirty(lf); } */ //precalclos(lf); // refresh name of lf, in case it came into view. getlfname(lf, lfname); if (isplayer(lf) || cansee(player, lf)) { needredraw = B_TRUE; } didmsg = moveeffects(lf, B_TRUE); if (lfhasflag(lf, F_HIDING) && (getskill(lf, SK_STEALTH) < PR_BEGINNER)) { 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 flying = isairborne(lf, NULL); if (flying == F_NONE) { 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)) { if (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; } } else { if (isplayer(lf)) { msg("You enter the water."); didmsg = B_TRUE; } else if (cansee(player, lf)) { msg("%s enters the water.", lfname); didmsg = B_TRUE; } } // put out fires extinguishlf(lf); // stop sprinting stopsprinting(lf); noise(lf->cell, NULL, NC_OTHER, SV_TALK, "a splash.", NULL); } } } f = hasflag(o->flags, F_SHARP); if (f && hasbp(lf, BP_FEET) && !lfhasflag(lf, F_CAREFULMOVE) && !isairborne(lf, NULL)) { object_t *boots; // has boots on? boots = getouterequippedob(lf, 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); } } if (!lfhasflag(lf, F_CAREFULMOVE)) { if (cancrush(lf, o)) { int maxhp; // crush it 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 = strdup(obname); makeplural(&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 which is being crushed. //removeob(o, o->amt); getobhp(o, &maxhp); if (maxhp) { takedamage(o, maxhp, DT_CRUSH, lf); } else { removeob(o, ALL); } if (isplayer(lf)) { angergodmaybe(R_GODNATURE, 10, GA_ATTACKOBJECT); pleasegodmaybe(R_GODFIRE, 2); } continue; } if (hasflag(o->flags, F_DIMONDISTURB)) { f = hasflag(o->flags, F_PRODUCESLIGHT); getobname(o, obname, o->amt); if (f) f->val[0]--; if (!f || (f->val[0] <= 0)) { if (haslos(player, newcell)) { msg("%s dim%s and crumbles.",obname, (o->amt == 1) ? "s" : ""); didmsg = B_TRUE; } removeob(o, ALL); continue; } else { if (haslos(player, newcell)) { msg("%s dim%s slightly.",obname, (o->amt == 1) ? "s" : ""); didmsg = B_TRUE; } } } // end if dimondisturb } // 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,OBS1(o)); } else if (cansee(player, lf)) { msg("%s grab%s %s!",obname, OBS1(o), lfname); } } // objects which explode when you walk onto them f = hasflag(o->flags, F_EXPLODEONMOTION); if (f) { if (f->val[2] == B_IFACTIVATED) { if (isactivated(o)) { explodeob(o, f, f->val[1], NULL); break; // stop processing other objects, the explosion may have killed them. } } else { explodeob(o, f, f->val[1], NULL); break; // stop processing other objects, the explosion may have killed them. } } } // 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); } } } } */ } if (isplayer(lf) && !isblind(lf)) { // see the vault if (!preroom && postroom && postroom->vault) { f = hasflag(postroom->vault->flags, F_VAULTENTERTEXT); if (f) { msg("%s", f->text); more(); didmsg = B_TRUE; } } } // amulet of victimisation? if (hasequippedobid(lf->pack, OT_AMU_VICTIM)) { autoangry = B_TRUE; } // does anyone else see you? for (l = newcell->map->lf ; l ; l = l->next) { if (l != lf) { flag_t *alarm; if (cansee(l, lf)) { int dointerrupt = B_FALSE; if (autoangry && !isplayer(l) && !lfhasflag(l, F_HOSTILE)) { addflag(l->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); } // much larger creatures moving will cause our los to be recalculated if (getlfsize(lf) - getlfsize(l) >= 2) { setlosdirty(l); //precalclos(l); } if (isplayer(l) && !isplayer(lf)) { // player saw someone move if (areenemies(lf, l) || !isknownpeaceful(lf) ) { if (!preseenbyplayer) { char buf[BUFLEN]; // TODO: also check for isresting(l), if we have allies standing watch getlfnamea(lf, lfname); getmoveverbother(lf, buf); msg("%s %s into view.", lfname, buf); } dointerrupt = B_TRUE; // mark the observed race as known. if (!lf->race->known) { raceclass_t *rc; lf->race->known = B_TRUE; rc = findraceclass(lf->race->raceclass->id); if (rc) { practice(lf, rc->skill, 2); } } } } else if (isplayer(lf)) { // someone saw the player move. 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)) { // incredibly loud 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_CAR, NA, NULL); didmsg = B_TRUE; fightback(shk, lf); // shopkeeper attacks callguards(shk, lf); // guards come running } } } */ if (onpurpose && preseenbyplayer && !cansee(player, lf) && !changedlev) { if (isadjacent(lf->cell, precell)) { // ie don't say this if we teleported/jumped if (areenemies(player, lf)) { char buf[BUFLEN]; int behind = B_FALSE; getmoveverbother(lf, buf); real_getlfnamea(lf, lfname, NULL, B_NOSHOWALL, B_CURRACE); if (isbehind(lf, player) && isadjacent(lf->cell, player->cell)) { behind = B_TRUE; } msg("%s %s %s.", lfname, buf, behind ? "behind you" : "out of view"); } } } if (pretemp != posttemp) { char colchar; char punc; switch (posttemp) { case T_VCOLD: case T_VHOT: colchar = 'B'; punc = '!'; break; case T_COLD: case T_HOT: colchar = 'w'; punc = '!'; break; case T_WARM: case T_CHILLY: case T_NORMAL: colchar = 'n'; punc = '.'; break; } if (isplayer(lf)) { if (posttemp == T_NORMAL) { msg("^%cThe temperature is now more comfortable%c", colchar, punc); didmsg = B_TRUE; } else { msg("^%cYou feel %s%c", colchar, gettemperaturename(posttemp), punc); didmsg = B_TRUE; } } else if (cansee(player, lf) && (getlorelevel(player, getraceclass(lf)) >= PR_BEGINNER)) { if (posttemp != T_NORMAL) { msg("%s looks %s%c", lfname, gettemperaturename(posttemp), punc); didmsg = B_TRUE; } } } getflags(lf->flags, retflag, &nretflags, F_DAMAGEGROUNDOBS, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; // will we cause damage to ground objects ? if (!isairborne(lf, NULL) || (f->val[2] == B_TRUE)) { damageallobs(NULL, lf->cell->obpile, f->val[0], f->val[1], lf); } } // status bar if (isplayer(lf)) { if ((prespeed != postspeed) || (pretemp != posttemp)) { statdirty = B_TRUE; } } } // end if gamestarted lf->changinglev = B_FALSE; return didmsg; } int movelfsoutofway(cell_t *newcell) { // anyone in the way? if (newcell->lf) { cell_t *c; // if they are, find somewhere to move them. c = getrandomadjcell(newcell, &ccwalkable, B_ALLOWEXPAND); if (c) { // move them there movelf(newcell->lf, c, B_FALSE); } else { return B_TRUE; } } return B_FALSE; } // 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,*nexto; char lfname[BUFLEN]; int didmsg; int predark = B_FALSE,postdark = B_FALSE; int willmakenoise = B_TRUE; // for the player, moving means that we don't regenerate stamina. // this is the equivilant of losing the same amount of stamina which we // would regenerate, only it avoids constantly redrawing the status // bar every single move. addflagifneeded(lf->flags, F_TOOKACTION, B_TRUE, NA, NA, NULL); addflagifneeded(lf->flags, F_MOVED, B_TRUE, NA, NA, NULL); 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, onpurpose); 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, getlfsize(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, getlfsize(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); } } } } if (isplayer(lf)) { // see/feel objects on ground int numobs; numobs = countnoncosmeticobs(newcell->obpile, B_TRUE, B_TRUE); if ((numobs == 0) && !newcell->writing) { // just clear the message buffer if (!didmsg) clearmsg(); } else { // tell player what is here if (onpurpose && !lfhasflag(player, F_RAGE)) { dolook(newcell, B_FALSE); } } } } // maybe make some noise // (stealth check to avoid this) willmakenoise = B_TRUE; if (lfhasflag(lf, F_HIDING)) { if (skillcheck(lf, SC_STEALTH, 70, isairborne(lf, NULL) ? 10 : 0)) { willmakenoise = B_FALSE; } } // swapping places? if (willmakenoise) { if (isairborne(lf, NULL)) { makenoise(lf, N_FLY); } else { makenoise(lf, N_WALK); } } // slip on blood in new cell? if (!isairborne(lf, NULL) && !isswimming(lf)) { int slip; object_t *slipob; if (!lfhasflag(lf, F_CAREFULMOVE)) { 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 ) { flag_t *f; nexto = o->next; f = hasflag(o->flags, F_TRAP); if (f) { if (strstr(f->text, "ground") && isairborne(lf, NULL)) { // nothing happens } else { 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 strafe) { int dir; int rv = B_TRUE; int db = B_FALSE; if (lfhasflag(lf, F_DEBUG)) db = B_TRUE; if (isblind(lf)) { if (db) dblog(".oO { i am blind - movetorwards calling dorandommove. }"); dorandommove(lf, B_TRUE, B_TRUE, B_FALSE); return B_FALSE; } // move towards them if (isadjacent(lf->cell, dst)) { dir = whichwayto(lf->cell, dst, lf, B_TRUE); } else { dir = getdirtowards(lf->cell, dst, lf, B_TRUE, dirtype); } if (dir != D_NONE) { if (db) { dblog(".oO { dir from %d,%d -> %d,%d is %s }", lf->cell->x, lf->cell->y, dst->x, dst->y, getdirname(dir)); } if (isplayer(lf)) { rv = trymove(lf, dir, B_TRUE, strafe); } else { flag_t *f; f = lfhasflag(lf, F_SLIPPEDLASTTURN); if (f) { killflag(f); rv = trysneak(lf, dir); } else { rv = trymove(lf, dir, B_TRUE, strafe); } } } else { if (db) dblog(".oO { dir from %d,%d -> %d,%d is DT_NONE ! }", lf->cell->x, lf->cell->y, dst->x, dst->y); } return rv; } int move_will_hurt(lifeform_t *lf) { flag_t *retflag[MAXCANDIDATES],*retflag2[MAXCANDIDATES]; int nretflags,nretflags2,i,n; 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: if (!isairborne(lf, NULL)) return B_TRUE; break; case IJ_WINGBLEED: getflags(lf->flags, retflag2, &nretflags2, F_FLYING, F_NONE); for (n = 0; n < nretflags2; n++) { if (retflag[n]->lifetime == FROMRACE) { return B_TRUE; } } break; default: break; } } } if (hasbleedinginjury(lf, BP_LEGS) && !isairborne(lf, NULL)) { return B_TRUE; } 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; enum ATTRBRACKET wis = AT_AVERAGE; doorcell = o->pile->where; assert(doorcell); if (lf) { if (isplayer(lf) && godprayedto(R_GODMERCY)) { wis = AT_VHIGH; } else { wis = getattrbracket(getattr(lf, A_WIS), A_WIS, NULL); } } getobname(o, obname, 1); if (!isdoor(o, NULL)) { return B_TRUE; } if (lf) { if (isclimbing(lf)) { if (isplayer(lf)) msg("You can't open doors while climbing!"); return B_TRUE; } else if (isswimming(lf)) { if (isplayer(lf)) msg("You can't open doors while swimming!"); return B_TRUE; } } f = hasflag(o->flags, F_OPEN); if (f) { if (lf && isplayer(lf)) { msg("It is already open!"); } return B_TRUE; } else { int wasjammed = B_FALSE; int touchrv = B_FALSE; if (lf) { if (!canopendoors(lf)) { if (isplayer(lf)) { msg("You have no hands with which to open the door!"); } else { // ai will try to break down doors if they know the lf they are // chasing is behind it. ie. if they can still "see" them (maybe via // scent) or can hear them. // if (willattackdoors(lf)) { attackcell(lf, doorcell, B_TRUE); } else { loseaitargets(lf); } } return B_TRUE; } if (isplayer(lf)) { int dir; cell_t *pastdoorcell; // has known trap? if (hasflagval(o->flags, F_TRAPPED, NA, NA, B_TRUE, NULL)) { if (wis >= AT_AVERAGE) { char ch; snprintf(buf, BUFLEN,"Really open %s?", obname); ch = askchar(buf,"yn","n", B_TRUE, B_FALSE); if (ch != 'y') { msg("Cancelled."); return B_TRUE; } } } // hear water behind it? dir = getdirtowards(doorcell, lf->cell, NULL, B_FALSE, DT_ORTH); pastdoorcell = getcellindir(doorcell, dir); if (pastdoorcell && getcellwaterdepth(pastdoorcell, NULL)) { if (getskill(lf, SK_LISTEN) || haslos(lf, pastdoorcell)) { if (wis >= AT_AVERAGE) { char ch; snprintf(buf, BUFLEN,"%s running water behind %s. Really open it?", haslos(lf, pastdoorcell) ? "There is" : "You can hear", obname); ch = askchar(buf,"yn","n", B_TRUE, B_FALSE); if (ch != 'y') { msg("Cancelled."); return B_TRUE; } } } } } // stop sprinting stopsprinting(lf); taketime(lf, getactspeed(lf)); touchrv = touch(lf, o); } // end if lf // trapped? if (lf && hasflag(o->flags, F_TRAPPED)) { if (triggerattachedtraps(o, lf, B_TRUE)) { if (isplayer(lf)) stoppathfinding(lf); return B_TRUE; } } if (touchrv) return B_TRUE; // locked? if (hasflag(o->flags, F_LOCKED)) { if (lf) { if (isplayer(lf)) { msg("The %s is locked.", noprefix(obname)); } else { if (willattackdoors(lf)) { attackcell(lf, doorcell, B_TRUE); return B_FALSE; } else { noise(doorcell, NULL, NC_OTHER, SV_TALK, "a door handle rattling.", NULL); loseaitargets(lf); } } } if (isplayer(lf)) stoppathfinding(lf); return B_TRUE; } else { // ie. door not locked, but it might be jammed int openit = B_TRUE; f = hasflag(o->flags, F_JAMMED); if (f && lf) { wasjammed = B_TRUE; openit = B_FALSE; if (isplayer(lf) && (f->val[1] != B_TRUE)) { // not known yet msg("The %s is jammed.", noprefix(obname)); openit = B_FALSE; f->val[1] = B_TRUE; } else { openit = unjam(lf, o); } // end if jammedknown } // end if jammed 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 (wasjammed) { if (isplayer(lf)) { msg("You force %s open!",obname); } else { if (cansee(player, lf)) { getlfname(lf, buf); capitalise(buf); msg("%s forces %s open!",buf, obname); } else if (haslos(player, doorcell)) { capitalise(obname); msg("%s bursts open!",obname); } else { char noisebuf[BUFLEN]; snprintf(noisebuf, BUFLEN, "%s bursting open!", obname); noise(doorcell, NULL, NC_OTHER, SV_CAR, noisebuf, NULL); } } } else { if (isplayer(lf)) { msg("You open %s.",obname); } else { if (cansee(player, lf)) { getlfname(lf, buf); capitalise(buf); msg("%s opens %s.",buf, obname); } else if (haslos(player, doorcell)) { capitalise(obname); msg("%s opens.",obname); } else { char noisebuf[BUFLEN]; snprintf(noisebuf, BUFLEN, "%s opening.", obname); noise(doorcell, NULL, NC_OTHER, SV_TALK, noisebuf, NULL); } } } // end if wasjammed } // end if lf 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, SV_TALK, "a door opening.", NULL); } } } else { // !openit if (isplayer(lf)) { stoppathfinding(lf); } } return B_FALSE; } } // end if door locked 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 && !canopendoors(lf)) { 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) && isimpassableob(o, NULL, getobsize(o))) { 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); if (hasflag(o->type->flags, F_BLOCKSVIEW)) { copyflag(o->flags, o->type->flags, F_BLOCKSVIEW); } if (lf) { // stop sprinting stopsprinting(lf); taketime(lf, getactspeed(lf)); if (touch(lf, o)) { return B_TRUE; } 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); } } } if (player && haslos(player, cell)) { needredraw = B_TRUE; drawscreen(); } } return B_FALSE; } int tryrun(lifeform_t *lf, int dir) { int willrun = B_TRUE,rv; if (!moveclear(lf, dir, NULL)) { // don't double move into monsters, etc willrun = B_FALSE; } rv = trymove(lf, dir, B_TRUE, B_TRUE); if (!rv && willrun) { // successful move - start running. addflag(lf->flags, F_RUNNING, dir, B_FALSE, getleftrightwalls(lf), NULL); } return rv; } 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, B_TRUE); dir = chartodir(ch); if (dir == D_NONE) return B_TRUE; } else { return B_TRUE; } } addflag(lf->flags, F_CAREFULMOVE, NA, NA, NA, NULL); trymove(lf, dir, B_TRUE, B_FALSE); killflagsofid(lf->flags, F_CAREFULMOVE); 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, NULL) ? "through the air" : "along the ground"); } movelf(lf, dst, B_FALSE); 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 onpurpose, int *didmsg) { object_t *o, *nexto; char buf[BUFLEN]; flag_t *f; int attacking = B_FALSE; if (cell && cell->lf && !canswapwith(lf, cell->lf)) attacking = B_TRUE; // climbing along a wall? if (isplayer(lf) && isclimbing(lf)) { if (cell != getcellindir(lf->cell, lf->facing)) { // not dropping off the wall if (isexhausted(lf)) { // this shouldn't be able to happen, since if you run out // of stamina while on a wall you'll fall off during startlfturn(). msg("You are too tired to climb!"); reason = E_OK; return B_TRUE; } else { // must pass a skill check to keep climbing! if (!skillcheck(lf, SC_CLIMB, getcellclimbdifficulty(cell), 0)) { msg("^bYou lose your footing!"); stopclimbing(lf, B_FALSE); reason = E_OK; return B_TRUE; } if (!lfhasflag(lf, F_SPIDERCLIMB)) { modstamina(lf, -1); } } } } // too tired? (if we're attacking, leave the 'too tired' message to // the attack code) // note: this only impacts the plaeyr /* if (!attacking && !getstamina(lf) && isplayer(lf)) { msg("You are too tired to move!"); //} else { // this doesn't count as an action for the player, but it // does for monsters // taketime(lf, getmovespeed(lf)); //} reason = E_OK; return B_TRUE; } */ // checks which only happen if we're MOVING (ie not attacking) // 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 (!attacking) { if (lfhasflag(lf, F_GRAVBOOSTED)) { // make a saving throw to move if (skillcheck(lf, SC_STR, 125, 0)) { if (isplayer(lf)) { msg("You manage to %s despite the strong gravity.", isprone(lf) ? "stand" : "move"); if (didmsg) *didmsg = B_TRUE; } } else { 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; // 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 (o->type->id == OT_WEB) { if (isairborne(lf, NULL)) { checkmod -= 5; } if (lf->race->raceclass->id == RC_INSECT) { // basically impossible. checkmod -= 20; if (!isplayer(lf)) { // AND you will never escape! getsweaker = 0; } } } getlfname(lf,lfname); real_getobname(o, buf, o->amt, B_PREMODS, B_NOCONDITION, B_BLINDADJUST, B_NOBLESSINGS, B_NOUSED, B_NOSHOWALL); 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, NULL); } taketime(lf, getmovespeed(lf)); reason = E_OK; return B_TRUE; } } } } // are we on the ground? if (isprone(lf) && (!isplayer(lf) || !attacking) && onpurpose) { int willstand = B_FALSE; if (isplayer(lf)) { if (!attacking) { // player can attack from the ground willstand = B_TRUE; } } else { // monsters always stand up willstand = B_TRUE; } if (willstand) { standup(lf); reason = E_OK; // doesn't count as a move for monster who were feigning death! if (!isplayer(lf) && lfhasflag(lf, F_FEIGNINGDEATH)) { return B_FALSE; } return B_TRUE; } } // slipping on something before moving? if (!attacking && onpurpose) { if (cell && !isairborne(lf, NULL)) { // use 'cell' rather than lf->cell so that we can skip the check when using stairs. int slip; object_t *slipob; if (!lfhasflag(lf, F_CAREFULMOVE)) { 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 && onpurpose) { 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, 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, 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; } int rotatedir(int origdir, enum TURNDIR whichway, int amt) { int newdir,mod,i; newdir = origdir; if (whichway == TD_RIGHT) { mod = 1; } else { mod = -1; } for (i = 0; i < amt; i++) { newdir += mod; if (newdir > DC_NW) newdir = DC_N; if (newdir < DC_N) newdir = DC_NW; } return newdir; } void standup(lifeform_t *lf) { 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); if (killflagsofid(lf->flags, F_FEIGNINGDEATH)) { // have to redraw the screen so that the corpse glyph will // change into the lf's glyph if (cansee(player, lf)) needredraw = B_TRUE; } // monsters don't take time to stand up if they were feigning death! if (!isplayer(lf) && lfhasflag(lf, F_FEIGNINGDEATH)) { } else { int baseunits = 0,armourunits = 0,totunits = 0,howlong; float quartermax; enum LFSIZE sz; // time to get up depends on size and armour // base time depends on size sz = getlfsize(lf); if (sz < SZ_HUMAN) { baseunits = 0; } else if (sz == SZ_HUMAN) { baseunits = 1; } else if (sz == SZ_LARGE) { baseunits = 2; } else { // ie. SZ_HUGE or larger baseunits = 3; } // 1*movespeed for every 1/4 of maxcarryweight being worn. quartermax = getmaxcarryweight(lf) / 4; if (quartermax > 0) { armourunits = (getequippedweight(lf) / quartermax); } totunits = baseunits + armourunits; howlong = getmovespeed(lf) * totunits; if (howlong > 0) { taketime(lf, howlong); } } } void swapplaces(lifeform_t *lf1, lifeform_t *lf2, int changedir1, int changedir2, int onpurpose) { cell_t *cell1,*cell2; int tempfacing; cell1 = lf1->cell; cell2 = lf2->cell; // make lf who is there vanish temporarily... cell2->lf = NULL; lf2->cell = NULL; // make moving lf vanish temporarily... //cell1->lf = NULL; //lf1->cell = NULL; // move you moveto(lf1, cell2, onpurpose, B_FALSE); // move them... lf2->cell = cell1; cell1->lf = lf2; tempfacing = lf1->facing; if (changedir1) { setfacing(lf1, lf2->facing); } if (changedir2) { setfacing(lf2, tempfacing); } // remember that we just swapped, and this counts as a move if (!isplayer(lf1)) { if (!hasflag(lf1->flags, F_NOSWAP)) addflag(lf1->flags, F_NOSWAP, B_TRUE, NA, NA, NULL); addflagifneeded(lf1->flags, F_MOVED, B_TRUE, NA, NA, NULL); addflagifneeded(lf1->flags, F_TOOKACTION, B_TRUE, NA, NA, NULL); } setlosdirty(lf1); setlosdirty(lf2); // ie. asleep, meditating or unconscious if (lfhasflag(lf2, F_ASLEEP)) { stir(lf2, SV_TALK, 1, 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, &ccwalkable, 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, B_FALSE); if (cansee(player, lf)) { redraw(); // redraw screen } if (isplayer(lf)) { msg("Suddenly, your surroundings appear different!"); } else if (cansee(player, lf)) { getlfname(lf, buf); if (getraceclass(lf) == RC_GOD) { msg("^w%s %s!", buf, getflagtext(lf->flags, F_GODTEXTAPPEAR)); } else { 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, B_TRUE)) { dolook(lf->cell, B_FALSE); } // for enemies, if we teleported and now can't see the person(s) we were fleeing // from, stop fleeing. if (!isplayer(lf)) { flag_t *retflag[MAXCANDIDATES]; int nretflags,i; getflags(lf->flags, retflag, &nretflags, F_FLEEFROM, F_NONE); for (i = 0; i < nretflags; i++) { lifeform_t *thisone; thisone = findlf(lf->cell->map, retflag[i]->val[0]); if (thisone && !haslof(lf->cell, thisone->cell, LOF_WALLSTOP, NULL)) { killflag(retflag[i]); } } } return B_FALSE; } // trigger an actual trap object (ie. OC_TRAP) void triggertrap(lifeform_t *lf, object_t *o, object_t *trapob, cell_t *where) { char triggerer[BUFLEN]; char trapname[BUFLEN]; int wants; flag_t *wassecret = NULL; wassecret = hasflag(trapob->flags, F_SECRET); 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); } if (lf && wassecret && isplayer(lf) && godprayedto(R_GODMERCY)) { enum PIETYLEV plev; plev = getpietylev(R_GODMERCY, NULL, NULL); if ((plev >= PL_PLEASED) && pctchance(plev*33)) { godsay(R_GODMERCY, B_TRUE, "We all make mistakes..."); // bamf away dospelleffects(NULL, OT_S_BLINK, 1, lf, NULL, lf->cell, B_UNCURSED, NULL, B_FALSE, NULL); } } // NOTE: after trapeffects(), oo might be killed. trapeffects(trapob, trapob->type->id, where, NULL); if (lf) interrupt(lf); } int trymove(lifeform_t *lf, int dir, int onpurpose, int strafe) { cell_t *cell; enum ERROR errcode; char buf[BUFLEN]; int dontclearmsg = B_FALSE; int moveok; int rndmove = B_FALSE; int howlong; int reldir; int srcmoney = 0; int prebattle = B_FALSE; int prebattlearmed = B_FALSE; flag_t *fleeing = NULL; // are we next to an enemy who we can see? prebattle = isinbattle(lf, B_NODISTANT, B_FALSE); if (isplayer(lf)) { // only care about this for players prebattlearmed = isinbattle(lf, B_NODISTANT, B_ONLYIFARMED); } howlong = getmovespeed(lf); reldir = getrelativedir(lf, dir); fleeing = isfleeing(lf); if (isclimbing(lf)) strafe = B_TRUE; if (onpurpose) { if (isplayer(lf)) { srcmoney = countmoney(lf->cell->obpile); } if ((reldir != RD_FORWARDS) && !lfhasflag(lf, F_AWARENESS)) { // if the given dir is behind us, just turn. if (!strafe) { int prerotated = lf->rotated; takerotationtime(lf); setfacing(lf, dir); drawscreen(); if (isplayer(lf) && !lfhasflag(lf, F_AICONTROLLED) && !lfhasflag(lf, F_RUNNING)) { addflagifneeded(lf->flags, F_TURNED, B_TRUE, NA, NA, NULL); lf->turncounter++; if (lf->turncounter >= 5) { // get dizzy for 2 turns addtempflag(lf->flags, F_CONFUSED, B_TRUE, NA, NA, NULL, 3); } } // check for fleeing... if (isplayer(lf) && !isdead(lf)) { if (prebattlearmed && !isinbattle(lf, B_NODISTANT, B_ONLYIFARMED) && onpurpose) { angergodmaybe(R_GODBATTLE, 5, GA_COWARD); } } if (isplayer(lf)) { return B_FALSE; } else { // for ai, only return if this wasn't our 'free' turn action. if (prerotated) { return B_FALSE; } } } else { // player can't strafe while stuck. if (isstuck(lf) && isplayer(lf)) { msg("You can't move %s while stuck!", (reldir == RD_BACKWARDS) ? "backwards" : "sideways"); return B_TRUE; } } } } rndmove = willmoverandomly(lf); if (rndmove) { dir = rnd(DC_N, DC_NW); strafe = B_TRUE; } cell = getcellindir(lf->cell, dir); // warn if moving will cause damage /* if (cell && !cell->lf && onpurpose && isplayer(lf) ) { if (!confirm_injury_action(BP_LEGS, DT_SLASH, "move")) { return B_TRUE; } } */ // warn before moving onto dangerous cells if (onpurpose && isplayer(lf) && !lfhasflag(lf, F_CAREFULMOVE) && !rndmove && !lfhasflag(lf, F_RAGE)) { char ques[BUFLEN]; char ch; if (cell && celldangerous(lf, cell, B_TRUE, &errcode)) { 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, B_FALSE); if (ch != 'y') { return B_TRUE; } } if (isclimbing(lf) && (cell != getcellindir(lf->cell, lf->facing)) && (getstamina(lf) - 1 <= 0)) { snprintf(ques, BUFLEN, "Climbing further will exhaust you - continue?"); ch = askchar(ques, "yn","n", B_TRUE, B_FALSE); 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, onpurpose, &dontclearmsg)) { // failed? if (fleeing) taketime(lf, getmovespeed(lf)); // still take time 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/scents in our current cell. addtrail(lf, lf->cell, dir, B_TRUE, B_TRUE); // 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)) { char dirbuf[BUFLEN]; // update text field snprintf(dirbuf, BUFLEN, "%d", dir); changeflagtext(tf, dirbuf); } } } } } if (errcode == E_STOPCLIMBING) { stopclimbing(lf, B_TRUE); } else { // now move to new cell moveto(lf, cell, rndmove ? B_FALSE : onpurpose, dontclearmsg); // take some time if (onpurpose) { // strafing sideways/backwards takes longer if (strafe && !lfhasflag(lf, F_AWARENESS)) { object_t *o; switch (reldir) { //case RD_SIDEWAYS: howlong = pctof(125, howlong); break; case RD_SIDEWAYS: break; case RD_BACKWARDS: switch (getskill(lf, SK_ATHLETICS)) { case PR_INEPT: howlong = pctof(150, howlong); break; case PR_NOVICE: case PR_BEGINNER: howlong = pctof(125, howlong); break; default: break; } break; case RD_FORWARDS: if (hasactivespell(lf, OT_S_TAILWIND) || hasequippedobid(lf->pack, OT_ROLLERSKATES)) { // 50% faster if moving forwards howlong = pctof(50, howlong); } o = hasequippedobid(lf->pack, OT_JETSKATES); if (o && isactivated(o) && (getcharges(o) > 0)) { // super fast if moving forwards howlong = pctof(10, howlong); } break; default: break; } } limit(&howlong, SP_GODLIKE, NA); taketime(lf, howlong); if (!rndmove && !strafe) setfacing(lf, dir); // face the way we moved } } // 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, B_FALSE); } } } } } // train if (isswimming(lf)) { practice(lf, SK_SWIMMING, 1); } else if (isclimbing(lf)) { practice(lf, SK_CLIMBING, 1); } // autopickup objects? if (isplayer(lf) && getoption(OPT_RETRIEVE_MISSILES) && !lfhasflag(lf, F_LEVITATING)) { object_t *o,*nexto; for (o = lf->cell->obpile->first ; o ; o = nexto) { nexto = o->next; if (hasflag(o->flags, F_PLAYERMISSILE) && canpickup(lf, o, o->amt)) { killflagsofid(o->flags, F_PLAYERMISSILE); pickup(lf, o, o->amt, B_TRUE, B_TRUE); } } } } else { // ie !moveok object_t *inway = NULL; int door, dooropen; reason = errcode; switch (errcode) { case E_BADCLIMBDIR: if (isplayer(lf)) msg("You can't climb in that direction."); break; case E_OFFMAP: if (lf->cell->map->region->rtype->id == BH_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)) { char buf2[BUFLEN]; getmoveverbother(lf, buf2); getlfname(lf, buf); msg("%s %s into a %s.", buf, buf2, cell ? cell->type->name : "wall"); } if (cell && isplayer(lf) && haslos(lf, cell)) { object_t *door; door = hassecretdoor(cell->obpile); if (door) { msg("^gThere seems to be a secret door here!"); killflagsofid(door->flags, F_SECRET); needredraw = B_TRUE; } } //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) || iswoozy(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); } } } else { snprintf(buf, BUFLEN, "%sing into a %s", getmoveverb(lf), cell ? cell->type->name : "wall"); losehp(lf, 1, DT_BASH, NULL, buf); } } if (onpurpose || fleeing) taketime(lf, getmovespeed(lf)); break; case E_DOORINWAY: // can't open doors while climbing 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 if (opendoor(lf, inway)) { // failed. if (isplayer(lf)) stoppathfinding(lf); } else { // opening a door counts as a successful move. reason = E_OK; } } 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 || fleeing) taketime(lf, getmovespeed(lf)); if (isplayer(lf)) stoppathfinding(lf); } } else { if (cansee(player, lf)) { char buf2[BUFLEN]; getmoveverbother(lf, buf2); getlfname(lf, buf); msg("%s %s into a door.", buf, buf2); } snprintf(buf, BUFLEN, "%sing into a door", getmoveverb(lf)); losehp(lf, 1, DT_BASH, NULL, buf); if (onpurpose || fleeing) taketime(lf, getmovespeed(lf)); if (isplayer(lf)) stoppathfinding(lf); } } else { if (lfhasflag(lf, F_RAGE) || (!canopendoors(lf) && willattackdoors(lf))) { // attack it return attackcell(lf, cell, B_FALSE); } else { // try to open it if (opendoor(lf, inway)) { // fail if (isplayer(lf)) stoppathfinding(lf); } else { // opening a door counts as a successful move. reason = E_OK; } } } } break; case E_OBINWAY: inway = (object_t *)rdata; // walked into someone who was feigning death? if (inway && hasflag(inway->flags, F_ISMONSTER)) { lifeform_t *newlf; if (haslos(lf, cell)) { char inwayname[BUFLEN]; getobname(inway, inwayname, 1); msg("^B%s starts to move!", inwayname); more(); } // reaveal newlf = reveal_pretendob(inway); if (newlf) { turntoface(newlf, lf->cell); aiattack(newlf, lf, aigetchasetime(newlf)); } if (onpurpose || fleeing) taketime(lf, getmovespeed(lf)); return B_FALSE; } // 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 for you 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 (inway) { // walking into your own magic barrier? if ((inway->type->id == OT_MAGICBARRIER) && hasflagval(inway->flags, F_CREATEDBY, lf->id, NA, NA, NULL)) { enum OBTYPE barriertype; int x,y; cell_t *cc; barriertype = inway->type->id; // vanish this and any other magical barriers we made. for (y = 0; y < cell->map->h; y++) { for (x = 0; x < cell->map->w; x++) { cc = getcellat(cell->map, x,y); if (cc) { object_t *oo,*nextoo; int ndone = 0; for (oo = cc->obpile->first ; oo ; oo = nextoo) { nextoo = oo->next; if ((oo->type->id == barriertype) && hasflagval(oo->flags, F_CREATEDBY, lf->id, NA, NA, NULL)) { takedamage(oo, 999, DT_DIRECT, NULL); ndone++; } } if (ndone) { removedeadobs(cc->obpile); } } } } // NOW is the move possible? if (moveclear(lf, dir, &errcode)) { moveto(lf, cell, B_TRUE, B_TRUE); } } else if (hasflag(inway->flags, F_CLIMBOBSTACLE) && haslos(lf, cell)) { int doclimb = B_TRUE; if (isplayer(lf)) { char ch; char obname[BUFLEN]; getobname(inway, obname, 1); snprintf(buf, BUFLEN,"Attempt to climb onto %s?", obname); ch = askchar(buf,"yn","n", B_TRUE, B_FALSE); if (ch != 'y') { msg("Cancelled."); doclimb = B_FALSE; } } if (doclimb) { turntoface(lf, cell); startclimbing(lf); } } else { if (isplayer(lf)) { char obname[BUFLEN],text[BUFLEN]; if (haslos(lf, cell)) { getobname(inway, obname, 1); } else { strcpy(obname, "something"); } snprintf(text, BUFLEN, "There is %s in your way.",obname); 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 (initiatemove(lf, cell, onpurpose, &dontclearmsg)) { // failed? return B_TRUE; } // walking backwards/sideways into someone if (!lfhasflag(lf, F_AWARENESS) && (getrelativedir(lf, dir) != RD_FORWARDS)) { char lfname[BUFLEN]; char inwayname[BUFLEN]; getlfname(cell->lf, inwayname); if (isplayer(lf)) { msg("You bump into %s.", inwayname); } else if (isplayer(cell->lf)) { getlfname(lf, lfname); msg("%s bumps into you.", lfname); } else if (cansee(player, lf)) { getlfname(lf, lfname); msg("%s bumps into %s.", lfname, inwayname); } if (onpurpose || fleeing) taketime(lf, getmovespeed(lf)); } else { if (canswapwith(lf, cell->lf)) { lifeform_t *lfinway; char lfname[BUFLEN]; strcpy(lfname, "__not used yet__"); // otherwise swap locations. lfinway = cell->lf; // need to get the name of whoever you swapped with // BEFORE moving, as afterwards we may no longer be // able to see it. if (isplayer(lf)) { getlfname(lfinway, lfname); } swapplaces(lf, lfinway, B_NOCHANGEDIR, B_NOCHANGEDIR, onpurpose); // need to print this AFTER moving, because otherwise the // act of moving will clear the message line and we'll never // see it. if (isplayer(lf)) { if (isunconscious(lfinway)) { msg("You swap places with the unconscious %s.", noprefix(lfname)); } else if (isasleep(lfinway)) { msg("You swap places with the sleeping %s.", noprefix(lfname)); } else { msg("You swap places with %s.", lfname); } dontclearmsg = B_TRUE; } if (isplayer(lf)) { dontclearmsg = B_TRUE; } //if (onpurpose) taketime(lf, getmovespeed(lf)); taketime(lf, getmovespeed(lf)); } else { if (!onpurpose || canandwillmove(lf, dir, &errcode)) { int forceattack = B_FALSE; if (lfhasflag(lf, F_RAGE)) { forceattack = B_TRUE; } return attackcell(lf, cell, forceattack); } else { // won't attack for some reason. return B_TRUE; } } } break; case E_CANTMOVE: if (isplayer(lf)) { msg("You cannot move!"); } if (onpurpose || fleeing) 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 || fleeing) taketime(lf, getmovespeed(lf)); break; case E_PENTAGRAM: if (isplayer(lf)) { msg("You cannot seem to enter the pentagram."); } if (onpurpose || fleeing) taketime(lf, getmovespeed(lf)); break; case E_TOOHEAVY: if (isplayer(lf)) { msg("Your load is too heavy to move with!"); } if (onpurpose || fleeing) taketime(lf, getmovespeed(lf)); break; default: break; } } // purposely moving away from money? if (onpurpose && isplayer(lf) && srcmoney) { angergodmaybe(R_GODTHIEVES, srcmoney, GA_MONEY); } // check for fleeing... if (isplayer(lf) && !isdead(lf)) { if (prebattlearmed && !isinbattle(lf, B_NODISTANT, B_ONLYIFARMED) && onpurpose) { angergodmaybe(R_GODBATTLE, 10, GA_COWARD); } } if (reason != E_OK) { 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[MAXFOLLOWLFS]; int seen[MAXFOLLOWLFS]; 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]; char mverb[BUFLEN]; getmoveverbother(lf, mverb); getlfname(lf, lfname); curs_set(1); strcpy(dirname, getdirname(dir)); dirname[0] = tolower(dirname[0]); msg("%s %s to the %s...", lfname, mverb, 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 && isplayer(lf)) { // get list of adjacent allies getwhowillfollow(lf, NULL, adjally, seen, &nadjallies); for (n = 0; n < nadjallies; n++) { if (seen[n]) { char lname[BUFLEN]; real_getlfname(adjally[n], lname, NULL, B_NOSHOWALL, B_CURRACE); msg("%s follows you.", lname); } } } // 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, &ccwalkable, B_ALLOWEXPAND); if (c) { if (!initiatemove(adjally[n], NULL, B_TRUE, NULL)) { movelf(adjally[n], c, B_FALSE); taketime(adjally[n], getmovespeed(adjally[n])); } } } } if (isplayer(lf)) { statdirty = B_TRUE; needredraw = B_TRUE; //calclight(lf->cell->map); setlosdirty(lf); //precalclos(lf); drawscreen(); } return B_FALSE; } // returns direction to an adjacent cell int whichwayto(cell_t *start, cell_t *end, lifeform_t *srclf, int wantcheck) { int i; cell_t *c; for (i = DC_N; i <= DC_NW; i++) { c = getcellindir(start, i); if (c == end) { int ok = B_FALSE; enum ERROR error = E_OK; if (wantcheck) { if (srclf) { if (canandwillmove(srclf, i, &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 (ok) { return i; } else { return getdirtowards(start, end, srclf, wantcheck, DT_ORTH); } } } return D_NONE; } int willmove(lifeform_t *lf, int dir, enum ERROR *error) { cell_t *cell; enum ATTRBRACKET iq; enum ATTRBRACKET wis; object_t *o; char buf[BUFLEN]; flag_t *f; //object_t *o; iq = getattrbracket(getattr(lf, A_IQ), A_IQ, NULL); wis = getattrbracket(getattr(lf, A_WIS), A_WIS, NULL); // default if (error) { *error = E_OK; rdata = NULL; } if (iswoozy(lf)) { return B_TRUE; } cell = getcellindir(lf->cell, dir); if (!cell) { // won't walk off map return B_FALSE; } // mosnters will never move on top of other monsters // pretending to be objects (ie. gargoyles) if (hasobwithflag(cell->obpile, F_ISMONSTER)) { if (error) *error = E_WONT; return B_FALSE; } if (celldangerous(lf, cell, B_TRUE, error)) { // if lfs are fleeing and have below average wisdom, // they will walk onto dangerous cells if (isfleeing(lf) && (wis < AT_AVERAGE)) { } else { if (error) *error = E_WONT; return B_FALSE; } } // dont move out of range of lifeobs, but attacking out of range lfs is ok if (!cell->lf) { f = lfhasflag(lf, F_LIFEOB); if (f) { if (!findnearbylifeob(cell, NA, f, NULL)) { if (error) *error = E_WONT; return B_FALSE; } } } // glyph of warding? if (cell->writing && strstr(cell->writing, "*WARD")) { char buf[BUFLEN]; char *bp, *p; int power; // extract number bp = buf; for (p = cell->writing; *p; p++) { if (isdigit(*p)) { *bp = *p; bp++; } } *bp = '\0'; power = atoi(buf); limit(&power, 1, NA); if ( gettr(lf) <= power) { 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; int goingtodiffroom = B_FALSE; roomid = f->val[0]; if (roomid == NA) { if (getroomid(lf->cell) != getroomid(cell)) { goingtodiffroom = B_TRUE; } } else { if ((getroomid(lf->cell) == roomid) && (getroomid(lf->cell) != getroomid(cell))) { goingtodiffroom = B_TRUE; } } if (goingtodiffroom) { if ((f->val[1] == B_MAYCHASE) && aihastarget(lf)) { // exception! } else { // won't go to a different room. 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 move anywhere which would put other non-allied lfs // within our territorial range. f = aihastarget(lf); if (f && (f->id == F_TARGETLF)) { } else { f = lfhasflag(lf, F_TERRITORIAL); if (f && !isfleeing(lf) && !lfhasflag(lf, F_RAGE)) { int tdist,i; tdist = f->val[0]; // is there a lf in sight, who is within our territorial // radius of the cell we are testing? for (i = 0; i < lf->nlos; i++) { lifeform_t *otherlf; otherlf = lf->los[i]->lf; if (otherlf && !areallies(lf, otherlf) && (getcelldist(cell, otherlf->cell) <= tdist)) { if (!isasleep(otherlf)) { if (error) *error = E_WONT; return B_FALSE; } } } } } // don't attack other monsters // or non-enemies // (unless we have targetted them) 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, or is KO'd/dead... if (!areenemies(lf, cell->lf) || isdead(cell->lf) || isunconscious(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 wisdom things... if (wis >= AT_AVERAGE) { // don't move if in pain if (move_will_hurt(lf) && !isfleeing(lf)) { if (error) *error = E_WONT; return B_FALSE; } } // look for avoided objects (because they are cursed or otherwise). for (o = cell->obpile->first ; o ; o = o->next) { flag_t *f; int oblit = 0; 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); } } oblit = obproduceslight(o); if (oblit) { if (isundead(lf)) { if (oblit >= gettr(lf)) { if (error) *error = E_WONT; return B_FALSE; } } if (lighthurtseyes(lf)) { if (wis >= AT_AVERAGE) { if (error) *error = E_WONT; return B_FALSE; } } } 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; }