fbffadb9f8
- add __unreachable() after functions that can return but won't in this case, and thus can't be marked __dead easily
450 lines
11 KiB
C
450 lines
11 KiB
C
/* $NetBSD: movem.c,v 1.10 2019/02/03 03:19:25 mrg Exp $ */
|
|
|
|
/*
|
|
* movem.c (move monster) Larn is copyrighted 1986 by Noah Morgan.
|
|
*
|
|
* Here are the functions in this file:
|
|
*
|
|
* movemonst() Routine to move the monsters toward the player
|
|
* movemt(x,y) Function to move a monster at (x,y) -- must determine where
|
|
* mmove(x,y,xd,yd) Function to actually perform the monster movement
|
|
* movsphere() Function to look for and move spheres of annihilation
|
|
*/
|
|
#include <sys/cdefs.h>
|
|
#ifndef lint
|
|
__RCSID("$NetBSD: movem.c,v 1.10 2019/02/03 03:19:25 mrg Exp $");
|
|
#endif /* not lint */
|
|
|
|
#include "header.h"
|
|
#include "extern.h"
|
|
|
|
static void movemt(int, int);
|
|
static void mmove(int, int, int, int);
|
|
static void movsphere(void);
|
|
|
|
/*
|
|
* movemonst() Routine to move the monsters toward the player
|
|
*
|
|
* This routine has the responsibility to determine which monsters are to
|
|
* move, and call movemt() to do the move.
|
|
* Returns no value.
|
|
*/
|
|
static short w1[9], w1x[9], w1y[9];
|
|
static int tmp1, tmp2, tmp3, tmp4, distance;
|
|
void
|
|
movemonst(void)
|
|
{
|
|
int i, j;
|
|
if (c[TIMESTOP])
|
|
return; /* no action if time is stopped */
|
|
if (c[HASTESELF])
|
|
if ((c[HASTESELF] & 1) == 0)
|
|
return;
|
|
if (spheres)
|
|
movsphere(); /* move the spheres of annihilation if any */
|
|
if (c[HOLDMONST])
|
|
return; /* no action if monsters are held */
|
|
|
|
if (c[AGGRAVATE]) { /* determine window of monsters to move */
|
|
tmp1 = playery - 5;
|
|
tmp2 = playery + 6;
|
|
tmp3 = playerx - 10;
|
|
tmp4 = playerx + 11;
|
|
distance = 40; /* depth of intelligent monster movement */
|
|
} else {
|
|
tmp1 = playery - 3;
|
|
tmp2 = playery + 4;
|
|
tmp3 = playerx - 5;
|
|
tmp4 = playerx + 6;
|
|
distance = 17; /* depth of intelligent monster movement */
|
|
}
|
|
|
|
if (level == 0) { /* if on outside level monsters can move in
|
|
* perimeter */
|
|
if (tmp1 < 0)
|
|
tmp1 = 0;
|
|
if (tmp2 > MAXY)
|
|
tmp2 = MAXY;
|
|
if (tmp3 < 0)
|
|
tmp3 = 0;
|
|
if (tmp4 > MAXX)
|
|
tmp4 = MAXX;
|
|
} else { /* if in a dungeon monsters can't be on the
|
|
* perimeter (wall there) */
|
|
if (tmp1 < 1)
|
|
tmp1 = 1;
|
|
if (tmp2 > MAXY - 1)
|
|
tmp2 = MAXY - 1;
|
|
if (tmp3 < 1)
|
|
tmp3 = 1;
|
|
if (tmp4 > MAXX - 1)
|
|
tmp4 = MAXX - 1;
|
|
}
|
|
|
|
for (j = tmp1; j < tmp2; j++) /* now reset monster moved flags */
|
|
for (i = tmp3; i < tmp4; i++)
|
|
moved[i][j] = 0;
|
|
moved[lasthx][lasthy] = 0;
|
|
|
|
if (c[AGGRAVATE] || !c[STEALTH]) { /* who gets moved? split for
|
|
* efficiency */
|
|
for (j = tmp1; j < tmp2; j++) /* look thru all locations in
|
|
* window */
|
|
for (i = tmp3; i < tmp4; i++)
|
|
if (mitem[i][j]) /* if there is a monster
|
|
* to move */
|
|
if (moved[i][j] == 0) /* if it has not already
|
|
* been moved */
|
|
movemt(i, j); /* go and move the
|
|
* monster */
|
|
} else { /* not aggravated and not stealth */
|
|
for (j = tmp1; j < tmp2; j++) /* look thru all locations in
|
|
* window */
|
|
for (i = tmp3; i < tmp4; i++)
|
|
if (mitem[i][j]) /* if there is a monster
|
|
* to move */
|
|
if (moved[i][j] == 0) /* if it has not already
|
|
* been moved */
|
|
if (stealth[i][j]) /* if it is asleep due
|
|
* to stealth */
|
|
movemt(i, j); /* go and move the
|
|
* monster */
|
|
}
|
|
|
|
if (mitem[lasthx][lasthy]) { /* now move monster last hit by
|
|
* player if not already moved */
|
|
if (moved[lasthx][lasthy] == 0) { /* if it has not already
|
|
* been moved */
|
|
movemt(lasthx, lasthy);
|
|
lasthx = w1x[0];
|
|
lasthy = w1y[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* movemt(x,y) Function to move a monster at (x,y) -- must determine where
|
|
* int x,y;
|
|
*
|
|
* This routine is responsible for determining where one monster at (x,y) will
|
|
* move to. Enter with the monsters coordinates in (x,y).
|
|
* Returns no value.
|
|
*/
|
|
static int tmpitem, xl, xh, yl, yh;
|
|
static void
|
|
movemt(int i, int j)
|
|
{
|
|
int k, m, z, tmp, xtmp, ytmp, monst;
|
|
switch (monst = mitem[i][j]) { /* for half speed monsters */
|
|
case TROGLODYTE:
|
|
case HOBGOBLIN:
|
|
case METAMORPH:
|
|
case XVART:
|
|
case INVISIBLESTALKER:
|
|
case ICELIZARD:
|
|
if ((gltime & 1) == 1)
|
|
return;
|
|
};
|
|
|
|
if (c[SCAREMONST]) { /* choose destination randomly if scared */
|
|
if ((xl = i + rnd(3) - 2) < 0)
|
|
xl = 0;
|
|
if (xl >= MAXX)
|
|
xl = MAXX - 1;
|
|
if ((yl = j + rnd(3) - 2) < 0)
|
|
yl = 0;
|
|
if (yl >= MAXY)
|
|
yl = MAXY - 1;
|
|
if ((tmp = item[xl][yl]) != OWALL)
|
|
if (mitem[xl][yl] == 0)
|
|
if ((mitem[i][j] != VAMPIRE) || (tmpitem != OMIRROR))
|
|
if (tmp != OCLOSEDDOOR)
|
|
mmove(i, j, xl, yl);
|
|
return;
|
|
}
|
|
if (monster[monst].intelligence > 10 - c[HARDGAME]) { /* if smart monster */
|
|
/* intelligent movement here -- first setup screen array */
|
|
xl = tmp3 - 2;
|
|
yl = tmp1 - 2;
|
|
xh = tmp4 + 2;
|
|
yh = tmp2 + 2;
|
|
vxy(&xl, &yl);
|
|
vxy(&xh, &yh);
|
|
for (k = yl; k < yh; k++)
|
|
for (m = xl; m < xh; m++) {
|
|
switch (item[m][k]) {
|
|
case OWALL:
|
|
case OPIT:
|
|
case OTRAPARROW:
|
|
case ODARTRAP:
|
|
case OCLOSEDDOOR:
|
|
case OTRAPDOOR:
|
|
case OTELEPORTER:
|
|
smm: screen[m][k] = 127;
|
|
break;
|
|
case OMIRROR:
|
|
if (mitem[m][k] == VAMPIRE)
|
|
goto smm;
|
|
/* FALLTHROUGH */
|
|
default:
|
|
screen[m][k] = 0;
|
|
break;
|
|
};
|
|
}
|
|
screen[playerx][playery] = 1;
|
|
|
|
/*
|
|
* now perform proximity ripple from playerx,playery to
|
|
* monster
|
|
*/
|
|
xl = tmp3 - 1;
|
|
yl = tmp1 - 1;
|
|
xh = tmp4 + 1;
|
|
yh = tmp2 + 1;
|
|
vxy(&xl, &yl);
|
|
vxy(&xh, &yh);
|
|
for (tmp = 1; tmp < distance; tmp++) /* only up to 20 squares
|
|
* away */
|
|
for (k = yl; k < yh; k++)
|
|
for (m = xl; m < xh; m++)
|
|
if (screen[m][k] == tmp) /* if find proximity n
|
|
* advance it */
|
|
for (z = 1; z < 9; z++) { /* go around in a circle */
|
|
if (screen[xtmp = m + diroffx[z]][ytmp = k + diroffy[z]] == 0)
|
|
screen[xtmp][ytmp] = tmp + 1;
|
|
if (xtmp == i && ytmp == j)
|
|
goto out;
|
|
}
|
|
|
|
out: if (tmp < distance) /* did find connectivity */
|
|
/* now select lowest value around playerx,playery */
|
|
for (z = 1; z < 9; z++) /* go around in a circle */
|
|
if (screen[xl = i + diroffx[z]][yl = j + diroffy[z]] == tmp)
|
|
if (!mitem[xl][yl]) {
|
|
mmove(i, j, w1x[0] = xl, w1y[0] = yl);
|
|
return;
|
|
}
|
|
}
|
|
/* dumb monsters move here */
|
|
xl = i - 1;
|
|
yl = j - 1;
|
|
xh = i + 2;
|
|
yh = j + 2;
|
|
if (i < playerx)
|
|
xl++;
|
|
else if (i > playerx)
|
|
--xh;
|
|
if (j < playery)
|
|
yl++;
|
|
else if (j > playery)
|
|
--yh;
|
|
for (k = 0; k < 9; k++)
|
|
w1[k] = 10000;
|
|
|
|
for (k = xl; k < xh; k++)
|
|
for (m = yl; m < yh; m++) { /* for each square compute
|
|
* distance to player */
|
|
tmp = k - i + 4 + 3 * (m - j);
|
|
tmpitem = item[k][m];
|
|
if (tmpitem != OWALL || (k == playerx && m == playery))
|
|
if (mitem[k][m] == 0)
|
|
if ((mitem[i][j] != VAMPIRE) || (tmpitem != OMIRROR))
|
|
if (tmpitem != OCLOSEDDOOR) {
|
|
w1[tmp] = (playerx - k) * (playerx - k) + (playery - m) * (playery - m);
|
|
w1x[tmp] = k;
|
|
w1y[tmp] = m;
|
|
}
|
|
}
|
|
|
|
tmp = 0;
|
|
for (k = 1; k < 9; k++)
|
|
if (w1[tmp] > w1[k])
|
|
tmp = k;
|
|
|
|
if (w1[tmp] < 10000)
|
|
if ((i != w1x[tmp]) || (j != w1y[tmp]))
|
|
mmove(i, j, w1x[tmp], w1y[tmp]);
|
|
}
|
|
|
|
/*
|
|
* mmove(x,y,xd,yd) Function to actually perform the monster movement
|
|
* int x,y,xd,yd;
|
|
*
|
|
* Enter with the from coordinates in (x,y) and the destination coordinates
|
|
* in (xd,yd).
|
|
*/
|
|
static void
|
|
mmove(int aa, int bb, int cc, int dd)
|
|
{
|
|
int tmp, i, flag;
|
|
const char *who = NULL;
|
|
|
|
flag = 0; /* set to 1 if monster hit by arrow trap */
|
|
if ((cc == playerx) && (dd == playery)) {
|
|
hitplayer(aa, bb);
|
|
moved[aa][bb] = 1;
|
|
return;
|
|
}
|
|
i = item[cc][dd];
|
|
if ((i == OPIT) || (i == OTRAPDOOR))
|
|
switch (mitem[aa][bb]) {
|
|
case SPIRITNAGA:
|
|
case PLATINUMDRAGON:
|
|
case WRAITH:
|
|
case VAMPIRE:
|
|
case SILVERDRAGON:
|
|
case POLTERGEIST:
|
|
case DEMONLORD:
|
|
case DEMONLORD + 1:
|
|
case DEMONLORD + 2:
|
|
case DEMONLORD + 3:
|
|
case DEMONLORD + 4:
|
|
case DEMONLORD + 5:
|
|
case DEMONLORD + 6:
|
|
case DEMONPRINCE:
|
|
break;
|
|
|
|
default:
|
|
mitem[aa][bb] = 0; /* fell in a pit or trapdoor */
|
|
};
|
|
tmp = mitem[cc][dd] = mitem[aa][bb];
|
|
if (i == OANNIHILATION) {
|
|
if (tmp >= DEMONLORD + 3) { /* demons dispel spheres */
|
|
cursors();
|
|
lprintf("\nThe %s dispels the sphere!", monster[tmp].name);
|
|
rmsphere(cc, dd); /* delete the sphere */
|
|
} else
|
|
i = tmp = mitem[cc][dd] = 0;
|
|
}
|
|
stealth[cc][dd] = 1;
|
|
if ((hitp[cc][dd] = hitp[aa][bb]) < 0)
|
|
hitp[cc][dd] = 1;
|
|
mitem[aa][bb] = 0;
|
|
moved[cc][dd] = 1;
|
|
if (tmp == LEPRECHAUN)
|
|
switch (i) {
|
|
case OGOLDPILE:
|
|
case OMAXGOLD:
|
|
case OKGOLD:
|
|
case ODGOLD:
|
|
case ODIAMOND:
|
|
case ORUBY:
|
|
case OEMERALD:
|
|
case OSAPPHIRE:
|
|
item[cc][dd] = 0; /* leprechaun takes gold */
|
|
};
|
|
|
|
if (tmp == TROLL) /* if a troll regenerate him */
|
|
if ((gltime & 1) == 0)
|
|
if (monster[tmp].hitpoints > hitp[cc][dd])
|
|
hitp[cc][dd]++;
|
|
|
|
if (i == OTRAPARROW) { /* arrow hits monster */
|
|
who = "An arrow";
|
|
if ((hitp[cc][dd] -= rnd(10) + level) <= 0) {
|
|
mitem[cc][dd] = 0;
|
|
flag = 2;
|
|
} else
|
|
flag = 1;
|
|
}
|
|
if (i == ODARTRAP) { /* dart hits monster */
|
|
who = "A dart";
|
|
if ((hitp[cc][dd] -= rnd(6)) <= 0) {
|
|
mitem[cc][dd] = 0;
|
|
flag = 2;
|
|
} else
|
|
flag = 1;
|
|
}
|
|
if (i == OTELEPORTER) { /* monster hits teleport trap */
|
|
flag = 3;
|
|
fillmonst(mitem[cc][dd]);
|
|
mitem[cc][dd] = 0;
|
|
}
|
|
if (c[BLINDCOUNT])
|
|
return; /* if blind don't show where monsters are */
|
|
if (know[cc][dd] & 1) {
|
|
if (flag)
|
|
cursors();
|
|
switch (flag) {
|
|
case 1:
|
|
lprintf("\n%s hits the %s", who, monster[tmp].name);
|
|
beep();
|
|
break;
|
|
case 2:
|
|
lprintf("\n%s hits and kills the %s",
|
|
who, monster[tmp].name);
|
|
beep();
|
|
break;
|
|
case 3:
|
|
lprintf("\nThe %s gets teleported", monster[tmp].name);
|
|
beep();
|
|
break;
|
|
}
|
|
}
|
|
/*
|
|
* if (yrepcount>1) { know[aa][bb] &= 2; know[cc][dd] &= 2; return;
|
|
* }
|
|
*/
|
|
if (know[aa][bb] & 1)
|
|
show1cell(aa, bb);
|
|
if (know[cc][dd] & 1)
|
|
show1cell(cc, dd);
|
|
}
|
|
|
|
/*
|
|
* movsphere() Function to look for and move spheres of annihilation
|
|
*
|
|
* This function works on the sphere linked list, first duplicating the list
|
|
* (the act of moving changes the list), then processing each sphere in order
|
|
* to move it. They eat anything in their way, including stairs, volcanic
|
|
* shafts, potions, etc, except for upper level demons, who can dispel
|
|
* spheres.
|
|
* No value is returned.
|
|
*/
|
|
#define SPHMAX 20 /* maximum number of spheres movsphere can
|
|
* handle */
|
|
static void
|
|
movsphere(void)
|
|
{
|
|
int x, y, dir, len;
|
|
struct sphere *sp, *sp2;
|
|
struct sphere sph[SPHMAX];
|
|
|
|
/* first duplicate sphere list */
|
|
for (sp = 0, x = 0, sp2 = spheres; sp2; sp2 = sp2->p) /* look through sphere
|
|
* list */
|
|
if (sp2->lev == level) { /* only if this level */
|
|
sph[x] = *sp2;
|
|
sph[x++].p = 0; /* copy the struct */
|
|
if (x > 1)
|
|
sph[x - 2].p = &sph[x - 1]; /* link pointers */
|
|
}
|
|
if (x)
|
|
sp = sph; /* if any spheres, point to them */
|
|
else
|
|
return; /* no spheres */
|
|
|
|
for (sp = sph; sp; sp = sp->p) { /* look through sphere list */
|
|
x = sp->x;
|
|
y = sp->y;
|
|
if (item[x][y] != OANNIHILATION)
|
|
continue; /* not really there */
|
|
if (--(sp->lifetime) < 0) { /* has sphere run out of gas? */
|
|
rmsphere(x, y); /* delete sphere */
|
|
continue;
|
|
}
|
|
switch (rnd((int) max(7, c[INTELLIGENCE] >> 1))) { /* time to move the
|
|
* sphere */
|
|
case 1:
|
|
case 2: /* change direction to a random one */
|
|
sp->dir = rnd(8);
|
|
/* FALLTHROUGH */
|
|
default: /* move in normal direction */
|
|
dir = sp->dir;
|
|
len = sp->lifetime;
|
|
rmsphere(x, y);
|
|
newsphere(x + diroffx[dir], y + diroffy[dir], dir, len);
|
|
};
|
|
}
|
|
}
|