comparison xrogue/bolt.c @ 133:e6179860cb76

Import XRogue 8.0 from the Roguelike Restoration Project (r1490)
author John "Elwin" Edwards
date Tue, 21 Apr 2015 08:55:20 -0400
parents
children f54901b9c39b
comparison
equal deleted inserted replaced
124:d10fc4a065ac 133:e6179860cb76
1 /*
2 bolt.c - functions shooting an object across the room
3
4 XRogue: Expeditions into the Dungeons of Doom
5 Copyright (C) 1991 Robert Pietkivitch
6 All rights reserved.
7
8 Based on "Advanced Rogue"
9 Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka and AT&T
10 All rights reserved.
11
12 Based on "Rogue: Exploring the Dungeons of Doom"
13 Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
14 All rights reserved.
15
16 See the file LICENSE.TXT for full copyright and licensing information.
17 */
18
19 #include <curses.h>
20 #include <ctype.h>
21 #include "rogue.h"
22
23 /*
24 * shoot_bolt fires a bolt from the given starting point in the
25 * given direction
26 */
27
28 shoot_bolt(shooter, start, dir, get_points, reason, name, damage)
29 struct thing *shooter;
30 coord start, dir;
31 bool get_points;
32 short reason;
33 char *name;
34 int damage;
35 {
36 unsigned char dirch = 0, ch;
37 bool used, change, see_him;
38 short y, x, bounces;
39 coord pos;
40 struct linked_list *target=NULL;
41 struct {
42 coord place;
43 char oldch;
44 } spotpos[BOLT_LENGTH];
45
46 switch (dir.y + dir.x) {
47 case 0: dirch = '/';
48 when 1: case -1: dirch = (dir.y == 0 ? '-' : '|');
49 when 2: case -2: dirch = '\\';
50 }
51 pos.y = start.y + dir.y;
52 pos.x = start.x + dir.x;
53 used = FALSE;
54 change = FALSE;
55
56 bounces = 0; /* No bounces yet */
57 nofont(cw);
58 for (y = 0; y < BOLT_LENGTH && !used; y++) {
59 ch = winat(pos.y, pos.x);
60 spotpos[y].place = pos;
61 spotpos[y].oldch = mvwinch(cw, pos.y, pos.x);
62
63 /* Are we at hero? */
64 if (ce(pos, hero)) goto at_hero;
65
66 switch (ch) {
67 case SECRETDOOR:
68 case VERTWALL:
69 case HORZWALL:
70 case ' ':
71 if (dirch == '-' || dirch == '|') {
72 dir.y = -dir.y;
73 dir.x = -dir.x;
74 }
75 else {
76 unsigned char chx = mvinch(pos.y-dir.y, pos.x),
77 chy = mvinch(pos.y, pos.x-dir.x);
78 bool anychange = FALSE; /* Did we change anthing */
79
80 if (chy == WALL || chy == SECRETDOOR ||
81 chy == HORZWALL || chy == VERTWALL) {
82 dir.y = -dir.y;
83 change ^= TRUE; /* Change at least one direction */
84 anychange = TRUE;
85 }
86 if (chx == WALL || chx == SECRETDOOR ||
87 chx == HORZWALL || chx == VERTWALL) {
88 dir.x = -dir.x;
89 change ^= TRUE; /* Change at least one direction */
90 anychange = TRUE;
91 }
92
93 /* If we didn't make any change, make both changes */
94 if (!anychange) {
95 dir.x = -dir.x;
96 dir.y = -dir.y;
97 }
98 }
99
100 /* Do we change how the bolt looks? */
101 if (change) {
102 change = FALSE;
103 if (dirch == '\\') dirch = '/';
104 else if (dirch == '/') dirch = '\\';
105 }
106
107 y--; /* The bounce doesn't count as using up the bolt */
108
109 /* Make sure we aren't in an infinite bounce */
110 if (++bounces > BOLT_LENGTH) used = TRUE;
111 msg("The %s bounces", name);
112 break;
113 default:
114 if (isalpha(ch)) {
115 register struct linked_list *item;
116 struct thing *tp;
117 register char *mname;
118 bool see_monster = cansee(pos.y, pos.x);
119
120 item = find_mons(unc(pos));
121 assert(item != NULL);
122 tp = THINGPTR(item);
123 mname = monster_name(tp);
124
125 /*
126 * If our prey shot this, let's record the fact that
127 * he can shoot, regardless of whether he hits us.
128 */
129 if (tp->t_dest != NULL && ce(*tp->t_dest, shooter->t_pos))
130 tp->t_wasshot = TRUE;
131
132 if (!save(VS_BREATH, tp, -(shooter->t_stats.s_lvl/10))) {
133 if (see_monster) {
134 if (on(*tp, ISDISGUISE) &&
135 (tp->t_type != tp->t_disguise)) {
136 msg("Wait! That's a %s!", mname);
137 turn_off(*tp, ISDISGUISE);
138 }
139
140 turn_off(*tp, CANSURPRISE);
141 msg("The %s hits %s", name, prname(mname, FALSE));
142 }
143
144 /* Should we start to chase the shooter? */
145 if (shooter != &player &&
146 shooter != tp &&
147 shooter->t_index != tp->t_index &&
148 (tp->t_dest == NULL || rnd(100) < 25)) {
149 /*
150 * If we're intelligent enough to realize that this
151 * is a friendly monster, we will attack the hero
152 * instead.
153 */
154 if (on(*shooter, ISFRIENDLY) &&
155 roll(3,6) < tp->t_stats.s_intel)
156 runto(tp, &hero);
157
158 /* Otherwise, let's chase the monster */
159 else runto(tp, &shooter->t_pos);
160 }
161 else if (shooter == &player) {
162 runto(tp, &hero);
163
164 /*
165 * If the player shot a charmed monster, it may
166 * not like being shot at.
167 */
168 if (on(*tp, ISCHARMED) && save(VS_MAGIC, tp, 0)) {
169 msg("The eyes of %s turn clear.",
170 prname(mname, FALSE));
171 turn_off(*tp, ISCHARMED);
172 mname = monster_name(tp);
173 }
174 }
175
176 /*
177 * Let the defender know that the attacker has
178 * missiles!
179 */
180 if (ce(*tp->t_dest, shooter->t_pos))
181 tp->t_wasshot = TRUE;
182
183 used = TRUE;
184
185 /* Hit the monster -- does it do anything? */
186 if ((EQUAL(name,"ice") && on(*tp, NOCOLD)) ||
187 (EQUAL(name,"flame") && on(*tp, NOFIRE)) ||
188 (EQUAL(name,"acid") && on(*tp, NOACID)) ||
189 (EQUAL(name,"lightning bolt")&& on(*tp,NOBOLT)) ||
190 (EQUAL(name,"nerve gas") &&on(*tp,NOPARALYZE))||
191 (EQUAL(name,"sleeping gas") &&
192 (on(*tp, NOSLEEP) || on(*tp, ISUNDEAD))) ||
193 (EQUAL(name,"slow gas") && on(*tp,NOSLOW)) ||
194 (EQUAL(name,"fear gas") && on(*tp,NOFEAR)) ||
195 (EQUAL(name,"confusion gas") && on(*tp,ISCLEAR)) ||
196 (EQUAL(name,"chlorine gas") && on(*tp,NOGAS))) {
197 if (see_monster)
198 msg("The %s has no effect on %s.",
199 name, prname(mname, FALSE));
200 }
201
202 else {
203 see_him = !invisible(tp);
204
205 /* Did a spell get disrupted? */
206 dsrpt_monster(tp, FALSE, see_him);
207
208 /*
209 * Check for gas with special effects
210 */
211 if (EQUAL(name, "nerve gas")) {
212 tp->t_no_move = movement(tp) * FREEZETIME;
213 tp->t_action = A_FREEZE;
214 }
215 else if (EQUAL(name, "sleeping gas")) {
216 tp->t_no_move = movement(tp) * SLEEPTIME;
217 tp->t_action = A_FREEZE;
218 }
219 else if (EQUAL(name, "slow gas")) {
220 if (on(*tp, ISHASTE))
221 turn_off(*tp, ISHASTE);
222 else
223 turn_on(*tp, ISSLOW);
224 }
225 else if (EQUAL(name, "fear gas")) {
226 turn_on(*tp, ISFLEE);
227 tp->t_dest = &hero;
228
229 /* It is okay to turn tail */
230 tp->t_oldpos = tp->t_pos;
231 }
232 else if (EQUAL(name, "confusion gas")) {
233 turn_on(*tp, ISHUH);
234 tp->t_dest = &hero;
235 }
236 else if ((EQUAL(name, "lightning bolt")) &&
237 on(*tp, BOLTDIVIDE)) {
238 if (creat_mons(tp, tp->t_index, FALSE)) {
239 if (see_monster)
240 msg("The %s divides %s.",
241 name,prname(mname, FALSE));
242 light(&hero);
243 }
244 else if (see_monster)
245 msg("The %s has no effect on %s.",
246 name, prname(mname, FALSE));
247 }
248 else {
249 if (save(VS_BREATH, tp,
250 -(shooter->t_stats.s_lvl/10)))
251 damage /= 2;
252
253 /* The poor fellow got killed! */
254 if ((tp->t_stats.s_hpt -= damage) <= 0) {
255 if (see_monster)
256 msg("The %s kills %s",
257 name, prname(mname, FALSE));
258 else
259 msg("You hear a faint groan in the distance");
260 /*
261 * Instead of calling killed() here, we
262 * will record that the monster was killed
263 * and call it at the end of the routine,
264 * after we restore what was under the bolt.
265 * We have to do this because in the case
266 * of a bolt that first misses the monster
267 * and then gets it on the bounce. If we
268 * call killed here, the 'missed' space in
269 * spotpos puts the monster back on the
270 * screen
271 */
272 target = item;
273 }
274 else { /* Not dead, so just scream */
275 if (!see_monster)
276 msg("You hear a scream in the distance");
277 }
278 }
279 }
280 }
281 else if (isalpha(show(pos.y, pos.x))) {
282 if (see_monster) {
283 if (terse)
284 msg("%s misses", name);
285 else
286 msg("The %s whizzes past %s",
287 name, prname(mname, FALSE));
288 }
289 if (get_points) runto(tp, &hero);
290 }
291 }
292 else if (pos.y == hero.y && pos.x == hero.x) {
293 at_hero: if (!save(VS_BREATH, &player,
294 -(shooter->t_stats.s_lvl/10))){
295 if (terse)
296 msg("The %s hits you", name);
297 else
298 msg("You are hit by the %s", name);
299 used = TRUE;
300
301 /*
302 * The Amulet of Yendor protects against all "breath"
303 *
304 * The following two if statements could be combined
305 * into one, but it makes the compiler barf, so split
306 * it up
307 */
308 if (cur_relic[YENDOR_AMULET] ||
309 (EQUAL(name,"chlorine gas")&&on(player, NOGAS)) ||
310 (EQUAL(name,"acid")&&on(player, NOACID)) ||
311 (EQUAL(name,"sleeping gas")&&ISWEARING(R_ALERT))){
312 msg("The %s has no effect", name);
313 }
314 else if((EQUAL(name, "flame") && on(player, NOFIRE)) ||
315 (EQUAL(name, "ice") && on(player, NOCOLD)) ||
316 (EQUAL(name,"lightning bolt")&&
317 on(player,NOBOLT)) ||
318 (EQUAL(name,"fear gas")&&ISWEARING(R_HEROISM))){
319 msg("The %s has no effect", name);
320 }
321
322 else {
323 dsrpt_player();
324
325 /*
326 * Check for gas with special effects
327 */
328 if (EQUAL(name, "nerve gas")) {
329 msg("The nerve gas paralyzes you.");
330 player.t_no_move +=
331 movement(&player) * FREEZETIME;
332 player.t_action = A_FREEZE;
333 }
334 else if (EQUAL(name, "sleeping gas")) {
335 msg("The sleeping gas puts you to sleep.");
336 player.t_no_move +=
337 movement(&player) * SLEEPTIME;
338 player.t_action = A_FREEZE;
339 }
340 else if (EQUAL(name, "confusion gas")) {
341 if (off(player, ISCLEAR)) {
342 if (on(player, ISHUH))
343 lengthen(unconfuse,
344 rnd(20)+HUHDURATION);
345 else {
346 turn_on(player, ISHUH);
347 fuse(unconfuse, (VOID *)NULL,
348 rnd(20)+HUHDURATION, AFTER);
349 msg("The confusion gas has confused you.");
350 }
351 }
352 else msg("You feel dizzy for a moment, but it quickly passes.");
353 }
354 else if (EQUAL(name, "slow gas")) {
355 add_slow();
356 }
357 else if (EQUAL(name, "fear gas")) {
358 turn_on(player, ISFLEE);
359 player.t_dest = &shooter->t_pos;
360 msg("The fear gas terrifies you.");
361 }
362 else {
363 if (EQUAL(name, "acid") &&
364 cur_armor != NULL &&
365 !(cur_armor->o_flags & ISPROT) &&
366 !save(VS_BREATH, &player, -2) &&
367 cur_armor->o_ac < pstats.s_arm+1) {
368 msg("Your armor corrodes from the acid");
369 cur_armor->o_ac++;
370 }
371 if (save(VS_BREATH, &player,
372 -(shooter->t_stats.s_lvl/10)) &&
373 off(player, NOACID))
374 damage /= 2;
375 if ((pstats.s_hpt -= damage) <= 0)
376 death(reason);
377 }
378 }
379 }
380 else
381 msg("The %s whizzes by you", name);
382 }
383
384 mvwaddch(cw, pos.y, pos.x, dirch);
385 draw(cw);
386 }
387
388 pos.y += dir.y;
389 pos.x += dir.x;
390 }
391
392 /* Restore what was under the bolt */
393 newfont(cw);
394 for (x = y - 1; x >= 0; x--)
395 mvwaddch(cw, spotpos[x].place.y, spotpos[x].place.x, spotpos[x].oldch);
396
397 /* If we killed something, do so now. This will also blank the monster. */
398 if (target) killed(target, FALSE, get_points, TRUE);
399 return;
400 }
401