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