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