1 /* $NetBSD: spec_hit.c,v 1.9 2011/05/23 23:01:17 joerg Exp $ */
2
3 /*
4 * Copyright (c) 1988, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Timothy C. Stoehr.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include <sys/cdefs.h>
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)spec_hit.c 8.1 (Berkeley) 5/31/93";
39 #else
40 __RCSID("$NetBSD: spec_hit.c,v 1.9 2011/05/23 23:01:17 joerg Exp $");
41 #endif
42 #endif /* not lint */
43
44 /*
45 * special_hit.c
46 *
47 * This source herein may be modified and/or distributed by anybody who
48 * so desires, with the following restrictions:
49 * 1.) No portion of this notice shall be removed.
50 * 2.) Credit shall not be taken for the creation of this source.
51 * 3.) This code is not to be traded, sold, or used for personal
52 * gain or profit.
53 *
54 */
55
56 #include "rogue.h"
57
58 static void disappear(object *);
59 static void drain_life(void);
60 static void drop_level(void);
61 static void freeze(object *);
62 static int get_dir(short, short, short, short);
63 static boolean gold_at(short, short);
64 static void steal_gold(object *);
65 static void steal_item(object *);
66 static void sting(object *);
67 static boolean try_to_cough(short, short, object *);
68
69 short less_hp = 0;
70 boolean being_held;
71
72 void
special_hit(object * monster)73 special_hit(object *monster)
74 {
75 if ((monster->m_flags & CONFUSED) && rand_percent(66)) {
76 return;
77 }
78 if (monster->m_flags & RUSTS) {
79 rust(monster);
80 }
81 if ((monster->m_flags & HOLDS) && !levitate) {
82 being_held = 1;
83 }
84 if (monster->m_flags & FREEZES) {
85 freeze(monster);
86 }
87 if (monster->m_flags & STINGS) {
88 sting(monster);
89 }
90 if (monster->m_flags & DRAINS_LIFE) {
91 drain_life();
92 }
93 if (monster->m_flags & DROPS_LEVEL) {
94 drop_level();
95 }
96 if (monster->m_flags & STEALS_GOLD) {
97 steal_gold(monster);
98 } else if (monster->m_flags & STEALS_ITEM) {
99 steal_item(monster);
100 }
101 }
102
103 void
rust(object * monster)104 rust(object *monster)
105 {
106 if ((!rogue.armor) || (get_armor_class(rogue.armor) <= 1) ||
107 (rogue.armor->which_kind == LEATHER)) {
108 return;
109 }
110 if ((rogue.armor->is_protected) || maintain_armor) {
111 if (monster && (!(monster->m_flags & RUST_VANISHED))) {
112 messagef(0, "the rust vanishes instantly");
113 monster->m_flags |= RUST_VANISHED;
114 }
115 } else {
116 rogue.armor->d_enchant--;
117 messagef(0, "your armor weakens");
118 print_stats(STAT_ARMOR);
119 }
120 }
121
122 void
freeze(object * monster)123 freeze(object *monster)
124 {
125 short freeze_percent = 99;
126 short i, n;
127
128 if (rand_percent(12)) {
129 return;
130 }
131 freeze_percent -= (rogue.str_current+(rogue.str_current / 2));
132 freeze_percent -= ((rogue.exp + ring_exp) * 4);
133 freeze_percent -= (get_armor_class(rogue.armor) * 5);
134 freeze_percent -= (rogue.hp_max / 3);
135
136 if (freeze_percent > 10) {
137 monster->m_flags |= FREEZING_ROGUE;
138 messagef(1, "you are frozen");
139
140 n = get_rand(4, 8);
141 for (i = 0; i < n; i++) {
142 mv_mons();
143 }
144 if (rand_percent(freeze_percent)) {
145 for (i = 0; i < 50; i++) {
146 mv_mons();
147 }
148 killed_by(NULL, HYPOTHERMIA);
149 }
150 messagef(1, "%s", you_can_move_again);
151 monster->m_flags &= (~FREEZING_ROGUE);
152 }
153 }
154
155 void
steal_gold(object * monster)156 steal_gold(object *monster)
157 {
158 int amount;
159
160 if ((rogue.gold <= 0) || rand_percent(10)) {
161 return;
162 }
163
164 amount = get_rand((cur_level * 10), (cur_level * 30));
165
166 if (amount > rogue.gold) {
167 amount = rogue.gold;
168 }
169 rogue.gold -= amount;
170 messagef(0, "your purse feels lighter");
171 print_stats(STAT_GOLD);
172 disappear(monster);
173 }
174
175 void
steal_item(object * monster)176 steal_item(object *monster)
177 {
178 object *obj;
179 short i, n, t = 0;
180 char desc[80];
181 boolean has_something = 0;
182
183 if (rand_percent(15)) {
184 return;
185 }
186 obj = rogue.pack.next_object;
187
188 if (!obj) {
189 goto DSPR;
190 }
191 while (obj) {
192 if (!(obj->in_use_flags & BEING_USED)) {
193 has_something = 1;
194 break;
195 }
196 obj = obj->next_object;
197 }
198 if (!has_something) {
199 goto DSPR;
200 }
201 n = get_rand(0, MAX_PACK_COUNT);
202 obj = rogue.pack.next_object;
203
204 for (i = 0; i <= n; i++) {
205 obj = obj->next_object;
206 while ((!obj) || (obj->in_use_flags & BEING_USED)) {
207 if (!obj) {
208 obj = rogue.pack.next_object;
209 } else {
210 obj = obj->next_object;
211 }
212 }
213 }
214 if (obj->what_is != WEAPON) {
215 t = obj->quantity;
216 obj->quantity = 1;
217 }
218 get_desc(obj, desc, sizeof(desc));
219 messagef(0, "she stole %s", desc);
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
disappear(object * monster)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
cough_up(object * monster)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
try_to_cough(short row,short col,object * obj)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
seek_gold(object * monster)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
gold_at(short row,short col)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
check_gold_seeker(object * monster)355 check_gold_seeker(object *monster)
356 {
357 monster->m_flags &= (~SEEKS_GOLD);
358 }
359
360 boolean
check_imitator(object * monster)361 check_imitator(object *monster)
362 {
363 if (monster->m_flags & IMITATES) {
364 wake_up(monster);
365 if (!blind) {
366 mvaddch(monster->row, monster->col,
367 get_dungeon_char(monster->row, monster->col));
368 check_message();
369 messagef(1, "wait, that's a %s!", mon_name(monster));
370 }
371 return(1);
372 }
373 return(0);
374 }
375
376 boolean
imitating(short row,short col)377 imitating(short row, short col)
378 {
379 if (dungeon[row][col] & MONSTER) {
380 object *monster;
381
382 if ((monster = object_at(&level_monsters, row, col)) != NULL) {
383 if (monster->m_flags & IMITATES) {
384 return(1);
385 }
386 }
387 }
388 return(0);
389 }
390
391 static void
sting(object * monster)392 sting(object *monster)
393 {
394 short sting_chance = 35;
395
396 if ((rogue.str_current <= 3) || sustain_strength) {
397 return;
398 }
399 sting_chance += (6 * (6 - get_armor_class(rogue.armor)));
400
401 if ((rogue.exp + ring_exp) > 8) {
402 sting_chance -= (6 * ((rogue.exp + ring_exp) - 8));
403 }
404 if (rand_percent(sting_chance)) {
405 messagef(0, "the %s's bite has weakened you",
406 mon_name(monster));
407 rogue.str_current--;
408 print_stats(STAT_STRENGTH);
409 }
410 }
411
412 static void
drop_level(void)413 drop_level(void)
414 {
415 int hp;
416
417 if (rand_percent(80) || (rogue.exp <= 5)) {
418 return;
419 }
420 rogue.exp_points = level_points[rogue.exp-2] - get_rand(9, 29);
421 rogue.exp -= 2;
422 hp = hp_raise();
423 if ((rogue.hp_current -= hp) <= 0) {
424 rogue.hp_current = 1;
425 }
426 if ((rogue.hp_max -= hp) <= 0) {
427 rogue.hp_max = 1;
428 }
429 add_exp(1, 0);
430 }
431
432 void
drain_life(void)433 drain_life(void)
434 {
435 short n;
436
437 if (rand_percent(60) || (rogue.hp_max <= 30) || (rogue.hp_current < 10)) {
438 return;
439 }
440 n = get_rand(1, 3); /* 1 Hp, 2 Str, 3 both */
441
442 if ((n != 2) || (!sustain_strength)) {
443 messagef(0, "you feel weaker");
444 }
445 if (n != 2) {
446 rogue.hp_max--;
447 rogue.hp_current--;
448 less_hp++;
449 }
450 if (n != 1) {
451 if ((rogue.str_current > 3) && (!sustain_strength)) {
452 rogue.str_current--;
453 if (coin_toss()) {
454 rogue.str_max--;
455 }
456 }
457 }
458 print_stats((STAT_STRENGTH | STAT_HP));
459 }
460
461 boolean
m_confuse(object * monster)462 m_confuse(object *monster)
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 messagef(1, "the gaze of the %s has confused you",
474 mon_name(monster));
475 cnfs();
476 return(1);
477 }
478 return(0);
479 }
480
481 boolean
flame_broil(object * monster)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 int
get_dir(short srow,short scol,short drow,short dcol)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