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