2010-11-25 12:21:41 +00:00
|
|
|
/*
|
|
|
|
|
* File with various monster functions in it
|
|
|
|
|
*
|
|
|
|
|
* @(#)monsters.c 9.0 (rdk) 7/17/84
|
|
|
|
|
*
|
|
|
|
|
* Super-Rogue
|
|
|
|
|
* Copyright (C) 1984 Robert D. Kindelberger
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* Based on "Rogue: Exploring the Dungeons of Doom"
|
|
|
|
|
* Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* See the file LICENSE.TXT for full copyright and licensing information.
|
|
|
|
|
*/
|
|
|
|
|
|
2016-01-31 13:45:07 -05:00
|
|
|
#include <string.h>
|
2010-11-25 12:21:41 +00:00
|
|
|
#include "rogue.h"
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include "rogue.ext"
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* rnd_mon:
|
|
|
|
|
* Pick a monster to show up. The lower the level,
|
|
|
|
|
* the meaner the monster.
|
|
|
|
|
*/
|
2021-05-04 21:03:47 -04:00
|
|
|
int
|
2016-01-31 13:45:07 -05:00
|
|
|
rnd_mon(bool wander, bool baddie)
|
2010-11-25 12:21:41 +00:00
|
|
|
{
|
2016-01-31 13:45:07 -05:00
|
|
|
/* baddie; TRUE when from a polymorph stick */
|
2010-11-25 12:21:41 +00:00
|
|
|
reg int i, ok, cnt;
|
|
|
|
|
|
|
|
|
|
cnt = 0;
|
|
|
|
|
if (levcount == 0) /* if only asmodeus possible */
|
|
|
|
|
return(MAXMONS);
|
|
|
|
|
if (baddie) {
|
|
|
|
|
while (1) {
|
|
|
|
|
i = rnd(MAXMONS); /* pick ANY monster */
|
|
|
|
|
if (monsters[i].m_lev.l_lev < 0) /* skip genocided ones */
|
|
|
|
|
continue;
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ok = FALSE;
|
|
|
|
|
do {
|
|
|
|
|
/*
|
|
|
|
|
* get a random monster from this range
|
|
|
|
|
*/
|
|
|
|
|
i = rnd(levcount);
|
|
|
|
|
/*
|
|
|
|
|
* Only create a wandering monster if we want one
|
|
|
|
|
* (or the count is exceeded)
|
|
|
|
|
*/
|
|
|
|
|
if (!wander || mtlev[i]->m_lev.d_wand || ++cnt > 500)
|
|
|
|
|
ok = TRUE;
|
|
|
|
|
} while(!ok);
|
|
|
|
|
return (midx(mtlev[i]->m_show));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* lev_mon:
|
|
|
|
|
* This gets all monsters possible on this level
|
|
|
|
|
*/
|
2016-01-31 13:45:07 -05:00
|
|
|
void
|
|
|
|
|
lev_mon(void)
|
2010-11-25 12:21:41 +00:00
|
|
|
{
|
|
|
|
|
reg int i;
|
|
|
|
|
reg struct monster *mm;
|
|
|
|
|
|
|
|
|
|
levcount = 0;
|
|
|
|
|
for (i = 0; i < MAXMONS; i++) {
|
|
|
|
|
mm = &monsters[i];
|
|
|
|
|
if (mm->m_lev.h_lev >= level && mm->m_lev.l_lev <= level) {
|
|
|
|
|
mtlev[levcount] = mm;
|
|
|
|
|
if (++levcount >= MONRANGE)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (levcount == 0) /* if no monsters are possible */
|
|
|
|
|
mtlev[0] = &monsters[MAXMONS]; /* then asmodeus 'A' */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* new_monster:
|
|
|
|
|
* Pick a new monster and add it to the list
|
|
|
|
|
*/
|
|
|
|
|
struct linked_list *
|
2021-05-04 21:03:47 -04:00
|
|
|
new_monster(int indx, struct coord *cp, bool treas)
|
2010-11-25 12:21:41 +00:00
|
|
|
{
|
|
|
|
|
reg struct linked_list *item;
|
|
|
|
|
reg struct thing *tp;
|
|
|
|
|
reg struct monster *mp;
|
|
|
|
|
reg struct stats *st;
|
|
|
|
|
float killexp; /* experience gotten for killing him */
|
|
|
|
|
|
|
|
|
|
item = new_item(sizeof(struct thing));
|
|
|
|
|
attach(mlist, item);
|
|
|
|
|
tp = THINGPTR(item);
|
|
|
|
|
st = &tp->t_stats;
|
2021-05-04 21:03:47 -04:00
|
|
|
mp = &monsters[indx]; /* point to this monsters structure */
|
2010-11-25 12:21:41 +00:00
|
|
|
tp->t_type = mp->m_show;
|
2021-05-04 21:03:47 -04:00
|
|
|
tp->t_indx = indx;
|
2010-11-25 12:21:41 +00:00
|
|
|
tp->t_pos = *cp;
|
|
|
|
|
tp->t_room = roomin(cp);
|
2021-04-14 18:55:33 -04:00
|
|
|
tp->t_oldch = mvwinch(cw, cp->y, cp->x) & A_CHARTEXT;
|
2010-11-25 12:21:41 +00:00
|
|
|
tp->t_nomove = 0;
|
|
|
|
|
tp->t_nocmd = 0;
|
|
|
|
|
mvwaddch(mw, cp->y, cp->x, tp->t_type);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* copy monster data
|
|
|
|
|
*/
|
|
|
|
|
tp->t_stats = mp->m_stats;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If below amulet level, make the monsters meaner the
|
|
|
|
|
* deeper the hero goes.
|
|
|
|
|
*/
|
|
|
|
|
if (level > AMLEVEL)
|
|
|
|
|
st->s_lvl += ((level - AMLEVEL) / 4);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If monster in treasure room, then tougher.
|
|
|
|
|
*/
|
|
|
|
|
if (treas)
|
|
|
|
|
st->s_lvl += 1;
|
|
|
|
|
if (levtype == MAZELEV)
|
|
|
|
|
st->s_lvl += 1;
|
|
|
|
|
/*
|
|
|
|
|
* If the hero is going back up, then the monsters are more
|
|
|
|
|
* prepared for him, so tougher.
|
|
|
|
|
*/
|
|
|
|
|
if (goingup())
|
|
|
|
|
st->s_lvl += 1;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Get hit points for monster depending on his experience
|
|
|
|
|
*/
|
|
|
|
|
st->s_hpt = roll(st->s_lvl, 8);
|
|
|
|
|
st->s_maxhp = st->s_hpt;
|
|
|
|
|
/*
|
|
|
|
|
* Adjust experience point we get for killing it by the
|
|
|
|
|
* strength of this particular monster by ~~ +- 50%
|
|
|
|
|
*/
|
|
|
|
|
killexp = mp->m_stats.s_exp * (0.47 + (float)st->s_hpt /
|
|
|
|
|
(8 * (float)st->s_lvl));
|
|
|
|
|
|
|
|
|
|
st->s_exp = killexp; /* use float for accuracy */
|
|
|
|
|
if(st->s_exp < 1)
|
|
|
|
|
st->s_exp = 1; /* minimum 1 experience point */
|
|
|
|
|
tp->t_flags = mp->m_flags;
|
|
|
|
|
/*
|
|
|
|
|
* If monster in treasure room, then MEAN
|
|
|
|
|
*/
|
|
|
|
|
if (treas || levtype == MAZELEV)
|
|
|
|
|
tp->t_flags |= ISMEAN;
|
|
|
|
|
tp->t_turn = TRUE;
|
|
|
|
|
tp->t_pack = NULL;
|
|
|
|
|
/*
|
|
|
|
|
* Dont wander if treas room
|
|
|
|
|
*/
|
|
|
|
|
if (iswearing(R_AGGR) && !treas)
|
|
|
|
|
runto(cp, &hero);
|
|
|
|
|
if (tp->t_type == 'M') {
|
|
|
|
|
char mch;
|
|
|
|
|
|
|
|
|
|
if (tp->t_pack != NULL)
|
|
|
|
|
mch = (OBJPTR(tp->t_pack))->o_type;
|
|
|
|
|
else {
|
|
|
|
|
switch (rnd(level >= AMLEVEL ? 9 : 8)) {
|
|
|
|
|
case 0: mch = GOLD;
|
|
|
|
|
when 1: mch = POTION;
|
|
|
|
|
when 2: mch = SCROLL;
|
|
|
|
|
when 3: mch = STAIRS;
|
|
|
|
|
when 4: mch = WEAPON;
|
|
|
|
|
when 5: mch = ARMOR;
|
|
|
|
|
when 6: mch = RING;
|
|
|
|
|
when 7: mch = STICK;
|
|
|
|
|
when 8: mch = AMULET;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (treas)
|
|
|
|
|
mch = 'M'; /* no disguise in treasure room */
|
|
|
|
|
tp->t_disguise = mch;
|
|
|
|
|
}
|
|
|
|
|
return item;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* wanderer:
|
|
|
|
|
* A wandering monster has awakened and is headed for the player
|
|
|
|
|
*/
|
2016-01-31 13:45:07 -05:00
|
|
|
void
|
|
|
|
|
wanderer(void)
|
2010-11-25 12:21:41 +00:00
|
|
|
{
|
2015-08-04 11:39:49 -04:00
|
|
|
reg int ch = '-';
|
2010-11-25 12:21:41 +00:00
|
|
|
reg struct room *rp, *hr = player.t_room;
|
|
|
|
|
reg struct linked_list *item;
|
|
|
|
|
reg struct thing *tp;
|
|
|
|
|
struct coord mp;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
rp = &rooms[rnd_room()];
|
|
|
|
|
if (rp != hr || levtype == MAZELEV) {
|
|
|
|
|
mp = *rnd_pos(rp);
|
|
|
|
|
ch = mvinch(mp.y, mp.x);
|
|
|
|
|
}
|
|
|
|
|
} while (!step_ok(ch));
|
|
|
|
|
item = new_monster(rnd_mon(TRUE,FALSE), &mp, FALSE);
|
|
|
|
|
tp = THINGPTR(item);
|
|
|
|
|
tp->t_flags |= ISRUN;
|
|
|
|
|
tp->t_dest = &hero;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* wake_monster:
|
|
|
|
|
* What to do when the hero steps next to a monster
|
|
|
|
|
*/
|
|
|
|
|
struct linked_list *
|
2016-01-31 13:45:07 -05:00
|
|
|
wake_monster(int y, int x)
|
2010-11-25 12:21:41 +00:00
|
|
|
{
|
|
|
|
|
reg struct thing *tp;
|
|
|
|
|
reg struct linked_list *it;
|
|
|
|
|
reg struct room *rp;
|
|
|
|
|
reg char ch;
|
|
|
|
|
bool treas = FALSE;
|
|
|
|
|
|
|
|
|
|
if ((it = find_mons(y, x)) == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
tp = THINGPTR(it);
|
|
|
|
|
ch = tp->t_type;
|
|
|
|
|
/*
|
|
|
|
|
* Every time he sees mean monster, it might start chasing him
|
|
|
|
|
*/
|
|
|
|
|
rp = player.t_room;
|
|
|
|
|
if (rp != NULL && rf_on(rp,ISTREAS)) {
|
|
|
|
|
tp->t_flags &= ~ISHELD;
|
|
|
|
|
treas = TRUE;
|
|
|
|
|
}
|
|
|
|
|
if (treas || (rnd(100) > 33 && on(*tp,ISMEAN) && off(*tp,ISHELD) &&
|
|
|
|
|
!iswearing(R_STEALTH))) {
|
|
|
|
|
tp->t_dest = &hero;
|
|
|
|
|
tp->t_flags |= ISRUN;
|
|
|
|
|
}
|
|
|
|
|
if (ch == 'U' && pl_off(ISBLIND)) {
|
|
|
|
|
if ((rp != NULL && !rf_on(rp,ISDARK) && levtype != MAZELEV)
|
|
|
|
|
|| DISTANCE(y, x, hero.y, hero.x) < 3) {
|
|
|
|
|
if (off(*tp,ISFOUND) && !save(VS_PETRIFICATION)
|
|
|
|
|
&& !iswearing(R_SUSAB) && pl_off(ISINVINC)) {
|
|
|
|
|
msg("The umber hulk's gaze has confused you.");
|
|
|
|
|
if (pl_on(ISHUH))
|
|
|
|
|
lengthen(unconfuse,rnd(20)+HUHDURATION);
|
|
|
|
|
else
|
|
|
|
|
fuse(unconfuse,TRUE,rnd(20)+HUHDURATION);
|
|
|
|
|
player.t_flags |= ISHUH;
|
|
|
|
|
}
|
|
|
|
|
tp->t_flags |= ISFOUND;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* Hide invisible monsters
|
|
|
|
|
*/
|
|
|
|
|
if ((tp->t_flags & ISINVIS) && pl_off(CANSEE))
|
2021-04-14 18:55:33 -04:00
|
|
|
ch = mvinch(y, x) & A_CHARTEXT;
|
2010-11-25 12:21:41 +00:00
|
|
|
/*
|
|
|
|
|
* Let greedy ones guard gold
|
|
|
|
|
*/
|
|
|
|
|
if (on(*tp, ISGREED) && off(*tp, ISRUN)) {
|
|
|
|
|
if (rp != NULL && rp->r_goldval) {
|
|
|
|
|
tp->t_dest = &rp->r_gold;
|
|
|
|
|
tp->t_flags |= ISRUN;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return it;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* genocide:
|
|
|
|
|
* Eradicate a monster forevermore
|
|
|
|
|
*/
|
2016-01-31 13:45:07 -05:00
|
|
|
void
|
|
|
|
|
genocide(void)
|
2010-11-25 12:21:41 +00:00
|
|
|
{
|
|
|
|
|
reg struct linked_list *ip, *nip;
|
|
|
|
|
reg struct thing *mp;
|
|
|
|
|
struct monster *mm;
|
2021-04-14 18:55:33 -04:00
|
|
|
reg int i, c;
|
2010-11-25 12:21:41 +00:00
|
|
|
|
|
|
|
|
if (levcount == 0) {
|
|
|
|
|
mpos = 0;
|
|
|
|
|
msg("You cannot genocide Asmodeus !!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
tryagain:
|
|
|
|
|
i = TRUE; /* assume an error now */
|
|
|
|
|
while (i) {
|
|
|
|
|
msg("Which monster (remember UPPER & lower case)?");
|
|
|
|
|
c = readchar(); /* get a char */
|
|
|
|
|
if (c == ESCAPE) { /* he can abort (the fool) */
|
|
|
|
|
msg("");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (isalpha(c)) /* valid char here */
|
|
|
|
|
i = FALSE; /* exit the loop */
|
|
|
|
|
else { /* he didn't type a letter */
|
|
|
|
|
mpos = 0;
|
|
|
|
|
msg("Please specify a letter between 'A' and 'z'");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
i = midx(c); /* get index to monster */
|
|
|
|
|
mm = &monsters[i];
|
|
|
|
|
if (mm->m_lev.l_lev < 0) {
|
|
|
|
|
mpos = 0;
|
|
|
|
|
msg("You have already eliminated the %s.",mm->m_name);
|
|
|
|
|
goto tryagain;
|
|
|
|
|
}
|
|
|
|
|
for (ip = mlist; ip != NULL; ip = nip) {
|
|
|
|
|
mp = THINGPTR(ip);
|
|
|
|
|
nip = next(ip);
|
|
|
|
|
if (mp->t_type == c)
|
|
|
|
|
remove_monster(&mp->t_pos, ip);
|
|
|
|
|
}
|
|
|
|
|
mm->m_lev.l_lev = -1; /* get rid of it */
|
|
|
|
|
mm->m_lev.h_lev = -1;
|
|
|
|
|
lev_mon(); /* redo monster list */
|
|
|
|
|
mpos = 0;
|
|
|
|
|
msg("You have wiped out the %s.",mm->m_name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* unhold:
|
|
|
|
|
* Release the player from being held
|
|
|
|
|
*/
|
2016-01-31 13:45:07 -05:00
|
|
|
void
|
|
|
|
|
unhold(char whichmon)
|
2010-11-25 12:21:41 +00:00
|
|
|
{
|
2016-05-01 19:39:56 -04:00
|
|
|
struct linked_list *item;
|
|
|
|
|
struct thing *mon;
|
|
|
|
|
|
2010-11-25 12:21:41 +00:00
|
|
|
switch (whichmon) {
|
|
|
|
|
case 'F':
|
|
|
|
|
fung_hit = 0;
|
2016-05-01 19:39:56 -04:00
|
|
|
for (item = mlist; item != NULL; item = next(item)) {
|
|
|
|
|
mon = THINGPTR(item);
|
|
|
|
|
if (mon->t_type == 'F')
|
|
|
|
|
strcpy(mon->t_stats.s_dmg, "000d0");
|
|
|
|
|
}
|
2010-11-25 12:21:41 +00:00
|
|
|
case 'd':
|
|
|
|
|
player.t_flags &= ~ISHELD;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* midx:
|
|
|
|
|
* This returns an index to 'whichmon'
|
|
|
|
|
*/
|
2016-01-31 13:45:07 -05:00
|
|
|
int
|
|
|
|
|
midx(char whichmon)
|
2010-11-25 12:21:41 +00:00
|
|
|
{
|
|
|
|
|
if (isupper(whichmon))
|
|
|
|
|
return(whichmon - 'A'); /* 0 to 25 for uppercase */
|
|
|
|
|
else if (islower(whichmon))
|
|
|
|
|
return(whichmon - 'a' + 26); /* 26 to 51 for lowercase */
|
|
|
|
|
else
|
|
|
|
|
return(MAXMONS); /* 52 for Asmodeus */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* monhurt:
|
|
|
|
|
* See when monster should run or fight. Return
|
|
|
|
|
* TRUE if hit points less than acceptable.
|
|
|
|
|
*/
|
2016-01-31 13:45:07 -05:00
|
|
|
bool
|
|
|
|
|
monhurt(struct thing *th)
|
2010-11-25 12:21:41 +00:00
|
|
|
{
|
|
|
|
|
reg int ewis, crithp, f1, f2;
|
|
|
|
|
reg struct stats *st;
|
|
|
|
|
|
|
|
|
|
st = &th->t_stats;
|
|
|
|
|
ewis = st->s_ef.a_wis;
|
|
|
|
|
if (ewis <= MONWIS) /* stupid monsters dont know */
|
|
|
|
|
return FALSE;
|
|
|
|
|
f1 = st->s_maxhp / 4; /* base hpt for being hurt */
|
|
|
|
|
f2 = (ewis - MONWIS) * 5 / 3; /* bonus for smart monsters */
|
|
|
|
|
if (th->t_flags & ISWOUND) /* if recovering from being */
|
|
|
|
|
f1 *= 2; /* wounded, then double the base */
|
|
|
|
|
crithp = f1 + f2; /* get critical hpt for hurt */
|
|
|
|
|
if (crithp > st->s_maxhp) /* only up to max hpt */
|
|
|
|
|
crithp = st->s_maxhp;
|
|
|
|
|
if (st->s_hpt < crithp) /* if < critical, then still hurt */
|
|
|
|
|
return TRUE;
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|