/* * 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. */ #include #include "rogue.h" #include #include "rogue.ext" /* * rnd_mon: * Pick a monster to show up. The lower the level, * the meaner the monster. */ char rnd_mon(bool wander, bool baddie) { /* baddie; TRUE when from a polymorph stick */ 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 */ void lev_mon(void) { 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 * new_monster(char type, struct coord *cp, bool treas) { 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; mp = &monsters[type]; /* point to this monsters structure */ tp->t_type = mp->m_show; tp->t_indx = type; tp->t_pos = *cp; tp->t_room = roomin(cp); tp->t_oldch = mvwinch(cw, cp->y, cp->x); 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 */ void wanderer(void) { reg int ch = '-'; 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 * wake_monster(int y, int x) { 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)) ch = mvinch(y, x); /* * 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 */ void genocide(void) { reg struct linked_list *ip, *nip; reg struct thing *mp; struct monster *mm; reg int i, ii, c; 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 */ void unhold(char whichmon) { struct linked_list *item; struct thing *mon; switch (whichmon) { case 'F': fung_hit = 0; for (item = mlist; item != NULL; item = next(item)) { mon = THINGPTR(item); if (mon->t_type == 'F') strcpy(mon->t_stats.s_dmg, "000d0"); } case 'd': player.t_flags &= ~ISHELD; } } /* * midx: * This returns an index to 'whichmon' */ int midx(char whichmon) { 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. */ bool monhurt(struct thing *th) { 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; }