Mercurial > hg > early-roguelike
comparison urogue/monsters.c @ 256:c495a4f288c6
Import UltraRogue from the Roguelike Restoration Project (r1490)
| author | John "Elwin" Edwards |
|---|---|
| date | Tue, 31 Jan 2017 19:56:04 -0500 |
| parents | |
| children | 0250220d8cdd |
comparison
equal
deleted
inserted
replaced
| 253:d9badb9c0179 | 256:c495a4f288c6 |
|---|---|
| 1 /* | |
| 2 monsters.c - File with various monster functions in it | |
| 3 | |
| 4 UltraRogue: The Ultimate Adventure in the Dungeons of Doom | |
| 5 Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong | |
| 6 All rights reserved. | |
| 7 | |
| 8 Based on "Advanced Rogue" | |
| 9 Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka | |
| 10 All rights reserved. | |
| 11 | |
| 12 Based on "Rogue: Exploring the Dungeons of Doom" | |
| 13 Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman | |
| 14 All rights reserved. | |
| 15 | |
| 16 See the file LICENSE.TXT for full copyright and licensing information. | |
| 17 */ | |
| 18 | |
| 19 #include <stdlib.h> | |
| 20 #include <string.h> | |
| 21 #include <ctype.h> | |
| 22 #include "rogue.h" | |
| 23 | |
| 24 /* | |
| 25 summon_monster() | |
| 26 Summon a monster. | |
| 27 */ | |
| 28 | |
| 29 struct linked_list * | |
| 30 summon_monster(int type, int familiar, int print_message) | |
| 31 { | |
| 32 struct linked_list *mp; | |
| 33 struct thing *tp; | |
| 34 int monster; | |
| 35 | |
| 36 if (familiar && !is_wearing(R_WIZARD) && off(player, CANSUMMON)) | |
| 37 { | |
| 38 msg("Only spellcasters can summon familiars!"); | |
| 39 return(NULL); | |
| 40 } | |
| 41 | |
| 42 if (type == 0) /* Random monster modified by level */ | |
| 43 { | |
| 44 int ndice = min(pstats.s_lvl, (nummonst - NUMSUMMON) / 8); | |
| 45 | |
| 46 monster = min(nummonst, roll(ndice, pstats.s_charisma)); | |
| 47 | |
| 48 /* | |
| 49 * if a familiar exists, and it is higher in level, make it | |
| 50 * again | |
| 51 */ | |
| 52 | |
| 53 if (fam_ptr != NULL) | |
| 54 { | |
| 55 struct thing *fp = THINGPTR(fam_ptr); | |
| 56 | |
| 57 monster = max(fp->t_index, monster); | |
| 58 } | |
| 59 } | |
| 60 else | |
| 61 monster = type; | |
| 62 | |
| 63 turn_on(player, SUMMONING); | |
| 64 | |
| 65 mp = creat_mons(&player, monster, NOMESSAGE); | |
| 66 | |
| 67 if (!mp) | |
| 68 { | |
| 69 msg("Summon failed."); | |
| 70 turn_off(player, SUMMONING); | |
| 71 return(NULL); | |
| 72 } | |
| 73 | |
| 74 if (print_message == MESSAGE) | |
| 75 { | |
| 76 msg("A %s appears out of nowhere!", monsters[monster].m_name); | |
| 77 | |
| 78 if (familiar) | |
| 79 msg("I am here to serve %s.", whoami); | |
| 80 else | |
| 81 { | |
| 82 msg("My goodness, are you Yendor?"); | |
| 83 ++mons_summoned; | |
| 84 debug("%d monsters now summoned.", mons_summoned); | |
| 85 } | |
| 86 } | |
| 87 | |
| 88 tp = THINGPTR(mp); | |
| 89 turn_on(*tp, ISCHARMED); /* Summoned monsters are always charmed */ | |
| 90 | |
| 91 if (familiar) | |
| 92 { | |
| 93 int i; | |
| 94 static const unsigned long fam_on[]= {ISREGEN,CANSHOOT,CANWIELD,HASARMOR,ISFAMILIAR,0}; | |
| 95 static const unsigned long fam_off[]={ISMEAN, ISHUH, ISINVIS, | |
| 96 CANSURPRISE, NOMOVE, | |
| 97 ISSLOW, ISSHADOW, ISGREED, ISFAST, | |
| 98 CANFLY, ISFLEE, 0}; | |
| 99 | |
| 100 for (i = 0; fam_on[i]; i++) | |
| 101 turn_on(*tp, fam_on[i]); | |
| 102 | |
| 103 for (i = 0; fam_off[i]; i++) | |
| 104 turn_off(*tp, fam_off[i]); | |
| 105 | |
| 106 if (fam_ptr != NULL) /* Get rid of old familiar */ | |
| 107 { | |
| 108 struct thing *fp = THINGPTR(fam_ptr); | |
| 109 struct linked_list *fpack = fp->t_pack; | |
| 110 struct linked_list *item; | |
| 111 | |
| 112 if (fpack != NULL) /* Transfer pack */ | |
| 113 { | |
| 114 if (tp->t_pack == NULL) | |
| 115 tp->t_pack = fpack; | |
| 116 else | |
| 117 { | |
| 118 for(item=tp->t_pack; item->l_next != NULL; item=next(item)) | |
| 119 ; /* find last item in list */ | |
| 120 | |
| 121 item->l_next = fpack; | |
| 122 fpack->l_prev = item; | |
| 123 } | |
| 124 } | |
| 125 | |
| 126 fpack = NULL; | |
| 127 killed(NULL, fam_ptr, NOMESSAGE, NOPOINTS); | |
| 128 } | |
| 129 | |
| 130 fam_ptr = mp; | |
| 131 fam_type = monster; | |
| 132 | |
| 133 /* improve their abilities a bit */ | |
| 134 | |
| 135 tp->t_stats.s_hpt += roll(2, pstats.s_lvl); | |
| 136 tp->t_stats.s_lvl += roll(2, (pstats.s_lvl / 4) + 1); | |
| 137 tp->t_stats.s_arm -= roll(2, (pstats.s_lvl / 4) + 1); | |
| 138 tp->t_stats.s_str += roll(2, (pstats.s_lvl / 4) + 1); | |
| 139 tp->t_stats.s_intel += roll(2, (pstats.s_lvl / 4) + 1); | |
| 140 tp->t_stats.s_wisdom += roll(2, (pstats.s_lvl / 4) + 1); | |
| 141 tp->t_stats.s_dext += roll(2, (pstats.s_lvl / 4) + 1); | |
| 142 tp->t_stats.s_const += roll(2, (pstats.s_lvl / 4) + 1); | |
| 143 tp->t_stats.s_charisma += roll(2, (pstats.s_lvl / 4) + 1); | |
| 144 | |
| 145 /* some monsters do no damage by default */ | |
| 146 | |
| 147 if (strcmp(tp->t_stats.s_dmg, "0d0") == 0) | |
| 148 tp->t_stats.s_dmg = "1d8"; | |
| 149 | |
| 150 tp->maxstats = tp->t_stats; /* structure assignment */ | |
| 151 } | |
| 152 | |
| 153 turn_off(player, SUMMONING); | |
| 154 | |
| 155 return(mp); | |
| 156 } | |
| 157 | |
| 158 /* | |
| 159 randmonster() | |
| 160 wander - wandering monster allowed | |
| 161 grab - a throne room monster allowed | |
| 162 */ | |
| 163 | |
| 164 int | |
| 165 randmonster(int wander, int grab) | |
| 166 { | |
| 167 int mons_number, cur_level, range, i; | |
| 168 | |
| 169 /* Do we want a merchant? */ | |
| 170 | |
| 171 if (wander == WANDER && monsters[nummonst].m_wander && rnd(5000) < 3) | |
| 172 return(nummonst); | |
| 173 | |
| 174 cur_level = level; | |
| 175 range = 4 * NLEVMONS; | |
| 176 i = 0; | |
| 177 | |
| 178 do | |
| 179 { | |
| 180 if (i++ > range * 10) /* just in case all have be genocided */ | |
| 181 { | |
| 182 i = 0; | |
| 183 | |
| 184 if (--cur_level <= 0) | |
| 185 fatal("Rogue could not find a monster to make"); | |
| 186 } | |
| 187 | |
| 188 mons_number = NLEVMONS * (cur_level - 1) + | |
| 189 (rnd(range) - (range - 1 - NLEVMONS)); | |
| 190 | |
| 191 if (mons_number < 1) | |
| 192 mons_number = rnd(NLEVMONS) + 1; | |
| 193 else if (mons_number > nummonst - NUMSUMMON - 1) | |
| 194 { | |
| 195 if (grab == GRAB) | |
| 196 mons_number = rnd(range + NUMSUMMON) + | |
| 197 (nummonst - 1) - | |
| 198 (range + NUMSUMMON - 1); | |
| 199 else if (mons_number > nummonst - 1) | |
| 200 mons_number = rnd(range) + | |
| 201 (nummonst - NUMSUMMON - 1) - | |
| 202 (range - 1); | |
| 203 } | |
| 204 } | |
| 205 while (wander == WANDER ? !monsters[mons_number].m_wander || | |
| 206 !monsters[mons_number].m_normal : | |
| 207 !monsters[mons_number].m_normal); | |
| 208 | |
| 209 return((short)mons_number); | |
| 210 } | |
| 211 | |
| 212 /* | |
| 213 new_monster() | |
| 214 Pick a new monster and add it to the list | |
| 215 */ | |
| 216 | |
| 217 void | |
| 218 new_monster(struct linked_list *item, int type, coord *cp, int max_monster) | |
| 219 { | |
| 220 struct thing *tp; | |
| 221 struct monster *mp; | |
| 222 char *ip, *hitp; | |
| 223 int i, min_intel, max_intel; | |
| 224 int num_dice, num_sides = 8, num_extra = 0; | |
| 225 int eff_charisma = pstats.s_charisma; | |
| 226 int eff_intel = pstats.s_intel; | |
| 227 | |
| 228 attach(mlist, item); | |
| 229 tp = THINGPTR(item); | |
| 230 tp->t_index = type; | |
| 231 tp->t_wasshot = FALSE; | |
| 232 tp->t_type = monsters[type].m_appear; | |
| 233 tp->t_ctype = C_MONSTER; | |
| 234 tp->t_no_move = 0; | |
| 235 tp->t_doorgoal = -1; | |
| 236 tp->t_pos = *cp; | |
| 237 tp->t_oldpos = *cp; | |
| 238 tp->t_oldch = CCHAR( mvwinch(cw, cp->y, cp->x) ); | |
| 239 mvwaddch(mw, cp->y, cp->x, tp->t_type); | |
| 240 mp = &monsters[tp->t_index]; | |
| 241 | |
| 242 /* Figure out monster's hit points */ | |
| 243 | |
| 244 hitp = mp->m_stats.s_hpt; | |
| 245 num_dice = atoi(hitp); | |
| 246 | |
| 247 if ((hitp = strchr(hitp, 'd')) != NULL) | |
| 248 { | |
| 249 num_sides = atoi(++hitp); | |
| 250 | |
| 251 if ((hitp = strchr(hitp, '+')) != NULL) | |
| 252 num_extra = atoi(++hitp); | |
| 253 } | |
| 254 | |
| 255 if (max_monster == MAXSTATS) | |
| 256 tp->t_stats.s_hpt = num_dice * num_sides + num_extra; | |
| 257 else | |
| 258 tp->t_stats.s_hpt = roll(num_dice, num_sides) + num_extra; | |
| 259 | |
| 260 tp->t_stats.s_lvl = mp->m_stats.s_lvl; | |
| 261 tp->t_stats.s_arm = mp->m_stats.s_arm; | |
| 262 tp->t_stats.s_dmg = mp->m_stats.s_dmg; | |
| 263 tp->t_stats.s_exp = mp->m_stats.s_exp + mp->m_add_exp * tp->t_stats.s_hpt; | |
| 264 tp->t_stats.s_str = mp->m_stats.s_str; | |
| 265 | |
| 266 if (max_level > 30) | |
| 267 { | |
| 268 tp->t_stats.s_hpt += roll(4, (max_level - 60) * 2); | |
| 269 tp->t_stats.s_lvl += roll(4, (max_level - 60) / 8); | |
| 270 tp->t_stats.s_arm -= roll(2, (max_level - 60) / 8); | |
| 271 tp->t_stats.s_str += roll(2, (max_level - 60) / 12); | |
| 272 tp->t_stats.s_exp += roll(4, (max_level - 60) * 2) * mp->m_add_exp; | |
| 273 } | |
| 274 | |
| 275 /* | |
| 276 * just initailize others values to something reasonable for now | |
| 277 * maybe someday will *really* put these in monster table | |
| 278 */ | |
| 279 | |
| 280 tp->t_stats.s_wisdom = 8 + rnd(4); | |
| 281 tp->t_stats.s_dext = 8 + rnd(4); | |
| 282 tp->t_stats.s_const = 8 + rnd(4); | |
| 283 tp->t_stats.s_charisma = 8 + rnd(4); | |
| 284 | |
| 285 if (max_level > 45) | |
| 286 tp->t_stats.s_dext += roll(2, (max_level - 50) / 8); | |
| 287 | |
| 288 /* Set the initial flags */ | |
| 289 | |
| 290 for (i = 0; i < 16; i++) | |
| 291 tp->t_flags[i] = 0; | |
| 292 | |
| 293 for (i = 0; i < 16; i++) | |
| 294 turn_on(*tp, mp->m_flags[i]); | |
| 295 | |
| 296 /* suprising monsters don't always surprise you */ | |
| 297 | |
| 298 if (!max_monster && on(*tp, CANSURPRISE) && rnd(100) < 20) | |
| 299 turn_off(*tp, CANSURPRISE); | |
| 300 | |
| 301 /* If this monster is unique, genocide it */ | |
| 302 | |
| 303 if (on(*tp, ISUNIQUE)) | |
| 304 mp->m_normal = FALSE; | |
| 305 | |
| 306 /* gods automatically get special abilities */ | |
| 307 | |
| 308 if (on(*tp, ISGOD)) | |
| 309 { | |
| 310 turn_on(*tp, CANFRIGHTEN); | |
| 311 turn_on(*tp, CANCAST); | |
| 312 turn_on(*tp, CANFLY); | |
| 313 turn_on(*tp, CANBARGAIN); | |
| 314 turn_on(*tp, ISLARGE); | |
| 315 turn_on(*tp, CANTELEPORT); | |
| 316 turn_on(*tp, CANSPEAK); | |
| 317 turn_on(*tp, CANDARKEN); | |
| 318 turn_on(*tp, CANSEE); | |
| 319 turn_on(*tp, CANLIGHT); | |
| 320 turn_on(*tp, BMAGICHIT); | |
| 321 } | |
| 322 | |
| 323 tp->t_turn = TRUE; | |
| 324 tp->t_pack = NULL; | |
| 325 | |
| 326 /* Figure intelligence */ | |
| 327 | |
| 328 min_intel = atoi(mp->m_intel); | |
| 329 | |
| 330 if ((ip = (char *) strchr(mp->m_intel, '-')) == NULL) | |
| 331 tp->t_stats.s_intel = min_intel; | |
| 332 else | |
| 333 { | |
| 334 max_intel = atoi(++ip); | |
| 335 | |
| 336 if (max_monster) | |
| 337 tp->t_stats.s_intel = max_intel; | |
| 338 else | |
| 339 tp->t_stats.s_intel = min_intel + rnd(max_intel - min_intel); | |
| 340 } | |
| 341 | |
| 342 tp->t_stats.s_power = (rnd(tp->t_stats.s_lvl / 5) + 1) * tp->t_stats.s_intel; | |
| 343 | |
| 344 tp->maxstats = tp->t_stats; /* structure assignment */ | |
| 345 | |
| 346 /* If the monster can shoot, it may have a weapon */ | |
| 347 | |
| 348 if (on(*tp, CANSHOOT) && (max_monster || rnd(9) < 6)) | |
| 349 { | |
| 350 struct linked_list *thrower_item, *missile_item; | |
| 351 struct object *thrower, *a_missile; | |
| 352 | |
| 353 thrower_item = new_item(sizeof *thrower); | |
| 354 thrower = OBJPTR(thrower_item); | |
| 355 carried_weapon(tp, thrower); | |
| 356 | |
| 357 missile_item = new_item(sizeof *a_missile); | |
| 358 a_missile = OBJPTR(missile_item); | |
| 359 carried_weapon(tp, a_missile); | |
| 360 | |
| 361 /* The monster may use a crossbow, sling, footbow, or an arrow */ | |
| 362 /* Take racial preferences into account */ | |
| 363 | |
| 364 if ((strcmp(mp->m_name, "elf") == 0) || | |
| 365 (strcmp(mp->m_name, "noldor elf") == 0)) | |
| 366 { | |
| 367 thrower->o_which = BOW; | |
| 368 | |
| 369 if (rnd(5) == 0) | |
