xref: /dragonfly/games/rogue/hit.c (revision 0ca59c34)
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 }
452