Mercurial > hg > early-roguelike
view xrogue/bolt.c @ 155:1af259ac4ed2
arogue7, xrogue: fix save/restore of alchemy jugs.
state.c now saves the fuse that refills alchemy jugs, using the method
of numbering objects previously applied to Advanced Rogue 5. Alchemy
jugs that are empty when the game is saved will continue to function
after it is restored.
Savefile compatibility should not be affected.
author | John "Elwin" Edwards |
---|---|
date | Fri, 29 May 2015 17:34:04 -0400 |
parents | e6179860cb76 |
children | f54901b9c39b |
line wrap: on
line source
/* bolt.c - functions shooting an object across the room XRogue: Expeditions into the Dungeons of Doom Copyright (C) 1991 Robert Pietkivitch All rights reserved. Based on "Advanced Rogue" Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka and AT&T All rights reserved. Based on "Rogue: Exploring the Dungeons of Doom" Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman All rights reserved. See the file LICENSE.TXT for full copyright and licensing information. */ #include <curses.h> #include <ctype.h> #include "rogue.h" /* * shoot_bolt fires a bolt from the given starting point in the * given direction */ shoot_bolt(shooter, start, dir, get_points, reason, name, damage) struct thing *shooter; coord start, dir; bool get_points; short reason; char *name; int damage; { unsigned char dirch = 0, ch; bool used, change, see_him; short y, x, bounces; coord pos; struct linked_list *target=NULL; struct { coord place; char oldch; } spotpos[BOLT_LENGTH]; switch (dir.y + dir.x) { case 0: dirch = '/'; when 1: case -1: dirch = (dir.y == 0 ? '-' : '|'); when 2: case -2: dirch = '\\'; } pos.y = start.y + dir.y; pos.x = start.x + dir.x; used = FALSE; change = FALSE; bounces = 0; /* No bounces yet */ nofont(cw); for (y = 0; y < BOLT_LENGTH && !used; y++) { ch = winat(pos.y, pos.x); spotpos[y].place = pos; spotpos[y].oldch = mvwinch(cw, pos.y, pos.x); /* Are we at hero? */ if (ce(pos, hero)) goto at_hero; switch (ch) { case SECRETDOOR: case VERTWALL: case HORZWALL: case ' ': if (dirch == '-' || dirch == '|') { dir.y = -dir.y; dir.x = -dir.x; } else { unsigned char chx = mvinch(pos.y-dir.y, pos.x), chy = mvinch(pos.y, pos.x-dir.x); bool anychange = FALSE; /* Did we change anthing */ if (chy == WALL || chy == SECRETDOOR || chy == HORZWALL || chy == VERTWALL) { dir.y = -dir.y; change ^= TRUE; /* Change at least one direction */ anychange = TRUE; } if (chx == WALL || chx == SECRETDOOR || chx == HORZWALL || chx == VERTWALL) { dir.x = -dir.x; change ^= TRUE; /* Change at least one direction */ anychange = TRUE; } /* If we didn't make any change, make both changes */ if (!anychange) { dir.x = -dir.x; dir.y = -dir.y; } } /* Do we change how the bolt looks? */ if (change) { change = FALSE; if (dirch == '\\') dirch = '/'; else if (dirch == '/') dirch = '\\'; } y--; /* The bounce doesn't count as using up the bolt */ /* Make sure we aren't in an infinite bounce */ if (++bounces > BOLT_LENGTH) used = TRUE; msg("The %s bounces", name); break; default: if (isalpha(ch)) { register struct linked_list *item; struct thing *tp; register char *mname; bool see_monster = cansee(pos.y, pos.x); item = find_mons(unc(pos)); assert(item != NULL); tp = THINGPTR(item); mname = monster_name(tp); /* * If our prey shot this, let's record the fact that * he can shoot, regardless of whether he hits us. */ if (tp->t_dest != NULL && ce(*tp->t_dest, shooter->t_pos)) tp->t_wasshot = TRUE; if (!save(VS_BREATH, tp, -(shooter->t_stats.s_lvl/10))) { if (see_monster) { if (on(*tp, ISDISGUISE) && (tp->t_type != tp->t_disguise)) { msg("Wait! That's a %s!", mname); turn_off(*tp, ISDISGUISE); } turn_off(*tp, CANSURPRISE); msg("The %s hits %s", name, prname(mname, FALSE)); } /* Should we start to chase the shooter? */ if (shooter != &player && shooter != tp && shooter->t_index != tp->t_index && (tp->t_dest == NULL || rnd(100) < 25)) { /* * If we're intelligent enough to realize that this * is a friendly monster, we will attack the hero * instead. */ if (on(*shooter, ISFRIENDLY) && roll(3,6) < tp->t_stats.s_intel) runto(tp, &hero); /* Otherwise, let's chase the monster */ else runto(tp, &shooter->t_pos); } else if (shooter == &player) { runto(tp, &hero); /* * If the player shot a charmed monster, it may * not like being shot at. */ if (on(*tp, ISCHARMED) && save(VS_MAGIC, tp, 0)) { msg("The eyes of %s turn clear.", prname(mname, FALSE)); turn_off(*tp, ISCHARMED); mname = monster_name(tp); } } /* * Let the defender know that the attacker has * missiles! */ if (ce(*tp->t_dest, shooter->t_pos)) tp->t_wasshot = TRUE; used = TRUE; /* Hit the monster -- does it do anything? */ if ((EQUAL(name,"ice") && on(*tp, NOCOLD)) || (EQUAL(name,"flame") && on(*tp, NOFIRE)) || (EQUAL(name,"acid") && on(*tp, NOACID)) || (EQUAL(name,"lightning bolt")&& on(*tp,NOBOLT)) || (EQUAL(name,"nerve gas") &&on(*tp,NOPARALYZE))|| (EQUAL(name,"sleeping gas") && (on(*tp, NOSLEEP) || on(*tp, ISUNDEAD))) || (EQUAL(name,"slow gas") && on(*tp,NOSLOW)) || (EQUAL(name,"fear gas") && on(*tp,NOFEAR)) || (EQUAL(name,"confusion gas") && on(*tp,ISCLEAR)) || (EQUAL(name,"chlorine gas") && on(*tp,NOGAS))) { if (see_monster) msg("The %s has no effect on %s.", name, prname(mname, FALSE)); } else { see_him = !invisible(tp); /* Did a spell get disrupted? */ dsrpt_monster(tp, FALSE, see_him); /* * Check for gas with special effects */ if (EQUAL(name, "nerve gas")) { tp->t_no_move = movement(tp) * FREEZETIME; tp->t_action = A_FREEZE; } else if (EQUAL(name, "sleeping gas")) { tp->t_no_move = movement(tp) * SLEEPTIME; tp->t_action = A_FREEZE; } else if (EQUAL(name, "slow gas")) { if (on(*tp, ISHASTE)) turn_off(*tp, ISHASTE); else turn_on(*tp, ISSLOW); } else if (EQUAL(name, "fear gas")) { turn_on(*tp, ISFLEE); tp->t_dest = &hero; /* It is okay to turn tail */ tp->t_oldpos = tp->t_pos; } else if (EQUAL(name, "confusion gas")) { turn_on(*tp, ISHUH); tp->t_dest = &hero; } else if ((EQUAL(name, "lightning bolt")) && on(*tp, BOLTDIVIDE)) { if (creat_mons(tp, tp->t_index, FALSE)) { if (see_monster) msg("The %s divides %s.", name,prname(mname, FALSE)); light(&hero); } else if (see_monster) msg("The %s has no effect on %s.", name, prname(mname, FALSE)); } else { if (save(VS_BREATH, tp, -(shooter->t_stats.s_lvl/10))) damage /= 2; /* The poor fellow got killed! */ if ((tp->t_stats.s_hpt -= damage) <= 0) { if (see_monster) msg("The %s kills %s", name, prname(mname, FALSE)); else msg("You hear a faint groan in the distance"); /* * Instead of calling killed() here, we * will record that the monster was killed * and call it at the end of the routine, * after we restore what was under the bolt. * We have to do this because in the case * of a bolt that first misses the monster * and then gets it on the bounce. If we * call killed here, the 'missed' space in * spotpos puts the monster back on the * screen */ target = item; } else { /* Not dead, so just scream */ if (!see_monster) msg("You hear a scream in the distance"); } } } } else if (isalpha(show(pos.y, pos.x))) { if (see_monster) { if (terse) msg("%s misses", name); else msg("The %s whizzes past %s", name, prname(mname, FALSE)); } if (get_points) runto(tp, &hero); } } else if (pos.y == hero.y && pos.x == hero.x) { at_hero: if (!save(VS_BREATH, &player, -(shooter->t_stats.s_lvl/10))){ if (terse) msg("The %s hits you", name); else msg("You are hit by the %s", name); used = TRUE; /* * The Amulet of Yendor protects against all "breath" * * The following two if statements could be combined * into one, but it makes the compiler barf, so split * it up */ if (cur_relic[YENDOR_AMULET] || (EQUAL(name,"chlorine gas")&&on(player, NOGAS)) || (EQUAL(name,"acid")&&on(player, NOACID)) || (EQUAL(name,"sleeping gas")&&ISWEARING(R_ALERT))){ msg("The %s has no effect", name); } else if((EQUAL(name, "flame") && on(player, NOFIRE)) || (EQUAL(name, "ice") && on(player, NOCOLD)) || (EQUAL(name,"lightning bolt")&& on(player,NOBOLT)) || (EQUAL(name,"fear gas")&&ISWEARING(R_HEROISM))){ msg("The %s has no effect", name); } else { dsrpt_player(); /* * Check for gas with special effects */ if (EQUAL(name, "nerve gas")) { msg("The nerve gas paralyzes you."); player.t_no_move += movement(&player) * FREEZETIME; player.t_action = A_FREEZE; } else if (EQUAL(name, "sleeping gas")) { msg("The sleeping gas puts you to sleep."); player.t_no_move += movement(&player) * SLEEPTIME; player.t_action = A_FREEZE; } else if (EQUAL(name, "confusion gas")) { if (off(player, ISCLEAR)) { if (on(player, ISHUH)) lengthen(unconfuse, rnd(20)+HUHDURATION); else { turn_on(player, ISHUH); fuse(unconfuse, (VOID *)NULL, rnd(20)+HUHDURATION, AFTER); msg("The confusion gas has confused you."); } } else msg("You feel dizzy for a moment, but it quickly passes."); } else if (EQUAL(name, "slow gas")) { add_slow(); } else if (EQUAL(name, "fear gas")) { turn_on(player, ISFLEE); player.t_dest = &shooter->t_pos; msg("The fear gas terrifies you."); } else { if (EQUAL(name, "acid") && cur_armor != NULL && !(cur_armor->o_flags & ISPROT) && !save(VS_BREATH, &player, -2) && cur_armor->o_ac < pstats.s_arm+1) { msg("Your armor corrodes from the acid"); cur_armor->o_ac++; } if (save(VS_BREATH, &player, -(shooter->t_stats.s_lvl/10)) && off(player, NOACID)) damage /= 2; if ((pstats.s_hpt -= damage) <= 0) death(reason); } } } else msg("The %s whizzes by you", name); } mvwaddch(cw, pos.y, pos.x, dirch); draw(cw); } pos.y += dir.y; pos.x += dir.x; } /* Restore what was under the bolt */ newfont(cw); for (x = y - 1; x >= 0; x--) mvwaddch(cw, spotpos[x].place.y, spotpos[x].place.x, spotpos[x].oldch); /* If we killed something, do so now. This will also blank the monster. */ if (target) killed(target, FALSE, get_points, TRUE); return; }