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