3675 lines
88 KiB
C
3675 lines
88 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 "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 int noredraw;
|
|
|
|
extern long curtime;
|
|
|
|
extern WINDOW *gamewin, *msgwin;
|
|
|
|
int canandwillmove(lifeform_t *lf, int dir, enum ERROR *error) {
|
|
if (isplayer(lf)) {
|
|
if (ispossiblemove(lf, dir)) {
|
|
return B_TRUE;
|
|
}
|
|
} else {
|
|
if (ispossiblemove(lf, dir) && willmove(lf, dir, error)) {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int isorthogonal(int dir) {
|
|
switch (dir) {
|
|
case D_N:
|
|
case D_E:
|
|
case D_S:
|
|
case D_W:
|
|
case DC_N:
|
|
case DC_E:
|
|
case DC_S:
|
|
case DC_W:
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
// ie. moving into a wall isn't possible
|
|
// moving into a lf/door IS possible since you will attack/open it
|
|
// moving while grabbed IS possible since you'll try to break free
|
|
int ispossiblemove(lifeform_t *lf, int dir) {
|
|
enum ERROR error;
|
|
if (moveclear(lf, dir, &error)) {
|
|
return B_TRUE;
|
|
} else {
|
|
object_t *inway = NULL;
|
|
switch (error) {
|
|
case E_OFFMAP:
|
|
if (lf->cell->map->region->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;
|
|
}
|
|
|
|
// 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)) {
|
|
if (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_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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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) && !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) && 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 (!getequippedob(lf->pack, BP_FEET)) {
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
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 i,dam;
|
|
char lfname[BUFLEN];
|
|
char newlfname[BUFLEN];
|
|
int seen;
|
|
int mightfall = B_TRUE;
|
|
lifeform_t *newlf;
|
|
int preventchance = 0;
|
|
enum LFSIZE lfsize;
|
|
|
|
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;
|
|
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) ? "position" : "ground");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
breakgrabs(lf, 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) strcpy(thing, newcell->type->name);
|
|
else strcpy(thing, "wall");
|
|
} else { // ie door or object
|
|
getobname(rdata, thing, 1);
|
|
}
|
|
if (seen) msg("^%c%s slam%s into %s!^n",getlfcol(lf, CC_BAD), lfname,isplayer(lf) ? "" : "s", thing);
|
|
snprintf(buf, BUFLEN, "slamming into %s", thing);
|
|
// 1d6 dam per cell pushed
|
|
dam = rolldie(howfar, 6);
|
|
losehp(lf, dam, DT_BASH, pusher, buf);
|
|
// stop moving
|
|
i = howfar;
|
|
// don't fall
|
|
mightfall = B_FALSE;
|
|
// 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*5);
|
|
}
|
|
// confer our remaining momentum on to them
|
|
knockback(newcell->lf, dir, momentumleft, lf, fallcheckdiff, B_TRUE); // 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 (lfhasflagval(lf, F_INJURY, IJ_HAMSTRUNG, NA, NA, NULL)) {
|
|
if (!skillcheck(lf, SC_FALL, 18, 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 (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) {
|
|
object_t *o,*nexto;
|
|
cell_t *precell;
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
addflag(lf->flags, F_JUSTENTERED, B_TRUE, NA, NA, NULL);
|
|
}
|
|
|
|
// 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)) noredraw = B_FALSE;
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
// 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);
|
|
|
|
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);
|
|
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)) {
|
|
object_t *boots;
|
|
// has boots on?
|
|
boots = getequippedob(lf->pack, BP_FEET);
|
|
if (!boots) {
|
|
// take damage
|
|
getobname(o, obname, 1);
|
|
if (isplayer(lf)) {
|
|
msg("Ow - you step on %s!",obname);
|
|
didmsg = B_TRUE;
|
|
} else if (haslos(player, newcell)) {
|
|
msg("%s steps on %s!",lfname, obname);
|
|
didmsg = B_TRUE;
|
|
}
|
|
snprintf(buf, BUFLEN, "stepping on %s", obname);
|
|
losehp(lf, rnd(f->val[0],f->val[1]), DT_SLASH, NULL, buf);
|
|
}
|
|
}
|
|
|
|
if (!lfhasflag(lf, F_CAREFULMOVE) && cancrush(lf, o)) {
|
|
// 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 = 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 which is being crushed.
|
|
removeob(o, o->amt);
|
|
if (isplayer(lf)) {
|
|
angergodmaybe(R_GODNATURE, 10, GA_ATTACKOBJECT);
|
|
}
|
|
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,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]);
|
|
break; // stop processing other objects, the explosion may have killed them.
|
|
}
|
|
} else {
|
|
explodeob(o, f, f->val[1]);
|
|
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) {
|
|
// TODO: also check for isresting(l), if we have allies standing watch
|
|
getlfnamea(lf, lfname);
|
|
msg("%s %ss into view.", lfname, getmoveverb(lf));
|
|
}
|
|
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, getskill(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 (preseenbyplayer && !cansee(player, lf) && !changedlev) {
|
|
if (isadjacent(lf->cell, precell)) { // ie don't say this if we teleported/jumped
|
|
if (areenemies(player, lf)) {
|
|
real_getlfnamea(lf, lfname, NULL, B_NOSHOWALL, B_CURRACE);
|
|
msg("%s %ss out of view.", lfname, getmoveverb(lf));
|
|
}
|
|
}
|
|
}
|
|
|
|
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) || (f->val[2] == B_TRUE)) {
|
|
damageallobs(NULL, lf->cell->obpile, f->val[0], f->val[1], lf);
|
|
}
|
|
}
|
|
|
|
// status bar
|
|
if ((prespeed != postspeed) && isplayer(lf)) {
|
|
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, WE_WALKABLE, B_ALLOWEXPAND);
|
|
if (c) {
|
|
// move them there
|
|
movelf(newcell->lf, c);
|
|
} 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);
|
|
|
|
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) {
|
|
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, 20, isairborne(lf) ? 5 : 0)) {
|
|
willmakenoise = B_FALSE;
|
|
}
|
|
}
|
|
|
|
if (willmakenoise) {
|
|
if (isairborne(lf)) {
|
|
makenoise(lf, N_FLY);
|
|
} else {
|
|
makenoise(lf, N_WALK);
|
|
}
|
|
}
|
|
|
|
// slip on blood in new cell?
|
|
if (!isairborne(lf) && !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)) {
|
|
// 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
|
|
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));
|
|
}
|
|
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)) 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)) {
|
|
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;
|
|
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 (gettargetlf(lf)) {
|
|
attackcell(lf, doorcell, B_TRUE);
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
taketime(lf, getactspeed(lf));
|
|
touch(lf, o);
|
|
|
|
// stop sprinting
|
|
stopsprinting(lf);
|
|
} // end if lf
|
|
|
|
// trapped?
|
|
if (lf && hasflag(o->flags, F_TRAPPED)) {
|
|
if (doobtraps(o, lf)) {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
// locked?
|
|
if (hasflag(o->flags, F_LOCKED)) {
|
|
if (lf) {
|
|
if (isplayer(lf)) {
|
|
msg("The %s is locked.", noprefix(obname));
|
|
} else {
|
|
if (gettargetlf(lf)) {
|
|
// ai will automatically attack locked doors to
|
|
// get at the player
|
|
attackcell(lf, doorcell, B_TRUE);
|
|
return B_FALSE;
|
|
} else {
|
|
noise(doorcell, NULL, NC_OTHER, SV_TALK, "a door handle rattling.", NULL);
|
|
}
|
|
}
|
|
taketime(lf, getactspeed(lf));
|
|
touch(lf, o);
|
|
}
|
|
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 {
|
|
// known - try to force it
|
|
int amt = 0;
|
|
amt = getattr(lf, A_STR) - 10;
|
|
if (amt < 0) amt = 0;
|
|
|
|
if (isplayer(lf)) {
|
|
if (amt > 0) {
|
|
msg("The %s moves slightly but remains jammed.", noprefix(obname));
|
|
} else {
|
|
msg("You cannot budge the jammed %s.", noprefix(obname));
|
|
}
|
|
} else {
|
|
// ai chasing someone and not strong enough to unjam the door?
|
|
if ((amt == 0) && gettargetlf(lf)) {
|
|
attackcell(lf, doorcell, B_TRUE);
|
|
return B_FALSE;
|
|
} else {
|
|
// try to force it
|
|
if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s tries to open %s, but fails.", lfname, obname);
|
|
} else if (haslos(player, doorcell)) {
|
|
msg("Something tries to open %s.", obname);
|
|
} else {
|
|
char noisebuf[BUFLEN];
|
|
sprintf(noisebuf, "%s jiggling against its hinges.", obname);
|
|
noise(doorcell, NULL, NC_OTHER, SV_SHOUT, noisebuf, NULL);
|
|
}
|
|
}
|
|
}
|
|
// loosen a bit
|
|
if (amt) {
|
|
f->val[0] -= amt;
|
|
}
|
|
if (f->val[0] <= 0) {
|
|
killflag(f);
|
|
openit = B_TRUE;
|
|
} else {
|
|
openit = B_FALSE; // don't open the door
|
|
}
|
|
} // 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) && isadjacent(lf->cell, doorcell)) {
|
|
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];
|
|
sprintf(noisebuf, "%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];
|
|
sprintf(noisebuf, "%s opening.", obname);
|
|
noise(doorcell, NULL, NC_OTHER, SV_TALK, noisebuf, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
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);
|
|
|
|
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) {
|
|
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) ? "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 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 (!getstamina(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("You 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, 25, 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)) {
|
|
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);
|
|
}
|
|
|
|
taketime(lf, getmovespeed(lf));
|
|
|
|
reason = E_OK;
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// are we on the ground?
|
|
if (isprone(lf) && (!isplayer(lf) || !attacking)) {
|
|
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) {
|
|
if (!isairborne(lf)) {
|
|
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)) {
|
|
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;
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
// 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, 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;
|
|
}
|
|
|
|
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);
|
|
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 *f;
|
|
// 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);
|
|
|
|
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) {
|
|
takerotationtime(lf);
|
|
setfacing(lf, dir);
|
|
drawscreen();
|
|
|
|
if (isplayer(lf)) {
|
|
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);
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
f = lfhasflag(lf, F_DRUNK);
|
|
if (f) {
|
|
if (!hasjob(lf, J_PIRATE)) {
|
|
if (rnd(1,6) <= ((f->lifetime/TM_DRUNKTIME)+1)) {
|
|
// randomize move
|
|
rndmove = B_TRUE; // ie. you can walk into walls now.
|
|
}
|
|
}
|
|
} else if (iswoozy(lf)) {
|
|
rndmove = B_TRUE;
|
|
} else if (lfhasflagval(lf, F_INJURY, IJ_TAILBRUISED, NA, NA, NULL) && onein(6)) {
|
|
rndmove = B_TRUE;
|
|
}
|
|
|
|
|
|
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) {
|
|
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?
|
|
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
|
|
sprintf(dirbuf, "%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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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)) {
|
|
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) || 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);
|
|
}
|
|
|
|
if (onpurpose) taketime(lf, getmovespeed(lf));
|
|
}
|
|
} else {
|
|
snprintf(buf, BUFLEN, "%sing into a %s", getmoveverb(lf),
|
|
cell ? cell->type->name : "wall");
|
|
losehp(lf, 1, DT_BASH, NULL, buf);
|
|
if (onpurpose) taketime(lf, getmovespeed(lf));
|
|
}
|
|
}
|
|
break;
|
|
case E_DOORINWAY:
|
|
// 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)) {
|
|
// 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) taketime(lf, getmovespeed(lf));
|
|
}
|
|
} else {
|
|
if (cansee(player, lf)) {
|
|
getlfname(lf, buf);
|
|
msg("%s %ss into a door.", buf, getmoveverb(lf));
|
|
}
|
|
snprintf(buf, BUFLEN, "%sing into a door", getmoveverb(lf));
|
|
losehp(lf, 1, DT_BASH, NULL, buf);
|
|
if (onpurpose) taketime(lf, getmovespeed(lf));
|
|
}
|
|
} else {
|
|
if (lfhasflag(lf, F_RAGE)) {
|
|
// attack it
|
|
return attackcell(lf, cell, B_FALSE);
|
|
} 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 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);
|
|
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 (isplayer(lf)) {
|
|
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 (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) taketime(lf, getmovespeed(lf));
|
|
} else {
|
|
if (canswapwith(lf, cell->lf)) {
|
|
lifeform_t *lfinway;
|
|
// otherwise swap locations.
|
|
lfinway = cell->lf;
|
|
|
|
if (isplayer(lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lfinway, lfname);
|
|
msg("You swap places with %s.", lfname);
|
|
dontclearmsg = B_TRUE;
|
|
}
|
|
|
|
swapplaces(lf, lfinway, B_NOCHANGEDIR, B_NOCHANGEDIR, onpurpose);
|
|
|
|
//if (onpurpose) taketime(lf, getmovespeed(lf));
|
|
taketime(lf, getmovespeed(lf));
|
|
} 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;
|
|
}
|
|
}
|
|
|
|
|
|
// 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];
|
|
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 && 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, WE_WALKABLE, B_ALLOWEXPAND);
|
|
if (c) {
|
|
if (!initiatemove(adjally[n], NULL, B_TRUE, NULL)) {
|
|
movelf(adjally[n], c);
|
|
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;
|
|
}
|
|
|
|
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 (iswoozy(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;
|
|
}
|
|
|
|
// 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 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 iq things...
|
|
if (iq >= 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).
|
|
for (o = cell->obpile->first ; o ; o = o->next) {
|
|
flag_t *f;
|
|
snprintf(buf, BUFLEN, "%ld",o->id);
|
|
f = lfhasflagval(lf, F_AVOIDOB, NA, NA, NA, buf);
|
|
if (f) {
|
|
// still cursed?
|
|
if ((f->val[0] != NA) && (o->blessed == f->val[0])) {
|
|
if (error) *error = E_WONT;
|
|
return B_FALSE;
|
|
} else {
|
|
// remove the flag.
|
|
killflag(f);
|
|
}
|
|
}
|
|
|
|
if (hasflag(o->flags, F_TRAP)) {
|
|
if (hasflag(o->flags, F_SECRET)) {
|
|
// hidden traps?
|
|
if (iq >= AT_GTAVERAGE) {
|
|
if (error) *error = E_WONT;
|
|
return B_FALSE;
|
|
}
|
|
} else {
|
|
// non-hidden traps?
|
|
if (iq >= AT_AVERAGE) {
|
|
if (error) *error = E_WONT;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return B_TRUE;
|
|
}
|
|
|