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
38static 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
58static void	disappear(object *);
59static void	drain_life(void);
60static void	drop_level(void);
61static void	freeze(object *);
62static int	get_dir(short, short, short, short);
63static boolean	gold_at(short, short);
64static void	steal_gold(object *);
65static void	steal_item(object *);
66static void	sting(object *);
67static boolean	try_to_cough(short, short, object *);
68
69short less_hp = 0;
70boolean being_held;
71
72void
73special_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
103void
104rust(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
122void
123freeze(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, "%s", you_can_move_again);
151		monster->m_flags &= (~FREEZING_ROGUE);
152	}
153}
154
155void
156steal_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
175void
176steal_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);
224DSPR:
225	disappear(monster);
226}
227
228static void
229disappear(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
245void
246cough_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
289static boolean
290try_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
308boolean
309seek_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
340static boolean
341gold_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
354void
355check_gold_seeker(object *monster)
356{
357	monster->m_flags &= (~SEEKS_GOLD);
358}
359
360boolean
361check_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
376boolean
377imitating(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
391static void
392sting(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
412static void
413drop_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
432void
433drain_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
461boolean
462m_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
481boolean
482flame_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
507static int
508get_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