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