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