xref: /dragonfly/games/rogue/hit.c (revision 37de577a)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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