xref: /netbsd-src/games/rogue/spec_hit.c (revision aaf4ece63a859a04e37cf3a7229b5fab0157cc06)
1 /*	$NetBSD: spec_hit.c,v 1.5 2003/08/07 09:37:40 agc Exp $	*/
2 
3 /*
4  * Copyright (c) 1988, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Timothy C. Stoehr.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/cdefs.h>
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)spec_hit.c	8.1 (Berkeley) 5/31/93";
39 #else
40 __RCSID("$NetBSD: spec_hit.c,v 1.5 2003/08/07 09:37:40 agc Exp $");
41 #endif
42 #endif /* not lint */
43 
44 /*
45  * special_hit.c
46  *
47  * This source herein may be modified and/or distributed by anybody who
48  * so desires, with the following restrictions:
49  *    1.)  No portion of this notice shall be removed.
50  *    2.)  Credit shall not be taken for the creation of this source.
51  *    3.)  This code is not to be traded, sold, or used for personal
52  *         gain or profit.
53  *
54  */
55 
56 #include "rogue.h"
57 
58 short less_hp = 0;
59 boolean being_held;
60 
61 void
62 special_hit(monster)
63 	object *monster;
64 {
65 	if ((monster->m_flags & CONFUSED) && rand_percent(66)) {
66 		return;
67 	}
68 	if (monster->m_flags & RUSTS) {
69 		rust(monster);
70 	}
71 	if ((monster->m_flags & HOLDS) && !levitate) {
72 		being_held = 1;
73 	}
74 	if (monster->m_flags & FREEZES) {
75 		freeze(monster);
76 	}
77 	if (monster->m_flags & STINGS) {
78 		sting(monster);
79 	}
80 	if (monster->m_flags & DRAINS_LIFE) {
81 		drain_life();
82 	}
83 	if (monster->m_flags & DROPS_LEVEL) {
84 		drop_level();
85 	}
86 	if (monster->m_flags & STEALS_GOLD) {
87 		steal_gold(monster);
88 	} else if (monster->m_flags & STEALS_ITEM) {
89 		steal_item(monster);
90 	}
91 }
92 
93 void
94 rust(monster)
95 	object *monster;
96 {
97 	if ((!rogue.armor) || (get_armor_class(rogue.armor) <= 1) ||
98 		(rogue.armor->which_kind == LEATHER)) {
99 		return;
100 	}
101 	if ((rogue.armor->is_protected) || maintain_armor) {
102 		if (monster && (!(monster->m_flags & RUST_VANISHED))) {
103 			message("the rust vanishes instantly", 0);
104 			monster->m_flags |= RUST_VANISHED;
105 		}
106 	} else {
107 		rogue.armor->d_enchant--;
108 		message("your armor weakens", 0);
109 		print_stats(STAT_ARMOR);
110 	}
111 }
112 
113 void
114 freeze(monster)
115 	object *monster;
116 {
117 	short freeze_percent = 99;
118 	short i, n;
119 
120 	if (rand_percent(12)) {
121 		return;
122 	}
123 	freeze_percent -= (rogue.str_current+(rogue.str_current / 2));
124 	freeze_percent -= ((rogue.exp + ring_exp) * 4);
125 	freeze_percent -= (get_armor_class(rogue.armor) * 5);
126 	freeze_percent -= (rogue.hp_max / 3);
127 
128 	if (freeze_percent > 10) {
129 		monster->m_flags |= FREEZING_ROGUE;
130 		message("you are frozen", 1);
131 
132 		n = get_rand(4, 8);
133 		for (i = 0; i < n; i++) {
134 			mv_mons();
135 		}
136 		if (rand_percent(freeze_percent)) {
137 			for (i = 0; i < 50; i++) {
138 				mv_mons();
139 			}
140 			killed_by((object *)0, HYPOTHERMIA);
141 		}
142 		message(you_can_move_again, 1);
143 		monster->m_flags &= (~FREEZING_ROGUE);
144 	}
145 }
146 
147 void
148 steal_gold(monster)
149 	object *monster;
150 {
151 	int amount;
152 
153 	if ((rogue.gold <= 0) || rand_percent(10)) {
154 		return;
155 	}
156 
157 	amount = get_rand((cur_level * 10), (cur_level * 30));
158 
159 	if (amount > rogue.gold) {
160 		amount = rogue.gold;
161 	}
162 	rogue.gold -= amount;
163 	message("your purse feels lighter", 0);
164 	print_stats(STAT_GOLD);
165 	disappear(monster);
166 }
167 
168 void
169 steal_item(monster)
170 	object *monster;
171 {
172 	object *obj;
173 	short i, n, t = 0;
174 	char desc[80];
175 	boolean has_something = 0;
176 
177 	if (rand_percent(15)) {
178 		return;
179 	}
180 	obj = rogue.pack.next_object;
181 
182 	if (!obj) {
183 		goto DSPR;
184 	}
185 	while (obj) {
186 		if (!(obj->in_use_flags & BEING_USED)) {
187 			has_something = 1;
188 			break;
189 		}
190 		obj = obj->next_object;
191 	}
192 	if (!has_something) {
193 		goto DSPR;
194 	}
195 	n = get_rand(0, MAX_PACK_COUNT);
196 	obj = rogue.pack.next_object;
197 
198 	for (i = 0; i <= n; i++) {
199 		obj = obj->next_object;
200 		while ((!obj) || (obj->in_use_flags & BEING_USED)) {
201 			if (!obj) {
202 				obj = rogue.pack.next_object;
203 			} else {
204 				obj = obj->next_object;
205 			}
206 		}
207 	}
208 	(void) strcpy(desc, "she stole ");
209 	if (obj->what_is != WEAPON) {
210 		t = obj->quantity;
211 		obj->quantity = 1;
212 	}
213 	get_desc(obj, desc+10);
214 	message(desc, 0);
215 
216 	obj->quantity = ((obj->what_is != WEAPON) ? t : 1);
217 
218 	vanish(obj, 0, &rogue.pack);
219 DSPR:
220 	disappear(monster);
221 }
222 
223 void
224 disappear(monster)
225 	object *monster;
226 {
227 	short row, col;
228 
229 	row = monster->row;
230 	col = monster->col;
231 
232 	dungeon[row][col] &= ~MONSTER;
233 	if (rogue_can_see(row, col)) {
234 		mvaddch(row, col, get_dungeon_char(row, col));
235 	}
236 	take_from_pack(monster, &level_monsters);
237 	free_object(monster);
238 	mon_disappeared = 1;
239 }
240 
241 void
242 cough_up(monster)
243 	object *monster;
244 {
245 	object *obj;
246 	short row, col, i, n;
247 
248 	if (cur_level < max_level) {
249 		return;
250 	}
251 
252 	if (monster->m_flags & STEALS_GOLD) {
253 		obj = alloc_object();
254 		obj->what_is = GOLD;
255 		obj->quantity = get_rand((cur_level * 15), (cur_level * 30));
256 	} else {
257 		if (!rand_percent((int) monster->drop_percent)) {
258 			return;
259 		}
260 		obj = gr_object();
261 	}
262 	row = monster->row;
263 	col = monster->col;
264 
265 	for (n = 0; n <= 5; n++) {
266 		for (i = -n; i <= n; i++) {
267 			if (try_to_cough(row+n, col+i, obj)) {
268 				return;
269 			}
270 			if (try_to_cough(row-n, col+i, obj)) {
271 				return;
272 			}
273 		}
274 		for (i = -n; i <= n; i++) {
275 			if (try_to_cough(row+i, col-n, obj)) {
276 				return;
277 			}
278 			if (try_to_cough(row+i, col+n, obj)) {
279 				return;
280 			}
281 		}
282 	}
283 	free_object(obj);
284 }
285 
286 boolean
287 try_to_cough(row, col, obj)
288 	short row, col;
289 	object *obj;
290 {
291 	if ((row < MIN_ROW) ||
292 	    (row > (DROWS-2)) || (col < 0) || (col>(DCOLS-1))) {
293 		return(0);
294 	}
295 	if ((!(dungeon[row][col] & (OBJECT | STAIRS | TRAP))) &&
296 		(dungeon[row][col] & (TUNNEL | FLOOR | DOOR))) {
297 		place_at(obj, row, col);
298 		if (((row != rogue.row) || (col != rogue.col)) &&
299 			(!(dungeon[row][col] & MONSTER))) {
300 			mvaddch(row, col, get_dungeon_char(row, col));
301 		}
302 		return(1);
303 	}
304 	return(0);
305 }
306 
307 boolean
308 seek_gold(monster)
309 	object *monster;
310 {
311 	short i, j, rn, s;
312 
313 	if ((rn = get_room_number(monster->row, monster->col)) < 0) {
314 		return(0);
315 	}
316 	for (i = rooms[rn].top_row+1; i < rooms[rn].bottom_row; i++) {
317 		for (j = rooms[rn].left_col+1; j < rooms[rn].right_col; j++) {
318 			if ((gold_at(i, j)) && !(dungeon[i][j] & MONSTER)) {
319 				monster->m_flags |= CAN_FLIT;
320 				s = mon_can_go(monster, i, j);
321 				monster->m_flags &= (~CAN_FLIT);
322 				if (s) {
323 					move_mon_to(monster, i, j);
324 					monster->m_flags |= ASLEEP;
325 					monster->m_flags &= (~(WAKENS | SEEKS_GOLD));
326 					return(1);
327 				}
328 				monster->m_flags &= (~SEEKS_GOLD);
329 				monster->m_flags |= CAN_FLIT;
330 				mv_1_monster(monster, i, j);
331 				monster->m_flags &= (~CAN_FLIT);
332 				monster->m_flags |= SEEKS_GOLD;
333 				return(1);
334 			}
335 		}
336 	}
337 	return(0);
338 }
339 
340 boolean
341 gold_at(row, col)
342 	short row, col;
343 {
344 	if (dungeon[row][col] & OBJECT) {
345 		object *obj;
346 
347 		if ((obj = object_at(&level_objects, row, col)) &&
348 				(obj->what_is == GOLD)) {
349 			return(1);
350 		}
351 	}
352 	return(0);
353 }
354 
355 void
356 check_gold_seeker(monster)
357 	object *monster;
358 {
359 	monster->m_flags &= (~SEEKS_GOLD);
360 }
361 
362 boolean
363 check_imitator(monster)
364 	object *monster;
365 {
366 	char msg[80];
367 
368 	if (monster->m_flags & IMITATES) {
369 		wake_up(monster);
370 		if (!blind) {
371 			mvaddch(monster->row, monster->col,
372 					get_dungeon_char(monster->row, monster->col));
373 			check_message();
374 			sprintf(msg, "wait, that's a %s!", mon_name(monster));
375 			message(msg, 1);
376 		}
377 		return(1);
378 	}
379 	return(0);
380 }
381 
382 boolean
383 imitating(row, col)
384 	short row, col;
385 {
386 	if (dungeon[row][col] & MONSTER) {
387 		object *monster;
388 
389 		if ((monster = object_at(&level_monsters, row, col)) != NULL) {
390 			if (monster->m_flags & IMITATES) {
391 				return(1);
392 			}
393 		}
394 	}
395 	return(0);
396 }
397 
398 void
399 sting(monster)
400 	object *monster;
401 {
402 	short sting_chance = 35;
403 	char msg[80];
404 
405 	if ((rogue.str_current <= 3) || sustain_strength) {
406 		return;
407 	}
408 	sting_chance += (6 * (6 - get_armor_class(rogue.armor)));
409 
410 	if ((rogue.exp + ring_exp) > 8) {
411 		sting_chance -= (6 * ((rogue.exp + ring_exp) - 8));
412 	}
413 	if (rand_percent(sting_chance)) {
414 		sprintf(msg, "the %s's bite has weakened you",
415 		mon_name(monster));
416 		message(msg, 0);
417 		rogue.str_current--;
418 		print_stats(STAT_STRENGTH);
419 	}
420 }
421 
422 void
423 drop_level()
424 {
425 	int hp;
426 
427 	if (rand_percent(80) || (rogue.exp <= 5)) {
428 		return;
429 	}
430 	rogue.exp_points = level_points[rogue.exp-2] - get_rand(9, 29);
431 	rogue.exp -= 2;
432 	hp = hp_raise();
433 	if ((rogue.hp_current -= hp) <= 0) {
434 		rogue.hp_current = 1;
435 	}
436 	if ((rogue.hp_max -= hp) <= 0) {
437 		rogue.hp_max = 1;
438 	}
439 	add_exp(1, 0);
440 }
441 
442 void
443 drain_life()
444 {
445 	short n;
446 
447 	if (rand_percent(60) || (rogue.hp_max <= 30) || (rogue.hp_current < 10)) {
448 		return;
449 	}
450 	n = get_rand(1, 3);		/* 1 Hp, 2 Str, 3 both */
451 
452 	if ((n != 2) || (!sustain_strength)) {
453 		message("you feel weaker", 0);
454 	}
455 	if (n != 2) {
456 		rogue.hp_max--;
457 		rogue.hp_current--;
458 		less_hp++;
459 	}
460 	if (n != 1) {
461 		if ((rogue.str_current > 3) && (!sustain_strength)) {
462 			rogue.str_current--;
463 			if (coin_toss()) {
464 				rogue.str_max--;
465 			}
466 		}
467 	}
468 	print_stats((STAT_STRENGTH | STAT_HP));
469 }
470 
471 boolean
472 m_confuse(monster)
473 	object *monster;
474 {
475 	char msg[80];
476 
477 	if (!rogue_can_see(monster->row, monster->col)) {
478 		return(0);
479 	}
480 	if (rand_percent(45)) {
481 		monster->m_flags &= (~CONFUSES);	/* will not confuse the rogue */
482 		return(0);
483 	}
484 	if (rand_percent(55)) {
485 		monster->m_flags &= (~CONFUSES);
486 		sprintf(msg, "the gaze of the %s has confused you", mon_name(monster));
487 		message(msg, 1);
488 		cnfs();
489 		return(1);
490 	}
491 	return(0);
492 }
493 
494 boolean
495 flame_broil(monster)
496 	object *monster;
497 {
498 	short row, col, dir;
499 
500 	if ((!mon_sees(monster, rogue.row, rogue.col)) || coin_toss()) {
501 		return(0);
502 	}
503 	row = rogue.row - monster->row;
504 	col = rogue.col - monster->col;
505 	if (row < 0) {
506 		row = -row;
507 	}
508 	if (col < 0) {
509 		col = -col;
510 	}
511 	if (((row != 0) && (col != 0) && (row != col)) ||
512 		((row > 7) || (col > 7))) {
513 		return(0);
514 	}
515 	dir = get_dir(monster->row, monster->col, row, col);
516 	bounce(FIRE, dir, monster->row, monster->col, 0);
517 
518 	return(1);
519 }
520 
521 int
522 get_dir(srow, scol, drow, dcol)
523 	short srow, scol, drow, dcol;
524 {
525 	if (srow == drow) {
526 		if (scol < dcol) {
527 			return(RIGHT);
528 		} else {
529 			return(LEFT);
530 		}
531 	}
532 	if (scol == dcol) {
533 		if (srow < drow) {
534 			return(DOWN);
535 		} else {
536 			return(UPWARD);
537 		}
538 	}
539 	if ((srow > drow) && (scol > dcol)) {
540 		return(UPLEFT);
541 	}
542 	if ((srow < drow) && (scol < dcol)) {
543 		return(DOWNRIGHT);
544 	}
545 	if ((srow < drow) && (scol > dcol)) {
546 		return(DOWNLEFT);
547 	}
548 	/*if ((srow > drow) && (scol < dcol)) {*/
549 		return(UPRIGHT);
550 	/*}*/
551 }
552