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