Mercurial > hg > early-roguelike
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xrogue/bolt.c Tue Apr 21 08:55:20 2015 -0400 @@ -0,0 +1,401 @@ +/* + 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; +} +