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