xref: /dragonfly/games/rogue/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  * @(#)hit.c        8.1 (Berkeley) 5/31/93
33  * $FreeBSD: src/games/rogue/hit.c,v 1.6 1999/11/30 03:49:22 billf Exp $
34  */
35 
36 /*
37  * 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 short damage_for_strength(void);
51 static int get_w_damage(const object *);
52 static int to_hit(const object *);
53 
54 static object *fight_monster = NULL;
55 char hit_message[HIT_MESSAGE_SIZE] = "";
56 
57 void
mon_hit(object * monster)58 mon_hit(object *monster)
59 {
60           short damage, hit_chance;
61           const char *mn;
62           float minus;
63 
64           if (fight_monster && (monster != fight_monster)) {
65                     fight_monster = NULL;
66           }
67           monster->trow = NO_ROOM;
68           if (cur_level >= (AMULET_LEVEL * 2)) {
69                     hit_chance = 100;
70           } else {
71                     hit_chance = monster->m_hit_chance;
72                     hit_chance -= (((2 * rogue.exp) + (2 * ring_exp)) - r_rings);
73           }
74           if (wizard) {
75                     hit_chance /= 2;
76           }
77           if (!fight_monster) {
78                     interrupted = 1;
79           }
80           mn = mon_name(monster);
81 
82           if (!rand_percent(hit_chance)) {
83                     if (!fight_monster) {
84                               sprintf(hit_message + strlen(hit_message), "the %s misses", mn);
85                               message(hit_message, 1);
86                               hit_message[0] = 0;
87                     }
88                     return;
89           }
90           if (!fight_monster) {
91                     sprintf(hit_message + strlen(hit_message), "the %s hit", mn);
92                     message(hit_message, 1);
93                     hit_message[0] = 0;
94           }
95           if (!(monster->m_flags & STATIONARY)) {
96                     damage = get_damage(monster->m_damage, 1);
97                     if (cur_level >= (AMULET_LEVEL * 2)) {
98                               minus = (float)((AMULET_LEVEL * 2) - cur_level);
99                     } else {
100                               minus = (float)get_armor_class(rogue.armor) * 3.00;
101                               minus = minus / 100.00 * (float)damage;
102                     }
103                     damage -= (short)minus;
104           } else {
105                     damage = monster->stationary_damage++;
106           }
107           if (wizard) {
108                     damage /= 3;
109           }
110           if (damage > 0) {
111                     rogue_damage(damage, monster, 0);
112           }
113           if (monster->m_flags & SPECIAL_HIT) {
114                     special_hit(monster);
115           }
116 }
117 
118 void
rogue_hit(object * monster,boolean force_hit)119 rogue_hit(object *monster, boolean force_hit)
120 {
121           short damage, hit_chance;
122 
123           if (monster) {
124                     if (check_imitator(monster)) {
125                               return;
126                     }
127                     hit_chance = force_hit ? 100 : get_hit_chance(rogue.weapon);
128 
129                     if (wizard) {
130                               hit_chance *= 2;
131                     }
132                     if (!rand_percent(hit_chance)) {
133                               if (!fight_monster) {
134                                         strcpy(hit_message, "you miss  ");
135                               }
136                               goto RET;
137                     }
138                     damage = get_weapon_damage(rogue.weapon);
139                     if (wizard) {
140                               damage *= 3;
141                     }
142                     if (con_mon) {
143                               s_con_mon(monster);
144                     }
145                     if (mon_damage(monster, damage)) {      /* still alive? */
146                               if (!fight_monster) {
147                                         strcpy(hit_message, "you hit  ");
148                               }
149                     }
150 RET:      check_gold_seeker(monster);
151                     wake_up(monster);
152           }
153 }
154 
155 void
rogue_damage(short d,object * monster,short other)156 rogue_damage(short d, object *monster, short other)
157 {
158           if (d >= rogue.hp_current) {
159                     rogue.hp_current = 0;
160                     print_stats(STAT_HP);
161                     killed_by(monster, other);
162           }
163           if (d > 0) {
164                     rogue.hp_current -= d;
165                     print_stats(STAT_HP);
166           }
167 }
168 
169 int
get_damage(const char * ds,boolean r)170 get_damage(const char *ds, boolean r)
171 {
172           int i = 0, j, n, d, total = 0;
173 
174           while (ds[i]) {
175                     n = get_number(ds+i);
176                     while (ds[i++] != 'd')
177                               ;
178                     d = get_number(ds+i);
179                     while ((ds[i] != '/') && ds[i])
180                               i++;
181 
182                     for (j = 0; j < n; j++) {
183                               if (r) {
184                                         total += get_rand(1, d);
185                               } else {
186                                         total += d;
187                               }
188                     }
189                     if (ds[i] == '/') {
190                               i++;
191                     }
192           }
193           return(total);
194 }
195 
196 static int
get_w_damage(const object * obj)197 get_w_damage(const object *obj)
198 {
199           char new_damage[12];
200           int t_hit, damage;
201           int i = 0;
202 
203           if ((!obj) || (obj->what_is != WEAPON)) {
204                     return(-1);
205           }
206           t_hit = get_number(obj->damage) + obj->hit_enchant;
207           while (obj->damage[i++] != 'd')
208                     ;
209           damage = get_number(obj->damage + i) + obj->d_enchant;
210 
211           sprintf(new_damage, "%dd%d", t_hit, damage);
212 
213           return(get_damage(new_damage, 1));
214 }
215 
216 int
get_number(const char * s)217 get_number(const char *s)
218 {
219           int i = 0;
220           int total = 0;
221 
222           while ((s[i] >= '0') && (s[i] <= '9')) {
223                     total = (10 * total) + (s[i] - '0');
224                     i++;
225           }
226           return(total);
227 }
228 
229 long
lget_number(const char * s)230 lget_number(const char *s)
231 {
232           short i = 0;
233           long total = 0;
234 
235           while ((s[i] >= '0') && (s[i] <= '9')) {
236                     total = (10 * total) + (s[i] - '0');
237                     i++;
238           }
239           return(total);
240 }
241 
242 static int
to_hit(const object * obj)243 to_hit(const object *obj)
244 {
245           if (!obj) {
246                     return(1);
247           }
248           return(get_number(obj->damage) + obj->hit_enchant);
249 }
250 
251 static short
damage_for_strength(void)252 damage_for_strength(void)
253 {
254           short strength;
255 
256           strength = rogue.str_current + add_strength;
257 
258           if (strength <= 6) {
259                     return(strength-5);
260           }
261           if (strength <= 14) {
262                     return(1);
263           }
264           if (strength <= 17) {
265                     return(3);
266           }
267           if (strength <= 18) {
268                     return(4);
269           }
270           if (strength <= 20) {
271                     return(5);
272           }
273           if (strength <= 21) {
274                     return(6);
275           }
276           if (strength <= 30) {
277                     return(7);
278           }
279           return(8);
280 }
281 
282 boolean
mon_damage(object * monster,short damage)283 mon_damage(object *monster, short damage)
284 {
285           const char *mn;
286           short row, col;
287 
288           monster->hp_to_kill -= damage;
289 
290           if (monster->hp_to_kill <= 0) {
291                     row = monster->row;
292                     col = monster->col;
293                     dungeon[row][col] &= ~MONSTER;
294                     mvaddch(row, col, (int)get_dungeon_char(row, col));
295 
296                     fight_monster = NULL;
297                     cough_up(monster);
298                     mn = mon_name(monster);
299                     sprintf(hit_message+strlen(hit_message), "defeated the %s", mn);
300                     message(hit_message, 1);
301                     hit_message[0] = 0;
302                     add_exp(monster->kill_exp, 1);
303                     take_from_pack(monster, &level_monsters);
304 
305                     if (monster->m_flags & HOLDS) {
306                               being_held = 0;
307                     }
308                     free_object(monster);
309                     return(0);
310           }
311           return(1);
312 }
313 
314 void
fight(boolean to_the_death)315 fight(boolean to_the_death)
316 {
317           short ch, c, d;
318           short row, col;
319           boolean first_miss = 1;
320           short possible_damage;
321           object *monster;
322 
323           while (!is_direction(ch = rgetchar(), &d)) {
324                     sound_bell();
325                     if (first_miss) {
326                               message("direction?", 0);
327                               first_miss = 0;
328                     }
329           }
330           check_message();
331           if (ch == CANCEL) {
332                     return;
333           }
334           row = rogue.row; col = rogue.col;
335           get_dir_rc(d, &row, &col, 0);
336 
337           c = mvinch(row, col);
338           if (((c < 'A') || (c > 'Z')) ||
339                     (!can_move(rogue.row, rogue.col, row, col))) {
340                     message("I see no monster there", 0);
341                     return;
342           }
343           if (!(fight_monster = object_at(&level_monsters, row, col))) {
344                     return;
345           }
346           if (!(fight_monster->m_flags & STATIONARY)) {
347                     possible_damage = ((get_damage(fight_monster->m_damage, 0) * 2) / 3);
348           } else {
349                     possible_damage = fight_monster->stationary_damage - 1;
350           }
351           while (fight_monster) {
352                     one_move_rogue(ch, 0);
353                     if (((!to_the_death) && (rogue.hp_current <= possible_damage)) ||
354                               interrupted || (!(dungeon[row][col] & MONSTER))) {
355                               fight_monster = NULL;
356                     } else {
357                               monster = object_at(&level_monsters, row, col);
358                               if (monster != fight_monster) {
359                                         fight_monster = NULL;
360                               }
361                     }
362           }
363 }
364 
365 void
get_dir_rc(short dir,short * row,short * col,short allow_off_screen)366 get_dir_rc(short dir, short *row, short *col, short allow_off_screen)
367 {
368           switch(dir) {
369           case LEFT:
370                     if (allow_off_screen || (*col > 0)) {
371                               (*col)--;
372                     }
373                     break;
374           case DOWN:
375                     if (allow_off_screen || (*row < (DROWS-2))) {
376                               (*row)++;
377                     }
378                     break;
379           case UPWARD:
380                     if (allow_off_screen || (*row > MIN_ROW)) {
381                               (*row)--;
382                     }
383                     break;
384           case RIGHT:
385                     if (allow_off_screen || (*col < (DCOLS-1))) {
386                               (*col)++;
387                     }
388                     break;
389           case UPLEFT:
390                     if (allow_off_screen || ((*row > MIN_ROW) && (*col > 0))) {
391                               (*row)--;
392                               (*col)--;
393                     }
394                     break;
395           case UPRIGHT:
396                     if (allow_off_screen || ((*row > MIN_ROW) && (*col < (DCOLS-1)))) {
397                               (*row)--;
398                               (*col)++;
399                     }
400                     break;
401           case DOWNRIGHT:
402                     if (allow_off_screen || ((*row < (DROWS-2)) && (*col < (DCOLS-1)))) {
403                               (*row)++;
404                               (*col)++;
405                     }
406                     break;
407           case DOWNLEFT:
408                     if (allow_off_screen || ((*row < (DROWS-2)) && (*col > 0))) {
409                               (*row)++;
410                               (*col)--;
411                     }
412                     break;
413           }
414 }
415 
416 short
get_hit_chance(const object * weapon)417 get_hit_chance(const object *weapon)
418 {
419           short hit_chance;
420 
421           hit_chance = 40;
422           hit_chance += 3 * to_hit(weapon);
423           hit_chance += (((2 * rogue.exp) + (2 * ring_exp)) - r_rings);
424           return(hit_chance);
425 }
426 
427 short
get_weapon_damage(const object * weapon)428 get_weapon_damage(const object *weapon)
429 {
430           short damage;
431 
432           damage = get_w_damage(weapon);
433           damage += damage_for_strength();
434           damage += ((((rogue.exp + ring_exp) - r_rings) + 1) / 2);
435           return(damage);
436 }
437 
438 void
s_con_mon(object * monster)439 s_con_mon(object *monster)
440 {
441           if (con_mon) {
442                     monster->m_flags |= CONFUSED;
443                     monster->moves_confused += get_rand(12, 22);
444                     message("the monster appears confused", 0);
445                     con_mon = 0;
446           }
447 }
448