nexus/move.c

2647 lines
59 KiB
C

#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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 enum ERROR reason;
extern void *rdata;
extern flag_t *retflag[];
extern int nretflags;
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 iq;
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 {
iq = getattrbracket(getattr(lf, A_IQ), A_IQ, NULL);
if ((iq >= 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)) {
// 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;
}
breakallgrabs(lf);
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);
sprintf(buf, "slamming into %s", thing);
losehp(lf, rnd(1,6), DT_BASH, pusher, buf);
// stop moving
i = howfar;
// don't fall
mightfall = B_FALSE;
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);
}
// 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 (isbleeding(lf)) {
if (rnd(1,2) == 1) {
bleed(lf);
}
}
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;
assert(newcell);
getlfname(lf, lfname);
if (isplayer(lf) || cansee(player, lf)) {
needredraw = B_TRUE;
}
if (newcell->map != lf->cell->map) {
changedlev = B_TRUE;
}
// update current cell + room id
prespeed = getmovespeed(lf);
preroom = lf->cell->roomid;
if (lf->cell->vault && hasflag(lf->cell->vault->flags, F_VAULTISSHOP)) {
preshop = lf->cell->roomid;
}
// getting out of water?
if (hasobwithflag(lf->cell->obpile, F_DEEPWATER)) {
prewater = B_TRUE;
}
// 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 = lf->cell->roomid;
postspeed = getmovespeed(lf);
// update new cell
newcell->lf = lf;
// update light
if (lfproduceslight(lf) || (changedlev && isplayer(lf))) {
calclight(lf->cell->map);
precalclos(lf);
}
if (isplayer(lf) || cansee(player, lf)) {
needredraw = B_TRUE;
}
// special effects when the player moves to a new map
if (changedlev && isplayer(lf)) {
mapentereffects(newcell->map);
}
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;
}
sprintf(buf, "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;
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 ((lf->cell->roomid == 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 (lf->cell->roomid != 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)) {
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)) {
char trapname[BUFLEN];
getobname(o, trapname, 1);
if (haslos(player, newcell)) {
msg("%s trigger%s %s!", lfname, isplayer(lf) ? "" : "s", trapname);
if (isplayer(lf)) more();
}
if (haslos(player, newcell)) {
// no longer hidden
killflagsofid(o->flags, F_SECRET);
}
// NOTE: after trapeffects(), o might be killed.
trapeffects(o, o->type->id, lf);
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 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;
sprintf(buf,"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;
sprintf(buf,"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 (!haslos(player, where)) {
// 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);
}
if (player && haslos(player, where)) {
needredraw = B_TRUE;
drawscreen();
}
}
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;
sprintf(ques, "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
sprintf(buf, "%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
sprintf(buf, "%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;
}
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/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)) {
if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_AVERAGE) {
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);
sprintf(ques, "Really %s into %s?", getmoveverb(lf), obname);
} else {
sprintf(ques, "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)) {
sprintf(buf, "%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 {
sprintf(buf, "%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);
sprintf(buf, "%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));
}
sprintf(buf, "%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)) {
getadjallies(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 ((lf->cell->roomid != cell->roomid)) {
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 ((lf->cell->roomid == roomid) && (lf->cell->roomid != cell->roomid)) {
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 (lfhasflag(lf, F_PAIN)) {
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;
sprintf(buf, "%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;
}