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