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