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