xref: /dragonfly/games/rogue/spec_hit.c (revision 3536f98c1c75c45bd29f481cf18b0f90cea395f7)
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
special_hit(object * monster)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
rust(object * monster)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
freeze(object * monster)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
steal_gold(object * monster)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
steal_item(object * monster)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
disappear(object * monster)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
cough_up(object * monster)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
try_to_cough(short row,short col,object * obj)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
seek_gold(object * monster)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
gold_at(short row,short col)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
check_gold_seeker(object * monster)348 check_gold_seeker(object *monster)
349 {
350           monster->m_flags &= (~SEEKS_GOLD);
351 }
352 
353 boolean
check_imitator(object * monster)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
imitating(short row,short col)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
sting(object * monster)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
drop_level(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
drain_life(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
m_confuse(object * monster)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
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 short
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