xref: /netbsd/games/rogue/spec_hit.c (revision 6550d01e)
1 /*	$NetBSD: spec_hit.c,v 1.8 2008/01/14 03:50:02 dholland 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.8 2008/01/14 03:50:02 dholland 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 static void	disappear(object *);
59 static void	drain_life(void);
60 static void	drop_level(void);
61 static void	freeze(object *);
62 static int	get_dir(short, short, short, short);
63 static boolean	gold_at(short, short);
64 static void	steal_gold(object *);
65 static void	steal_item(object *);
66 static void	sting(object *);
67 static boolean	try_to_cough(short, short, object *);
68 
69 short less_hp = 0;
70 boolean being_held;
71 
72 void
73 special_hit(object *monster)
74 {
75 	if ((monster->m_flags & CONFUSED) && rand_percent(66)) {
76 		return;
77 	}
78 	if (monster->m_flags & RUSTS) {
79 		rust(monster);
80 	}
81 	if ((monster->m_flags & HOLDS) && !levitate) {
82 		being_held = 1;
83 	}
84 	if (monster->m_flags & FREEZES) {
85 		freeze(monster);
86 	}
87 	if (monster->m_flags & STINGS) {
88 		sting(monster);
89 	}
90 	if (monster->m_flags & DRAINS_LIFE) {
91 		drain_life();
92 	}
93 	if (monster->m_flags & DROPS_LEVEL) {
94 		drop_level();
95 	}
96 	if (monster->m_flags & STEALS_GOLD) {
97 		steal_gold(monster);
98 	} else if (monster->m_flags & STEALS_ITEM) {
99 		steal_item(monster);
100 	}
101 }
102 
103 void
104 rust(object *monster)
105 {
106 	if ((!rogue.armor) || (get_armor_class(rogue.armor) <= 1) ||
107 		(rogue.armor->which_kind == LEATHER)) {
108 		return;
109 	}
110 	if ((rogue.armor->is_protected) || maintain_armor) {
111 		if (monster && (!(monster->m_flags & RUST_VANISHED))) {
112 			messagef(0, "the rust vanishes instantly");
113 			monster->m_flags |= RUST_VANISHED;
114 		}
115 	} else {
116 		rogue.armor->d_enchant--;
117 		messagef(0, "your armor weakens");
118 		print_stats(STAT_ARMOR);
119 	}
120 }
121 
122 void
123 freeze(object *monster)
124 {
125 	short freeze_percent = 99;
126 	short i, n;
127 
128 	if (rand_percent(12)) {
129 		return;
130 	}
131 	freeze_percent -= (rogue.str_current+(rogue.str_current / 2));
132 	freeze_percent -= ((rogue.exp + ring_exp) * 4);
133 	freeze_percent -= (get_armor_class(rogue.armor) * 5);
134 	freeze_percent -= (rogue.hp_max / 3);
135 
136 	if (freeze_percent > 10) {
137 		monster->m_flags |= FREEZING_ROGUE;
138 		messagef(1, "you are frozen");
139 
140 		n = get_rand(4, 8);
141 		for (i = 0; i < n; i++) {
142 			mv_mons();
143 		}
144 		if (rand_percent(freeze_percent)) {
145 			for (i = 0; i < 50; i++) {
146 				mv_mons();
147 			}
148 			killed_by(NULL, HYPOTHERMIA);
149 		}
150 		messagef(1, you_can_move_again);
151 		monster->m_flags &= (~FREEZING_ROGUE);
152 	}
153 }
154 
155 void
156 steal_gold(object *monster)
157 {
158 	int amount;
159 
160 	if ((rogue.gold <= 0) || rand_percent(10)) {
161 		return;
162 	}
163 
164 	amount = get_rand((cur_level * 10), (cur_level * 30));
165 
166 	if (amount > rogue.gold) {
167 		amount = rogue.gold;
168 	}
169 	rogue.gold -= amount;
170 	messagef(0, "your purse feels lighter");
171 	print_stats(STAT_GOLD);
172 	disappear(monster);
173 }
174 
175 void
176 steal_item(object *monster)
177 {
178 	object *obj;
179 	short i, n, t = 0;
180 	char desc[80];
181 	boolean has_something = 0;
182 
183 	if (rand_percent(15)) {
184 		return;
185 	}
186 	obj = rogue.pack.next_object;
187 
188 	if (!obj) {
189 		goto DSPR;
190 	}
191 	while (obj) {
192 		if (!(obj->in_use_flags & BEING_USED)) {
193 			has_something = 1;
194 			break;
195 		}
196 		obj = obj->next_object;
197 	}
198 	if (!has_something) {
199 		goto DSPR;
200 	}
201 	n = get_rand(0, MAX_PACK_COUNT);
202 	obj = rogue.pack.next_object;
203 
204 	for (i = 0; i <= n; i++) {
205 		obj = obj->next_object;
206 		while ((!obj) || (obj->in_use_flags & BEING_USED)) {
207 			if (!obj) {
208 				obj = rogue.pack.next_object;
209 			} else {
210 				obj = obj->next_object;
211 			}
212 		}
213 	}
214 	if (obj->what_is != WEAPON) {
215 		t = obj->quantity;
216 		obj->quantity = 1;
217 	}
218 	get_desc(obj, desc, sizeof(desc));
219 	messagef(0, "she stole %s", desc);
220 
221 	obj->quantity = ((obj->what_is != WEAPON) ? t : 1);
222 
223 	vanish(obj, 0, &rogue.pack);
224 DSPR:
225 	disappear(monster);
226 }
227 
228 static void
229 disappear(object *monster)
230 {
231 	short row, col;
232 
233 	row = monster->row;
234 	col = monster->col;
235 
236 	dungeon[row][col] &= ~MONSTER;
237 	if (rogue_can_see(row, col)) {
238 		mvaddch(row, col, get_dungeon_char(row, col));
239 	}
240 	take_from_pack(monster, &level_monsters);
241 	free_object(monster);
242 	mon_disappeared = 1;
243 }
244 
245 void
246 cough_up(object *monster)
247 {
248 	object *obj;
249 	short row, col, i, n;
250 
251 	if (cur_level < max_level) {
252 		return;
253 	}
254 
255 	if (monster->m_flags & STEALS_GOLD) {
256 		obj = alloc_object();
257 		obj->what_is = GOLD;
258 		obj->quantity = get_rand((cur_level * 15), (cur_level * 30));
259 	} else {
260 		if (!rand_percent((int)monster->drop_percent)) {
261 			return;
262 		}
263 		obj = gr_object();
264 	}
265 	row = monster->row;
266 	col = monster->col;
267 
268 	for (n = 0; n <= 5; n++) {
269 		for (i = -n; i <= n; i++) {
270 			if (try_to_cough(row+n, col+i, obj)) {
271 				return;
272 			}
273 			if (try_to_cough(row-n, col+i, obj)) {
274 				return;
275 			}
276 		}
277 		for (i = -n; i <= n; i++) {
278 			if (try_to_cough(row+i, col-n, obj)) {
279 				return;
280 			}
281 			if (try_to_cough(row+i, col+n, obj)) {
282 				return;
283 			}
284 		}
285 	}
286 	free_object(obj);
287 }
288 
289 static boolean
290 try_to_cough(short row, short col, object *obj)
291 {
292 	if ((row < MIN_ROW) ||
293 	    (row > (DROWS-2)) || (col < 0) || (col>(DCOLS-1))) {
294 		return(0);
295 	}
296 	if ((!(dungeon[row][col] & (OBJECT | STAIRS | TRAP))) &&
297 		(dungeon[row][col] & (TUNNEL | FLOOR | DOOR))) {
298 		place_at(obj, row, col);
299 		if (((row != rogue.row) || (col != rogue.col)) &&
300 			(!(dungeon[row][col] & MONSTER))) {
301 			mvaddch(row, col, get_dungeon_char(row, col));
302 		}
303 		return(1);
304 	}
305 	return(0);
306 }
307 
308 boolean
309 seek_gold(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 static boolean
341 gold_at(short row, short col)
342 {
343 	if (dungeon[row][col] & OBJECT) {
344 		object *obj;
345 
346 		if ((obj = object_at(&level_objects, row, col)) &&
347 				(obj->what_is == GOLD)) {
348 			return(1);
349 		}
350 	}
351 	return(0);
352 }
353 
354 void
355 check_gold_seeker(object *monster)
356 {
357 	monster->m_flags &= (~SEEKS_GOLD);
358 }
359 
360 boolean
361 check_imitator(object *monster)
362 {
363 	if (monster->m_flags & IMITATES) {
364 		wake_up(monster);
365 		if (!blind) {
366 			mvaddch(monster->row, monster->col,
367 					get_dungeon_char(monster->row, monster->col));
368 			check_message();
369 			messagef(1, "wait, that's a %s!", mon_name(monster));
370 		}
371 		return(1);
372 	}
373 	return(0);
374 }
375 
376 boolean
377 imitating(short row, short col)
378 {
379 	if (dungeon[row][col] & MONSTER) {
380 		object *monster;
381 
382 		if ((monster = object_at(&level_monsters, row, col)) != NULL) {
383 			if (monster->m_flags & IMITATES) {
384 				return(1);
385 			}
386 		}
387 	}
388 	return(0);
389 }
390 
391 static void
392 sting(object *monster)
393 {
394 	short sting_chance = 35;
395 
396 	if ((rogue.str_current <= 3) || sustain_strength) {
397 		return;
398 	}
399 	sting_chance += (6 * (6 - get_armor_class(rogue.armor)));
400 
401 	if ((rogue.exp + ring_exp) > 8) {
402 		sting_chance -= (6 * ((rogue.exp + ring_exp) - 8));
403 	}
404 	if (rand_percent(sting_chance)) {
405 		messagef(0, "the %s's bite has weakened you",
406 			mon_name(monster));
407 		rogue.str_current--;
408 		print_stats(STAT_STRENGTH);
409 	}
410 }
411 
412 static void
413 drop_level(void)
414 {
415 	int hp;
416 
417 	if (rand_percent(80) || (rogue.exp <= 5)) {
418 		return;
419 	}
420 	rogue.exp_points = level_points[rogue.exp-2] - get_rand(9, 29);
421 	rogue.exp -= 2;
422 	hp = hp_raise();
423 	if ((rogue.hp_current -= hp) <= 0) {
424 		rogue.hp_current = 1;
425 	}
426 	if ((rogue.hp_max -= hp) <= 0) {
427 		rogue.hp_max = 1;
428 	}
429 	add_exp(1, 0);
430 }
431 
432 void
433 drain_life(void)
434 {
435 	short n;
436 
437 	if (rand_percent(60) || (rogue.hp_max <= 30) || (rogue.hp_current < 10)) {
438 		return;
439 	}
440 	n = get_rand(1, 3);		/* 1 Hp, 2 Str, 3 both */
441 
442 	if ((n != 2) || (!sustain_strength)) {
443 		messagef(0, "you feel weaker");
444 	}
445 	if (n != 2) {
446 		rogue.hp_max--;
447 		rogue.hp_current--;
448 		less_hp++;
449 	}
450 	if (n != 1) {
451 		if ((rogue.str_current > 3) && (!sustain_strength)) {
452 			rogue.str_current--;
453 			if (coin_toss()) {
454 				rogue.str_max--;
455 			}
456 		}
457 	}
458 	print_stats((STAT_STRENGTH | STAT_HP));
459 }
460 
461 boolean
462 m_confuse(object *monster)
463 {
464 	if (!rogue_can_see(monster->row, monster->col)) {
465 		return(0);
466 	}
467 	if (rand_percent(45)) {
468 		monster->m_flags &= (~CONFUSES);	/* will not confuse the rogue */
469 		return(0);
470 	}
471 	if (rand_percent(55)) {
472 		monster->m_flags &= (~CONFUSES);
473 		messagef(1, "the gaze of the %s has confused you",
474 			mon_name(monster));
475 		cnfs();
476 		return(1);
477 	}
478 	return(0);
479 }
480 
481 boolean
482 flame_broil(object *monster)
483 {
484 	short row, col, dir;
485 
486 	if ((!mon_sees(monster, rogue.row, rogue.col)) || coin_toss()) {
487 		return(0);
488 	}
489 	row = rogue.row - monster->row;
490 	col = rogue.col - monster->col;
491 	if (row < 0) {
492 		row = -row;
493 	}
494 	if (col < 0) {
495 		col = -col;
496 	}
497 	if (((row != 0) && (col != 0) && (row != col)) ||
498 		((row > 7) || (col > 7))) {
499 		return(0);
500 	}
501 	dir = get_dir(monster->row, monster->col, row, col);
502 	bounce(FIRE, dir, monster->row, monster->col, 0);
503 
504 	return(1);
505 }
506 
507 static int
508 get_dir(short srow, short scol, short drow, short dcol)
509 {
510 	if (srow == drow) {
511 		if (scol < dcol) {
512 			return(RIGHT);
513 		} else {
514 			return(LEFT);
515 		}
516 	}
517 	if (scol == dcol) {
518 		if (srow < drow) {
519 			return(DOWN);
520 		} else {
521 			return(UPWARD);
522 		}
523 	}
524 	if ((srow > drow) && (scol > dcol)) {
525 		return(UPLEFT);
526 	}
527 	if ((srow < drow) && (scol < dcol)) {
528 		return(DOWNRIGHT);
529 	}
530 	if ((srow < drow) && (scol > dcol)) {
531 		return(DOWNLEFT);
532 	}
533 	/*if ((srow > drow) && (scol < dcol)) {*/
534 		return(UPRIGHT);
535 	/*}*/
536 }
537