| 1 | /* |
| 2 | * Copyright (c) 1988 The Regents of the University of California. |
| 3 | * All rights reserved. |
| 4 | * |
| 5 | * This code is derived from software contributed to Berkeley by |
| 6 | * Timothy C. Stoehr. |
| 7 | * |
| 8 | * Redistribution and use in source and binary forms are permitted |
| 9 | * provided that the above copyright notice and this paragraph are |
| 10 | * duplicated in all such forms and that any documentation, |
| 11 | * advertising materials, and other materials related to such |
| 12 | * distribution and use acknowledge that the software was developed |
| 13 | * by the University of California, Berkeley. The name of the |
| 14 | * University may not be used to endorse or promote products derived |
| 15 | * from this software without specific prior written permission. |
| 16 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR |
| 17 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED |
| 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
| 19 | */ |
| 20 | |
| 21 | #ifndef lint |
| 22 | static char sccsid[] = "@(#)spec_hit.c 5.2 (Berkeley) %G%"; |
| 23 | #endif /* not lint */ |
| 24 | |
| 25 | /* |
| 26 | * special_hit.c |
| 27 | * |
| 28 | * This source herein may be modified and/or distributed by anybody who |
| 29 | * so desires, with the following restrictions: |
| 30 | * 1.) No portion of this notice shall be removed. |
| 31 | * 2.) Credit shall not be taken for the creation of this source. |
| 32 | * 3.) This code is not to be traded, sold, or used for personal |
| 33 | * gain or profit. |
| 34 | * |
| 35 | */ |
| 36 | |
| 37 | #include "rogue.h" |
| 38 | |
| 39 | short less_hp = 0; |
| 40 | boolean being_held; |
| 41 | |
| 42 | extern short cur_level, max_level, blind, levitate, ring_exp; |
| 43 | extern long level_points[]; |
| 44 | extern boolean detect_monster, mon_disappeared; |
| 45 | extern boolean sustain_strength, maintain_armor; |
| 46 | extern char *you_can_move_again; |
| 47 | |
| 48 | special_hit(monster) |
| 49 | object *monster; |
| 50 | { |
| 51 | if ((monster->m_flags & CONFUSED) && rand_percent(66)) { |
| 52 | return; |
| 53 | } |
| 54 | if (monster->m_flags & RUSTS) { |
| 55 | rust(monster); |
| 56 | } |
| 57 | if ((monster->m_flags & HOLDS) && !levitate) { |
| 58 | being_held = 1; |
| 59 | } |
| 60 | if (monster->m_flags & FREEZES) { |
| 61 | freeze(monster); |
| 62 | } |
| 63 | if (monster->m_flags & STINGS) { |
| 64 | sting(monster); |
| 65 | } |
| 66 | if (monster->m_flags & DRAINS_LIFE) { |
| 67 | drain_life(); |
| 68 | } |
| 69 | if (monster->m_flags & DROPS_LEVEL) { |
| 70 | drop_level(); |
| 71 | } |
| 72 | if (monster->m_flags & STEALS_GOLD) { |
| 73 | steal_gold(monster); |
| 74 | } else if (monster->m_flags & STEALS_ITEM) { |
| 75 | steal_item(monster); |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | rust(monster) |
| 80 | object *monster; |
| 81 | { |
| 82 | if ((!rogue.armor) || (get_armor_class(rogue.armor) <= 1) || |
| 83 | (rogue.armor->which_kind == LEATHER)) { |
| 84 | return; |
| 85 | } |
| 86 | if ((rogue.armor->is_protected) || maintain_armor) { |
| 87 | if (monster && (!(monster->m_flags & RUST_VANISHED))) { |
| 88 | message("the rust vanishes instantly", 0); |
| 89 | monster->m_flags |= RUST_VANISHED; |
| 90 | } |
| 91 | } else { |
| 92 | rogue.armor->d_enchant--; |
| 93 | message("your armor weakens", 0); |
| 94 | print_stats(STAT_ARMOR); |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | freeze(monster) |
| 99 | object *monster; |
| 100 | { |
| 101 | short freeze_percent = 99; |
| 102 | short i, n; |
| 103 | |
| 104 | if (rand_percent(12)) { |
| 105 | return; |
| 106 | } |
| 107 | freeze_percent -= (rogue.str_current+(rogue.str_current / 2)); |
| 108 | freeze_percent -= ((rogue.exp + ring_exp) * 4); |
| 109 | freeze_percent -= (get_armor_class(rogue.armor) * 5); |
| 110 | freeze_percent -= (rogue.hp_max / 3); |
| 111 | |
| 112 | if (freeze_percent > 10) { |
| 113 | monster->m_flags |= FREEZING_ROGUE; |
| 114 | message("you are frozen", 1); |
| 115 | |
| 116 | n = get_rand(4, 8); |
| 117 | for (i = 0; i < n; i++) { |
| 118 | mv_mons(); |
| 119 | } |
| 120 | if (rand_percent(freeze_percent)) { |
| 121 | for (i = 0; i < 50; i++) { |
| 122 | mv_mons(); |
| 123 | } |
| 124 | killed_by((object *)0, HYPOTHERMIA); |
| 125 | } |
| 126 | message(you_can_move_again, 1); |
| 127 | monster->m_flags &= (~FREEZING_ROGUE); |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | steal_gold(monster) |
| 132 | object *monster; |
| 133 | { |
| 134 | int amount; |
| 135 | |
| 136 | if ((rogue.gold <= 0) || rand_percent(10)) { |
| 137 | return; |
| 138 | } |
| 139 | |
| 140 | amount = get_rand((cur_level * 10), (cur_level * 30)); |
| 141 | |
| 142 | if (amount > rogue.gold) { |
| 143 | amount = rogue.gold; |
| 144 | } |
| 145 | rogue.gold -= amount; |
| 146 | message("your purse feels lighter", 0); |
| 147 | print_stats(STAT_GOLD); |
| 148 | disappear(monster); |
| 149 | } |
| 150 | |
| 151 | steal_item(monster) |
| 152 | object *monster; |
| 153 | { |
| 154 | object *obj; |
| 155 | short i, n, t; |
| 156 | char desc[80]; |
| 157 | boolean has_something = 0; |
| 158 | |
| 159 | if (rand_percent(15)) { |
| 160 | return; |
| 161 | } |
| 162 | obj = rogue.pack.next_object; |
| 163 | |
| 164 | if (!obj) { |
| 165 | goto DSPR; |
| 166 | } |
| 167 | while (obj) { |
| 168 | if (!(obj->in_use_flags & BEING_USED)) { |
| 169 | has_something = 1; |
| 170 | break; |
| 171 | } |
| 172 | obj = obj->next_object; |
| 173 | } |
| 174 | if (!has_something) { |
| 175 | goto DSPR; |
| 176 | } |
| 177 | n = get_rand(0, MAX_PACK_COUNT); |
| 178 | obj = rogue.pack.next_object; |
| 179 | |
| 180 | for (i = 0; i <= n; i++) { |
| 181 | obj = obj->next_object; |
| 182 | while ((!obj) || (obj->in_use_flags & BEING_USED)) { |
| 183 | if (!obj) { |
| 184 | obj = rogue.pack.next_object; |
| 185 | } else { |
| 186 | obj = obj->next_object; |
| 187 | } |
| 188 | } |
| 189 | } |
| 190 | (void) strcpy(desc, "she stole "); |
| 191 | if (obj->what_is != WEAPON) { |
| 192 | t = obj->quantity; |
| 193 | obj->quantity = 1; |
| 194 | } |
| 195 | get_desc(obj, desc+10); |
| 196 | message(desc, 0); |
| 197 | |
| 198 | obj->quantity = ((obj->what_is != WEAPON) ? t : 1); |
| 199 | |
| 200 | vanish(obj, 0, &rogue.pack); |
| 201 | DSPR: |
| 202 | disappear(monster); |
| 203 | } |
| 204 | |
| 205 | disappear(monster) |
| 206 | object *monster; |
| 207 | { |
| 208 | short row, col; |
| 209 | |
| 210 | row = monster->row; |
| 211 | col = monster->col; |
| 212 | |
| 213 | dungeon[row][col] &= ~MONSTER; |
| 214 | if (rogue_can_see(row, col)) { |
| 215 | mvaddch(row, col, get_dungeon_char(row, col)); |
| 216 | } |
| 217 | take_from_pack(monster, &level_monsters); |
| 218 | free_object(monster); |
| 219 | mon_disappeared = 1; |
| 220 | } |
| 221 | |
| 222 | cough_up(monster) |
| 223 | object *monster; |
| 224 | { |
| 225 | object *obj; |
| 226 | short row, col, i, n; |
| 227 | |
| 228 | if (cur_level < max_level) { |
| 229 | return; |
| 230 | } |
| 231 | |
| 232 | if (monster->m_flags & STEALS_GOLD) { |
| 233 | obj = alloc_object(); |
| 234 | obj->what_is = GOLD; |
| 235 | obj->quantity = get_rand((cur_level * 15), (cur_level * 30)); |
| 236 | } else { |
| 237 | if (!rand_percent((int) monster->drop_percent)) { |
| 238 | return; |
| 239 | } |
| 240 | obj = gr_object(); |
| 241 | } |
| 242 | row = monster->row; |
| 243 | col = monster->col; |
| 244 | |
| 245 | for (n = 0; n <= 5; n++) { |
| 246 | for (i = -n; i <= n; i++) { |
| 247 | if (try_to_cough(row+n, col+i, obj)) { |
| 248 | return; |
| 249 | } |
| 250 | if (try_to_cough(row-n, col+i, obj)) { |
| 251 | return; |
| 252 | } |
| 253 | } |
| 254 | for (i = -n; i <= n; i++) { |
| 255 | if (try_to_cough(row+i, col-n, obj)) { |
| 256 | return; |
| 257 | } |
| 258 | if (try_to_cough(row+i, col+n, obj)) { |
| 259 | return; |
| 260 | } |
| 261 | } |
| 262 | } |
| 263 | free_object(obj); |
| 264 | } |
| 265 | |
| 266 | try_to_cough(row, col, obj) |
| 267 | short row, col; |
| 268 | object *obj; |
| 269 | { |
| 270 | if ((row < MIN_ROW) || (row > (DROWS-2)) || (col < 0) || (col>(DCOLS-1))) { |
| 271 | return(0); |
| 272 | } |
| 273 | if ((!(dungeon[row][col] & (OBJECT | STAIRS | TRAP))) && |
| 274 | (dungeon[row][col] & (TUNNEL | FLOOR | DOOR))) { |
| 275 | place_at(obj, row, col); |
| 276 | if (((row != rogue.row) || (col != rogue.col)) && |
| 277 | (!(dungeon[row][col] & MONSTER))) { |
| 278 | mvaddch(row, col, get_dungeon_char(row, col)); |
| 279 | } |
| 280 | return(1); |
| 281 | } |
| 282 | return(0); |
| 283 | } |
| 284 | |
| 285 | seek_gold(monster) |
| 286 | object *monster; |
| 287 | { |
| 288 | short i, j, rn, s; |
| 289 | |
| 290 | if ((rn = get_room_number(monster->row, monster->col)) < 0) { |
| 291 | return(0); |
| 292 | } |
| 293 | for (i = rooms[rn].top_row+1; i < rooms[rn].bottom_row; i++) { |
| 294 | for (j = rooms[rn].left_col+1; j < rooms[rn].right_col; j++) { |
| 295 | if ((gold_at(i, j)) && !(dungeon[i][j] & MONSTER)) { |
| 296 | monster->m_flags |= CAN_FLIT; |
| 297 | s = mon_can_go(monster, i, j); |
| 298 | monster->m_flags &= (~CAN_FLIT); |
| 299 | if (s) { |
| 300 | move_mon_to(monster, i, j); |
| 301 | monster->m_flags |= ASLEEP; |
| 302 | monster->m_flags &= (~(WAKENS | SEEKS_GOLD)); |
| 303 | return(1); |
| 304 | } |
| 305 | monster->m_flags &= (~SEEKS_GOLD); |
| 306 | monster->m_flags |= CAN_FLIT; |
| 307 | mv_1_monster(monster, i, j); |
| 308 | monster->m_flags &= (~CAN_FLIT); |
| 309 | monster->m_flags |= SEEKS_GOLD; |
| 310 | return(1); |
| 311 | } |
| 312 | } |
| 313 | } |
| 314 | return(0); |
| 315 | } |
| 316 | |
| 317 | gold_at(row, col) |
| 318 | short row, col; |
| 319 | { |
| 320 | if (dungeon[row][col] & OBJECT) { |
| 321 | object *obj; |
| 322 | |
| 323 | if ((obj = object_at(&level_objects, row, col)) && |
| 324 | (obj->what_is == GOLD)) { |
| 325 | return(1); |
| 326 | } |
| 327 | } |
| 328 | return(0); |
| 329 | } |
| 330 | |
| 331 | check_gold_seeker(monster) |
| 332 | object *monster; |
| 333 | { |
| 334 | monster->m_flags &= (~SEEKS_GOLD); |
| 335 | } |
| 336 | |
| 337 | check_imitator(monster) |
| 338 | object *monster; |
| 339 | { |
| 340 | char msg[80]; |
| 341 | |
| 342 | if (monster->m_flags & IMITATES) { |
| 343 | wake_up(monster); |
| 344 | if (!blind) { |
| 345 | mvaddch(monster->row, monster->col, |
| 346 | get_dungeon_char(monster->row, monster->col)); |
| 347 | check_message(); |
| 348 | sprintf(msg, "wait, that's a %s!", mon_name(monster)); |
| 349 | message(msg, 1); |
| 350 | } |
| 351 | return(1); |
| 352 | } |
| 353 | return(0); |
| 354 | } |
| 355 | |
| 356 | imitating(row, col) |
| 357 | register short row, col; |
| 358 | { |
| 359 | if (dungeon[row][col] & MONSTER) { |
| 360 | object *object_at(), *monster; |
| 361 | |
| 362 | if (monster = object_at(&level_monsters, row, col)) { |
| 363 | if (monster->m_flags & IMITATES) { |
| 364 | return(1); |
| 365 | } |
| 366 | } |
| 367 | } |
| 368 | return(0); |
| 369 | } |
| 370 | |
| 371 | sting(monster) |
| 372 | object *monster; |
| 373 | { |
| 374 | short sting_chance = 35; |
| 375 | char msg[80]; |
| 376 | |
| 377 | if ((rogue.str_current <= 3) || sustain_strength) { |
| 378 | return; |
| 379 | } |
| 380 | sting_chance += (6 * (6 - get_armor_class(rogue.armor))); |
| 381 | |
| 382 | if ((rogue.exp + ring_exp) > 8) { |
| 383 | sting_chance -= (6 * ((rogue.exp + ring_exp) - 8)); |
| 384 | } |
| 385 | if (rand_percent(sting_chance)) { |
| 386 | sprintf(msg, "the %s's bite has weakened you", |
| 387 | mon_name(monster)); |
| 388 | message(msg, 0); |
| 389 | rogue.str_current--; |
| 390 | print_stats(STAT_STRENGTH); |
| 391 | } |
| 392 | } |
| 393 | |
| 394 | drop_level() |
| 395 | { |
| 396 | int hp; |
| 397 | |
| 398 | if (rand_percent(80) || (rogue.exp <= 5)) { |
| 399 | return; |
| 400 | } |
| 401 | rogue.exp_points = level_points[rogue.exp-2] - get_rand(9, 29); |
| 402 | rogue.exp -= 2; |
| 403 | hp = hp_raise(); |
| 404 | if ((rogue.hp_current -= hp) <= 0) { |
| 405 | rogue.hp_current = 1; |
| 406 | } |
| 407 | if ((rogue.hp_max -= hp) <= 0) { |
| 408 | rogue.hp_max = 1; |
| 409 | } |
| 410 | add_exp(1, 0); |
| 411 | } |
| 412 | |
| 413 | drain_life() |
| 414 | { |
| 415 | short n; |
| 416 | |
| 417 | if (rand_percent(60) || (rogue.hp_max <= 30) || (rogue.hp_current < 10)) { |
| 418 | return; |
| 419 | } |
| 420 | n = get_rand(1, 3); /* 1 Hp, 2 Str, 3 both */ |
| 421 | |
| 422 | if ((n != 2) || (!sustain_strength)) { |
| 423 | message("you feel weaker", 0); |
| 424 | } |
| 425 | if (n != 2) { |
| 426 | rogue.hp_max--; |
| 427 | rogue.hp_current--; |
| 428 | less_hp++; |
| 429 | } |
| 430 | if (n != 1) { |
| 431 | if ((rogue.str_current > 3) && (!sustain_strength)) { |
| 432 | rogue.str_current--; |
| 433 | if (coin_toss()) { |
| 434 | rogue.str_max--; |
| 435 | } |
| 436 | } |
| 437 | } |
| 438 | print_stats((STAT_STRENGTH | STAT_HP)); |
| 439 | } |
| 440 | |
| 441 | m_confuse(monster) |
| 442 | object *monster; |
| 443 | { |
| 444 | char msg[80]; |
| 445 | |
| 446 | if (!rogue_can_see(monster->row, monster->col)) { |
| 447 | return(0); |
| 448 | } |
| 449 | if (rand_percent(45)) { |
| 450 | monster->m_flags &= (~CONFUSES); /* will not confuse the rogue */ |
| 451 | return(0); |
| 452 | } |
| 453 | if (rand_percent(55)) { |
| 454 | monster->m_flags &= (~CONFUSES); |
| 455 | sprintf(msg, "the gaze of the %s has confused you", mon_name(monster)); |
| 456 | message(msg, 1); |
| 457 | cnfs(); |
| 458 | return(1); |
| 459 | } |
| 460 | return(0); |
| 461 | } |
| 462 | |
| 463 | flame_broil(monster) |
| 464 | object *monster; |
| 465 | { |
| 466 | short row, col, dir; |
| 467 | |
| 468 | if ((!mon_sees(monster, rogue.row, rogue.col)) || coin_toss()) { |
| 469 | return(0); |
| 470 | } |
| 471 | row = rogue.row - monster->row; |
| 472 | col = rogue.col - monster->col; |
| 473 | if (row < 0) { |
| 474 | row = -row; |
| 475 | } |
| 476 | if (col < 0) { |
| 477 | col = -col; |
| 478 | } |
| 479 | if (((row != 0) && (col != 0) && (row != col)) || |
| 480 | ((row > 7) || (col > 7))) { |
| 481 | return(0); |
| 482 | } |
| 483 | dir = get_dir(monster->row, monster->col, row, col); |
| 484 | bounce(FIRE, dir, monster->row, monster->col, 0); |
| 485 | |
| 486 | return(1); |
| 487 | } |
| 488 | |
| 489 | get_dir(srow, scol, drow, dcol) |
| 490 | short srow, scol, drow, dcol; |
| 491 | { |
| 492 | if (srow == drow) { |
| 493 | if (scol < dcol) { |
| 494 | return(RIGHT); |
| 495 | } else { |
| 496 | return(LEFT); |
| 497 | } |
| 498 | } |
| 499 | if (scol == dcol) { |
| 500 | if (srow < drow) { |
| 501 | return(DOWN); |
| 502 | } else { |
| 503 | return(UPWARD); |
| 504 | } |
| 505 | } |
| 506 | if ((srow > drow) && (scol > dcol)) { |
| 507 | return(UPLEFT); |
| 508 | } |
| 509 | if ((srow < drow) && (scol < dcol)) { |
| 510 | return(DOWNRIGHT); |
| 511 | } |
| 512 | if ((srow < drow) && (scol > dcol)) { |
| 513 | return(DOWNLEFT); |
| 514 | } |
| 515 | /*if ((srow > drow) && (scol < dcol)) {*/ |
| 516 | return(UPRIGHT); |
| 517 | /*}*/ |
| 518 | } |