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