xref: /dragonfly/games/rogue/zap.c (revision 3536f98c)
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  * 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. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * @(#)zap.c	8.1 (Berkeley) 5/31/93
33  * $FreeBSD: src/games/rogue/zap.c,v 1.3 1999/11/30 03:49:29 billf Exp $
34  */
35 
36 /*
37  * zap.c
38  *
39  * This source herein may be modified and/or distributed by anybody who
40  * so desires, with the following restrictions:
41  *    1.)  No portion of this notice shall be removed.
42  *    2.)  Credit shall not be taken for the creation of this source.
43  *    3.)  This code is not to be traded, sold, or used for personal
44  *         gain or profit.
45  *
46  */
47 
48 #include "rogue.h"
49 
50 static object *get_zapped_monster(short, short *, short *);
51 static void tele_away(object *);
52 static void wdrain_life(object *);
53 static void zap_monster(object *, unsigned short);
54 
55 boolean wizard = 0;
56 
57 void
zapp(void)58 zapp(void)
59 {
60 	short wch;
61 	boolean first_miss = 1;
62 	object *wand;
63 	short dir, d, row, col;
64 	object *monster;
65 
66 	while (!is_direction(dir = rgetchar(), &d)) {
67 		sound_bell();
68 		if (first_miss) {
69 			message("direction? ", 0);
70 			first_miss = 0;
71 		}
72 	}
73 	check_message();
74 	if (dir == CANCEL) {
75 		return;
76 	}
77 	if ((wch = pack_letter("zap with what?", WAND)) == CANCEL) {
78 		return;
79 	}
80 	check_message();
81 
82 	if (!(wand = get_letter_object(wch))) {
83 		message("no such item.", 0);
84 		return;
85 	}
86 	if (wand->what_is != WAND) {
87 		message("you can't zap with that", 0);
88 		return;
89 	}
90 	if (wand->class <= 0) {
91 		message("nothing happens", 0);
92 	} else {
93 		wand->class--;
94 		row = rogue.row; col = rogue.col;
95 		if ((wand->which_kind == COLD) || (wand->which_kind == FIRE)) {
96 			bounce((short)wand->which_kind, d, row, col, 0);
97 		} else {
98 			monster = get_zapped_monster(d, &row, &col);
99 			if (wand->which_kind == DRAIN_LIFE) {
100 				wdrain_life(monster);
101 			} else if (monster) {
102 				wake_up(monster);
103 				s_con_mon(monster);
104 				zap_monster(monster, wand->which_kind);
105 				relight();
106 			}
107 		}
108 	}
109 	reg_move();
110 }
111 
112 static object *
get_zapped_monster(short dir,short * row,short * col)113 get_zapped_monster(short dir, short *row, short *col)
114 {
115 	short orow, ocol;
116 
117 	for (;;) {
118 		orow = *row; ocol = *col;
119 		get_dir_rc(dir, row, col, 0);
120 		if (((*row == orow) && (*col == ocol)) ||
121 		   (dungeon[*row][*col] & (HORWALL | VERTWALL)) ||
122 		   (dungeon[*row][*col] == NOTHING)) {
123 			return(0);
124 		}
125 		if (dungeon[*row][*col] & MONSTER) {
126 			if (!imitating(*row, *col)) {
127 				return(object_at(&level_monsters, *row, *col));
128 			}
129 		}
130 	}
131 }
132 
133 static void
zap_monster(object * monster,unsigned short kind)134 zap_monster(object *monster, unsigned short kind)
135 {
136 	short row, col;
137 	object *nm;
138 	short tc;
139 
140 	row = monster->row;
141 	col = monster->col;
142 
143 	switch(kind) {
144 	case SLOW_MONSTER:
145 		if (monster->m_flags & HASTED) {
146 			monster->m_flags &= (~HASTED);
147 		} else {
148 			monster->slowed_toggle = 0;
149 			monster->m_flags |= SLOWED;
150 		}
151 		break;
152 	case HASTE_MONSTER:
153 		if (monster->m_flags & SLOWED) {
154 			monster->m_flags &= (~SLOWED);
155 		} else {
156 			monster->m_flags |= HASTED;
157 		}
158 		break;
159 	case TELE_AWAY:
160 		tele_away(monster);
161 		break;
162 	case INVISIBILITY:
163 		monster->m_flags |= INVISIBLE;
164 		break;
165 	case POLYMORPH:
166 		if (monster->m_flags & HOLDS) {
167 			being_held = 0;
168 		}
169 		nm = monster->next_monster;
170 		tc = monster->trail_char;
171 		gr_monster(monster, get_rand(0, MONSTERS-1));
172 		monster->row = row;
173 		monster->col = col;
174 		monster->next_monster = nm;
175 		monster->trail_char = tc;
176 		if (!(monster->m_flags & IMITATES)) {
177 			wake_up(monster);
178 		}
179 		break;
180 	case MAGIC_MISSILE:
181 		rogue_hit(monster, 1);
182 		break;
183 	case CANCELLATION:
184 		if (monster->m_flags & HOLDS) {
185 			being_held = 0;
186 		}
187 		if (monster->m_flags & STEALS_ITEM) {
188 			monster->drop_percent = 0;
189 		}
190 		monster->m_flags &= (~(FLIES | FLITS | SPECIAL_HIT | INVISIBLE |
191 			FLAMES | IMITATES | CONFUSES | SEEKS_GOLD | HOLDS));
192 		break;
193 	case DO_NOTHING:
194 		message("nothing happens", 0);
195 		break;
196 	}
197 }
198 
199 static void
tele_away(object * monster)200 tele_away(object *monster)
201 {
202 	short row, col;
203 
204 	if (monster->m_flags & HOLDS) {
205 		being_held = 0;
206 	}
207 	gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT));
208 	mvaddch(monster->row, monster->col, monster->trail_char);
209 	dungeon[monster->row][monster->col] &= ~MONSTER;
210 	monster->row = row; monster->col = col;
211 	dungeon[row][col] |= MONSTER;
212 	monster->trail_char = mvinch(row, col);
213 	if (detect_monster || rogue_can_see(row, col)) {
214 		mvaddch(row, col, gmc(monster));
215 	}
216 }
217 
218 void
wizardize(void)219 wizardize(void)
220 {
221 	char buf[100];
222 
223 	if (wizard) {
224 		wizard = 0;
225 		message("not wizard anymore", 0);
226 	} else {
227 		if (get_input_line("wizard's password:", "", buf, "", 0, 0)) {
228 			xxx(1);
229 			xxxx(buf, strlen(buf));
230 			if (!strncmp(buf, "\247\104\126\272\115\243\027", 7)) {
231 				wizard = 1;
232 				score_only = 1;
233 				message("Welcome, mighty wizard!", 0);
234 			} else {
235 				message("sorry", 0);
236 			}
237 		}
238 	}
239 }
240 
241 static void
wdrain_life(object * monster)242 wdrain_life(object *monster)
243 {
244 	short hp;
245 	object *lmon, *nm;
246 
247 	hp = rogue.hp_current / 3;
248 	rogue.hp_current = (rogue.hp_current + 1) / 2;
249 
250 	if (cur_room >= 0) {
251 		lmon = level_monsters.next_monster;
252 		while (lmon) {
253 			nm = lmon->next_monster;
254 			if (get_room_number(lmon->row, lmon->col) == cur_room) {
255 				wake_up(lmon);
256 				mon_damage(lmon, hp);
257 			}
258 			lmon = nm;
259 		}
260 	} else {
261 		if (monster) {
262 			wake_up(monster);
263 			mon_damage(monster, hp);
264 		}
265 	}
266 	print_stats(STAT_HP);
267 	relight();
268 }
269 
270 void
bounce(short ball,short dir,short row,short col,short r)271 bounce(short ball, short dir, short row, short col, short r)
272 {
273 	short orow, ocol;
274 	char buf[DCOLS];
275 	const char *s;
276 	short i, ch, new_dir = -1, damage;
277 	static short btime;
278 
279 	if (++r == 1) {
280 		btime = get_rand(3, 6);
281 	} else if (r > btime) {
282 		return;
283 	}
284 
285 	if (ball == FIRE) {
286 		s = "fire";
287 	} else {
288 		s = "ice";
289 	}
290 	if (r > 1) {
291 		sprintf(buf, "the %s bounces", s);
292 		message(buf, 0);
293 	}
294 	orow = row;
295 	ocol = col;
296 	do {
297 		ch = mvinch(orow, ocol);
298 		standout();
299 		mvaddch(orow, ocol, ch);
300 		get_dir_rc(dir, &orow, &ocol, 1);
301 	} while (!(	(ocol <= 0) ||
302 				(ocol >= DCOLS-1) ||
303 				(dungeon[orow][ocol] == NOTHING) ||
304 				(dungeon[orow][ocol] & MONSTER) ||
305 				(dungeon[orow][ocol] & (HORWALL | VERTWALL)) ||
306 				((orow == rogue.row) && (ocol == rogue.col))));
307 	standend();
308 	refresh();
309 	do {
310 		orow = row;
311 		ocol = col;
312 		ch = mvinch(row, col);
313 		mvaddch(row, col, ch);
314 		get_dir_rc(dir, &row, &col, 1);
315 	} while (!(	(col <= 0) ||
316 				(col >= DCOLS-1) ||
317 				(dungeon[row][col] == NOTHING) ||
318 				(dungeon[row][col] & MONSTER) ||
319 				(dungeon[row][col] & (HORWALL | VERTWALL)) ||
320 				((row == rogue.row) && (col == rogue.col))));
321 
322 	if (dungeon[row][col] & MONSTER) {
323 		object *monster;
324 
325 		monster = object_at(&level_monsters, row, col);
326 
327 		wake_up(monster);
328 		if (rand_percent(33)) {
329 			sprintf(buf, "the %s misses the %s", s, mon_name(monster));
330 			message(buf, 0);
331 			goto ND;
332 		}
333 		if (ball == FIRE) {
334 			if (!(monster->m_flags & RUSTS)) {
335 				if (monster->m_flags & FREEZES) {
336 					damage = monster->hp_to_kill;
337 				} else if (monster->m_flags & FLAMES) {
338 					damage = (monster->hp_to_kill / 10) + 1;
339 				} else {
340 					damage = get_rand((rogue.hp_current / 3), rogue.hp_max);
341 				}
342 			} else {
343 				damage = (monster->hp_to_kill / 2) + 1;
344 			}
345 			sprintf(buf, "the %s hits the %s", s, mon_name(monster));
346 			message(buf, 0);
347 			mon_damage(monster, damage);
348 		} else {
349 			damage = -1;
350 			if (!(monster->m_flags & FREEZES)) {
351 				if (rand_percent(33)) {
352 					message("the monster is frozen", 0);
353 					monster->m_flags |= (ASLEEP | NAPPING);
354 					monster->nap_length = get_rand(3, 6);
355 				} else {
356 					damage = rogue.hp_current / 4;
357 				}
358 			} else {
359 				damage = -2;
360 			}
361 			if (damage != -1) {
362 				sprintf(buf, "the %s hits the %s", s, mon_name(monster));
363 				message(buf, 0);
364 				mon_damage(monster, damage);
365 			}
366 		}
367 	} else if ((row == rogue.row) && (col == rogue.col)) {
368 		if (rand_percent(10 + (3 * get_armor_class(rogue.armor)))) {
369 			sprintf(buf, "the %s misses", s);
370 			message(buf, 0);
371 			goto ND;
372 		} else {
373 			damage = get_rand(3, (3 * rogue.exp));
374 			if (ball == FIRE) {
375 				damage = (damage * 3) / 2;
376 				damage -= get_armor_class(rogue.armor);
377 			}
378 			sprintf(buf, "the %s hits", s);
379 			rogue_damage(damage, NULL,
380 					((ball == FIRE) ? KFIRE : HYPOTHERMIA));
381 			message(buf, 0);
382 		}
383 	} else {
384 		short nrow, ncol;
385 
386 ND:		for (i = 0; i < 10; i++) {
387 			dir = get_rand(0, DIRS-1);
388 			nrow = orow;
389 			ncol = ocol;
390 			get_dir_rc(dir, &nrow, &ncol, 1);
391 			if (((ncol >= 0) && (ncol <= DCOLS-1)) &&
392 				(dungeon[nrow][ncol] != NOTHING) &&
393 				(!(dungeon[nrow][ncol] & (VERTWALL | HORWALL)))) {
394 				new_dir = dir;
395 				break;
396 			}
397 		}
398 		if (new_dir != -1) {
399 			bounce(ball, new_dir, orow, ocol, r);
400 		}
401 	}
402 }
403