Mercurial > hg > early-roguelike
comparison srogue/monsters.c @ 36:2128c7dc8a40
Import Super-Rogue 9.0 from the Roguelike Restoration Project (r1490)
| author | elwin |
|---|---|
| date | Thu, 25 Nov 2010 12:21:41 +0000 |
| parents | |
| children | 7bdac632ab9d |
comparison
equal
deleted
inserted
replaced
| 35:05018c63a721 | 36:2128c7dc8a40 |
|---|---|
| 1 /* | |
| 2 * File with various monster functions in it | |
| 3 * | |
| 4 * @(#)monsters.c 9.0 (rdk) 7/17/84 | |
| 5 * | |
| 6 * Super-Rogue | |
| 7 * Copyright (C) 1984 Robert D. Kindelberger | |
| 8 * All rights reserved. | |
| 9 * | |
| 10 * Based on "Rogue: Exploring the Dungeons of Doom" | |
| 11 * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman | |
| 12 * All rights reserved. | |
| 13 * | |
| 14 * See the file LICENSE.TXT for full copyright and licensing information. | |
| 15 */ | |
| 16 | |
| 17 #include "rogue.h" | |
| 18 #include <ctype.h> | |
| 19 #include "rogue.ext" | |
| 20 | |
| 21 /* | |
| 22 * rnd_mon: | |
| 23 * Pick a monster to show up. The lower the level, | |
| 24 * the meaner the monster. | |
| 25 */ | |
| 26 rnd_mon(wander,baddie) | |
| 27 bool wander; | |
| 28 bool baddie; /* TRUE when from a polymorph stick */ | |
| 29 { | |
| 30 reg int i, ok, cnt; | |
| 31 | |
| 32 cnt = 0; | |
| 33 if (levcount == 0) /* if only asmodeus possible */ | |
| 34 return(MAXMONS); | |
| 35 if (baddie) { | |
| 36 while (1) { | |
| 37 i = rnd(MAXMONS); /* pick ANY monster */ | |
| 38 if (monsters[i].m_lev.l_lev < 0) /* skip genocided ones */ | |
| 39 continue; | |
| 40 return i; | |
| 41 } | |
| 42 } | |
| 43 ok = FALSE; | |
| 44 do { | |
| 45 /* | |
| 46 * get a random monster from this range | |
| 47 */ | |
| 48 i = rnd(levcount); | |
| 49 /* | |
| 50 * Only create a wandering monster if we want one | |
| 51 * (or the count is exceeded) | |
| 52 */ | |
| 53 if (!wander || mtlev[i]->m_lev.d_wand || ++cnt > 500) | |
| 54 ok = TRUE; | |
| 55 } while(!ok); | |
| 56 return (midx(mtlev[i]->m_show)); | |
| 57 } | |
| 58 | |
| 59 /* | |
| 60 * lev_mon: | |
| 61 * This gets all monsters possible on this level | |
| 62 */ | |
| 63 lev_mon() | |
| 64 { | |
| 65 reg int i; | |
| 66 reg struct monster *mm; | |
| 67 | |
| 68 levcount = 0; | |
| 69 for (i = 0; i < MAXMONS; i++) { | |
| 70 mm = &monsters[i]; | |
| 71 if (mm->m_lev.h_lev >= level && mm->m_lev.l_lev <= level) { | |
| 72 mtlev[levcount] = mm; | |
| 73 if (++levcount >= MONRANGE) | |
| 74 break; | |
| 75 } | |
| 76 } | |
| 77 if (levcount == 0) /* if no monsters are possible */ | |
| 78 mtlev[0] = &monsters[MAXMONS]; /* then asmodeus 'A' */ | |
| 79 } | |
| 80 | |
| 81 /* | |
| 82 * new_monster: | |
| 83 * Pick a new monster and add it to the list | |
| 84 */ | |
| 85 struct linked_list * | |
| 86 new_monster(type, cp, treas) | |
| 87 struct coord *cp; | |
| 88 bool treas; | |
| 89 char type; | |
| 90 { | |
| 91 reg struct linked_list *item; | |
| 92 reg struct thing *tp; | |
| 93 reg struct monster *mp; | |
| 94 reg struct stats *st; | |
| 95 float killexp; /* experience gotten for killing him */ | |
| 96 | |
| 97 item = new_item(sizeof(struct thing)); | |
| 98 attach(mlist, item); | |
| 99 tp = THINGPTR(item); | |
| 100 st = &tp->t_stats; | |
| 101 mp = &monsters[type]; /* point to this monsters structure */ | |
| 102 tp->t_type = mp->m_show; | |
| 103 tp->t_indx = type; | |
| 104 tp->t_pos = *cp; | |
| 105 tp->t_room = roomin(cp); | |
| 106 tp->t_oldch = mvwinch(cw, cp->y, cp->x); | |
| 107 tp->t_nomove = 0; | |
| 108 tp->t_nocmd = 0; | |
| 109 mvwaddch(mw, cp->y, cp->x, tp->t_type); | |
| 110 | |
| 111 /* | |
| 112 * copy monster data | |
| 113 */ | |
| 114 tp->t_stats = mp->m_stats; | |
| 115 | |
| 116 /* | |
| 117 * If below amulet level, make the monsters meaner the | |
| 118 * deeper the hero goes. | |
| 119 */ | |
| 120 if (level > AMLEVEL) | |
| 121 st->s_lvl += ((level - AMLEVEL) / 4); | |
| 122 | |
| 123 /* | |
| 124 * If monster in treasure room, then tougher. | |
| 125 */ | |
| 126 if (treas) | |
| 127 st->s_lvl += 1; | |
| 128 if (levtype == MAZELEV) | |
| 129 st->s_lvl += 1; | |
| 130 /* | |
| 131 * If the hero is going back up, then the monsters are more | |
| 132 * prepared for him, so tougher. | |
| 133 */ | |
| 134 if (goingup()) | |
| 135 st->s_lvl += 1; | |
| 136 | |
| 137 /* | |
| 138 * Get hit points for monster depending on his experience | |
| 139 */ | |
| 140 st->s_hpt = roll(st->s_lvl, 8); | |
| 141 st->s_maxhp = st->s_hpt; | |
| 142 /* | |
| 143 * Adjust experience point we get for killing it by the | |
| 144 * strength of this particular monster by ~~ +- 50% | |
| 145 */ | |
| 146 killexp = mp->m_stats.s_exp * (0.47 + (float)st->s_hpt / | |
| 147 (8 * (float)st->s_lvl)); | |
| 148 | |
| 149 st->s_exp = killexp; /* use float for accuracy */ | |
| 150 if(st->s_exp < 1) | |
| 151 st->s_exp = 1; /* minimum 1 experience point */ | |
| 152 tp->t_flags = mp->m_flags; | |
| 153 /* | |
| 154 * If monster in treasure room, then MEAN | |
| 155 */ | |
| 156 if (treas || levtype == MAZELEV) | |
| 157 tp->t_flags |= ISMEAN; | |
| 158 tp->t_turn = TRUE; | |
| 159 tp->t_pack = NULL; | |
| 160 /* | |
| 161 * Dont wander if treas room | |
| 162 */ | |
| 163 if (iswearing(R_AGGR) && !treas) | |
| 164 runto(cp, &hero); | |
| 165 if (tp->t_type == 'M') { | |
| 166 char mch; | |
| 167 | |
| 168 if (tp->t_pack != NULL) | |
| 169 mch = (OBJPTR(tp->t_pack))->o_type; | |
| 170 else { | |
| 171 switch (rnd(level >= AMLEVEL ? 9 : 8)) { | |
| 172 case 0: mch = GOLD; | |
| 173 when 1: mch = POTION; | |
| 174 when 2: mch = SCROLL; | |
| 175 when 3: mch = STAIRS; | |
| 176 when 4: mch = WEAPON; | |
| 177 when 5: mch = ARMOR; | |
| 178 when 6: mch = RING; | |
| 179 when 7: mch = STICK; | |
| 180 when 8: mch = AMULET; | |
| 181 } | |
| 182 } | |
| 183 if (treas) | |
| 184 mch = 'M'; /* no disguise in treasure room */ | |
| 185 tp->t_disguise = mch; | |
| 186 } | |
| 187 return item; | |
| 188 } | |
| 189 | |
| 190 /* | |
| 191 * wanderer: | |
| 192 * A wandering monster has awakened and is headed for the player | |
| 193 */ | |
| 194 wanderer() | |
| 195 { | |
| 196 reg int ch; | |
| 197 reg struct room *rp, *hr = player.t_room; | |
| 198 reg struct linked_list *item; | |
| 199 reg struct thing *tp; | |
| 200 struct coord mp; | |
| 201 | |
| 202 do { | |
| 203 rp = &rooms[rnd_room()]; | |
| 204 if (rp != hr || levtype == MAZELEV) { | |
| 205 mp = *rnd_pos(rp); | |
| 206 ch = mvinch(mp.y, mp.x); | |
| 207 } | |
| 208 } while (!step_ok(ch)); | |
| 209 item = new_monster(rnd_mon(TRUE,FALSE), &mp, FALSE); | |
| 210 tp = THINGPTR(item); | |
| 211 tp->t_flags |= ISRUN; | |
| 212 tp->t_dest = &hero; | |
| 213 } | |
| 214 | |
| 215 /* | |
| 216 * wake_monster: | |
| 217 * What to do when the hero steps next to a monster | |
| 218 */ | |
| 219 struct linked_list * | |
| 220 wake_monster(y, x) | |
| 221 int y, x; | |
| 222 { | |
| 223 reg struct thing *tp; | |
| 224 reg struct linked_list *it; | |
| 225 reg struct room *rp; | |
| 226 reg char ch; | |
| 227 bool treas = FALSE; | |
| 228 | |
| 229 if ((it = find_mons(y, x)) == NULL) | |
| 230 return NULL; | |
| 231 tp = THINGPTR(it); | |
| 232 ch = tp->t_type; | |
| 233 /* | |
| 234 * Every time he sees mean monster, it might start chasing him | |
| 235 */ | |
| 236 rp = player.t_room; | |
| 237 if (rp != NULL && rf_on(rp,ISTREAS)) { | |
| 238 tp->t_flags &= ~ISHELD; | |
| 239 treas = TRUE; | |
| 240 } | |
| 241 if (treas || (rnd(100) > 33 && on(*tp,ISMEAN) && off(*tp,ISHELD) && | |
| 242 !iswearing(R_STEALTH))) { | |
| 243 tp->t_dest = &hero; | |
| 244 tp->t_flags |= ISRUN; | |
| 245 } | |
| 246 if (ch == 'U' && pl_off(ISBLIND)) { | |
| 247 if ((rp != NULL && !rf_on(rp,ISDARK) && levtype != MAZELEV) | |
| 248 || DISTANCE(y, x, hero.y, hero.x) < 3) { | |
| 249 if (off(*tp,ISFOUND) && !save(VS_PETRIFICATION) | |
| 250 && !iswearing(R_SUSAB) && pl_off(ISINVINC)) { | |
| 251 msg("The umber hulk's gaze has confused you."); | |
| 252 if (pl_on(ISHUH)) | |
| 253 lengthen(unconfuse,rnd(20)+HUHDURATION); | |
| 254 else | |
| 255 fuse(unconfuse,TRUE,rnd(20)+HUHDURATION); | |
| 256 player.t_flags |= ISHUH; | |
| 257 } | |
| 258 tp->t_flags |= ISFOUND; | |
| 259 } | |
| 260 } | |
| 261 /* | |
| 262 * Hide invisible monsters | |
| 263 */ | |
| 264 if ((tp->t_flags & ISINVIS) && pl_off(CANSEE)) | |
| 265 ch = mvinch(y, x); | |
| 266 /* | |
| 267 * Let greedy ones guard gold | |
| 268 */ | |
| 269 if (on(*tp, ISGREED) && off(*tp, ISRUN)) { | |
| 270 if (rp != NULL && rp->r_goldval) { | |
| 271 tp->t_dest = &rp->r_gold; | |
| 272 tp->t_flags |= ISRUN; | |
| 273 } | |
| 274 } | |
| 275 return it; | |
| 276 } | |
| 277 | |
| 278 /* | |
| 279 * genocide: | |
| 280 * Eradicate a monster forevermore | |
| 281 */ | |
| 282 genocide() | |
| 283 { | |
| 284 reg struct linked_list *ip, *nip; | |
| 285 reg struct thing *mp; | |
| 286 struct monster *mm; | |
| 287 reg int i, ii, c; | |
| 288 | |
| 289 if (levcount == 0) { | |
| 290 mpos = 0; | |
| 291 msg("You cannot genocide Asmodeus !!"); | |
| 292 return; | |
| 293 } | |
| 294 tryagain: | |
| 295 i = TRUE; /* assume an error now */ | |
| 296 while (i) { | |
| 297 msg("Which monster (remember UPPER & lower case)?"); | |
| 298 c = readchar(); /* get a char */ | |
| 299 if (c == ESCAPE) { /* he can abort (the fool) */ | |
| 300 msg(""); | |
| 301 return; | |
| 302 } | |
| 303 if (isalpha(c)) /* valid char here */ | |
| 304 i = FALSE; /* exit the loop */ | |
| 305 else { /* he didn't type a letter */ | |
| 306 mpos = 0; | |
| 307 msg("Please specify a letter between 'A' and 'z'"); | |
| 308 } | |
| 309 } | |
| 310 i = midx(c); /* get index to monster */ | |
| 311 mm = &monsters[i]; | |
| 312 if (mm->m_lev.l_lev < 0) { | |
| 313 mpos = 0; | |
| 314 msg("You have already eliminated the %s.",mm->m_name); | |
| 315 goto tryagain; | |
| 316 } | |
| 317 for (ip = mlist; ip != NULL; ip = nip) { | |
| 318 mp = THINGPTR(ip); | |
| 319 nip = next(ip); | |
| 320 if (mp->t_type == c) | |
| 321 remove_monster(&mp->t_pos, ip); | |
| 322 } | |
| 323 mm->m_lev.l_lev = -1; /* get rid of it */ | |
| 324 mm->m_lev.h_lev = -1; | |
| 325 lev_mon(); /* redo monster list */ | |
| 326 mpos = 0; | |
| 327 msg("You have wiped out the %s.",mm->m_name); | |
| 328 } | |
| 329 | |
| 330 /* | |
| 331 * unhold: | |
| 332 * Release the player from being held | |
| 333 */ | |
| 334 unhold(whichmon) | |
| 335 char whichmon; | |
| 336 { | |
| 337 switch (whichmon) { | |
| 338 case 'F': | |
| 339 fung_hit = 0; | |
| 340 strcpy(monsters[midx('F')].m_stats.s_dmg, "000d0"); | |
| 341 case 'd': | |
| 342 player.t_flags &= ~ISHELD; | |
| 343 } | |
| 344 } | |
| 345 | |
| 346 /* | |
| 347 * midx: | |
| 348 * This returns an index to 'whichmon' | |
| 349 */ | |
| 350 midx(whichmon) | |
| 351 char whichmon; | |
| 352 { | |
| 353 if (isupper(whichmon)) | |
| 354 return(whichmon - 'A'); /* 0 to 25 for uppercase */ | |
| 355 else if (islower(whichmon)) | |
| 356 return(whichmon - 'a' + 26); /* 26 to 51 for lowercase */ | |
| 357 else | |
| 358 return(MAXMONS); /* 52 for Asmodeus */ | |
| 359 } | |
| 360 | |
| 361 /* | |
| 362 * monhurt: | |
| 363 * See when monster should run or fight. Return | |
| 364 * TRUE if hit points less than acceptable. | |
| 365 */ | |
| 366 monhurt(th) | |
| 367 struct thing *th; | |
| 368 { | |
| 369 reg int ewis, crithp, f1, f2; | |
| 370 reg struct stats *st; | |
| 371 | |
| 372 st = &th->t_stats; | |
| 373 ewis = st->s_ef.a_wis; | |
| 374 if (ewis <= MONWIS) /* stupid monsters dont know */ | |
| 375 return FALSE; | |
| 376 f1 = st->s_maxhp / 4; /* base hpt for being hurt */ | |
| 377 f2 = (ewis - MONWIS) * 5 / 3; /* bonus for smart monsters */ | |
| 378 if (th->t_flags & ISWOUND) /* if recovering from being */ | |
| 379 f1 *= 2; /* wounded, then double the base */ | |
| 380 crithp = f1 + f2; /* get critical hpt for hurt */ | |
| 381 if (crithp > st->s_maxhp) /* only up to max hpt */ | |
| 382 crithp = st->s_maxhp; | |
| 383 if (st->s_hpt < crithp) /* if < critical, then still hurt */ | |
| 384 return TRUE; | |
| 385 return FALSE; | |
| 386 } |
