1156 lines
24 KiB
C
1156 lines
24 KiB
C
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "attack.h"
|
|
#include "defs.h"
|
|
#include "flag.h"
|
|
#include "io.h"
|
|
#include "lf.h"
|
|
#include "map.h"
|
|
#include "move.h"
|
|
#include "nexus.h"
|
|
#include "objects.h"
|
|
#include "text.h"
|
|
|
|
extern lifeform_t *player;
|
|
|
|
extern int statdirty;
|
|
extern int needredraw;
|
|
|
|
extern int gamestarted;
|
|
|
|
extern enum ERROR reason;
|
|
extern void *rdata;
|
|
|
|
int canandwillmove(lifeform_t *lf, int dir, enum ERROR *error) {
|
|
if (isplayer(lf)) {
|
|
if (canmove(lf, dir, error)) {
|
|
return B_TRUE;
|
|
}
|
|
} else {
|
|
if (canmove(lf, dir, error) && willmove(lf, dir, error)) {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int canmove(lifeform_t *lf, int dir, enum ERROR *error) {
|
|
cell_t *cell;
|
|
flag_t *f;
|
|
|
|
// default
|
|
if (error) {
|
|
*error = E_OK;
|
|
rdata = NULL;
|
|
}
|
|
|
|
if (lfhasflag(lf, F_GRAVBOOSTED)) {
|
|
if (error) *error = E_GRAVBOOSTED;
|
|
return B_FALSE;
|
|
}
|
|
|
|
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);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
return cellwalkable(lf, cell, error);
|
|
}
|
|
|
|
|
|
// will populate rdata
|
|
int celldangerous(lifeform_t *lf, cell_t *cell, enum ERROR *error) {
|
|
enum IQBRACKET iq;
|
|
// default
|
|
if (error) {
|
|
*error = E_OK;
|
|
rdata = NULL;
|
|
}
|
|
|
|
// check for _known_ dangerous objects
|
|
iq = getiqname(getattr(lf, A_IQ), NULL);
|
|
if ((iq >= IQ_AVERAGE) && !isblind(lf)) {
|
|
flag_t *f;
|
|
object_t *o;
|
|
for (o = cell->obpile->first ; o ; o = o->next) {
|
|
// don't walk on sharp objects without boots
|
|
if (hasflag(o->flags, F_SHARP)) {
|
|
if (!getequippedob(lf->pack, BP_FEET)) {
|
|
if (error) {
|
|
*error = E_AVOIDOB;
|
|
rdata = o;
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
f = hasflag(o->flags, F_WALKDAM);
|
|
if (f) {
|
|
// are we immune to this?
|
|
if (!lfhasflagval(lf, F_DTIMMUNE, f->val[1], NA, NA, NULL)) {
|
|
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_WALLINWAY;
|
|
return B_FALSE;
|
|
}
|
|
if (cell->type->solid) {
|
|
if (lf && lfhasflag(lf, F_NONCORPOREAL)) {
|
|
// 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)) {
|
|
if (error) *error = E_LFINWAY;
|
|
return B_FALSE;
|
|
}
|
|
|
|
for (o = cell->obpile->first ; o ; o = o->next) {
|
|
if (isimpassableob(o, lf)) {
|
|
if (lf) {
|
|
if ((lf->race->material->id == MT_GAS) ||
|
|
(lf->race->material->id == MT_SLIME)) {
|
|
// ok
|
|
} else if (lfhasflag(lf, F_NONCORPOREAL)) {
|
|
// ok
|
|
} else {
|
|
rdata = o;
|
|
if (error) *error = E_OBINWAY;
|
|
return B_FALSE;
|
|
}
|
|
} else {
|
|
rdata = o;
|
|
if (error) *error = E_OBINWAY;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return B_TRUE;
|
|
}
|
|
|
|
int diropposite(int dir) {
|
|
switch (dir) {
|
|
case D_N:
|
|
return D_S;
|
|
case D_E:
|
|
return D_W;
|
|
case D_S:
|
|
return D_N;
|
|
case D_W:
|
|
return D_E;
|
|
case DC_N:
|
|
return DC_S;
|
|
case DC_NE:
|
|
return DC_SW;
|
|
case DC_E:
|
|
return DC_W;
|
|
case DC_SE:
|
|
return DC_NW;
|
|
case DC_S:
|
|
return DC_N;
|
|
case DC_SW:
|
|
return DC_NE;
|
|
case DC_W:
|
|
return DC_E;
|
|
case DC_NW:
|
|
return DC_SE;
|
|
}
|
|
// should never happen!
|
|
return dir;
|
|
}
|
|
|
|
void dorandommove(lifeform_t *lf, int badmovesok) {
|
|
int dir;
|
|
int tries = 0;
|
|
int moveok;
|
|
enum ERROR why;
|
|
|
|
// find a valid direction
|
|
dir = getrandomdir(DT_COMPASS);
|
|
|
|
moveok = canandwillmove(lf, dir, &why);
|
|
|
|
if (!moveok && badmovesok) {
|
|
switch (why) {
|
|
// actually okay to move into someone
|
|
case E_WALLINWAY:
|
|
case E_LFINWAY:
|
|
moveok = B_TRUE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
while (!moveok) {
|
|
// try next direction...
|
|
if (++dir > DC_NW) dir = DC_N;
|
|
if (++tries >= MAXDIR_COMPASS) {
|
|
rest(lf, B_TRUE);
|
|
return;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
}
|
|
trymove(lf, dir);
|
|
}
|
|
|
|
|
|
|
|
int getdiraway(cell_t *src, cell_t *dst, int wantcheck) {
|
|
int d;
|
|
cell_t *c;
|
|
int maxdist=-1,bestdir=D_NONE;
|
|
|
|
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 = -1;
|
|
} else {
|
|
if (wantcheck) {
|
|
if (src->lf && canandwillmove(src->lf, d, NULL)) {
|
|
ok = B_TRUE;
|
|
} else if (!src->lf) {
|
|
ok = B_TRUE;
|
|
}
|
|
} else {
|
|
if (src->lf && cellwalkable(src->lf, c, NULL)) {
|
|
ok = B_TRUE;
|
|
} else if (!src->lf) {
|
|
ok = B_TRUE;
|
|
}
|
|
}
|
|
if (ok) {
|
|
thisdist = getcelldistorth(c, dst);
|
|
} else {
|
|
thisdist = -1;
|
|
}
|
|
|
|
if (thisdist > maxdist) {
|
|
maxdist = thisdist;
|
|
bestdir = d;
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: handle ties
|
|
if (bestdir != D_NONE) {
|
|
reason = E_OK;
|
|
}
|
|
return bestdir;
|
|
}
|
|
|
|
|
|
int getdirtowards(cell_t *src, cell_t *dst, lifeform_t *srclf, int wantcheck) {
|
|
int d;
|
|
cell_t *c;
|
|
int mindist=9999,bestdir=D_NONE;
|
|
|
|
for (d = DC_N; d <= DC_NW; d++) {
|
|
int ok = B_FALSE;
|
|
c = getcellindir(src, d);
|
|
if (!c) continue;
|
|
if (c == dst) {
|
|
// destination is adjacent!
|
|
bestdir = d;
|
|
break;
|
|
}
|
|
|
|
if (wantcheck) {
|
|
if (srclf && canandwillmove(srclf, d, NULL)) {
|
|
ok = B_TRUE;
|
|
} else if (!srclf) {
|
|
ok = B_TRUE;
|
|
}
|
|
} else {
|
|
if (srclf && cellwalkable(srclf, c, NULL)) {
|
|
ok = B_TRUE;
|
|
} else if (!srclf) {
|
|
ok = B_TRUE;
|
|
}
|
|
}
|
|
if (ok) {
|
|
int thisdist;
|
|
thisdist = getcelldistorth(c, dst);
|
|
if (thisdist < mindist) {
|
|
mindist = thisdist;
|
|
bestdir = d;
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: handle ties
|
|
|
|
return bestdir;
|
|
}
|
|
|
|
int knockback(lifeform_t *lf, int dir, int howfar, lifeform_t *pusher) {
|
|
int i;
|
|
char lfname[BUFLEN];
|
|
int seen;
|
|
|
|
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;
|
|
}
|
|
|
|
for (i = 0; i < howfar; i++) {
|
|
if (canmove(lf, dir, &reason)) {
|
|
if ((i == 0) && seen) {
|
|
msg("%s %s knocked backwards!",lfname,isplayer(lf) ? "are" : "is");
|
|
}
|
|
trymove(lf, dir);
|
|
}
|
|
if (reason != E_OK) {
|
|
// failed to move
|
|
switch (reason) {
|
|
case E_WALLINWAY:
|
|
msg("%s slam%s into a wall!",lfname,isplayer(lf) ? "" : "s");
|
|
losehp(lf, rnd(1,6), DT_BASH, pusher, "slamming into a wall");
|
|
// stop moving
|
|
i = howfar;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int moveawayfrom(lifeform_t *lf, cell_t *dst ) {
|
|
int dir;
|
|
int rv = B_TRUE;
|
|
|
|
if (isblind(lf)) {
|
|
dorandommove(lf, B_TRUE);
|
|
return B_FALSE;
|
|
}
|
|
|
|
// move towards them
|
|
dir = getdiraway(lf->cell, dst, B_TRUE);
|
|
if (dir == D_NONE) {
|
|
rv = B_TRUE;
|
|
} else {
|
|
rv = trymove(lf, dir);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
|
|
|
|
// returns TRUE if something happened
|
|
int movelf(lifeform_t *lf, cell_t *newcell) {
|
|
object_t *o,*nexto;
|
|
char obname[BUFLEN],lfname[BUFLEN],buf[BUFLEN];
|
|
lifeform_t *l;
|
|
int didmsg = B_FALSE;
|
|
flag_t *f;
|
|
|
|
getlfname(lf, lfname);
|
|
|
|
// update current cell
|
|
lf->cell->lf = NULL;
|
|
|
|
// if required, relink lifeform to new map
|
|
if (newcell->map != lf->cell->map) {
|
|
if (isplayer(lf)) {
|
|
statdirty = B_TRUE;
|
|
}
|
|
relinklf(lf, newcell->map);
|
|
}
|
|
|
|
// update lifeform
|
|
lf->cell = newcell;
|
|
|
|
// nothing should be in new cell..
|
|
assert(!newcell->lf);
|
|
|
|
// update new cell
|
|
newcell->lf = lf;
|
|
|
|
if (isbleeding(lf)) {
|
|
if (rnd(1,2) == 1) {
|
|
bleed(lf);
|
|
}
|
|
}
|
|
if (lfhasflag(lf, F_PAIN)) {
|
|
losehp(lf, rolldie(2,4)+2, DT_DIRECT, NULL, "extreme pain");
|
|
if (isplayer(lf)) {
|
|
msg("Your body is wracked with pain!");
|
|
} else if (cansee(player, lf)) {
|
|
msg("%s convulses in pain!",lfname);
|
|
}
|
|
}
|
|
|
|
// remove grabs
|
|
f = lfhasflag(lf, F_GRABBING);
|
|
if (f) {
|
|
lifeform_t *grabee;
|
|
grabee = findlf(NULL, f->val[0]);
|
|
assert(grabee);
|
|
killflagsofid(grabee->flags, F_GRABBEDBY);
|
|
killflagsofid(lf->flags, F_GRABBING);
|
|
}
|
|
|
|
|
|
// check ground objects
|
|
if (!isairborne(lf)) {
|
|
for (o = newcell->obpile->first ; o ; o = nexto ) {
|
|
nexto = o->next;
|
|
f = hasflag(o->flags, F_SHARP);
|
|
if (f) {
|
|
object_t *boots;
|
|
// has boots on?
|
|
boots = getequippedob(lf->pack, BP_FEET);
|
|
if (!boots) {
|
|
// take damage
|
|
getobname(o, obname, 1);
|
|
if (lf->controller == C_PLAYER) {
|
|
msg("Ow - you step on %s!",obname);
|
|
didmsg = B_TRUE;
|
|
} else if (haslos(player, newcell)) {
|
|
msg("%s steps on %s!",lfname, obname);
|
|
didmsg = B_TRUE;
|
|
}
|
|
sprintf(buf, "stepping on %s", obname);
|
|
losehp(lf, rnd(f->val[0],f->val[1]), DT_SLASH, NULL, buf);
|
|
}
|
|
}
|
|
|
|
f = hasflag(o->flags, F_CRUSHABLE);
|
|
if (f) {
|
|
enum LFSIZE crushsize;
|
|
crushsize = f->val[0];
|
|
|
|
if (getlfsize(lf) >= crushsize) {
|
|
// crunch it broken glass
|
|
getobname(o, obname, 1);
|
|
|
|
// special case
|
|
if (o->type->id == OT_BROKENGLASS) {
|
|
if (o->amt > 1) {
|
|
char *newname;
|
|
// we want 'xx steps on some pieces of broken glass'
|
|
// not 'xx steps on 5 pieces of broken glass'
|
|
newname = makeplural(obname);
|
|
newname = strrep(newname, "a ", "some ", NULL);
|
|
strcpy(obname, newname);
|
|
free(newname);
|
|
}
|
|
}
|
|
|
|
if (isplayer(lf)) {
|
|
msg("You crush %s underfoot.",obname);
|
|
didmsg = B_TRUE;
|
|
} else if (haslos(player, newcell)) {
|
|
msg("%s crushes %s.",lfname, obname);
|
|
didmsg = B_TRUE;
|
|
}
|
|
// kill object
|
|
removeob(o, o->amt);
|
|
}
|
|
} // end if crushable
|
|
} // 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();
|
|
}
|
|
|
|
// does anyone else see you?
|
|
if (gamestarted) {
|
|
for (l = newcell->map->lf ; l ; l = l->next) {
|
|
if (l != lf) {
|
|
if (haslos(l, newcell)) {
|
|
interrupt(l);
|
|
if (isplayer(l)) {
|
|
if (lfhasflag(l, F_RUNNING) || lfhasflag(l, F_RESTING)) {
|
|
char lfname2[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
sprintf(lfname2, "%s",noprefix(lfname));
|
|
msg("%s %s comes into view.",isvowel(lfname2[0]) ? "An" : "A", lfname2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return didmsg;
|
|
}
|
|
|
|
// basically this is a warpper for 'movelf' which
|
|
// does other game things like telling the player
|
|
// what is here.
|
|
int moveto(lifeform_t *lf, cell_t *newcell) {
|
|
return real_moveto(lf,newcell,B_FALSE);
|
|
}
|
|
|
|
int real_moveto(lifeform_t *lf, cell_t *newcell, int dontclearmsg) {
|
|
char lfname[BUFLEN];
|
|
int didmsg;
|
|
|
|
getlfname(lf, lfname);
|
|
|
|
// actually do the move
|
|
didmsg = movelf(lf, newcell);
|
|
|
|
if (dontclearmsg) {
|
|
didmsg = B_TRUE;
|
|
}
|
|
|
|
// tell player about things
|
|
if (!isdead(lf)) {
|
|
// some lifeforms can go through things
|
|
if (lf->race->material->id == MT_GAS) {
|
|
object_t *o;
|
|
char obname[BUFLEN];
|
|
for (o = newcell->obpile->first ; o ; o = o->next) {
|
|
if (isimpassableob(o, lf)) {
|
|
getobname(o, obname, o->amt);
|
|
if (isplayer(lf)) {
|
|
msg("You seep around %s.", obname);
|
|
} else if (haslos(player, newcell)) {
|
|
msg("%s seeps around %s.", lfname, obname);
|
|
}
|
|
}
|
|
}
|
|
} else if (lf->race->material->id == MT_SLIME) {
|
|
object_t *o;
|
|
char obname[BUFLEN];
|
|
for (o = newcell->obpile->first ; o ; o = o->next) {
|
|
if (isimpassableob(o, lf)) {
|
|
getobname(o, obname, o->amt);
|
|
if (isplayer(lf)) {
|
|
msg("You seep under %s.", obname);
|
|
} else if (haslos(player, newcell)) {
|
|
msg("%s seeps under %s.", lfname, obname);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// see objects on ground
|
|
if (isplayer(lf)) {
|
|
int numobs;
|
|
numobs = countobs(newcell->obpile);
|
|
if ((numobs == 0) && !newcell->writing) {
|
|
// just clear the message buffer
|
|
if (!didmsg) clearmsg();
|
|
} else { // tell player what is here
|
|
dolook(newcell);
|
|
}
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int movetowards(lifeform_t *lf, cell_t *dst) {
|
|
int dir;
|
|
int rv = B_TRUE;
|
|
|
|
if (isblind(lf)) {
|
|
dorandommove(lf, B_TRUE);
|
|
return B_FALSE;
|
|
}
|
|
|
|
// move towards them
|
|
dir = getdirtowards(lf->cell, dst, lf, B_TRUE);
|
|
if (dir != D_NONE) {
|
|
rv = trymove(lf, dir);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
int opendoorat(lifeform_t *lf, cell_t *c) {
|
|
object_t *o;
|
|
int rv;
|
|
o = hasobwithflag(c->obpile, F_DOOR);
|
|
if (o) {
|
|
rv = opendoor(lf, o);
|
|
} else {
|
|
rv = B_TRUE;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
int opendoor(lifeform_t *lf, object_t *o) {
|
|
cell_t *doorcell;
|
|
char buf[BUFLEN];
|
|
char obname[BUFLEN];
|
|
flag_t *f;
|
|
|
|
doorcell = o->pile->where;
|
|
assert(doorcell);
|
|
|
|
|
|
getobname(o, obname, 1);
|
|
|
|
if (!isdoor(o, NULL)) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (lf && lfhasflagval(lf, F_NOBODYPART, BP_HANDS, NA, NA, NULL)) {
|
|
if (isplayer(lf)) {
|
|
msg("You have no hands with which to open the door!");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
f = hasflag(o->flags, F_OPEN);
|
|
if (f) {
|
|
if (lf && isplayer(lf)) {
|
|
msg("It is already open!");
|
|
}
|
|
return B_TRUE;
|
|
} else {
|
|
if (lf) {
|
|
taketime(lf, getactspeed(lf));
|
|
touch(lf, o);
|
|
}
|
|
|
|
// locked?
|
|
if (hasflag(o->flags, F_LOCKED)) {
|
|
if (lf && isplayer(lf)) {
|
|
msg("The door is locked.");
|
|
}
|
|
return B_TRUE;
|
|
} else {
|
|
int openit = B_TRUE;
|
|
f = hasflag(o->flags, F_JAMMED);
|
|
if (f) {
|
|
|
|
f->val[0] --;
|
|
// loosen a bit
|
|
if (lf && isplayer(lf)) {
|
|
msg("You yank on the door but it holds fast.");
|
|
}
|
|
if (f->val[0] <= 0) {
|
|
killflag(f);
|
|
}
|
|
openit = B_FALSE; // don't open the door
|
|
}
|
|
if (openit) {
|
|
cell_t *where;
|
|
// open it
|
|
addflag(o->flags, F_OPEN, B_TRUE, NA, NA, NULL);
|
|
|
|
f = hasflag(o->flags, F_IMPASSABLE);
|
|
if (f) killflag(f);
|
|
f = hasflag(o->flags, F_BLOCKSVIEW);
|
|
if (f) killflag(f);
|
|
|
|
if (lf) {
|
|
if (lf->controller == C_PLAYER) {
|
|
msg("You open %s.",obname);
|
|
} else {
|
|
if (cansee(player, lf) && isadjacent(lf->cell, doorcell)) {
|
|
getlfname(lf, buf);
|
|
capitalise(buf);
|
|
msg("%s opens %s.",buf, obname);
|
|
} else if (haslos(player, doorcell)) {
|
|
capitalise(obname);
|
|
msg("%s opens.",obname);
|
|
}
|
|
}
|
|
|
|
}
|
|
where = getoblocation(o);
|
|
if (player && haslos(player, where)) {
|
|
needredraw = B_TRUE;
|
|
drawscreen();
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
int closedoorat(lifeform_t *lf, cell_t *c) {
|
|
object_t *o;
|
|
int rv;
|
|
o = hasobwithflag(c->obpile, F_DOOR);
|
|
if (o) {
|
|
rv = closedoor(lf, o);
|
|
} else {
|
|
rv = B_TRUE;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
int closedoor(lifeform_t *lf, object_t *o) {
|
|
cell_t *cell;
|
|
char buf[BUFLEN];
|
|
char obname[BUFLEN];
|
|
object_t *oo;
|
|
flag_t *f;
|
|
|
|
cell = getoblocation(o);
|
|
|
|
getobname(o, obname, 1);
|
|
|
|
if (!isdoor(o, NULL)) {
|
|
if (isplayer(lf)) {
|
|
msg("There is nothing to close there!");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (lf && lfhasflagval(lf, F_NOBODYPART, BP_HANDS, NA, NA, NULL)) {
|
|
if (isplayer(lf)) {
|
|
msg("You have no hands with which to close the door!");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (cell->lf) {
|
|
if (lf && isplayer(lf)) {
|
|
char inwayname[BUFLEN];
|
|
getlfname(cell->lf, inwayname);
|
|
msg("%s is in the way!", haslos(lf, cell) ? inwayname : "Something");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
// any object other than the door?
|
|
for (oo = cell->obpile->first ; oo ; oo = oo->next) {
|
|
if (oo != o) {
|
|
if (lf && isplayer(lf)) {
|
|
char inwayname[BUFLEN];
|
|
getobname(oo, inwayname, oo->amt);
|
|
msg("%s is in the way!", haslos(lf, cell) ? inwayname : "Something");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
f = hasflag(o->flags, F_OPEN);
|
|
if (!f) {
|
|
if (lf && (lf->controller == C_PLAYER)) {
|
|
msg("It is already closed!");
|
|
}
|
|
return B_TRUE;
|
|
} else {
|
|
// close it
|
|
killflag(f);
|
|
addflag(o->flags, F_IMPASSABLE, B_TRUE, NA, NA, NULL);
|
|
addflag(o->flags, F_BLOCKSVIEW, B_TRUE, NA, NA, NULL);
|
|
|
|
if (lf) {
|
|
if (lf->controller == C_PLAYER) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
int tryrun(lifeform_t *lf, int dir) {
|
|
if (!trymove(lf, dir)) {
|
|
addflag(lf->flags, F_RUNNING, dir, NA, NA, NULL);
|
|
}
|
|
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;
|
|
|
|
dst = c;
|
|
|
|
while (dst->lf) {
|
|
dir = getdirtowards(dst, lf->cell, lf, B_FALSE);
|
|
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 || !haslof(lf, dst)) {
|
|
return B_TRUE;
|
|
}
|
|
if (isplayer(lf) || cansee(player, lf)) {
|
|
char buf[BUFLEN];
|
|
getlfname(lf, buf);
|
|
msg("%s %s pulled %s!", buf, isplayer(lf) ? "are" : "is",
|
|
isairborne(lf) ? "through the air" :
|
|
"along the ground");
|
|
}
|
|
movelf(lf, dst);
|
|
return B_FALSE;
|
|
}
|
|
|
|
// 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) {
|
|
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);
|
|
// addob(lf->cell->obpile, "cloud of smoke");
|
|
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) && countobs(lf->cell->obpile)) {
|
|
dolook(lf->cell);
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
|
|
int trymove(lifeform_t *lf, int dir) {
|
|
cell_t *cell;
|
|
enum ERROR errcode;
|
|
char buf[BUFLEN];
|
|
|
|
cell = getcellindir(lf->cell, dir);
|
|
if (canandwillmove(lf, dir, &errcode)) {
|
|
object_t *o,*slipob;
|
|
int slip;
|
|
|
|
// check for cursed objects + animals
|
|
for (o = cell->obpile->first ; o ; o = o->next) {
|
|
if (!isplayer(lf)) {
|
|
if (o->blessed == B_CURSED) {
|
|
if (lfhasflag(lf, F_ANIMAL)) {
|
|
if (!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;
|
|
}
|
|
taketime(lf, getmovespeed(lf));
|
|
reason = E_OK;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// slipping on blood before moving?
|
|
slip = getslipperyness(lf->cell, &slipob);
|
|
if (slip && !skillcheck(lf, SC_SLIP, slip, 0)) {
|
|
slipon(lf, slipob);
|
|
// don't move
|
|
reason = E_OK;
|
|
return B_FALSE;
|
|
}
|
|
|
|
// move to new cell
|
|
reason = E_OK;
|
|
moveto(lf, cell);
|
|
taketime(lf, getmovespeed(lf));
|
|
|
|
// slip on blood in new cell?
|
|
slip = getslipperyness(cell, &slipob);
|
|
if (slip && !skillcheck(lf, SC_SLIP, slip, 0)) {
|
|
slipon(lf, slipob);
|
|
// don't move
|
|
reason = E_OK;
|
|
return B_FALSE;
|
|
}
|
|
} else {
|
|
object_t *inway;
|
|
int door, dooropen;
|
|
reason = errcode;
|
|
switch (errcode) {
|
|
case E_WALLINWAY:
|
|
if (isplayer(lf)) {
|
|
msg("Ouch! You %s into a wall.", getmoveverb(lf));
|
|
} else if (cansee(player, lf)) {
|
|
getlfname(lf, buf);
|
|
msg("%s %ss into a wall.", buf, getmoveverb(lf));
|
|
}
|
|
if (isblind(lf)) {
|
|
if (isplayer(lf)) {
|
|
// only take damage if we didn't know about this
|
|
if (!cell->known) {
|
|
sprintf(buf, "%sing into a wall", getmoveverb(lf));
|
|
losehp(lf, 1, DT_BASH, NULL, buf);
|
|
cell->known = B_TRUE;
|
|
taketime(lf, getmovespeed(lf));
|
|
}
|
|
} else {
|
|
sprintf(buf, "%sing into a wall", getmoveverb(lf));
|
|
losehp(lf, 1, DT_BASH, NULL, buf);
|
|
taketime(lf, getmovespeed(lf));
|
|
}
|
|
}
|
|
break;
|
|
case E_OBINWAY:
|
|
inway = (object_t *)rdata;
|
|
door = isdoor(inway, &dooropen);
|
|
if (door && !dooropen) {
|
|
if (isblind(lf)) {
|
|
// run into it
|
|
if (isplayer(lf)) {
|
|
if (cell->known) {
|
|
// try to open it
|
|
opendoor(lf, inway);
|
|
} else {
|
|
msg("Ouch! You %s into a door.", getmoveverb(lf));
|
|
cell->known = B_TRUE;
|
|
sprintf(buf, "%sing into a door", getmoveverb(lf));
|
|
losehp(lf, 1, DT_BASH, NULL, buf);
|
|
taketime(lf, getmovespeed(lf));
|
|
}
|
|
} else {
|
|
if (cansee(player, lf)) {
|
|
getlfname(lf, buf);
|
|
msg("%s %ss into a wall.", buf, getmoveverb(lf));
|
|
}
|
|
sprintf(buf, "%sing into a door", getmoveverb(lf));
|
|
losehp(lf, 1, DT_BASH, NULL, buf);
|
|
taketime(lf, getmovespeed(lf));
|
|
}
|
|
} else {
|
|
// try to open it
|
|
if (!opendoor(lf, inway)) {
|
|
// opening a door counts as a successful move.
|
|
reason = E_OK;
|
|
}
|
|
}
|
|
} else {
|
|
// can we push this?
|
|
if (ispushable(inway)) {
|
|
if (canpush(lf, inway, dir)) {
|
|
// push it!
|
|
push(lf, inway, dir);
|
|
} else {
|
|
if (lf->controller == C_PLAYER) {
|
|
char obname[BUFLEN];
|
|
getobname(inway, obname, 1);
|
|
switch (reason) {
|
|
case E_TOOHEAVY:
|
|
msg("The %s is too heavy to move.",strchr(obname, ' ')+1);
|
|
break;
|
|
case E_INSUBSTANTIAL:
|
|
msg("You cannot push %s without a physical body!",strchr(obname, ' ')+1);
|
|
break;
|
|
default:
|
|
msg("The %s won't move for some reason.",strchr(obname, ' ')+1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else { // object is in the way
|
|
if (lf->controller == C_PLAYER) {
|
|
char obname[BUFLEN];
|
|
getobname(inway, obname, 1);
|
|
msg("There is %s in your way.",obname);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case E_LFINWAY:
|
|
// attack!
|
|
return attacklf(lf, cell->lf);
|
|
case E_GRAVBOOSTED:
|
|
if (isplayer(lf)) {
|
|
msg("You try to move but are unable to lift your feet!");
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s tries to move but is unable to lift its feet!",lfname);
|
|
}
|
|
taketime(lf, getmovespeed(lf));
|
|
break;
|
|
case E_CANTMOVE:
|
|
if (isplayer(lf)) {
|
|
msg("You cannot move!");
|
|
}
|
|
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
|
|
real_moveto(lf, cell, 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!");
|
|
}
|
|
}
|
|
taketime(lf, getmovespeed(lf));
|
|
break;
|
|
case E_TOOHEAVY:
|
|
if (isplayer(lf)) {
|
|
msg("Your load is too heavy to move with!");
|
|
}
|
|
taketime(lf, getmovespeed(lf));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (reason == E_OK) {
|
|
return B_FALSE;
|
|
} else {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
|
|
int willmove(lifeform_t *lf, int dir, enum ERROR *error) {
|
|
cell_t *cell;
|
|
//object_t *o;
|
|
|
|
// default
|
|
if (error) {
|
|
*error = E_OK;
|
|
rdata = NULL;
|
|
}
|
|
|
|
cell = getcellindir(lf->cell, dir);
|
|
if (celldangerous(lf, cell, error)) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
// don't attack other monsters
|
|
if (cell->lf) {
|
|
if (!isplayer(lf)) {
|
|
if (!isplayer(cell->lf) && !isfriendly(cell->lf)) {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|