Annotation of early-roguelike/xrogue/bolt.c, Revision 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