summaryrefslogtreecommitdiffstats
path: root/stone_soup/crawl-ref/source/beam.cc
diff options
context:
space:
mode:
Diffstat (limited to 'stone_soup/crawl-ref/source/beam.cc')
-rw-r--r--stone_soup/crawl-ref/source/beam.cc4520
1 files changed, 0 insertions, 4520 deletions
diff --git a/stone_soup/crawl-ref/source/beam.cc b/stone_soup/crawl-ref/source/beam.cc
deleted file mode 100644
index 44d1fbc0d9..0000000000
--- a/stone_soup/crawl-ref/source/beam.cc
+++ /dev/null
@@ -1,4520 +0,0 @@
-/*
- * File: beam.cc
- * Summary: Functions related to ranged attacks.
- * Written by: Linley Henzell
- *
- * Change History (most recent first):
- *
- * <7> 21mar2001 GDL Replaced all FP arithmetic with integer*100 math
- * <6> 07jan2001 GDL complete rewrite.
- * <5> 22July2000 GDL allowed 'dummy' missiles from monsters
- * <4> 11/14/99 cdl evade beams with random40(ev) vice random2(ev)
- * all armour now protects against shrapnel
- * <3> 6/ 2/99 DML Added enums
- * <2> 5/20/99 BWR Added refreshs for curses
- * <1> -/--/-- LRH Created
- */
-
-#include "AppHdr.h"
-#include "beam.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#ifdef DOS
-#include <dos.h>
-#include <conio.h>
-#endif
-#if DEBUG_DIAGNOSTICS
-#include <stdio.h>
-#endif
-
-#include "externs.h"
-
-#include "cloud.h"
-#include "effects.h"
-#include "enum.h"
-#include "it_use2.h"
-#include "itemname.h"
-#include "items.h"
-#include "itemprop.h"
-#include "misc.h"
-#include "monplace.h"
-#include "monstuff.h"
-#include "mon-util.h"
-#include "mstuff2.h"
-#include "ouch.h"
-#include "player.h"
-#include "religion.h"
-#include "skills.h"
-#include "spells1.h"
-#include "spells3.h"
-#include "spells4.h"
-#include "stuff.h"
-#include "view.h"
-
-#define BEAM_STOP 1000 // all beams stopped by subtracting this
- // from remaining range
-#define MON_RESIST 0 // monster resisted
-#define MON_UNAFFECTED 1 // monster unaffected
-#define MON_AFFECTED 2 // monster was unaffected
-
-extern FixedVector< char, NUM_STATUE_TYPES > Visible_Statue; // in acr.cc
-
-static int spreadx[] = { 0, 0, 1, -1 };
-static int spready[] = { -1, 1, 0, 0 };
-static int opdir[] = { 2, 1, 4, 3 };
-static FixedArray < bool, 19, 19 > explode_map;
-
-// helper functions (some of these, esp. affect(), should probably
-// be public):
-static void sticky_flame_monster( int mn, bool source, int hurt_final );
-static bool affectsWalls(struct bolt &beam);
-static bool isBouncy(struct bolt &beam);
-static void beam_drop_object( struct bolt &beam, item_def *item, int x, int y );
-static bool beam_term_on_target(struct bolt &beam);
-static void beam_explodes(struct bolt &beam, int x, int y);
-static int bounce(int &step1, int &step2, int w1, int w2, int &n1, int &n2,
- int l1, int l2, int &t1, int &t2, bool topBlocked, bool sideBlocked);
-static bool fuzzyLine(int nx, int ny, int &tx, int &ty, int lx, int ly,
- int stepx, int stepy, bool roundX, bool roundY);
-static int affect_wall(struct bolt &beam, int x, int y);
-static int affect_place_clouds(struct bolt &beam, int x, int y);
-static void affect_place_explosion_clouds(struct bolt &beam, int x, int y);
-static int affect_player(struct bolt &beam);
-static void affect_items(struct bolt &beam, int x, int y);
-static int affect_monster(struct bolt &beam, struct monsters *mon);
-static int affect_monster_enchantment(struct bolt &beam, struct monsters *mon);
-static int range_used_on_hit(struct bolt &beam);
-static void explosion1(struct bolt &pbolt);
-static void explosion_map(struct bolt &beam, int x, int y,
- int count, int dir, int r);
-static void explosion_cell(struct bolt &beam, int x, int y, bool drawOnly);
-
-static void ench_animation( int flavour, const monsters *mon = NULL, bool force = false);
-static void zappy(char z_type, int power, struct bolt &pbolt);
-
-static bool beam_is_blockable( struct bolt &pbolt )
-{
- // BEAM_ELECTRICITY is added here because chain lighting is not
- // a true beam (stops at the first target it gets to and redirects
- // from there)... but we don't want it shield blockable.
- return (!pbolt.is_beam && !pbolt.is_explosion
- && pbolt.flavour != BEAM_ELECTRICITY);
-}
-
-// simple animated flash from Rupert Smith (and expanded to be more generic):
-void zap_animation( int colour, const monsters *mon, bool force )
-{
- int x = you.x_pos, y = you.y_pos;
-
- // default to whatever colour magic is today
- if (colour == -1)
- colour = element_colour( EC_MAGIC );
-
- if (mon)
- {
- if (!force && !player_monster_visible( mon ))
- return;
-
- x = mon->x;
- y = mon->y;
- }
-
- if (!see_grid( x, y ))
- return;
-
- const int drawx = x - you.x_pos + 18;
- const int drawy = y - you.y_pos + 9;
-
- if (drawx > 8 && drawx < 26 && drawy > 0 && drawy < 18)
- {
- textcolor( colour );
- gotoxy( drawx, drawy );
- putch( SYM_ZAP );
-
-#ifdef LINUX
- update_screen();
-#endif
-
- delay(50);
- }
-}
-
-// special front function for zap_animation to interpret enchantment flavours
-static void ench_animation( int flavour, const monsters *mon, bool force )
-{
- const int elem = (flavour == BEAM_HEALING) ? EC_HEAL :
- (flavour == BEAM_PAIN) ? EC_UNHOLY :
- (flavour == BEAM_DISPEL_UNDEAD) ? EC_HOLY :
- (flavour == BEAM_POLYMORPH) ? EC_MUTAGENIC :
- (flavour == BEAM_TELEPORT
- || flavour == BEAM_BANISH
- || flavour == BEAM_BLINK) ? EC_WARP
- : EC_ENCHANT;
- zap_animation( element_colour( elem ), mon, force );
-}
-
-void zapping(char ztype, int power, struct bolt &pbolt)
-{
-
-#if DEBUG_DIAGNOSTICS
- snprintf( info, INFO_SIZE, "zapping: power=%d", power );
- mpr( info, MSGCH_DIAGNOSTICS );
-#endif
-
- // GDL: note that rangeMax is set to 0, which means that max range is
- // equal to range. This is OK, since rangeMax really only matters for
- // stuff monsters throw/zap.
-
- // all of the following might be changed by zappy():
- pbolt.range = 8 + random2(5); // default for "0" beams (I think)
- pbolt.rangeMax = 0;
- pbolt.hit = 0; // default for "0" beams (I think)
- pbolt.damage = dice_def( 1, 0 ); // default for "0" beams (I think)
- pbolt.type = 0; // default for "0" beams
- pbolt.flavour = BEAM_MAGIC; // default for "0" beams
- pbolt.ench_power = power;
- pbolt.obvious_effect = false;
- pbolt.is_beam = false; // default for all beams.
- pbolt.is_tracer = false; // default for all player beams
- pbolt.thrower = KILL_YOU_MISSILE; // missile from player
- pbolt.aux_source = NULL; // additional source info, unused
-
- // fill in the bolt structure
- zappy( ztype, power, pbolt );
-
- if (ztype == ZAP_LIGHTNING && !silenced(you.x_pos, you.y_pos))
- // needs to check silenced at other location, too {dlb}
- {
- mpr("You hear a mighty clap of thunder!");
- noisy( 25, you.x_pos, you.y_pos );
- }
-
- fire_beam(pbolt);
-
- return;
-} // end zapping()
-
-dice_def calc_dice( int num_dice, int max_damage )
-{
- dice_def ret( num_dice, 0 );
-
- if (num_dice <= 1)
- {
- ret.num = 1;
- ret.size = max_damage;
- }
- else if (max_damage <= num_dice)
- {
- ret.num = max_damage;
- ret.size = 1;
- }
- else
- {
- // Divied the damage among the dice, and add one
- // occasionally to make up for the fractions. -- bwr
- ret.size = max_damage / num_dice;
- ret.size += (random2( num_dice ) < max_damage % num_dice);
- }
-
- return (ret);
-}
-
-// *do not* call this function directly (duh - it's static), need to
-// see zapping() for default values not set within this function {dlb}
-static void zappy( char z_type, int power, struct bolt &pbolt )
-{
- int temp_rand = 0; // probability determination {dlb}
-
- // Note: The incoming power is not linear in the case of spellcasting.
- // The power curve currently allows for the character to reasonably
- // get up to a power level of about a 100, but more than that will
- // be very hard (and the maximum is 200). The low level power caps
- // provide the useful feature in that they allow for low level spells
- // to have quick advancement, but don't cause them to obsolete the
- // higher level spells. -- bwr
- //
- // I've added some example characters below to show how little
- // people should be concerned about the power caps.
- //
- // The example characters are simplified to three stats:
- //
- // - Intelligence: This magifies power, its very useful.
- //
- // - Skills: This represents the character having Spellcasting
- // and the average of the component skills at this level.
- // Although, Spellcasting probably isn't quite as high as
- // other spell skills for a lot of characters, note that it
- // contributes much less to the total power (about 20%).
- //
- // - Enhancers: These are equipment that the player can use to
- // apply additional magnifiers (x1.5) to power. There are
- // also inhibitors that reduce power (/2.0), but we're not
- // concerned about those here. Anyways, the character can
- // currently have up to 3 levels (for x1.5, x2.25, x3.375).
- // The lists below should help to point out the difficulty
- // and cost of getting more than one level of enhancement.
- //
- // Here's a list of current magnifiers:
- //
- // - rings of fire/cold
- // - staff of fire/cold/air/earth/poison/death/conjure/enchant/summon
- // - staff of Olgreb (poison)
- // - robe of the Archmagi (necro, conjure, enchant, summon)
- // - Mummy intrinsic (+1 necromancy at level 13, +2 at level 26)
- // - Necromutation (+1 to necromancy -- note: undead can't use this)
- // - Ring of Fire (+1 to fire)
- //
- // The maximum enhancement, by school (but capped at 3):
- //
- // - Necromancy: 4 (Mummies), 3 (others)
- // - Fire: 4
- // - Cold: 3
- // - Conjuration: 2
- // - Enchantment: 2
- // - Summoning: 2
- // - Air: 1
- // - Earth: 1
- // - Poison: 1
- // - Translocations, Transmigrations, Divinations intentionally 0
-
- switch (z_type)
- {
- // level 1
- //
- // This cap is to keep these easy and very cheap spells from
- // becoming too powerful.
- //
- // Example characters with about 25 power:
- //
- // - int 5, skills 20, 0 enhancers
- // - int 5, skills 14, 1 enhancer
- // - int 10, skills 10, 0 enhancers
- // - int 10, skills 7, 1 enhancers
- // - int 15, skills 7, 0 enhancers
- // - int 20, skills 6, 0 enhancers
- case ZAP_STRIKING:
- case ZAP_MAGIC_DARTS:
- case ZAP_STING:
- case ZAP_ELECTRICITY:
- case ZAP_FLAME_TONGUE:
- case ZAP_SMALL_SANDBLAST:
- case ZAP_DISRUPTION: // ench_power boosted below
- case ZAP_PAIN: // ench_power boosted below
- if (power > 25)
- power = 25;
- break;
-
- // level 2/3
- //
- // The following examples should make it clear that in the
- // early game this cap is only limiting to serious spellcasters
- // (they could easily reach the 20-10-0 example).
- //
- // Example characters with about 50 power:
- //
- // - int 10, skills 20, 0 enhancers
- // - int 10, skills 14, 1 enhancer
- // - int 15, skills 14, 0 enhancers
- // - int 15, skills 10, 1 enhancer
- // - int 20, skills 10, 0 enhancers
- // - int 20, skills 7, 1 enhancer
- // - int 25, skills 8, 0 enhancers
- case ZAP_SANDBLAST:
- case ZAP_FLAME: // also ability (pow = lev * 2)
- case ZAP_FROST: // also ability (pow = lev * 2)
- case ZAP_STONE_ARROW:
- if (power > 50)
- power = 50;
- break;
-
- // Here are some examples that show that its fairly safe to assume
- // that a high level character can easily have 75 power.
- //
- // Example characters with about 75 power:
- //
- // - int 10, skills 27, 1 enhancer
- // - int 15, skills 27, 0 enhancers
- // - int 15, skills 16, 1 enhancer
- // - int 20, skills 20, 0 enhancers
- // - int 20, skills 14, 1 enhancer
- // - int 25, skills 16, 0 enhancers
-
- // level 4
- //
- // The following examples should make it clear that this is the
- // effective maximum power. Its not easy to get to 100 power,
- // but 20-20-1 or 25-16-1 is certainly attainable by a high level
- // spellcaster. As you can see from the examples at 150 and 200,
- // getting much power beyond this is very difficult.
- //
- // Level 3 and 4 spells cannot be overpowered.
- //
- // Example characters with about 100 power:
- //
- // - int 10, skills 27, 2 enhancers
- // - int 15, skills 27, 1 enhancer
- // - int 20, skills 20, 1 enhancer
- // - int 25, skills 24, 0 enhancers
- // - int 25, skills 16, 1 enhancer
- case ZAP_MYSTIC_BLAST:
- case ZAP_STICKY_FLAME:
- case ZAP_ICE_BOLT:
- case ZAP_DISPEL_UNDEAD: // ench_power raised below
- if (power > 100)
- power = 100;
- break;
-
- // levels 5-7
- //
- // These spells used to be capped, but its very hard to raise
- // power over 100, and these examples should show that.
- // Only the twinkiest of characters are expected to get to 150.
- //
- // Example characters with about 150 power:
- //
- // - int 15, skills 27, 3 enhancers (actually, only 146)
- // - int 20, skills 27, 2 enhancers (actually, only 137)
- // - int 20, skills 21, 3 enhancers
- // - int 25, skills 26, 2 enhancers
- // - int 30, skills 21, 2 enhancers
- // - int 40, skills 24, 1 enhancer
- // - int 70, skills 20, 0 enhancers
- case ZAP_FIRE:
- case ZAP_COLD:
- case ZAP_VENOM_BOLT:
- case ZAP_MAGMA:
- case ZAP_AGONY:
- case ZAP_LIGHTNING: // also invoc * 6 or lev * 2 (abils)
- case ZAP_NEGATIVE_ENERGY: // also ability (pow = lev * 6)
- case ZAP_IRON_BOLT:
- case ZAP_DISINTEGRATION:
- case ZAP_FIREBALL:
- case ZAP_ORB_OF_ELECTRICITY:
- case ZAP_ORB_OF_FRAGMENTATION:
- case ZAP_POISON_ARROW:
- // if (power > 150)
- // power = 150;
- break;
-
- // levels 8-9
- //
- // These spells are capped at 200 (which is the cap in calc_spell_power).
- // As an example of how little of a cap that is, consider the fact
- // that a 70-27-3 character has an uncapped power of 251. Characters
- // are never expected to get to this cap.
- //
- // Example characters with about 200 power:
- //
- // - int 30, skills 27, 3 enhancers (actually, only 190)
- // - int 40, skills 27, 2 enhancers (actually, only 181)
- // - int 40, skills 23, 3 enhancers
- // - int 70, skills 27, 0 enhancers (actually, only 164)
- // - int 70, skills 27, 1 enhancers (actually, only 194)
- // - int 70, skills 20, 2 enhancers
- // - int 70, skills 13, 3 enhancers
- case ZAP_CRYSTAL_SPEAR:
- case ZAP_HELLFIRE:
- case ZAP_ICE_STORM:
- case ZAP_CLEANSING_FLAME:
- // if (power > 200)
- // power = 200;
- break;
-
- // unlimited power (needs a good reason)
- case ZAP_BONE_SHARDS: // incoming power is modified for mass
- case ZAP_BEAM_OF_ENERGY: // inaccuracy (only on staff, hardly hits)
- break;
-
- // natural/mutant breath/spit powers (power ~= characer level)
- case ZAP_SPIT_POISON: // lev + mut * 5
- case ZAP_BREATHE_FIRE: // lev + mut * 4 + 12 (if dragonform)
- case ZAP_BREATHE_FROST: // lev
- case ZAP_BREATHE_ACID: // lev (or invoc * 3 from minor destr)
- case ZAP_BREATHE_POISON: // lev
- case ZAP_BREATHE_POWER: // lev
- case ZAP_BREATHE_STEAM: // lev
- if (power > 50)
- power = 50;
- break;
-
- // enchantments and other resistable effects
- case ZAP_SLOWING:
- case ZAP_HASTING:
- case ZAP_PARALYSIS:
- case ZAP_BACKLIGHT:
- case ZAP_SLEEP:
- case ZAP_CONFUSION:
- case ZAP_INVISIBILITY:
- case ZAP_ENSLAVEMENT:
- case ZAP_TELEPORTATION:
- case ZAP_DIGGING:
- case ZAP_POLYMORPH_OTHER:
- case ZAP_DEGENERATION:
- case ZAP_BANISHMENT:
- // This is the only power that matters. We magnify it apparently
- // to get values that work better with magic resistance checks...
- // those checks will scale down this value and max it out at 120.
- pbolt.ench_power *= 3;
- pbolt.ench_power /= 2;
- break;
-
- // anything else we cap to 100
- default:
- if (power > 100)
- power = 100;
- break;
- }
-
- // Note: I'm only displaying the top damage and such here, that's
- // because it's really not been known before (since the above caps
- // didn't exist), so they were all pretty much unlimited before.
- // Also note, that the high end damage occurs at the cap, only
- // players that are that powerful can get that damage... and
- // although these numbers might seem small, you should remember
- // that Dragons in this game are 60-90 hp monsters, and very
- // few monsters have more than 100 hp (and that 1d5 damage is
- // still capable of taking a good sized chunk (and possibly killing)
- // any monster you're likely to meet in the first three levels). -- bwr
-
- // Note: damage > 100 signals that "random2(damage - 100)" will be
- // applied three times, which not only ups the damage but gives
- // a more normal distribution.
- switch (z_type)
- {
- case ZAP_STRIKING: // cap 25
- strcpy(pbolt.beam_name, "force bolt");
- pbolt.colour = BLACK;
- pbolt.range = 8 + random2(5);
- pbolt.damage = dice_def( 1, 5 ); // dam: 5
- pbolt.hit = 8 + power / 10; // 25: 10
- pbolt.type = SYM_SPACE;
- pbolt.flavour = BEAM_MMISSILE; // unresistable
- pbolt.obvious_effect = true;
- break;
-
- case ZAP_MAGIC_DARTS: // cap 25
- strcpy(pbolt.beam_name, "magic dart");
- pbolt.colour = LIGHTMAGENTA;
- pbolt.range = random2(5) + 8;
- pbolt.damage = dice_def( 1, 3 + power / 5 ); // 25: 1d8
- pbolt.hit = 1500; // hits always
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_MMISSILE; // unresistable
- pbolt.obvious_effect = true;
- break;
-
- case ZAP_STING: // cap 25
- strcpy(pbolt.beam_name, "sting");
- pbolt.colour = GREEN;
- pbolt.range = 8 + random2(5);
- pbolt.damage = dice_def( 1, 3 + power / 5 ); // 25: 1d8
- pbolt.hit = 8 + power / 5; // 25: 13
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_POISON; // extra damage
-
- pbolt.obvious_effect = true;
- break;
-
- case ZAP_ELECTRICITY: // cap 20
- strcpy(pbolt.beam_name, "zap");
- pbolt.colour = LIGHTCYAN;
- pbolt.range = 6 + random2(8); // extended in beam
- pbolt.damage = dice_def( 1, 3 + random2(power) / 2 ); // 25: 1d11
- pbolt.hit = 8 + power / 7; // 25: 11
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_ELECTRICITY; // beams & reflects
-
- pbolt.obvious_effect = true;
- pbolt.is_beam = true;
- break;
-
- case ZAP_DISRUPTION: // cap 25
- strcpy(pbolt.beam_name, "0");
- pbolt.flavour = BEAM_DISINTEGRATION;
- pbolt.range = 7 + random2(8);
- pbolt.damage = dice_def( 1, 4 + power / 5 ); // 25: 1d9
- pbolt.ench_power *= 3;
- break;
-
- case ZAP_PAIN: // cap 25
- strcpy(pbolt.beam_name, "0");
- pbolt.flavour = BEAM_PAIN;
- pbolt.range = 7 + random2(8);
- pbolt.damage = dice_def( 1, 4 + power / 5 ); // 25: 1d9
- pbolt.ench_power *= 7;
- pbolt.ench_power /= 2;
- break;
-
- case ZAP_FLAME_TONGUE: // cap 25
- strcpy(pbolt.beam_name, "flame");
- pbolt.colour = RED;
-
- pbolt.range = 1 + random2(2) + random2(power) / 10;
- if (pbolt.range > 4)
- pbolt.range = 4;
-
- pbolt.damage = dice_def( 1, 8 + power / 4 ); // 25: 1d14
- pbolt.hit = 7 + power / 6; // 25: 11
- pbolt.type = SYM_BOLT;
- pbolt.flavour = BEAM_FIRE;
-
- pbolt.obvious_effect = true;
- break;
-
- case ZAP_SMALL_SANDBLAST: // cap 25
- strcpy(pbolt.beam_name, "blast of ");
-
- temp_rand = random2(4);
-
- strcat(pbolt.beam_name, (temp_rand == 0) ? "dust" :
- (temp_rand == 1) ? "dirt" :
- (temp_rand == 2) ? "grit" : "sand");
-
- pbolt.colour = BROWN;
- pbolt.range = (random2(power) > random2(30)) ? 2 : 1;
- pbolt.damage = dice_def( 1, 8 + power / 4 ); // 25: 1d14
- pbolt.hit = 8 + power / 5; // 25: 13
- pbolt.type = SYM_BOLT;
- pbolt.flavour = BEAM_FRAG; // extra AC resist
-
- pbolt.obvious_effect = true;
- break;
-
- case ZAP_SANDBLAST: // cap 50
- strcpy(pbolt.beam_name, coinflip() ? "blast of rock" : "rocky blast");
- pbolt.colour = BROWN;
-
- pbolt.range = 2 + random2(power) / 20;
- if (pbolt.range > 4)
- pbolt.range = 4;
-
- pbolt.damage = dice_def( 2, 4 + power / 3 ); // 25: 2d12
- pbolt.hit = 13 + power / 10; // 25: 15
- pbolt.type = SYM_BOLT;
- pbolt.flavour = BEAM_FRAG; // extra AC resist
-
- pbolt.obvious_effect = true;
- break;
-
- case ZAP_BONE_SHARDS:
- strcpy(pbolt.beam_name, "spray of bone shards");
- pbolt.colour = LIGHTGREY;
- pbolt.range = 7 + random2(10);
-
- // Incoming power is highly dependant on mass (see spells3.cc).
- // Basic function is power * 15 + mass... with the largest
- // available mass (3000) we get a power of 4500 at a power
- // level of 100 (for 3d20).
- pbolt.damage = dice_def( 3, 2 + (power / 250) );
- pbolt.hit = 8 + (power / 100); // max hit: 53
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_MAGIC; // unresisted
-
- pbolt.obvious_effect = true;
- pbolt.is_beam = true;
- break;
-
- case ZAP_FLAME: // cap 50
- strcpy(pbolt.beam_name, "puff of flame");
- pbolt.colour = RED;
- pbolt.range = 8 + random2(5);
- pbolt.damage = dice_def( 2, 4 + power / 10 ); // 25: 2d6 50: 2d9
- pbolt.hit = 8 + power / 10; // 25: 10 50: 13
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_FIRE;
-
- pbolt.obvious_effect = true;
- break;
-
- case ZAP_FROST: // cap 50
- strcpy(pbolt.beam_name, "puff of frost");
- pbolt.colour = WHITE;
- pbolt.range = 8 + random2(5);
- pbolt.damage = dice_def( 2, 4 + power / 10 ); // 25: 2d6 50: 2d9
- pbolt.hit = 8 + power / 10; // 50: 10 50: 13
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_COLD;
-
- pbolt.obvious_effect = true;
- break;
-
- case ZAP_STONE_ARROW: // cap 100
- strcpy(pbolt.beam_name, "stone arrow");
- pbolt.colour = LIGHTGREY;
- pbolt.range = 8 + random2(5);
- pbolt.damage = dice_def( 2, 4 + power / 8 ); // 25: 2d7 50: 2d10
- pbolt.hit = 5 + power / 10; // 25: 6 50: 7
- pbolt.type = SYM_MISSILE;
- pbolt.flavour = BEAM_MMISSILE; // unresistable
-
- pbolt.obvious_effect = true;
- break;
-
- case ZAP_STICKY_FLAME: // cap 100
- strcpy(pbolt.beam_name, "sticky flame"); // extra damage
- pbolt.colour = RED;
- pbolt.range = 8 + random2(5);
- pbolt.damage = dice_def( 2, 3 + power / 12 ); // 50: 2d7 100: 2d11
- pbolt.hit = 11 + power / 10; // 50: 16 100: 21
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_FIRE;
-
- pbolt.obvious_effect = true;
- break;
-
- case ZAP_MYSTIC_BLAST: // cap 100
- strcpy(pbolt.beam_name, "orb of energy");
- pbolt.colour = LIGHTMAGENTA;
- pbolt.range = 8 + random2(5);
- pbolt.damage = calc_dice( 2, 15 + (power * 2) / 5 );
- pbolt.hit = 10 + power / 7; // 50: 17 100: 24
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_MMISSILE; // unresistable
-
- pbolt.obvious_effect = true;
- break;
-
- case ZAP_ICE_BOLT: // cap 100
- strcpy(pbolt.beam_name, "bolt of ice");
- pbolt.colour = WHITE;
- pbolt.range = 8 + random2(5);
- pbolt.damage = calc_dice( 3, 10 + power / 2 );
- pbolt.hit = 9 + power / 12; // 50: 13 100: 17
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_ICE; // half resistable
- break;
-
- case ZAP_DISPEL_UNDEAD: // cap 100
- strcpy(pbolt.beam_name, "0");
- pbolt.flavour = BEAM_DISPEL_UNDEAD;
- pbolt.range = 7 + random2(8);
- pbolt.damage = calc_dice( 3, 20 + (power * 3) / 4 );
- pbolt.ench_power *= 3;
- pbolt.ench_power /= 2;
- break;
-
- case ZAP_MAGMA: // cap 150
- strcpy(pbolt.beam_name, "bolt of magma");
- pbolt.colour = RED;
- pbolt.range = 5 + random2(4);
- pbolt.damage = calc_dice( 4, 10 + (power * 3) / 5 );
- pbolt.hit = 8 + power / 25; // 50: 10 100: 14
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_LAVA;
-
- pbolt.obvious_effect = true;
- pbolt.is_beam = true;
- break;
-
- case ZAP_FIRE: // cap 150
- strcpy(pbolt.beam_name, "bolt of fire");
- pbolt.colour = RED;
- pbolt.range = 7 + random2(10);
- pbolt.damage = calc_dice( 6, 20 + (power * 3) / 4 );
- pbolt.hit = 10 + power / 25; // 50: 12 100: 14
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_FIRE;
-
- pbolt.obvious_effect = true;
- pbolt.is_beam = true;
- break;
-
- case ZAP_COLD: // cap 150
- strcpy(pbolt.beam_name, "bolt of cold");
- pbolt.colour = WHITE;
- pbolt.range = 7 + random2(10);
- pbolt.damage = calc_dice( 6, 20 + (power * 3) / 4 );
- pbolt.hit = 10 + power / 25; // 50: 12 100: 14
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_COLD;
-
- pbolt.obvious_effect = true;
- pbolt.is_beam = true;
- break;
-
- case ZAP_VENOM_BOLT: // cap 150
- strcpy(pbolt.beam_name, "bolt of poison");
- pbolt.colour = LIGHTGREEN;
- pbolt.range = 8 + random2(10);
- pbolt.damage = calc_dice( 4, 15 + power / 2 );
- pbolt.hit = 8 + power / 20; // 50: 10 100: 13
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_POISON; // extra damage
-
- pbolt.obvious_effect = true;
- pbolt.is_beam = true;
- break;
-
- case ZAP_NEGATIVE_ENERGY: // cap 150
- strcpy(pbolt.beam_name, "bolt of negative energy");
- pbolt.colour = DARKGREY;
- pbolt.range = 7 + random2(10);
- pbolt.damage = calc_dice( 4, 15 + (power * 3) / 5 );
- pbolt.hit = 8 + power / 20; // 50: 10 100: 13
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_NEG; // drains levels
-
- pbolt.obvious_effect = true;
- pbolt.is_beam = true;
- break;
-
- case ZAP_IRON_BOLT: // cap 150
- strcpy(pbolt.beam_name, "iron bolt");
- pbolt.colour = LIGHTCYAN;
- pbolt.range = 5 + random2(5);
- pbolt.damage = calc_dice( 9, 15 + (power * 3) / 4 );
- pbolt.hit = 7 + power / 15; // 50: 10 100: 13
- pbolt.type = SYM_MISSILE;
- pbolt.flavour = BEAM_MMISSILE; // unresistable
- pbolt.obvious_effect = true;
- break;
-
- case ZAP_POISON_ARROW: // cap 150
- strcpy(pbolt.beam_name, "poison arrow");
- pbolt.colour = LIGHTGREEN;
- pbolt.range = 8 + random2(5);
- pbolt.damage = calc_dice( 4, 15 + power );
- pbolt.hit = 5 + power / 10; // 50: 10 100: 15
- pbolt.type = SYM_MISSILE;
- pbolt.flavour = BEAM_POISON_ARROW; // extra damage
- pbolt.obvious_effect = true;
- break;
-
-
- case ZAP_DISINTEGRATION: // cap 150
- strcpy(pbolt.beam_name, "0");
- pbolt.flavour = BEAM_DISINTEGRATION;
- pbolt.range = 7 + random2(8);
- pbolt.damage = calc_dice( 3, 15 + (power * 3) / 4 );
- pbolt.ench_power *= 5;
- pbolt.ench_power /= 2;
- pbolt.is_beam = true;
- break;
-
- case ZAP_LIGHTNING: // cap 150
- // also for breath (at pow = lev * 2; max dam: 33)
- strcpy(pbolt.beam_name, "bolt of lightning");
- pbolt.colour = LIGHTCYAN;
- pbolt.range = 8 + random2(10); // extended in beam
- pbolt.damage = calc_dice( 1, 10 + (power * 3) / 5 );
- pbolt.hit = 7 + random2(power) / 20; // 50: 7-9 100: 7-12
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_ELECTRICITY; // beams & reflects
-
- pbolt.obvious_effect = true;
- pbolt.is_beam = true;
- break;
-
- case ZAP_FIREBALL: // cap 150
- strcpy(pbolt.beam_name, "fireball");
- pbolt.colour = RED;
- pbolt.range = 8 + random2(5);
- pbolt.damage = calc_dice( 3, 10 + power / 2 );
- pbolt.hit = 40; // hit: 40
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_FIRE; // fire
- pbolt.is_explosion = true;
- break;
-
- case ZAP_ORB_OF_ELECTRICITY: // cap 150
- strcpy(pbolt.beam_name, "orb of electricity");
- pbolt.colour = LIGHTBLUE;
- pbolt.range = 9 + random2(12);
- pbolt.damage = calc_dice( 1, 15 + (power * 4) / 5 );
- pbolt.damage.num = 0; // only does explosion damage
- pbolt.hit = 40; // hit: 40
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_ELECTRICITY;
- pbolt.is_explosion = true;
- break;
-
- case ZAP_ORB_OF_FRAGMENTATION: // cap 150
- strcpy(pbolt.beam_name, "metal orb");
- pbolt.colour = CYAN;
- pbolt.range = 9 + random2(7);
- pbolt.damage = calc_dice( 3, 30 + (power * 3) / 4 );
- pbolt.hit = 20; // hit: 20
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_FRAG; // extra AC resist
- pbolt.is_explosion = true;
- break;
-
- case ZAP_CLEANSING_FLAME:
- strcpy(pbolt.beam_name, "golden flame");
- pbolt.colour = YELLOW;
- pbolt.range = 7;
- pbolt.damage = calc_dice( 3, 30 + (power * 3) / 4 );
- pbolt.hit = 150;
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_HOLY;
-
- pbolt.obvious_effect = true;
- pbolt.is_explosion = true;
- break;
-
- case ZAP_CRYSTAL_SPEAR: // cap 200
- strcpy(pbolt.beam_name, "crystal spear");
- pbolt.colour = WHITE;
- pbolt.range = 7 + random2(10);
- pbolt.damage = calc_dice( 12, 30 + (power * 4) / 3 );
- pbolt.hit = 10 + power / 15; // 50: 13 100: 16
- pbolt.type = SYM_MISSILE;
- pbolt.flavour = BEAM_MMISSILE; // unresistable
-
- pbolt.obvious_effect = true;
- break;
-
- case ZAP_HELLFIRE: // cap 200
- strcpy(pbolt.beam_name, "hellfire");
- pbolt.colour = RED;
- pbolt.range = 7 + random2(10);
- pbolt.damage = calc_dice( 3, 10 + (power * 3) / 4 );
- pbolt.hit = 20 + power / 10; // 50: 25 100: 30
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_HELLFIRE;
-
- pbolt.obvious_effect = true;
- pbolt.is_explosion = true;
- break;
-
- case ZAP_ICE_STORM: // cap 200
- strcpy(pbolt.beam_name, "great blast of cold");
- pbolt.colour = BLUE;
- pbolt.range = 9 + random2(5);
- pbolt.damage = calc_dice( 6, 15 + power );
- pbolt.damage.num = 0; // only does explosion damage
- pbolt.hit = 20 + power / 10; // 50: 25 100: 30
- pbolt.ench_power = power; // used for radius
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_ICE; // half resisted
- pbolt.is_explosion = true;
- break;
-
- case ZAP_BEAM_OF_ENERGY: // bolt of innacuracy
- strcpy(pbolt.beam_name, "narrow beam of energy");
- pbolt.colour = YELLOW;
- pbolt.range = 7 + random2(10);
- pbolt.damage = calc_dice( 12, 40 + (power * 3) / 2 );
- pbolt.hit = 2; // hit: 2 (very hard)
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_ENERGY; // unresisted
-
- pbolt.obvious_effect = true;
- pbolt.is_beam = true;
- break;
-
- case ZAP_SPIT_POISON: // cap 50
- // max pow = lev + mut * 5 = 42
- strcpy(pbolt.beam_name, "splash of poison");
- pbolt.colour = GREEN;
-
- pbolt.range = 3 + random2( 1 + power / 2 );
- if (pbolt.range > 9)
- pbolt.range = 9;
-
- pbolt.damage = dice_def( 1, 4 + power / 2 ); // max dam: 25
- pbolt.hit = 5 + random2( 1 + power / 3 ); // max hit: 19
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_POISON;
- pbolt.obvious_effect = true;
- break;
-
- case ZAP_BREATHE_FIRE: // cap 50
- // max pow = lev + mut * 4 + 12 = 51 (capped to 50)
- strcpy(pbolt.beam_name, "fiery breath");
- pbolt.colour = RED;
-
- pbolt.range = 3 + random2( 1 + power / 2 );
- if (pbolt.range > 9)
- pbolt.range = 9;
-
- pbolt.damage = dice_def( 3, 4 + power / 3 ); // max dam: 60
- pbolt.hit = 8 + random2( 1 + power / 3 ); // max hit: 25
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_FIRE;
-
- pbolt.obvious_effect = true;
- pbolt.is_beam = true;
- break;
-
- case ZAP_BREATHE_FROST: // cap 50
- // max power = lev = 27
- strcpy(pbolt.beam_name, "freezing breath");
- pbolt.colour = WHITE;
-
- pbolt.range = 3 + random2( 1 + power / 2 );
- if (pbolt.range > 9)
- pbolt.range = 9;
-
- pbolt.damage = dice_def( 3, 4 + power / 3 ); // max dam: 39
- pbolt.hit = 8 + random2( 1 + power / 3 );
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_COLD;
-
- pbolt.obvious_effect = true;
- pbolt.is_beam = true;
- break;
-
- case ZAP_BREATHE_ACID: // cap 50
- // max power = lev for ability, 50 for minor destruction (max dam: 57)
- strcpy(pbolt.beam_name, "acid");
- pbolt.colour = YELLOW;
-
- pbolt.range = 3 + random2( 1 + power / 2 );
- if (pbolt.range > 9)
- pbolt.range = 9;
-
- pbolt.damage = dice_def( 3, 3 + power / 3 ); // max dam: 36
- pbolt.hit = 5 + random2( 1 + power / 3 );
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_ACID;
-
- pbolt.obvious_effect = true;
- pbolt.is_beam = true;
- break;
-
- case ZAP_BREATHE_POISON: // leaves clouds of gas // cap 50
- // max power = lev = 27
- strcpy(pbolt.beam_name, "poison gas");
- pbolt.colour = GREEN;
-
- pbolt.range = 3 + random2( 1 + power / 2 );
- if (pbolt.range > 9)
- pbolt.range = 9;
-
- pbolt.damage = dice_def( 3, 2 + power / 6 ); // max dam: 18
- pbolt.hit = 6 + random2( 1 + power / 3 );
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_POISON;
-
- pbolt.obvious_effect = true;
- pbolt.is_beam = true;
- break;
-
- case ZAP_BREATHE_POWER: // cap 50
- strcpy(pbolt.beam_name, "bolt of energy");
- // max power = lev = 27
-
- pbolt.colour = BLUE;
- if (random2(power) >= 8)
- pbolt.colour = LIGHTBLUE;
- if (random2(power) >= 12)
- pbolt.colour = MAGENTA;
- if (random2(power) >= 17)
- pbolt.colour = LIGHTMAGENTA;
-
- pbolt.range = 6 + random2( 1 + power / 2 );
- if (pbolt.range > 9)
- pbolt.range = 9;
-
- pbolt.damage = dice_def( 3, 3 + power / 3 ); // max dam: 36
- pbolt.hit = 5 + random2( 1 + power / 3 );
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_MMISSILE; // unresistable
-
- pbolt.obvious_effect = true;
- pbolt.is_beam = true;
- break;
-
- case ZAP_BREATHE_STEAM: // cap 50
- // max power = lev = 27
- strcpy(pbolt.beam_name, "ball of steam");
- pbolt.colour = LIGHTGREY;
-
- pbolt.range = 6 + random2(5);
- if (pbolt.range > 9)
- pbolt.range = 9;
-
- pbolt.damage = dice_def( 3, 4 + power / 5 ); // max dam: 27
- pbolt.hit = 10 + random2( 1 + power / 5 );
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_FIRE;
-
- pbolt.obvious_effect = true;
- pbolt.is_beam = true;
- break;
-
- case ZAP_SLOWING:
- strcpy(pbolt.beam_name, "0");
- pbolt.flavour = BEAM_SLOW;
- // pbolt.is_beam = true;
- break;
-
- case ZAP_HASTING:
- strcpy(pbolt.beam_name, "0");
- pbolt.flavour = BEAM_HASTE;
- // pbolt.is_beam = true;
- break;
-
- case ZAP_PARALYSIS:
- strcpy(pbolt.beam_name, "0");
- pbolt.flavour = BEAM_PARALYSIS;
- // pbolt.is_beam = true;
- break;
-
- case ZAP_CONFUSION:
- strcpy(pbolt.beam_name, "0");
- pbolt.flavour = BEAM_CONFUSION;
- // pbolt.is_beam = true;
- break;
-
- case ZAP_INVISIBILITY:
- strcpy(pbolt.beam_name, "0");
- pbolt.flavour = BEAM_INVISIBILITY;
- // pbolt.is_beam = true;
- break;
-
- case ZAP_HEALING:
- strcpy(pbolt.beam_name, "0");
- pbolt.flavour = BEAM_HEALING;
- pbolt.damage = dice_def( 1, 7 + power / 3 );
- // pbolt.is_beam = true;
- break;
-
- case ZAP_DIGGING:
- strcpy(pbolt.beam_name, "0");
- pbolt.flavour = BEAM_DIGGING;
- // not ordinary "0" beam range {dlb}
- pbolt.range = 3 + random2( power / 5 ) + random2(5);
- pbolt.is_beam = true;
- break;
-
- case ZAP_TELEPORTATION:
- strcpy(pbolt.beam_name, "0");
- pbolt.flavour = BEAM_TELEPORT;
- pbolt.range = 9 + random2(5);
- // pbolt.is_beam = true;
- break;
-
- case ZAP_POLYMORPH_OTHER:
- strcpy(pbolt.beam_name, "0");
- pbolt.flavour = BEAM_POLYMORPH;
- pbolt.range = 9 + random2(5);
- // pbolt.is_beam = true;
- break;
-
- case ZAP_ENSLAVEMENT:
- strcpy(pbolt.beam_name, "0");
- pbolt.flavour = BEAM_CHARM;
- pbolt.range = 7 + random2(5);
- // pbolt.is_beam = true;
- break;
-
- case ZAP_BANISHMENT:
- strcpy(pbolt.beam_name, "0");
- pbolt.flavour = BEAM_BANISH;
- pbolt.range = 7 + random2(5);
- // pbolt.is_beam = true;
- break;
-
- case ZAP_DEGENERATION:
- strcpy(pbolt.beam_name, "0");
- pbolt.flavour = BEAM_DEGENERATE;
- pbolt.range = 7 + random2(5);
- // pbolt.is_beam = true;
- break;
-
- case ZAP_ENSLAVE_UNDEAD:
- strcpy(pbolt.beam_name, "0");
- pbolt.flavour = BEAM_ENSLAVE_UNDEAD;
- pbolt.range = 7 + random2(5);
- // pbolt.is_beam = true;
- break;
-
- case ZAP_AGONY:
- strcpy(pbolt.beam_name, "0agony");
- pbolt.flavour = BEAM_PAIN;
- pbolt.range = 7 + random2(8);
- pbolt.ench_power *= 5;
- // pbolt.is_beam = true;
- break;
-
- case ZAP_CONTROL_DEMON:
- strcpy(pbolt.beam_name, "0");
- pbolt.flavour = BEAM_ENSLAVE_DEMON;
- pbolt.range = 7 + random2(5);
- pbolt.ench_power *= 3;
- pbolt.ench_power /= 2;
- // pbolt.is_beam = true;
- break;
-
- case ZAP_SLEEP: //jmf: added
- strcpy(pbolt.beam_name, "0");
- pbolt.flavour = BEAM_SLEEP;
- pbolt.range = 7 + random2(5);
- // pbolt.is_beam = true;
- break;
-
- case ZAP_BACKLIGHT: //jmf: added
- strcpy(pbolt.beam_name, "0");
- pbolt.flavour = BEAM_BACKLIGHT;
- pbolt.colour = BLUE;
- pbolt.range = 7 + random2(5);
- // pbolt.is_beam = true;
- break;
-
- case ZAP_DEBUGGING_RAY:
- strcpy( pbolt.beam_name, "debugging ray" );
- pbolt.colour = random_colour();
- pbolt.range = 7 + random2(10);
- pbolt.damage = dice_def( 1500, 1 ); // dam: 1500
- pbolt.hit = 1500; // hit: 1500
- pbolt.type = SYM_DEBUG;
- pbolt.flavour = BEAM_MMISSILE; // unresistable
-
- pbolt.obvious_effect = true;
- break;
-
- default:
- strcpy(pbolt.beam_name, "buggy beam");
- pbolt.colour = random_colour();
- pbolt.range = 7 + random2(10);
- pbolt.damage = dice_def( 1, 0 );
- pbolt.hit = 60;
- pbolt.type = SYM_DEBUG;
- pbolt.flavour = BEAM_MMISSILE; // unresistable
-
- pbolt.obvious_effect = true;
- break;
- } // end of switch
-} // end zappy()
-
-/* NEW (GDL):
- * Now handles all beamed/thrown items and spells, tracers, and their effects.
- * item is used for items actually thrown/launched
- *
- * if item is NULL, there is no physical object being thrown that could
- * land on the ground.
- */
-
-
-/*
- * Beam pseudo code:
- *
- * 1. Calculate stepx and stepy - algorithms depend on finding a step axis
- * which results in a line of rise 1 or less (ie 45 degrees or less)
- * 2. Calculate range. Tracers always have max range, otherwise the beam
- * will have somewhere between range and rangeMax
- * 3. Loop tracing out the line:
- * 3a. Check for walls and wall affecting beams
- * 3b. If no valid move is found, try a fuzzy move
- * 3c. If no valid move is yet found, try bouncing
- * 3d. If no valid move or bounce is found, break
- * 4. Check for beam termination on target
- * 5. Affect the cell which the beam just moved into -> affect()
- * 6. Decrease remaining range appropriately
- * 7. Check for early out due to aimed_at_feet
- * 8. Draw the beam
- * 9. Drop an object where the beam 'landed'
- *10. Beams explode where the beam 'landed'
- *11. If no message generated yet, send "nothing happens" (enchantments only)
- *
- */
-
-
-void fire_beam( struct bolt &pbolt, item_def *item )
-{
- int dx, dy; // total delta between source & target
- int lx, ly; // last affected x,y
- int stepx, stepy; // x,y increment - FP
- int wx, wy; // 'working' x,y - FP
- bool beamTerminate; // has beam been 'stopped' by something?
- int nx, ny; // test(new) x,y - FP
- int tx, ty; // test(new) x,y - integer
- bool roundX, roundY; // which to round?
- int rangeRemaining;
- bool fuzzyOK; // fuzzification resulted in OK move
- bool sideBlocked, topBlocked, random_beam;
-
-#if DEBUG_DIAGNOSTICS
- if (pbolt.flavour != BEAM_LINE_OF_SIGHT)
- {
- mprf( MSGCH_DIAGNOSTICS,
- "%s%s%s (%d,%d) to (%d,%d): ty=%d col=%d flav=%d hit=%d dam=%dd%d",
- (pbolt.is_beam) ? "beam" : "missile",
- (pbolt.is_explosion) ? "*" :
- (pbolt.is_big_cloud) ? "+" : "",
- (pbolt.is_tracer) ? " tracer" : "",
- pbolt.source_x, pbolt.source_y,
- pbolt.target_x, pbolt.target_y,
- pbolt.type, pbolt.colour, pbolt.flavour,
- pbolt.hit, pbolt.damage.num, pbolt.damage.size );
- }
-#endif
-
- // init
- pbolt.aimed_at_feet = false;
- pbolt.msg_generated = false;
- roundY = false;
- roundX = false;
-
- // first, calculate beam step
- dx = pbolt.target_x - pbolt.source_x;
- dy = pbolt.target_y - pbolt.source_y;
-
- // check for aim at feet
- if (dx == 0 && dy == 0)
- {
- pbolt.aimed_at_feet = true;
- stepx = 0;
- stepy = 0;
- tx = pbolt.source_x;
- ty = pbolt.source_y;
- }
- else
- {
- if (abs(dx) >= abs(dy))
- {
- stepx = (dx > 0) ? 100 : -100;
- stepy = 100 * dy / (abs(dx));
- roundY = true;
- }
- else
- {
- stepy = (dy > 0) ? 100 : -100;
- stepx = 100 * dx / (abs(dy));
- roundX = true;
- }
- }
-
- // give chance for beam to affect one cell even if aimed_at_feet.
- beamTerminate = false;
- // setup working coords
- lx = pbolt.source_x;
- wx = 100 * lx;
- ly = pbolt.source_y;
- wy = 100 * ly;
- // setup range
- rangeRemaining = pbolt.range;
- if (pbolt.rangeMax > pbolt.range)
- {
- if (pbolt.is_tracer)
- rangeRemaining = pbolt.rangeMax;
- else
- rangeRemaining += random2((pbolt.rangeMax - pbolt.range) + 1);
- }
-
- // before we start drawing the beam, turn buffering off
-#ifdef WIN32CONSOLE
- bool oldValue = true;
- if (!pbolt.is_tracer)
- oldValue = setBuffering(false);
-#endif
-
- // cannot use source_x, source_y, target_x, target_y during
- // step algorithm due to bouncing.
-
- // now, one step at a time, try to move towards target.
- while(!beamTerminate)
- {
- nx = wx + stepx;
- ny = wy + stepy;
-
- if (roundY)
- {
- tx = nx / 100;
- ty = (ny + 50) / 100;
- }
- if (roundX)
- {
- ty = ny / 100;
- tx = (nx + 50) / 100;
- }
-
- // check that tx, ty are valid. If not, set to last
- // x,y and break.
- if (tx < 0 || tx >= GXM || ty < 0 || ty >= GYM)
- {
- tx = lx;
- ty = ly;
- break;
- }
-
- // see if tx, ty is blocked by something
- if (grd[tx][ty] < MINMOVE)
- {
- // first, check to see if this beam affects walls.
- if (affectsWalls(pbolt))
- {
- // should we ever get a tracer with a wall-affecting
- // beam (possible I suppose), we'll quit tracing now.
- if (!pbolt.is_tracer)
- rangeRemaining -= affect(pbolt, tx, ty);
-
- // if it's still a wall, quit.
- if (grd[tx][ty] < MINMOVE)
- {
- break; // breaks from line tracing
- }
- }
- else
- {
- // BEGIN fuzzy line algorithm
- fuzzyOK = fuzzyLine(nx,ny,tx,ty,lx,ly,stepx,stepy,roundX,roundY);
- if (!fuzzyOK)
- {
- // BEGIN bounce case
- if (!isBouncy(pbolt))
- {
- tx = lx;
- ty = ly;
- break; // breaks from line tracing
- }
-
- sideBlocked = false;
- topBlocked = false;
- // BOUNCE -- guaranteed to return reasonable tx, ty.
- // if it doesn't, we'll quit in the next if stmt anyway.
- if (roundY)
- {
- if ( grd[lx + stepx / 100][ly] < MINMOVE)
- sideBlocked = true;
-
- if (dy != 0)
- {
- if ( grd[lx][ly + (stepy>0?1:-1)] < MINMOVE)
- topBlocked = true;
- }
-
- rangeRemaining -= bounce(stepx, stepy, wx, wy, nx, ny,
- lx, ly, tx, ty, topBlocked, sideBlocked);
- }
- else
- {
- if ( grd[lx][ly + stepy / 100] < MINMOVE)
- sideBlocked = true;
-
- if (dx != 0)
- {
- if ( grd[lx + (stepx>0?1:-1)][ly] < MINMOVE)
- topBlocked = true;
- }
-
- rangeRemaining -= bounce(stepy, stepx, wy, wx, ny, nx,
- ly, lx, ty, tx, topBlocked, sideBlocked);
- }
- // END bounce case - range check
- if (rangeRemaining < 1)
- {
- tx = lx;
- ty = ly;
- break;
- }
- }
- } // end else - beam doesn't affect walls
- } // endif - is tx, ty wall?
-
- // at this point, if grd[tx][ty] is still a wall, we
- // couldn't find any path: bouncy, fuzzy, or not - so break.
- if (grd[tx][ty] < MINMOVE)
- {
- tx = lx;
- ty = ly;
- break;
- }
-
- // check for "target termination"
- // occurs when beam can be targetted at empty
- // cell (e.g. a mage wants an explosion to happen
- // between two monsters)
-
- // in this case, don't affect the cell - players
- // /monsters have no chance to dodge or block such
- // a beam, and we want to avoid silly messages.
- if (tx == pbolt.target_x && ty == pbolt.target_y)
- beamTerminate = beam_term_on_target(pbolt);
-
- // affect the cell, except in the special case noted
- // above -- affect() will early out if something gets
- // hit and the beam is type 'term on target'.
- if (!beamTerminate || !pbolt.is_explosion)
- {
- // random beams: randomize before affect
- random_beam = false;
- if (pbolt.flavour == BEAM_RANDOM)
- {
- random_beam = true;
- pbolt.flavour = BEAM_FIRE + random2(7);
- }
-
- rangeRemaining -= affect(pbolt, tx, ty);
-
- if (random_beam)
- pbolt.flavour = BEAM_RANDOM;
- }
-
- // always decrease range by 1
- rangeRemaining -= 1;
-
- // check for range termination
- if (rangeRemaining <= 0)
- beamTerminate = true;
-
- // special case - beam was aimed at feet
- if (pbolt.aimed_at_feet)
- beamTerminate = true;
-
- // actually draw the beam/missile/whatever,
- // if the player can see the cell.
- if (!pbolt.is_tracer && pbolt.beam_name[0] != '0' && see_grid(tx,ty))
- {
- // we don't clean up the old position.
- // first, most people like seeing the full path,
- // and second, it is hard to do it right with
- // respect to killed monsters, cloud trails, etc.
-
- // draw new position
- int drawx = tx - you.x_pos + 18;
- int drawy = ty - you.y_pos + 9;
- // bounds check
- if (drawx > 8 && drawx < 26 && drawy > 0 && drawy < 18)
- {
- if (pbolt.colour == BLACK)
- textcolor(random_colour());
- else
- textcolor(pbolt.colour);
-
- gotoxy(drawx, drawy);
- putch(pbolt.type);
-
-#ifdef UNIX
- // get curses to update the screen so we can see the beam
- update_screen();
-#endif
-
- delay(15);
-
-#ifdef MISSILE_TRAILS_OFF
- if (!pbolt.is_beam || pbolt.beam_name[0] == '0')
- viewwindow(1,false); // mv: added. It's not optimal but
- // is usually enough
-#endif
- }
-
- }
-
- // set some stuff up for the next iteration
- lx = tx;
- ly = ty;
-
- wx = nx;
- wy = ny;
-
- } // end- while !beamTerminate
-
- // the beam has finished, and terminated at tx, ty
-
- // leave an object, if applicable
- if (item)
- beam_drop_object( pbolt, item, tx, ty );
-
- // check for explosion. NOTE that for tracers, we have to make a copy
- // of target co'ords and then reset after calling this -- tracers should
- // never change any non-tracers fields in the beam structure. -- GDL
- int ox = pbolt.target_x;
- int oy = pbolt.target_y;
-
- beam_explodes(pbolt, tx, ty);
-
- if (pbolt.is_tracer)
- {
- pbolt.target_x = ox;
- pbolt.target_y = oy;
- }
-
- // canned msg for enchantments that affected no-one
- if (pbolt.beam_name[0] == '0' && pbolt.flavour != BEAM_DIGGING)
- {
- if (!pbolt.is_tracer && !pbolt.msg_generated && !pbolt.obvious_effect)
- canned_msg(MSG_NOTHING_HAPPENS);
- }
-
- // that's it!
-#ifdef WIN32CONSOLE
- if (!pbolt.is_tracer)
- setBuffering(oldValue);
-#endif
-} // end fire_beam();
-
-
-// returns damage taken by a monster from a "flavoured" (fire, ice, etc.)
-// attack -- damage from clouds and branded weapons handled elsewhere.
-int mons_adjust_flavoured( struct monsters *monster, struct bolt &pbolt,
- int hurted, bool doFlavouredEffects )
-{
- // if we're not doing flavored effects, must be preliminary
- // damage check only; do not print messages or apply any side
- // effects!
- int resist;
-
- switch (pbolt.flavour)
- {
- case BEAM_FIRE:
- resist = mons_res_fire(monster);
- if (resist > 1)
- {
- if (doFlavouredEffects)
- simple_monster_message(monster, " appears unharmed.");
-
- hurted = 0;
- }
- else if (resist == 1)
- {
- if (doFlavouredEffects)
- simple_monster_message(monster, " resists.");
-
- hurted /= 3;
- }
- else if (resist < 0)
- {
- if (monster->type == MONS_ICE_BEAST
- || monster->type == MONS_SIMULACRUM_SMALL
- || monster->type == MONS_SIMULACRUM_LARGE)
- {
- if (doFlavouredEffects)
- simple_monster_message(monster, " melts!");
- }
- else
- {
- if (doFlavouredEffects)
- simple_monster_message(monster, " is burned terribly!");
- }
-
- hurted *= 15;
- hurted /= 10;
- }
- break;
-
-
- case BEAM_COLD:
- resist = mons_res_cold(monster);
- if (resist > 1)
- {
- if (doFlavouredEffects)
- simple_monster_message(monster, " appears unharmed.");
-
- hurted = 0;
- }
- else if (resist == 1)
- {
- if (doFlavouredEffects)
- simple_monster_message(monster, " resists.");
-
- hurted /= 3;
- }
- else if (resist < 0)
- {
- if (doFlavouredEffects)
- simple_monster_message(monster, " is frozen!");
-
- hurted *= 15;
- hurted /= 10;
- }
- break;
-
- case BEAM_ELECTRICITY:
- if (mons_res_elec(monster) > 0)
- {
- if (doFlavouredEffects)
- simple_monster_message(monster, " appears unharmed.");
-
- hurted = 0;
- }
- break;
-
-
- case BEAM_POISON:
- if (mons_res_poison(monster) > 0)
- {
- if (doFlavouredEffects)
- simple_monster_message( monster, " appears unharmed." );
-
- hurted = 0;
- }
- else if (doFlavouredEffects && !one_chance_in(3))
- {
- poison_monster( monster, YOU_KILL(pbolt.thrower) );
- }
- break;
-
- case BEAM_POISON_ARROW:
- if (mons_res_poison(monster) > 0)
- {
- if (doFlavouredEffects)
- {
- simple_monster_message( monster, " partially resists." );
-
- // Poison arrow can poison any living thing regardless of
- // poison resistance. -- bwr
- if (mons_has_lifeforce(monster))
- poison_monster( monster, YOU_KILL(pbolt.thrower), 2, true );
- }
-
- hurted /= 2;
- }
- else if (doFlavouredEffects)
- {
- poison_monster( monster, YOU_KILL(pbolt.thrower), 4 );
- }
- break;
-
- case BEAM_NEG:
- if (mons_res_negative_energy(monster) > 0)
- {
- if (doFlavouredEffects)
- simple_monster_message(monster, " appears unharmed.");
-
- hurted = 0;
- }
- else
- {
- // early out for tracer/no side effects
- if (!doFlavouredEffects)
- return (hurted);
-
- simple_monster_message(monster, " is drained.");
-
- if (one_chance_in(5))
- monster->hit_dice--;
-
- monster->max_hit_points -= 2 + random2(3);
- monster->hit_points -= 2 + random2(3);
-
- if (monster->hit_points >= monster->max_hit_points)
- monster->hit_points = monster->max_hit_points;
-
- if (monster->hit_dice < 1)
- monster->hit_points = 0;
- } // end else
- break;
-
- case BEAM_MIASMA:
- if (mons_res_negative_energy( monster ) >= 3)
- {
- if (doFlavouredEffects)
- simple_monster_message(monster, " appears unharmed.");
-
- hurted = 0;
- }
- else
- {
- // early out for tracer/no side effects
- if (!doFlavouredEffects)
- return (hurted);
-
- if (mons_res_poison( monster ) <= 0)
- poison_monster( monster, YOU_KILL(pbolt.thrower) );
-
- if (one_chance_in( 3 + 2 * mons_res_negative_energy(monster) ))
- {
- struct bolt beam;
- beam.flavour = BEAM_SLOW;
- mons_ench_f2( monster, beam );
- }
- }
- break;
-
- case BEAM_HOLY: // flame of cleansing
- if (mons_is_unholy( monster ))
- {
- if (doFlavouredEffects)
- simple_monster_message( monster, " writhes in agony!" );
-
- hurted = (hurted * 3) / 2;
- }
- else if (!mons_is_evil( monster ))
- {
- if (doFlavouredEffects)
- simple_monster_message( monster, " appears unharmed." );
-
- hurted = 0;
- }
- break;
-
- case BEAM_ICE:
- /* ice - about 50% of damage is cold, other 50% is impact and
- can't be resisted (except by AC, of course) */
- resist = mons_res_cold(monster);
- if (resist > 0)
- {
- if (doFlavouredEffects)
- simple_monster_message(monster, " partially resists.");
-
- hurted /= 2;
- }
- else if (resist < 0)
- {
- if (doFlavouredEffects)
- simple_monster_message(monster, " is frozen!");
-
- hurted *= 13;
- hurted /= 10;
- }
- break;
- } /* end of switch */
-
- if (pbolt.flavour == BEAM_LAVA) //jmf: lava != hellfire
- {
- resist = mons_res_fire(monster);
- if (resist > 0)
- {
- if (doFlavouredEffects)
- simple_monster_message(monster, " partially resists.");
-
- hurted /= 2;
- }
- else if (resist < 0)
- {
- if (monster->type == MONS_ICE_BEAST
- || monster->type == MONS_SIMULACRUM_SMALL
- || monster->type == MONS_SIMULACRUM_LARGE)
- {
- if (doFlavouredEffects)
- simple_monster_message(monster, " melts!");
- }
- else
- {
- if (doFlavouredEffects)
- simple_monster_message(monster, " is burned terribly!");
- }
-
- hurted *= 12;
- hurted /= 10;
- }
- }
- else if (stricmp(pbolt.beam_name, "hellfire") == 0)
- {
- resist = mons_res_fire(monster);
- if (resist > 2)
- {
- if (doFlavouredEffects)
- simple_monster_message(monster, " appears unharmed.");
-
- hurted = 0;
- }
- else if (resist > 0)
- {
- if (doFlavouredEffects)
- simple_monster_message(monster, " partially resists.");
-
- hurted /= 2;
- }
- else if (resist < 0)
- {
- if (monster->type == MONS_ICE_BEAST
- || monster->type == MONS_SIMULACRUM_SMALL
- || monster->type == MONS_SIMULACRUM_LARGE)
- {
- if (doFlavouredEffects)
- simple_monster_message(monster, " melts!");
- }
- else
- {
- if (doFlavouredEffects)
- simple_monster_message(monster, " is burned terribly!");
- }
-
- hurted *= 12; /* hellfire */
- hurted /= 10;
- }
- }
-
- return (hurted);
-} // end mons_adjust_flavoured()
-
-
-// Enchants all monsters in player's sight.
-bool mass_enchantment( int wh_enchant, int pow, int origin )
-{
- int i; // loop variable {dlb}
- bool msg_generated = false;
- struct monsters *monster;
-
- viewwindow(0, false);
-
- if (pow > 200)
- pow = 200;
-
- for (i = 0; i < MAX_MONSTERS; i++)
- {
- monster = &menv[i];
-
- if (monster->type == -1 || !mons_near(monster))
- continue;
-
- // assuming that the only mass charm is control undead:
- if (wh_enchant == ENCH_CHARM)
- {
- if (mons_friendly(monster))
- continue;
-
- if (mons_class_holiness(monster->type) != MH_UNDEAD)
- continue;
-
- if (check_mons_resist_magic( monster, pow ))
- {
- simple_monster_message(monster, " resists.");
- continue;
- }
- }
- else if (mons_holiness(monster) == MH_NATURAL)
- {
- if (check_mons_resist_magic( monster, pow ))
- {
- simple_monster_message(monster, " resists.");
- continue;
- }
- }
- else // trying to enchant an unnatural creature doesn't work
- {
- simple_monster_message(monster, " is unaffected.");
- continue;
- }
-
- if (mons_has_ench(monster, wh_enchant))
- continue;
-
- if (mons_add_ench(monster, wh_enchant))
- {
- if (player_monster_visible( monster ))
- {
- // turn message on
- msg_generated = true;
- switch (wh_enchant)
- {
- case ENCH_FEAR:
- simple_monster_message(monster,
- " looks frightened!");
- break;
- case ENCH_CONFUSION:
- simple_monster_message(monster,
- " looks rather confused.");
- break;
- case ENCH_CHARM:
- simple_monster_message(monster,
- " submits to your will.");
- break;
- default:
- // oops, I guess not!
- msg_generated = false;
- }
- }
-
- // extra check for fear (monster needs to reevaluate behaviour)
- if (wh_enchant == ENCH_FEAR)
- behaviour_event( monster, ME_SCARE, origin );
- }
- } // end "for i"
-
- if (!msg_generated)
- canned_msg(MSG_NOTHING_HAPPENS);
-
- return (msg_generated);
-} // end mass_enchantmenet()
-
-/*
- Monster has probably failed save, now it gets enchanted somehow.
-
- returns MON_RESIST if monster is unaffected due to magic resist.
- returns MON_UNAFFECTED if monster is immune to enchantment
- returns MON_AFFECTED in all other cases (already enchanted, etc)
- */
-int mons_ench_f2(struct monsters *monster, struct bolt &pbolt)
-{
- bool is_near = mons_near(monster); // single caluclation permissible {dlb}
- char buff[ ITEMNAME_SIZE ];
-
- switch (pbolt.flavour) /* put in magic resistance */
- {
- case BEAM_SLOW: /* 0 = slow monster */
- // try to remove haste, if monster is hasted
- if (mons_del_ench(monster, ENCH_HASTE))
- {
- if (simple_monster_message(monster, " is no longer moving quickly."))
- pbolt.obvious_effect = true;
-
- return (MON_AFFECTED);
- }
-
- // not hasted, slow it
- if (mons_add_ench(monster, ENCH_SLOW))
- {
- // put in an exception for fungi, plants and other things you won't
- // notice slow down.
- if (simple_monster_message(monster, " seems to slow down."))
- pbolt.obvious_effect = true;
- }
- return (MON_AFFECTED);
-
- case BEAM_HASTE: // 1 = haste
- if (mons_del_ench(monster, ENCH_SLOW))
- {
- if (simple_monster_message(monster, " is no longer moving slowly."))
- pbolt.obvious_effect = true;
-
- return (MON_AFFECTED);
- }
-
- // not slowed, haste it
- if (mons_add_ench(monster, ENCH_HASTE))
- {
- // put in an exception for fungi, plants and other things you won't
- // notice speed up.
- if (simple_monster_message(monster, " seems to speed up."))
- pbolt.obvious_effect = true;
- }
- return (MON_AFFECTED);
-
- case BEAM_HEALING: /* 2 = healing */
- if (heal_monster( monster, 5 + roll_dice( pbolt.damage ), false ))
- {
- if (monster->hit_points == monster->max_hit_points)
- {
- if (simple_monster_message(monster,
- "'s wounds heal themselves!"))
- pbolt.obvious_effect = true;
- }
- else
- {
- if (simple_monster_message(monster, " is healed somewhat."))
- pbolt.obvious_effect = true;
- }
- }
- return (MON_AFFECTED);
-
- case BEAM_PARALYSIS: /* 3 = paralysis */
- monster->speed_increment = 0;
-
- if (simple_monster_message(monster, " suddenly stops moving!"))
- pbolt.obvious_effect = true;
-
- if (grd[monster->x][monster->y] == DNGN_LAVA
- || grid_is_water(grd[monster->x][monster->y]))
- {
- if (mons_flies(monster) == 1)
- {
- // don't worry about invisibility - you should be able to
- // see if something has fallen into the lava
- if (is_near)
- {
- strcpy(info, ptr_monam(monster, DESC_CAP_THE));
- strcat(info, " falls into the ");
- strcat(info, (grd[monster->x][monster->y] == DNGN_LAVA)
- ? "lava" : "water");
- strcat(info, "!");
- mpr(info);
- }
-
- switch (pbolt.thrower)
- {
- case KILL_YOU:
- case KILL_YOU_MISSILE:
- monster_die(monster, KILL_YOU, pbolt.beam_source);
- break; /* " " */
-
- case KILL_MON:
- case KILL_MON_MISSILE:
- monster_die(monster, KILL_MON_MISSILE, pbolt.beam_source);
- break; /* dragon breath &c */
- }
- }
- }
- return (MON_AFFECTED);
-
- case BEAM_CONFUSION: /* 4 = confusion */
- if (mons_add_ench(monster, ENCH_CONFUSION))
- {
- // put in an exception for fungi, plants and other things you won't
- // notice becoming confused.
- if (simple_monster_message(monster, " appears confused."))
- pbolt.obvious_effect = true;
- }
- return (MON_AFFECTED);
-
- case BEAM_INVISIBILITY: /* 5 = invisibility */
- // Store the monster name before it becomes an "it" -- bwr
- strncpy( buff, ptr_monam( monster, DESC_CAP_THE ), sizeof(buff) );
-
- if (mons_add_ench(monster, ENCH_INVIS))
- {
- // Can't use simple_monster_message here, since it checks
- // for visibility of the monster (and its now invisible) -- bwr
- if (mons_near( monster ))
- {
- snprintf( info, INFO_SIZE, "%s flickers %s",
- buff, player_see_invis() ? "for a moment."
- : "and vanishes!" );
- mpr( info );
- }
-
- pbolt.obvious_effect = true;
- }
- return (MON_AFFECTED);
-
- case BEAM_CHARM: /* 9 = charm */
- if (mons_add_ench(monster, ENCH_CHARM))
- {
- // put in an exception for fungi, plants and other things you won't
- // notice becoming charmed.
- if (simple_monster_message(monster, " is charmed."))
- pbolt.obvious_effect = true;
- }
- return (MON_AFFECTED);
-
- default:
- break;
- } /* end of switch (beam_colour) */
-
- return (MON_AFFECTED);
-} // end mons_ench_f2()
-
-// degree is ignored.
-static void slow_monster(monsters *mon, int degree)
-{
- bolt beam;
- beam.flavour = BEAM_SLOW;
- mons_ench_f2(mon, beam);
-}
-
-// Returns true if the curare killed the monster.
-bool curare_hits_monster( const bolt &beam,
- monsters *monster,
- bool fromPlayer,
- int levels )
-{
- const bool res_poison = mons_res_poison(monster);
- bool mondied = false;
-
- poison_monster(monster, fromPlayer, levels, false);
-
- if (!mons_res_asphyx(monster))
- {
- int hurted = roll_dice(2, 6);
-
- // Note that the hurtage is halved by poison resistance.
- if (res_poison)
- hurted /= 2;
-
- if (hurted)
- {
- simple_monster_message(monster, " appears to choke.");
- if ((monster->hit_points -= hurted) < 1)
- {
- const int thrower = YOU_KILL(beam.thrower) ?
- KILL_YOU_MISSILE : KILL_MON_MISSILE;
- monster_die(monster, thrower, beam.beam_source);
- mondied = true;
- }
- }
-
- if (!mondied)
- slow_monster(monster, levels);
- }
-
- // Deities take notice.
- if (fromPlayer)
- did_god_conduct( DID_POISON, 5 + random2(3) );
-
- return (mondied);
-}
-
-// actually poisons a monster (w/ message)
-void poison_monster( struct monsters *monster, bool fromPlayer, int levels,
- bool force )
-{
- bool yourPoison = false;
- int ench = ENCH_NONE;
- int old_strength = 0;
-
- if (monster->type == -1)
- return;
-
- if (!force && mons_res_poison(monster) > 0)
- return;
-
- // who gets the credit if monster dies of poison?
- ench = mons_has_ench( monster, ENCH_POISON_I, ENCH_POISON_IV );
- if (ench != ENCH_NONE)
- {
- old_strength = ench - ENCH_POISON_I;
- }
- else
- {
- ench = mons_has_ench(monster, ENCH_YOUR_POISON_I, ENCH_YOUR_POISON_IV);
- if (ench != ENCH_NONE)
- {
- old_strength = ench - ENCH_YOUR_POISON_I;
- yourPoison = true;
- }
- }
-
- // delete old poison
- mons_del_ench( monster, ENCH_POISON_I, ENCH_POISON_IV, true );
- mons_del_ench( monster, ENCH_YOUR_POISON_I, ENCH_YOUR_POISON_IV, true );
-
- // Calculate new strength:
- int new_strength = old_strength + levels;
- if (new_strength > 3)
- new_strength = 3;
-
- // now, if player poisons the monster at ANY TIME, they should
- // get credit for the kill if the monster dies from poison. This
- // really isn't that abusable -- GDL.
- if (fromPlayer || yourPoison)
- ench = ENCH_YOUR_POISON_I + new_strength;
- else
- ench = ENCH_POISON_I + new_strength;
-
- // actually do the poisoning
- // note: order important here
- if (mons_add_ench( monster, ench ) && new_strength > old_strength)
- {
- simple_monster_message( monster,
- (old_strength == 0) ? " looks ill."
- : " looks even sicker." );
- }
-
- // finally, take care of deity preferences
- if (fromPlayer)
- did_god_conduct( DID_POISON, 5 + random2(3) );
-} // end poison_monster()
-
-// actually napalms a monster (w/ message)
-void sticky_flame_monster( int mn, bool fromPlayer, int levels )
-{
- bool yourFlame = fromPlayer;
- int currentFlame;
- int currentStrength = 0;
-
- struct monsters *monster = &menv[mn];
-
- if (monster->type == -1)
- return;
-
- if (mons_res_fire(monster) > 0)
- return;
-
- // who gets the credit if monster dies of napalm?
- currentFlame = mons_has_ench( monster, ENCH_STICKY_FLAME_I,
- ENCH_STICKY_FLAME_IV );
-
- if (currentFlame != ENCH_NONE)
- {
- currentStrength = currentFlame - ENCH_STICKY_FLAME_I;
- yourFlame = false;
- }
- else
- {
- currentFlame = mons_has_ench( monster, ENCH_YOUR_STICKY_FLAME_I,
- ENCH_YOUR_STICKY_FLAME_IV );
-
- if (currentFlame != ENCH_NONE)
- {
- currentStrength = currentFlame - ENCH_YOUR_STICKY_FLAME_I;
- yourFlame = true;
- }
- else
- currentStrength = -1; // no flame yet!
- }
-
- // delete old flame
- mons_del_ench( monster, ENCH_STICKY_FLAME_I, ENCH_STICKY_FLAME_IV, true );
- mons_del_ench( monster, ENCH_YOUR_STICKY_FLAME_I, ENCH_YOUR_STICKY_FLAME_IV,
- true );
-
- // increase sticky flame strength, cap at 3 (level is 0..3)
- currentStrength += levels;
-
- if (currentStrength > 3)
- currentStrength = 3;
-
- // now, if player flames the monster at ANY TIME, they should
- // get credit for the kill if the monster dies from napalm. This
- // really isn't that abusable -- GDL.
- if (fromPlayer || yourFlame)
- currentStrength += ENCH_YOUR_STICKY_FLAME_I;
- else
- currentStrength += ENCH_STICKY_FLAME_I;
-
- // actually do flame
- if (mons_add_ench( monster, currentStrength ))
- simple_monster_message(monster, " is covered in liquid fire!");
-
-} // end sticky_flame_monster
-
-/*
- * Used by monsters in "planning" which spell to cast. Fires off a "tracer"
- * which tells the monster what it'll hit if it breathes/casts etc.
- *
- * The output from this tracer function is four variables in the beam struct:
- * fr_count, foe_count: a count of how many friends and foes will (probably)
- * be hit by this beam
- * fr_power, foe_power: a measure of how many 'friendly' hit dice it will
- * affect, and how many 'unfriendly' hit dice.
- *
- * note that beam properties must be set, as the tracer will take them
- * into account, as well as the monster's intelligence.
- *
- */
-void fire_tracer(struct monsters *monster, struct bolt &pbolt)
-{
- // don't fiddle with any input parameters other than tracer stuff!
- pbolt.is_tracer = true;
- pbolt.source_x = monster->x; // always safe to do.
- pbolt.source_y = monster->y;
- pbolt.beam_source = monster_index(monster);
- pbolt.can_see_invis = (mons_see_invis(monster) != 0);
- pbolt.smart_monster = (mons_intel(monster->type) == I_HIGH ||
- mons_intel(monster->type) == I_NORMAL);
- pbolt.is_friendly = mons_friendly(monster);
-
- // init tracer variables
- pbolt.foe_count = pbolt.fr_count = 0;
- pbolt.foe_power = pbolt.fr_power = 0;
- pbolt.foe_ratio = 80; // default - see mons_should_fire()
-
- // foe ratio for summon gtr. demons & undead -- they may be
- // summoned, but they're hostile and would love nothing better
- // than to nuke the player and his minions
- if (monster->attitude != ATT_FRIENDLY)
- pbolt.foe_ratio = 25;
-
- // fire!
- fire_beam(pbolt);
-
- // unset tracer flag (convenience)
- pbolt.is_tracer = false;
-} // end tracer_f()
-
-bool check_line_of_sight( int sx, int sy, int tx, int ty )
-{
- struct bolt pbolt;
-
- const int dist = grid_distance( sx, sy, tx, ty );
-
- // can always see one square away
- if (dist <= 1)
- return (true);
-
- // currently we limit the range to 8
- if (dist > MONSTER_LOS_RANGE)
- return (false);
-
- // Redirect player centered LoS to the old method (using display table)...
- // note that this assumes that viewwindow() has been called if needed
- // before we get here (ie this won't work very well if this function gets
- // called between moving the player and updating the display).
- if (sx == you.x_pos && sy == you.y_pos)
- return (see_grid( tx, ty ));
- else if (tx == you.x_pos && ty == you.y_pos)
- return (see_grid( sx, sy ));
-
- // Okay, no easy way... set up a LoS beam between the points
- pbolt.flavour = BEAM_LINE_OF_SIGHT;
- pbolt.is_tracer = true;
- pbolt.source_x = sx;
- pbolt.source_y = sy;
- pbolt.target_x = tx;
- pbolt.target_y = ty;
- pbolt.range = MONSTER_LOS_RANGE;
- pbolt.rangeMax = MONSTER_LOS_RANGE;
-
- // setting these just to be safe:
- pbolt.hit = 0;
- pbolt.type = 0;
- pbolt.damage = dice_def( 0, 1 );
- pbolt.colour = BLACK;
- pbolt.is_beam = true;
-
- // init tracer variables (used to tell if we "hit" the target)
- pbolt.foe_count = pbolt.fr_count = 0;
- pbolt.foe_power = pbolt.fr_power = 0;
-
- // fire!
- fire_beam( pbolt );
-
- // got to target?
- return (pbolt.foe_count == 1);
-}
-
-/*
- When a mimic is hit by a ranged attack, it teleports away (the slow way)
- and changes its appearance - the appearance change is in monster_teleport
- in mstuff2.
- */
-void mimic_alert(struct monsters *mimic)
-{
- if (mons_has_ench( mimic, ENCH_TP_I, ENCH_TP_IV ))
- return;
-
- monster_teleport( mimic, !one_chance_in(3) );
-} // end mimic_alert()
-
-static bool isBouncy(struct bolt &beam)
-{
- // at present, only non-enchantment eletrcical beams bounce.
- if (beam.beam_name[0] != '0' && beam.flavour == BEAM_ELECTRICITY)
- return (true);
-
- return (false);
-}
-
-static void beam_explodes(struct bolt &beam, int x, int y)
-{
- int cloud_type;
-
- // this will be the last thing this beam does.. set target_x
- // and target_y to hold explosion co'ords.
-
- beam.target_x = x;
- beam.target_y = y;
-
- // generic explosion
- if (beam.is_explosion) // beam.flavour == BEAM_EXPLOSION || beam.flavour == BEAM_HOLY)
- {
- explosion1(beam);
- return;
- }
-
- if (beam.flavour >= BEAM_POTION_STINKING_CLOUD
- && beam.flavour <= BEAM_POTION_RANDOM)
- {
- switch (beam.flavour)
- {
- case BEAM_POTION_STINKING_CLOUD:
- beam.colour = GREEN;
- break;
-
- case BEAM_POTION_POISON:
- beam.colour = (coinflip() ? GREEN : LIGHTGREEN);
- break;
-
- case BEAM_POTION_MIASMA:
- case BEAM_POTION_BLACK_SMOKE:
- beam.colour = DARKGREY;
- break;
-
- case BEAM_POTION_STEAM:
- beam.colour = LIGHTGREY;
- break;
-
- case BEAM_POTION_FIRE:
- beam.colour = (coinflip() ? RED : LIGHTRED);
- break;
-
- case BEAM_POTION_COLD:
- beam.colour = (coinflip() ? BLUE : LIGHTBLUE);
- break;
-
- case BEAM_POTION_BLUE_SMOKE:
- beam.colour = LIGHTBLUE;
- break;
-
- case BEAM_POTION_PURP_SMOKE:
- beam.colour = MAGENTA;
- break;
-
- case BEAM_POTION_RANDOM:
- default:
- // Leave it the colour of the potion, the clouds will colour
- // themselves on the next refresh. -- bwr
- break;
- }
-
- explosion1(beam);
- return;
- }
-
-
- // cloud producer -- POISON BLAST
- if (strcmp(beam.beam_name, "blast of poison") == 0)
- {
- cloud_type = YOU_KILL(beam.thrower) ? CLOUD_POISON : CLOUD_POISON_MON;
- big_cloud( cloud_type, x, y, 0, 7 + random2(5) );
- return;
- }
-
- // cloud producer -- FOUL VAPOR (SWAMP DRAKE?)
- if (strcmp(beam.beam_name, "foul vapour") == 0)
- {
- cloud_type = YOU_KILL(beam.thrower) ? CLOUD_STINK : CLOUD_STINK_MON;
- if (beam.flavour == BEAM_MIASMA)
- cloud_type = YOU_KILL(beam.thrower) ?
- CLOUD_MIASMA : CLOUD_MIASMA_MON;
- big_cloud( cloud_type, x, y, 0, 9 );
- return;
- }
-
- // special cases - orbs & blasts of cold
- if (strcmp(beam.beam_name, "orb of electricity") == 0
- || strcmp(beam.beam_name, "metal orb") == 0
- || strcmp(beam.beam_name, "great blast of cold") == 0)
- {
- explosion1( beam );
- return;
- }
-
- // cloud producer only -- stinking cloud
- if (strcmp(beam.beam_name, "ball of vapour") == 0)
- {
- explosion1( beam );
- return;
- }
-}
-
-static bool beam_term_on_target(struct bolt &beam)
-{
- if (beam.flavour == BEAM_LINE_OF_SIGHT)
- {
- beam.foe_count++;
- return (true);
- }
-
- // generic - all explosion-type beams can be targetted at empty space,
- // and will explode there. This semantic also means that a creature
- // in the target cell will have no chance to dodge or block, so we
- // DON'T affect() the cell if this function returns true!
-
- if (beam.is_explosion || beam.is_big_cloud)
- return (true);
-
- // POISON BLAST
- if (strcmp(beam.beam_name, "blast of poison") == 0)
- return (true);
-
- // FOUL VAPOR (SWAMP DRAKE)
- if (strcmp(beam.beam_name, "foul vapour") == 0)
- return (true);
-
- // STINKING CLOUD
- if (strcmp(beam.beam_name, "ball of vapour") == 0)
- return (true);
-
- return (false);
-}
-
-static void beam_drop_object( struct bolt &beam, item_def *item, int x, int y )
-{
- ASSERT( item != NULL );
-
- // conditions: beam is missile and not tracer.
- if (beam.is_tracer || beam.flavour != BEAM_MISSILE)
- return;
-
- if (YOU_KILL(beam.thrower) // ie if you threw it.
- && (grd[x][y] != DNGN_LAVA && grd[x][y] != DNGN_DEEP_WATER))
- {
- int chance;
-
- // [dshaligram] Removed influence of Throwing on ammo preservation.
- // The effect is nigh impossible to perceive.
- switch (item->sub_type)
- {
- case MI_NEEDLE: chance = 7; break;
- case MI_STONE: chance = 6; break;
- case MI_DART: chance = 4; break;
- case MI_ARROW: chance = 4; break;
- case MI_BOLT: chance = 5; break;
-
- case MI_LARGE_ROCK:
- default:
- chance = 20;
- break;
- }
-
- if (item->base_type != OBJ_MISSILES || !one_chance_in(chance))
- copy_item_to_grid( *item, x, y, 1 );
- }
- else if (MON_KILL(beam.thrower) // monster threw it.
- && (grd[x][y] != DNGN_LAVA && grd[x][y] != DNGN_DEEP_WATER)
- && coinflip())
- {
- copy_item_to_grid( *item, x, y, 1 );
- } // if (thing_throw == 2) ...
-}
-
-// somewhat complicated BOUNCE function
-// returns # of times beam bounces during routine (usually 1)
-//
-// step 1 is always the step value from the stepping direction.
-#define B_HORZ 1
-#define B_VERT 2
-#define B_BOTH 3
-
-static int bounce(int &step1, int &step2, int w1, int w2, int &n1, int &n2,
- int l1, int l2, int &t1, int &t2, bool topBlocked, bool sideBlocked)
-{
- int bounceType = 0;
- int bounceCount = 1;
-
- if (topBlocked) bounceType = B_HORZ;
- if (sideBlocked) bounceType = B_VERT;
- if (topBlocked && sideBlocked)
- {
- // check for veritcal bounce only
- if ((w2 + step2 - 50)/100 == (w2 - 50)/100)
- bounceType = B_VERT;
- else
- bounceType = B_BOTH;
- }
-
- switch (bounceType)
- {
- case B_VERT: // easiest
- n1 = w1;
- n2 = w2 + step2;
- step1 = -step1;
- t1 = n1 / 100;
- t2 = (n2 + 50)/100;
- // check top
- if (t2 != n2/100 && topBlocked)
- t2 = n2/100;
- break;
- case B_HORZ: // a little tricky
- if (step2 > 0)
- n2 = (100 + 200*(w2/100)) - (w2 + step2);
- else
- n2 = (100 + 200*((w2 - 50)/100)) - (w2 + step2);
- n1 = w1 + step1;
- t1 = n1 /100;
- t2 = (n2 + 50) / 100;
- step2 = -step2;
- break;
- case B_BOTH:
- // vertical:
- n1 = w1;
- t1 = l1;
- t2 = l2;
- // horizontal:
- if (step2 > 0)
- n2 = (100 + 200*(w2/100)) - (w2 + step2);
- else
- n2 = (100 + 200*((w2 - 50)/100)) - (w2 + step2);
- // reverse both directions
- step1 =- step1;
- step2 =- step2;
- bounceCount = 2;
- break;
- default:
- bounceCount = 0;
- break;
- }
-
- return (bounceCount);
-}
-
-static bool fuzzyLine(int nx, int ny, int &tx, int &ty, int lx, int ly,
- int stepx, int stepy, bool roundX, bool roundY)
-{
- bool fuzzyOK = false;
- int fx, fy; // fuzzy x,y
-
- // BEGIN fuzzy line algorithm
- fx = tx;
- fy = ty;
- if (roundY)
- {
- // try up
- fy = (ny + 100) / 100;
- // check for monotonic
- if (fy != ty && ((stepy>0 && fy >= ly)
- || (stepy<0 && fy <= ly)))
- fuzzyOK = true;
- // see if up try is blocked
- if (fuzzyOK && grd[tx][fy] < MINMOVE)
- fuzzyOK = false;
-
- // try down
- if (!fuzzyOK)
- fy = ny / 100;
- // check for monotonic
- if (fy != ty && ((stepy>0 && fy >= ly)
- || (stepy<0 && fy <= ly)))
- fuzzyOK = true;
- if (fuzzyOK && grd[tx][fy] < MINMOVE)
- fuzzyOK = false;
- }
- if (roundX)
- {
- // try up
- fx = (nx + 100) / 100;
- // check for monotonic
- if (fx != tx && ((stepx>0 && fx >= lx)
- || (stepx<0 && fx <= lx)))
- fuzzyOK = true;
- // see if up try is blocked
- if (fuzzyOK && grd[fx][ty] < MINMOVE)
- fuzzyOK = false;
-
- // try down
- if (!fuzzyOK)
- fx = nx / 100;
- // check for monotonic
- if (fx != tx && ((stepx>0 && fx >= lx)
- || (stepx<0 && fx <= lx)))
- fuzzyOK = true;
- if (fuzzyOK && grd[fx][ty] < MINMOVE)
- fuzzyOK = false;
- }
- // END fuzzy line algorithm
-
- if (fuzzyOK)
- {
- tx = fx;
- ty = fy;
- }
-
- return (fuzzyOK);
-}
-
-// affects a single cell.
-// returns the amount of extra range 'used up' by this beam
-// during the affectation.
-//
-// pseudo-code:
-//
-// 1. If wall, and wall affecting non-tracer, affect the wall.
-// 1b. If for some reason the wall-affect didn't make it into
-// a non-wall, return affect_wall()
-// 2. for non-tracers, produce cloud effects affect_place_clouds()
-// 3. if cell holds player, affect player affect_player()
-// 4. if cell holds monster, affect monster affect_monster()
-// 5. return range used affectation.
-
-int affect(struct bolt &beam, int x, int y)
-{
- // extra range used by hitting something
- int rangeUsed = 0;
-
- // line of sight never affects anything
- if (beam.flavour == BEAM_LINE_OF_SIGHT)
- return (0);
-
- if (grd[x][y] < MINMOVE)
- {
- if (beam.is_tracer) // tracers always stop on walls.
- return (BEAM_STOP);
-
- if (affectsWalls(beam))
- {
- rangeUsed += affect_wall(beam, x, y);
- }
- // if it's still a wall, quit - we can't do anything else to
- // a wall. Otherwise effects (like clouds, etc) are still possible.
- if (grd[x][y] < MINMOVE)
- return (rangeUsed);
- }
-
- // grd[x][y] will NOT be a wall for the remainder of this function.
-
- // if not a tracer, place clouds
- if (!beam.is_tracer)
- rangeUsed += affect_place_clouds(beam, x, y);
-
- // if player is at this location, try to affect unless term_on_target
- if (x == you.x_pos && y == you.y_pos)
- {
- // Done this way so that poison blasts affect the target once (via
- // place_cloud) and explosion spells only affect the target once
- // (during the explosion phase, not an initial hit during the
- // beam phase).
- if (!beam.is_big_cloud
- && (!beam.is_explosion || beam.in_explosion_phase))
- {
- rangeUsed += affect_player( beam );
- }
-
- if (beam_term_on_target(beam))
- return (BEAM_STOP);
- }
-
- // if there is a monster at this location, affect it
- // submerged monsters aren't really there -- bwr
- int mid = mgrd[x][y];
- if (mid != NON_MONSTER && !mons_has_ench( &menv[mid], ENCH_SUBMERGED ))
- {
- if (!beam.is_big_cloud
- && (!beam.is_explosion || beam.in_explosion_phase))
- {
- rangeUsed += affect_monster( beam, &menv[mid] );
- }
-
- if (beam_term_on_target(beam))
- return (BEAM_STOP);
- }
-
- return (rangeUsed);
-}
-
-static bool affectsWalls(struct bolt &beam)
-{
- // don't know of any explosion that affects walls. But change it here
- // if there is.
- if (beam.is_explosion)
- return (false);
-
- // digging
- if (beam.flavour == BEAM_DIGGING)
- return (true);
-
- // Isn't this much nicer than the hack to remove ice bolts, disrupt,
- // and needles (just because they were also coloured "white") -- bwr
- if (beam.flavour == BEAM_DISINTEGRATION && beam.damage.num >= 3)
- return (true);
-
- // eye of devestation?
- if (beam.flavour == BEAM_NUKE)
- return (true);
-
- return (false);
-}
-
-// return amount of extra range used up by affectation of this wall.
-static int affect_wall(struct bolt &beam, int x, int y)
-{
- int rangeUsed = 0;
-
- // DIGGING
- if (beam.flavour == BEAM_DIGGING)
- {
- if (grd[x][y] == DNGN_STONE_WALL
- || grd[x][y] == DNGN_METAL_WALL
- || grd[x][y] == DNGN_PERMAROCK_WALL
- || x <= 5 || x >= (GXM - 5)
- || y <= 5 || y >= (GYM - 5))
- {
- return (0);
- }
-
- if (grd[x][y] == DNGN_ROCK_WALL)
- {
- grd[x][y] = DNGN_FLOOR;
-
- if (!beam.msg_generated)
- {
- if (!silenced(you.x_pos, you.y_pos))
- {
- mpr("You hear a grinding noise.", MSGCH_SOUND);
- beam.obvious_effect = true;
- }
-
- beam.msg_generated = true;
- }
- }
-
- return (rangeUsed);
- }
- // END DIGGING EFFECT
-
- // NUKE / DISRUPT
- if (beam.flavour == BEAM_DISINTEGRATION || beam.flavour == BEAM_NUKE)
- {
- int targ_grid = grd[x][y];
-
- if ((targ_grid == DNGN_ROCK_WALL || targ_grid == DNGN_WAX_WALL)
- && !(x <= 6 || y <= 6 || x >= (GXM - 6) || y >= (GYM - 6)))
- {
- grd[ x ][ y ] = DNGN_FLOOR;
- if (!silenced(you.x_pos, you.y_pos))
- {
- mpr("You hear a grinding noise.", MSGCH_SOUND);
- beam.obvious_effect = true;
- }
- }
-
- if (targ_grid == DNGN_ORCISH_IDOL
- || targ_grid == DNGN_SILVER_STATUE
- || targ_grid == DNGN_GRANITE_STATUE
- || targ_grid == DNGN_ORANGE_CRYSTAL_STATUE)
- {
- grd[x][y] = DNGN_FLOOR;
-
- if (!silenced(you.x_pos, you.y_pos))
- {
- if (!see_grid( x, y ))
- mpr("You hear a hideous screaming!", MSGCH_SOUND);
- else
- mpr("The statue screams as its substance crumbles away!",
- MSGCH_SOUND);
- }
- else
- {
- if (see_grid(x,y))
- mpr("The statue twists and shakes as its substance crumbles away!");
- }
-
- if (targ_grid == DNGN_SILVER_STATUE)
- Visible_Statue[ STATUE_SILVER ] = 0;
- else if (targ_grid == DNGN_ORANGE_CRYSTAL_STATUE)
- Visible_Statue[ STATUE_ORANGE_CRYSTAL ] = 0;
-
- beam.obvious_effect = 1;
- }
-
- return (BEAM_STOP);
- }
-
- return (rangeUsed);
-}
-
-static int affect_place_clouds(struct bolt &beam, int x, int y)
-{
- int cloud_type;
-
- if (beam.in_explosion_phase)
- {
- affect_place_explosion_clouds( beam, x, y );
- return (0); // return value irrelevant for explosions
- }
-
- // check for CLOUD HITS
- if (env.cgrid[x][y] != EMPTY_CLOUD) // hit a cloud
- {
- // polymorph randomly changes clouds in its path
- if (beam.flavour == BEAM_POLYMORPH)
- env.cloud[ env.cgrid[x][y] ].type = 1 + random2(8);
-
- // now exit (all enchantments)
- if (beam.beam_name[0] == '0')
- return (0);
-
- int clouty = env.cgrid[x][y];
-
- // fire cancelling cold & vice versa
- if (((env.cloud[clouty].type == CLOUD_COLD
- || env.cloud[clouty].type == CLOUD_COLD_MON)
- && (beam.flavour == BEAM_FIRE
- || beam.flavour == BEAM_LAVA))
- || ((env.cloud[clouty].type == CLOUD_FIRE
- || env.cloud[clouty].type == CLOUD_FIRE_MON)
- && beam.flavour == BEAM_COLD))
- {
- if (!silenced(x, y)
- && !silenced(you.x_pos, you.y_pos))
- {
- mpr("You hear a sizzling sound!", MSGCH_SOUND);
- }
-
- delete_cloud( clouty );
- return (5);
- }
- }
-
- // POISON BLAST
- if (strcmp(beam.beam_name, "blast of poison") == 0)
- {
- cloud_type = YOU_KILL(beam.thrower) ? CLOUD_POISON : CLOUD_POISON_MON;
-
- place_cloud( cloud_type, x, y, random2(4) + 2 );
- }
-
- // FIRE/COLD over water/lava
- if ( (grd[x][y] == DNGN_LAVA && beam.flavour == BEAM_COLD)
- || ((grd[x][y] == DNGN_DEEP_WATER || grd[x][y] == DNGN_SHALLOW_WATER)
- && beam.flavour == BEAM_FIRE) )
- {
- cloud_type = YOU_KILL(beam.thrower) ? CLOUD_STEAM : CLOUD_STEAM_MON;
- place_cloud( cloud_type, x, y, 2 + random2(5) );
- }
-
- // ORB OF ENERGY
- if (strcmp(beam.beam_name, "orb of energy") == 0)
- place_cloud( CLOUD_PURP_SMOKE, x, y, random2(5) + 1 );
-
- // GREAT BLAST OF COLD
- if (strcmp(beam.beam_name, "great blast of cold") == 0)
- place_cloud( CLOUD_COLD, x, y, random2(5) + 3 );
-
-
- // BALL OF STEAM
- if (strcmp(beam.beam_name, "ball of steam") == 0)
- {
- cloud_type = YOU_KILL(beam.thrower) ? CLOUD_STEAM : CLOUD_STEAM_MON;
- place_cloud( cloud_type, x, y, random2(5) + 2 );
- }
-
- if (beam.flavour == BEAM_MIASMA)
- {
- cloud_type = YOU_KILL( beam.thrower ) ? CLOUD_MIASMA : CLOUD_MIASMA_MON;
- place_cloud( cloud_type, x, y, random2(5) + 2 );
- }
-
- // STICKY FLAME
- if (strcmp(beam.beam_name, "sticky flame") == 0)
- {
- place_cloud( CLOUD_BLACK_SMOKE, x, y, random2(4) + 2 );
- }
-
- // POISON GAS
- if (strcmp(beam.beam_name, "poison gas") == 0)
- {
- cloud_type = YOU_KILL(beam.thrower) ? CLOUD_POISON : CLOUD_POISON_MON;
- place_cloud( cloud_type, x, y, random2(4) + 3 );
- }
-
- return (0);
-}
-
-// following two functions used with explosions:
-static void affect_place_explosion_clouds(struct bolt &beam, int x, int y)
-{
- int cloud_type;
- int duration;
-
- // first check: FIRE/COLD over water/lava
- if ( (grd[x][y] == DNGN_LAVA && beam.flavour == BEAM_COLD)
- || ((grd[x][y] == DNGN_DEEP_WATER || grd[x][y] == DNGN_SHALLOW_WATER)
- && beam.flavour == BEAM_FIRE) )
- {
- cloud_type = YOU_KILL(beam.thrower) ? CLOUD_STEAM : CLOUD_STEAM_MON;
- place_cloud( cloud_type, x, y, 2 + random2(5) );
- return;
- }
-
- if (beam.flavour >= BEAM_POTION_STINKING_CLOUD
- && beam.flavour <= BEAM_POTION_RANDOM)
- {
- duration = roll_dice( 2, 3 + beam.ench_power / 20 );
-
- switch (beam.flavour)
- {
- case BEAM_POTION_STINKING_CLOUD:
- cloud_type = CLOUD_STINK;
- break;
-
- case BEAM_POTION_POISON:
- cloud_type = CLOUD_POISON;
- break;
-
- case BEAM_POTION_MIASMA:
- cloud_type = CLOUD_MIASMA;
- break;
-
- case BEAM_POTION_BLACK_SMOKE:
- cloud_type = CLOUD_BLACK_SMOKE;
- break;
-
- case BEAM_POTION_FIRE:
- cloud_type = CLOUD_FIRE;
- break;
-
- case BEAM_POTION_COLD:
- cloud_type = CLOUD_COLD;
- break;
-
- case BEAM_POTION_BLUE_SMOKE:
- cloud_type = CLOUD_BLUE_SMOKE;
- break;
-
- case BEAM_POTION_PURP_SMOKE:
- cloud_type = CLOUD_PURP_SMOKE;
- break;
-
- case BEAM_POTION_RANDOM:
- switch (random2(10))
- {
- case 0: cloud_type = CLOUD_FIRE; break;
- case 1: cloud_type = CLOUD_STINK; break;
- case 2: cloud_type = CLOUD_COLD; break;
- case 3: cloud_type = CLOUD_POISON; break;
- case 4: cloud_type = CLOUD_BLACK_SMOKE; break;
- case 5: cloud_type = CLOUD_BLUE_SMOKE; break;
- case 6: cloud_type = CLOUD_PURP_SMOKE; break;
- default: cloud_type = CLOUD_STEAM; break;
- }
- break;
-
- case BEAM_POTION_STEAM:
- default:
- cloud_type = CLOUD_STEAM;
- break;
- }
-
- place_cloud( cloud_type, x, y, duration );
- }
-
- // then check for more specific explosion cloud types.
- if (stricmp(beam.beam_name, "ice storm") == 0)
- {
- place_cloud( CLOUD_COLD, x, y, 2 + random2avg(5, 2) );
- }
-
- if (stricmp(beam.beam_name, "stinking cloud") == 0)
- {
- duration = 1 + random2(4) + random2( (beam.ench_power / 50) + 1 );
- place_cloud( CLOUD_STINK, x, y, duration );
- }
-
- if (strcmp(beam.beam_name, "great blast of fire") == 0)
- {
- duration = 1 + random2(5) + roll_dice( 2, beam.ench_power / 5 );
-
- if (duration > 20)
- duration = 20 + random2(4);
-
- place_cloud( CLOUD_FIRE, x, y, duration );
-
- if (grd[x][y] == DNGN_FLOOR && mgrd[x][y] == NON_MONSTER
- && one_chance_in(4))
- {
- mons_place( MONS_FIRE_VORTEX, BEH_HOSTILE, MHITNOT, true, x, y );
- }
- }
-}
-
-static void affect_items(struct bolt &beam, int x, int y)
-{
- char objs_vulnerable = -1;
-
- switch (beam.flavour)
- {
- case BEAM_FIRE:
- case BEAM_LAVA:
- objs_vulnerable = OBJ_SCROLLS;
- break;
- case BEAM_COLD:
- objs_vulnerable = OBJ_POTIONS;
- break;
- case BEAM_SPORE:
- objs_vulnerable = OBJ_FOOD;
- break;
- }
-
- if (stricmp(beam.beam_name, "hellfire") == 0)
- objs_vulnerable = OBJ_SCROLLS;
-
- if (igrd[x][y] != NON_ITEM)
- {
- if (objs_vulnerable != -1 &&
- mitm[igrd[x][y]].base_type == objs_vulnerable)
- {
- destroy_item( igrd[ x ][ y ] );
-
- if (objs_vulnerable == OBJ_SCROLLS && see_grid(x,y))
- {
- mpr("You see a puff of smoke.");
- }
-
- if (objs_vulnerable == OBJ_POTIONS && !silenced(x,y)
- && !silenced(you.x_pos, you.y_pos))
- {
- mpr("You hear glass shatter.");
- }
- }
- }
-}
-
-static int beam_ouch_agent(const bolt &beam)
-{
- return YOU_KILL(beam.thrower)? 0 : beam.beam_source;
-}
-
-// A little helper function to handle the calling of ouch()...
-static void beam_ouch( int dam, struct bolt &beam )
-{
- // The order of this is important.
- if (YOU_KILL( beam.thrower ) && !beam.aux_source)
- {
- ouch( dam, 0, KILLED_BY_TARGETTING );
- }
- else if (MON_KILL( beam.thrower ))
- {
- if (beam.flavour == BEAM_SPORE)
- ouch( dam, beam.beam_source, KILLED_BY_SPORE );
- else
- ouch( dam, beam.beam_source, KILLED_BY_BEAM, beam.aux_source );
- }
- else // KILL_MISC || (YOU_KILL && aux_source)
- {
- ouch( dam, beam.beam_source, KILLED_BY_WILD_MAGIC, beam.aux_source );
- }
-}
-
-// return amount of extra range used up by affectation of the player
-static int affect_player( struct bolt &beam )
-{
- int beamHit;
-
- // digging -- don't care.
- if (beam.flavour == BEAM_DIGGING)
- return (0);
-
- // check for tracer
- if (beam.is_tracer)
- {
- // check can see player
- // XXX: note the cheat to allow for ME_ALERT to target the player...
- // replace this with a time since alert system, rather than just
- // peeking to see if the character is still there. -- bwr
- if (beam.can_see_invis || !you.invis
- || (you.x_pos == beam.target_x && you.y_pos == beam.target_y))
- {
- if (beam.is_friendly)
- {
- beam.fr_count += 1;
- beam.fr_power += you.experience_level;
- }
- else
- {
- beam.foe_count += 1;
- beam.foe_power += you.experience_level;
- }
- }
- return (range_used_on_hit(beam));
- }
-
- // BEGIN real beam code
- beam.msg_generated = true;
-
- // use beamHit, NOT beam.hit, for modification of tohit.. geez!
- beamHit = beam.hit;
-
- if (beam.beam_name[0] != '0')
- {
- if (!beam.is_explosion && !beam.aimed_at_feet)
- {
- // BEGIN BEAM/MISSILE
- int dodge = random2limit( player_evasion(), 40 )
- + random2( you.dex ) / 3 - 2;
-
- if (beam.is_beam)
- {
- // beams can be dodged
- if (player_light_armour()
- && !beam.aimed_at_feet && coinflip())
- {
- exercise(SK_DODGING, 1);
- }
-
- if (you.duration[DUR_REPEL_MISSILES]
- || you.mutation[MUT_REPULSION_FIELD] == 3)
- {
- beamHit -= random2(beamHit / 2);
- }
-
- if (you.duration[DUR_DEFLECT_MISSILES])
- beamHit = random2(beamHit / 3);
-
- if (beamHit < dodge)
- {
- strcpy(info, "The ");
- strcat(info, beam.beam_name);
- strcat(info, " misses you.");
- mpr(info);
- return (0); // no extra used by miss!
- }
- }
- else if (beam_is_blockable(beam))
- {
- // non-beams can be blocked or dodged
- if (you.equip[EQ_SHIELD] != -1
- && !beam.aimed_at_feet
- && player_shield_class() > 0)
- {
- int exer = one_chance_in(3) ? 1 : 0;
- // [dshaligram] beam.hit multiplier lowered to 3 - was 5.
- // In favour of blocking, dex multiplier changed to .25
- // (was .2), added shield skill into the equation with a
- // skill bump.
- const int hit = random2( beam.hit * 3
- + 5 * you.shield_blocks * you.shield_blocks );
-
- const int block = random2(player_shield_class())
- + (random2(you.dex) / 4)
- + (random2(skill_bump(SK_SHIELDS)) / 4)
- - 1;
-
- if (hit < block)
- {
- you.shield_blocks++;
- snprintf( info, INFO_SIZE, "You block the %s.",
- beam.beam_name );
- mpr( info );
-
- exercise( SK_SHIELDS, exer + 1 );
- return (BEAM_STOP);
- }
-
- // some training just for the "attempt"
- exercise( SK_SHIELDS, exer );
- }
-
- if (player_light_armour() && !beam.aimed_at_feet
- && coinflip())
- exercise(SK_DODGING, 1);
-
- if (you.duration[DUR_REPEL_MISSILES]
- || you.mutation[MUT_REPULSION_FIELD] == 3)
- {
- beamHit = random2(beamHit);
- }
-
-
- // miss message
- if (beamHit < dodge || you.duration[DUR_DEFLECT_MISSILES])
- {
- strcpy(info, "The ");
- strcat(info, beam.beam_name);
- strcat(info, " misses you.");
- return (0);
- }
- }
- }
- }
- else
- {
- // BEGIN enchantment beam
- if (beam.flavour != BEAM_HASTE
- && beam.flavour != BEAM_INVISIBILITY
- && beam.flavour != BEAM_HEALING
- && ((beam.flavour != BEAM_TELEPORT && beam.flavour != BEAM_BANISH)
- || !beam.aimed_at_feet)
- && you_resist_magic( beam.ench_power ))
- {
- canned_msg(MSG_YOU_RESIST);
- return (range_used_on_hit(beam));
- }
-
- ench_animation( beam.flavour );
-
- // these colors are misapplied - see mons_ench_f2() {dlb}
- switch (beam.flavour)
- {
- case BEAM_SLOW:
- potion_effect( POT_SLOWING, beam.ench_power );
- beam.obvious_effect = true;
- break; // slow
-
- case BEAM_HASTE:
- potion_effect( POT_SPEED, beam.ench_power );
- contaminate_player( 1 );
- beam.obvious_effect = true;
- break; // haste
-
- case BEAM_HEALING:
- potion_effect( POT_HEAL_WOUNDS, beam.ench_power );
- beam.obvious_effect = true;
- break; // heal (heal wounds potion eff)
-
- case BEAM_PARALYSIS:
- potion_effect( POT_PARALYSIS, beam.ench_power );
- beam.obvious_effect = true;
- break; // paralysis
-
- case BEAM_CONFUSION:
- potion_effect( POT_CONFUSION, beam.ench_power );
- beam.obvious_effect = true;
- break; // confusion
-
- case BEAM_INVISIBILITY:
- potion_effect( POT_INVISIBILITY, beam.ench_power );
- contaminate_player( 1 + random2(2) );
- beam.obvious_effect = true;
- break; // invisibility
-
- // 6 is used by digging
-
- case BEAM_TELEPORT:
- you_teleport();
- beam.obvious_effect = true;
- break;
-
- case BEAM_BLINK:
- random_blink(0);
- beam.obvious_effect = true;
- break;
-
- case BEAM_POLYMORPH:
- mpr("This is polymorph other only!");
- beam.obvious_effect = true;
- break;
-
- case BEAM_CHARM:
- potion_effect( POT_CONFUSION, beam.ench_power );
- beam.obvious_effect = true;
- break; // enslavement - confusion?
-
- case BEAM_BANISH:
- if (you.level_type == LEVEL_ABYSS)
- {
- mpr("You feel trapped.");
- break;
- }
- mpr("You are cast into the Abyss!");
- more();
- banished(DNGN_ENTER_ABYSS);
- beam.obvious_effect = true;
- break; // banishment to the abyss
-
- case BEAM_PAIN: // pain
- if (you.is_undead || you.mutation[MUT_TORMENT_RESISTANCE])
- {
- mpr("You are unaffected.");
- break;
- }
-
- mpr("Pain shoots through your body!");
-
- if (!beam.aux_source)
- beam.aux_source = "by nerve-wracking pain";
-
- beam_ouch( roll_dice( beam.damage ), beam );
- beam.obvious_effect = true;
- break;
-
- case BEAM_DISPEL_UNDEAD:
- if (!you.is_undead)
- {
- mpr("You are unaffected.");
- break;
- }
-
- mpr( "You convulse!" );
-
- if (!beam.aux_source)
- beam.aux_source = "by dispel undead";
-
- beam_ouch( roll_dice( beam.damage ), beam );
- beam.obvious_effect = true;
- break;
-
- case BEAM_DISINTEGRATION:
- mpr("You are blasted!");
-
- if (!beam.aux_source)
- beam.aux_source = "disintegration bolt";
-
- beam_ouch( roll_dice( beam.damage ), beam );
- beam.obvious_effect = true;
- break;
-
- default:
- // _all_ enchantments should be enumerated here!
- mpr("Software bugs nibble your toes!");
- break;
- } // end of switch (beam.colour)
-
- // regardless of affect, we need to know if this is a stopper
- // or not - it seems all of the above are.
- return (range_used_on_hit(beam));
-
- // END enchantment beam
- }
-
- // THE BEAM IS NOW GUARANTEED TO BE A NON-ENCHANTMENT WHICH HIT
-
- const bool engulfs = (beam.is_explosion || beam.is_big_cloud);
- mprf( "The %s %s you!", beam.beam_name, (engulfs) ? "engulfs" : "hits" );
-
- int hurted = 0;
- int burn_power = (beam.is_explosion) ? 5 : ((beam.is_beam) ? 3 : 2);
-
- // Roll the damage
- hurted += roll_dice( beam.damage );
-
-#if DEBUG_DIAGNOSTICS
- int roll = hurted;
-#endif
-
- hurted -= random2( 1 + player_AC() );
-
-
- // shrapnel
- if (beam.flavour == BEAM_FRAG && !player_light_armour())
- {
- hurted -= random2( 1 + player_AC() );
- hurted -= random2( 1 + player_AC() );
- }
-
-#if DEBUG_DIAGNOSTICS
- snprintf( info, INFO_SIZE, "Player damage: rolled=%d; after AC=%d",
- roll, hurted );
-
- mpr( info, MSGCH_DIAGNOSTICS );
-#endif
-
- if (you.equip[EQ_BODY_ARMOUR] != -1)
- {
- if (!player_light_armour() && one_chance_in(4)
- && random2(1000) <= item_mass( you.inv[you.equip[EQ_BODY_ARMOUR]] ))
- {
- exercise( SK_ARMOUR, 1 );
- }
- }
-
- if (hurted < 0)
- hurted = 0;
-
- hurted = check_your_resists( hurted, beam.flavour );
-
- if (beam.flavour == BEAM_MIASMA && hurted > 0)
- {
- if (player_res_poison() <= 0)
- poison_player(1);
-
- if (one_chance_in( 3 + 2 * player_prot_life() ))
- potion_effect( POT_SLOWING, 5 );
- }
-
- // poisoning
- if (strstr(beam.beam_name, "poison") != NULL
- && beam.flavour != BEAM_POISON
- && beam.flavour != BEAM_POISON_ARROW
- && !player_res_poison())
- {
- if (hurted || (strstr( beam.beam_name, "needle" ) != NULL
- && random2(100) < 90 - (3 * player_AC())))
- {
- poison_player( 1 + random2(3) );
- }
- }
-
- if (strstr(beam.beam_name, "curare"))
- {
- if (random2(100) < 90 - (3 * player_AC()))
- {
- curare_hits_player( beam_ouch_agent(beam), 1 + random2(3) );
- }
- }
-
- // sticky flame
- if (strcmp(beam.beam_name, "sticky flame") == 0
- && (you.species != SP_MOTTLED_DRACONIAN
- || you.experience_level < 6))
- {
- if (!player_equip( EQ_BODY_ARMOUR, ARM_MOTTLED_DRAGON_ARMOUR ))
- you.duration[DUR_LIQUID_FLAMES] += random2avg(7, 3) + 1;
- }
-
- // simple cases for scroll burns
- if (beam.flavour == BEAM_LAVA || stricmp(beam.beam_name, "hellfire") == 0)
- scrolls_burn( burn_power, OBJ_SCROLLS );
-
- // more complex (geez..)
- if (beam.flavour == BEAM_FIRE && strcmp(beam.beam_name, "ball of steam") != 0)
- scrolls_burn( burn_power, OBJ_SCROLLS );
-
- // potions exploding
- if (beam.flavour == BEAM_COLD)
- scrolls_burn( burn_power, OBJ_POTIONS );
-
- if (beam.flavour == BEAM_ACID)
- splash_with_acid(5);
-
- // spore pops
- if (beam.in_explosion_phase && beam.flavour == BEAM_SPORE)
- scrolls_burn( 2, OBJ_FOOD );
-
-#if DEBUG_DIAGNOSTICS
- snprintf( info, INFO_SIZE, "Damage: %d", hurted );
- mpr( info, MSGCH_DIAGNOSTICS );
-#endif
-
- beam_ouch( hurted, beam );
-
- return (range_used_on_hit( beam ));
-}
-
-static int beam_source(const bolt &beam)
-{
- return MON_KILL(beam.thrower) ? beam.beam_source :
- beam.thrower == KILL_MISC ? MHITNOT :
- MHITYOU;
-}
-
-// return amount of range used up by affectation of this monster
-static int affect_monster(struct bolt &beam, struct monsters *mon)
-{
- int tid = mgrd[mon->x][mon->y];
- int hurt;
- int hurt_final;
-
- // digging -- don't care.
- if (beam.flavour == BEAM_DIGGING)
- return (0);
-
- // fire storm creates these, so we'll avoid affecting them
- if (strcmp(beam.beam_name, "great blast of fire") == 0
- && mon->type == MONS_FIRE_VORTEX)
- {
- return (0);
- }
-
- // check for tracer
- if (beam.is_tracer)
- {
- // check can see other monster
- if (!beam.can_see_invis && mons_has_ench(&menv[tid], ENCH_INVIS))
- {
- // can't see this monster, ignore it
- return 0;
- }
- }
-
- if (beam.beam_name[0] == '0')
- {
- if (beam.is_tracer)
- {
- // enchant case -- enchantments always hit, so update target immed.
- if (beam.is_friendly ^ mons_friendly(mon))
- {
- beam.foe_count += 1;
- beam.foe_power += mons_power(tid);
- }
- else
- {
- beam.fr_count += 1;
- beam.fr_power += mons_power(tid);
- }
-
- return (range_used_on_hit(beam));
- }
-
- // BEGIN non-tracer enchantment
-
- // nasty enchantments will annoy the monster, and are considered
- // naughty (even if a monster might resist)
- if (nasty_beam(mon, beam))
- {
- if (YOU_KILL( beam.thrower ))
- {
- if (mons_friendly( mon ))
- did_god_conduct( DID_ATTACK_FRIEND, 5 );
-
- if (mons_holiness( mon ) == MH_HOLY)
- did_god_conduct( DID_ATTACK_HOLY, mon->hit_dice );
- }
-
- behaviour_event( mon, ME_ANNOY, beam_source(beam) );
- }
- else
- {
- behaviour_event( mon, ME_ALERT, beam_source(beam) );
- }
-
- // !@#*( affect_monster_enchantment() has side-effects on
- // the beam structure which screw up range_used_on_hit(),
- // so call it now and store.
- int rangeUsed = range_used_on_hit(beam);
-
- // Doing this here so that the player gets to see monsters
- // "flicker and vanish" when turning invisible....
- ench_animation( beam.flavour, mon );
-
- // now do enchantment affect
- int ench_result = affect_monster_enchantment(beam, mon);
- switch(ench_result)
- {
- case MON_RESIST:
- if (simple_monster_message(mon, " resists."))
- beam.msg_generated = true;
- break;
- case MON_UNAFFECTED:
- if (simple_monster_message(mon, " is unaffected."))
- beam.msg_generated = true;
- break;
- default:
- break;
- }
- return (rangeUsed);
-
- // END non-tracer enchantment
- }
-
-
- // BEGIN non-enchantment (could still be tracer)
- if (mons_has_ench( mon, ENCH_SUBMERGED ) && !beam.aimed_at_feet)
- return (0); // missed me!
-
- // we need to know how much the monster _would_ be hurt by this, before
- // we decide if it actually hits.
-
- // Roll the damage:
- hurt = roll_dice( beam.damage );
-
- hurt_final = hurt;
-
- if (beam.is_tracer)
- hurt_final -= mon->armour_class / 2;
- else
- hurt_final -= random2(1 + mon->armour_class);
-
- if (beam.flavour == BEAM_FRAG)
- {
- hurt_final -= random2(1 + mon->armour_class);
- hurt_final -= random2(1 + mon->armour_class);
- }
-
- if (hurt_final < 1)
- {
- hurt_final = 0;
- }
-
-#if DEBUG_DIAGNOSTICS
- const int old_hurt = hurt_final;
-#endif
-
- // check monster resists, _without_ side effects (since the
- // beam/missile might yet miss!)
- hurt_final = mons_adjust_flavoured( mon, beam, hurt_final, false );
-
-#if DEBUG_DIAGNOSTICS
- if (!beam.is_tracer)
- {
- snprintf( info, INFO_SIZE,
- "Monster: %s; Damage: pre-AC: %d; post-AC: %d; post-resist: %d",
- ptr_monam( mon, DESC_PLAIN ), hurt, old_hurt, hurt_final );
-
- mpr( info, MSGCH_DIAGNOSTICS );
- }
-#endif
-
- // now, we know how much this monster would (probably) be
- // hurt by this beam.
- if (beam.is_tracer)
- {
- if (hurt_final != 0)
- {
- // monster could be hurt somewhat, but only apply the
- // monster's power based on how badly it is affected.
- // For example, if a fire giant (power 16) threw a
- // fireball at another fire giant, and it only took
- // 1/3 damage, then power of 5 would be applied to
- // foe_power or fr_power.
- if (beam.is_friendly ^ mons_friendly(mon))
- {
- beam.foe_count += 1;
- beam.foe_power += hurt_final * mons_power(tid) / hurt;
- }
- else
- {
- beam.fr_count += 1;
- beam.fr_power += hurt_final * mons_power(tid) / hurt;
- }
- }
- // either way, we could hit this monster, so return range used
- return (range_used_on_hit(beam));
- }
- // END non-enchantment (could still be tracer)
-
- // BEGIN real non-enchantment beam
-
- // player beams which hit friendly MIGHT annoy them and be considered
- // naughty if they do much damage (this is so as not to penalize
- // players that fling fireballs into a melee with fire elementals
- // on their side - the elementals won't give a sh*t, after all)
-
- if (nasty_beam(mon, beam))
- {
- if (YOU_KILL(beam.thrower))
- {
- if (mons_friendly(mon))
- did_god_conduct( DID_ATTACK_FRIEND, 5 );
-
- if (mons_holiness( mon ) == MH_HOLY)
- did_god_conduct( DID_ATTACK_HOLY, mon->hit_dice );
- }
-
- behaviour_event(mon, ME_ANNOY, beam_source(beam) );
- }
-
- // explosions always 'hit'
- const bool engulfs = (beam.is_explosion || beam.is_big_cloud);
-
- if (!engulfs && beam.hit < random2(mon->evasion))
- {
- // if the PLAYER cannot see the monster, don't tell them anything!
- if (player_monster_visible( &menv[tid] ) && mons_near(mon))
- {
- strcpy(info, "The ");
- strcat(info, beam.beam_name);
- strcat(info, " misses ");
- strcat(info, ptr_monam(mon, DESC_NOCAP_THE));
- strcat(info, ".");
- mpr(info);
- }
- return (0);
- }
-
- // the beam hit.
- if (mons_near(mon))
- {
- strcpy(info, "The ");
- strcat(info, beam.beam_name);
- strcat(info, engulfs? " engulfs ":" hits ");
-
- if (player_monster_visible( &menv[tid] ))
- strcat(info, ptr_monam(mon, DESC_NOCAP_THE));
- else
- strcat(info, "something");
-
- strcat(info, ".");
- mpr(info);
- }
- else
- {
- // the player might hear something,
- // if _they_ fired a missile (not beam)
- if (!silenced(you.x_pos, you.y_pos) && beam.flavour == BEAM_MISSILE
- && YOU_KILL(beam.thrower))
- {
- strcpy(info, "The ");
- strcat(info, beam.beam_name);
- strcat(info, " hits something.");
- mpr(info, MSGCH_SOUND);
- }
- }
-
- // note that hurt_final was calculated above, so we don't need it again.
- // just need to apply flavoured specials (since we called with
- // doFlavouredEffects = false above)
- hurt_final = mons_adjust_flavoured(mon, beam, hurt_final);
-
- // now hurt monster
- hurt_monster( mon, hurt_final );
-
- int thrower = YOU_KILL(beam.thrower) ? KILL_YOU_MISSILE : KILL_MON_MISSILE;
-
- if (mon->hit_points < 1)
- {
- monster_die(mon, thrower, beam.beam_source);
- }
- else
- {
- if (thrower == KILL_YOU_MISSILE && mons_near(mon))
- print_wounds(mon);
-
- // sticky flame
- if (strcmp(beam.beam_name, "sticky flame") == 0)
- {
- int levels = 1 + random2( hurt_final ) / 2;
- if (levels > 4)
- levels = 4;
-
- sticky_flame_monster( tid, YOU_KILL(beam.thrower), levels );
- }
-
-
- /* looks for missiles which aren't poison but
- are poison*ed* */
- if (strstr(beam.beam_name, "poison") != NULL
- && beam.flavour != BEAM_POISON
- && beam.flavour != BEAM_POISON_ARROW)
- {
- if (strstr(beam.beam_name, "needle") != NULL
- && random2(100) < 90 - (3 * mon->armour_class))
- {
- poison_monster( mon, YOU_KILL(beam.thrower), 2 );
- }
- else if (random2(hurt_final) - random2(mon->armour_class) > 0)
- {
- poison_monster( mon, YOU_KILL(beam.thrower) );
- }
- }
-
- bool wake_mimic = true;
- if (strstr(beam.beam_name, "curare"))
- {
- if (curare_hits_monster( beam, mon, YOU_KILL(beam.thrower), 2 ))
- wake_mimic = false;
- }
-
- if (wake_mimic && mons_is_mimic( mon->type ))
- mimic_alert(mon);
- }
-
- return (range_used_on_hit(beam));
-}
-
-static int affect_monster_enchantment(struct bolt &beam, struct monsters *mon)
-{
- if (beam.flavour == BEAM_TELEPORT) // teleportation
- {
- if (check_mons_resist_magic( mon, beam.ench_power )
- && !beam.aimed_at_feet)
- {
- return (MON_RESIST);
- }
-
- if (simple_monster_message(mon, " looks slightly unstable."))
- beam.obvious_effect = true;
-
- monster_teleport(mon, false);
-
- return (MON_AFFECTED);
- }
-
- if (beam.flavour == BEAM_BLINK)
- {
- if (!beam.aimed_at_feet
- && check_mons_resist_magic( mon, beam.ench_power ))
- {
- return (MON_RESIST);
- }
-
- if (mons_near( mon ) && player_monster_visible( mon ))
- beam.obvious_effect = true;
-
- monster_blink( mon );
- return (MON_AFFECTED);
- }
-
- if (beam.flavour == BEAM_POLYMORPH)
- {
- if (mons_holiness( mon ) != MH_NATURAL)
- return (MON_UNAFFECTED);
-
- if (check_mons_resist_magic( mon, beam.ench_power ))
- return (MON_RESIST);
-
- if (monster_polymorph(mon, RANDOM_MONSTER, 100))
- beam.obvious_effect = true;
-
- return (MON_AFFECTED);
- }
-
- if (beam.flavour == BEAM_BANISH)
- {
- if (check_mons_resist_magic( mon, beam.ench_power ))
- return (MON_RESIST);
-
- if (you.level_type == LEVEL_ABYSS)
- {
- simple_monster_message(mon, " wobbles for a moment.");
- }
- else
- monster_die(mon, KILL_RESET, beam.beam_source);
-
- beam.obvious_effect = true;
- return (MON_AFFECTED);
- }
-
- if (beam.flavour == BEAM_DEGENERATE)
- {
- if (mons_holiness(mon) != MH_NATURAL
- || mon->type == MONS_PULSATING_LUMP)
- {
- return (MON_UNAFFECTED);
- }
-
- if (check_mons_resist_magic( mon, beam.ench_power ))
- return (MON_RESIST);
-
- if (monster_polymorph(mon, MONS_PULSATING_LUMP, 100))
- beam.obvious_effect = true;
-
- return (MON_AFFECTED);
- }
-
- if (beam.flavour == BEAM_DISPEL_UNDEAD)
- {
- if (mons_holiness(mon) != MH_UNDEAD)
- return (MON_UNAFFECTED);
-
- if (simple_monster_message(mon, " convulses!"))
- beam.obvious_effect = true;
-
- hurt_monster( mon, roll_dice( beam.damage ) );
-
- goto deathCheck;
- }
-
- if (beam.flavour == BEAM_ENSLAVE_UNDEAD
- && mons_holiness(mon) == MH_UNDEAD)
- {
-#if DEBUG_DIAGNOSTICS
- snprintf( info, INFO_SIZE, "HD: %d; pow: %d",
- mon->hit_dice, beam.ench_power );
-
- mpr( info, MSGCH_DIAGNOSTICS );
-#endif
-
- if (check_mons_resist_magic( mon, beam.ench_power ))
- return (MON_RESIST);
-
- simple_monster_message(mon, " is enslaved.");
- beam.obvious_effect = true;
-
- // wow, permanent enslaving
- mon->attitude = ATT_FRIENDLY;
- return (MON_AFFECTED);
- }
-
- if (beam.flavour == BEAM_ENSLAVE_DEMON
- && mons_holiness(mon) == MH_DEMONIC)
- {
-#if DEBUG_DIAGNOSTICS
- snprintf( info, INFO_SIZE, "HD: %d; pow: %d",
- mon->hit_dice, beam.ench_power );
-
- mpr( info, MSGCH_DIAGNOSTICS );
-#endif
-
- if (mon->hit_dice * 4 >= random2(beam.ench_power))
- return (MON_RESIST);
-
- simple_monster_message(mon, " is enslaved.");
- beam.obvious_effect = true;
-
- // wow, permanent enslaving
- mon->attitude = ATT_FRIENDLY;
- return (MON_AFFECTED);
- }
-
- //
- // Everything past this point must pass this magic resistance test.
- //
- // Using check_mons_resist_magic here since things like disintegrate
- // are beyond this point. -- bwr
- if (check_mons_resist_magic( mon, beam.ench_power )
- && beam.flavour != BEAM_HASTE
- && beam.flavour != BEAM_HEALING
- && beam.flavour != BEAM_INVISIBILITY)
- {
- return (MON_RESIST);
- }
-
- if (beam.flavour == BEAM_PAIN) /* pain/agony */
- {
- if (mons_res_negative_energy( mon ))
- return (MON_UNAFFECTED);
-
- if (simple_monster_message(mon, " convulses in agony!"))
- beam.obvious_effect = true;
-
- if (strstr( beam.beam_name, "agony" ) != NULL)
- {
- // AGONY
- mon->hit_points = mon->hit_points / 2;
-
- if (mon->hit_points < 1)
- mon->hit_points = 1;
- }
- else
- {
- // PAIN
- hurt_monster( mon, roll_dice( beam.damage ) );
- }
-
- goto deathCheck;
- }
-
- if (beam.flavour == BEAM_DISINTEGRATION) /* disrupt/disintegrate */
- {
- if (simple_monster_message(mon, " is blasted."))
- beam.obvious_effect = true;
-
- hurt_monster( mon, roll_dice( beam.damage ) );
-
- goto deathCheck;
- }
-
-
- if (beam.flavour == BEAM_SLEEP)
- {
- if (mons_has_ench( mon, ENCH_SLEEP_WARY )) // slept recently
- return (MON_RESIST);
-
- if (mons_holiness(mon) != MH_NATURAL) // no unnatural
- return (MON_UNAFFECTED);
-
- if (simple_monster_message(mon, " looks drowsy..."))
- beam.obvious_effect = true;
-
- mon->behaviour = BEH_SLEEP;
- mons_add_ench( mon, ENCH_SLEEP_WARY );
-
- return (MON_AFFECTED);
- }
-
- if (beam.flavour == BEAM_BACKLIGHT)
- {
- if (backlight_monsters(mon->x, mon->y, beam.hit, 0))
- {
- beam.obvious_effect = true;
- return (MON_AFFECTED);
- }
- return (MON_UNAFFECTED);
- }
-
- // everything else?
- return (mons_ench_f2(mon, beam));
-
-deathCheck:
-
- int thrower = KILL_YOU_MISSILE;
- if (MON_KILL(beam.thrower))
- thrower = KILL_MON_MISSILE;
-
- if (mon->hit_points < 1)
- monster_die(mon, thrower, beam.beam_source);
- else
- {
- print_wounds(mon);
-
- if (mons_is_mimic( mon->type ))
- mimic_alert(mon);
- }
-
- return (MON_AFFECTED);
-}
-
-
-// extra range used on hit
-static int range_used_on_hit(struct bolt &beam)
-{
- // non-beams can only affect one thing (player/monster)
- if (!beam.is_beam)
- return (BEAM_STOP);
-
- // CHECK ENCHANTMENTS
- if (beam.beam_name[0] == '0')
- {
- switch(beam.flavour)
- {
- case BEAM_SLOW:
- case BEAM_HASTE:
- case BEAM_HEALING:
- case BEAM_PARALYSIS:
- case BEAM_CONFUSION:
- case BEAM_INVISIBILITY:
- case BEAM_TELEPORT:
- case BEAM_POLYMORPH:
- case BEAM_CHARM:
- case BEAM_BANISH:
- case BEAM_PAIN:
- case BEAM_DISINTEGRATION:
- case BEAM_DEGENERATE:
- case BEAM_DISPEL_UNDEAD:
- case BEAM_ENSLAVE_UNDEAD:
- case BEAM_ENSLAVE_DEMON:
- case BEAM_SLEEP:
- case BEAM_BACKLIGHT:
- return (BEAM_STOP);
- default:
- break;
- }
-
- return (0);
- }
-
- // hellfire stops for nobody!
- if (strcmp( beam.beam_name, "hellfire" ) == 0)
- return (0);
-
- // generic explosion
- if (beam.is_explosion || beam.is_big_cloud)
- return (BEAM_STOP);
-
- // plant spit
- if (beam.flavour == BEAM_ACID)
- return (BEAM_STOP);
-
- // lava doesn't go far, but it goes through most stuff
- if (beam.flavour == BEAM_LAVA)
- return (1);
-
- // If it isn't lightning, reduce range by a lot
- if (beam.flavour != BEAM_ELECTRICITY)
- return (random2(4) + 2);
-
- return (0);
-}
-
-/*
- Takes a bolt struct and refines it for use in the explosion function. Called
- from missile() and beam() in beam.cc. Explosions which do not follow from
- beams (eg scrolls of immolation) bypass this function.
- */
-static void explosion1(struct bolt &pbolt)
-{
- int ex_size = 1;
- // convenience
- int x = pbolt.target_x;
- int y = pbolt.target_y;
- const char *seeMsg = NULL;
- const char *hearMsg = NULL;
-
- // assume that the player can see/hear the explosion, or
- // gets burned by it anyway. :)
- pbolt.msg_generated = true;
-
- if (stricmp(pbolt.beam_name, "hellfire") == 0)
- {
- seeMsg = "The hellfire explodes!";
- hearMsg = "You hear a strangely unpleasant explosion.";
-
- pbolt.type = SYM_BURST;
- pbolt.flavour = BEAM_HELLFIRE;
- }
-
- if (stricmp(pbolt.beam_name, "golden flame") == 0)
- {
- seeMsg = "The flame explodes!";
- hearMsg = "You hear a strange explosion.";
-
- pbolt.type = SYM_BURST;
- pbolt.flavour = BEAM_HOLY; // same as golden flame? [dlb]
- }
-
- if (stricmp( pbolt.beam_name, "golden flame" ) == 0)
- {
- seeMsg = "The flame explodes!";
- hearMsg = "You feel a deep, resonant explosion.";
-
- pbolt.type = SYM_BURST;
- pbolt.flavour = BEAM_HOLY;
- ex_size = 2;
- }
-
-
- if (stricmp(pbolt.beam_name, "fireball") == 0)
- {
- seeMsg = "The fireball explodes!";
- hearMsg = "You hear an explosion.";
-
- pbolt.type = SYM_BURST;
- pbolt.flavour = BEAM_FIRE;
- ex_size = 1;
- }
-
- if (stricmp(pbolt.beam_name, "orb of electricity") == 0)
- {
- seeMsg = "The orb of electricity explodes!";
- hearMsg = "You hear a clap of thunder!";
-
- pbolt.type = SYM_BURST;
- pbolt.flavour = BEAM_ELECTRICITY;
- pbolt.colour = LIGHTCYAN;
- pbolt.damage.num = 1;
- ex_size = 2;
- }
-
- if (stricmp(pbolt.beam_name, "orb of energy") == 0)
- {
- seeMsg = "The orb of energy explodes.";
- hearMsg = "You hear an explosion.";
- }
-
- if (stricmp(pbolt.beam_name, "metal orb") == 0)
- {
- seeMsg = "The orb explodes into a blast of deadly shrapnel!";
- hearMsg = "You hear an explosion!";
-
- strcpy(pbolt.beam_name, "blast of shrapnel");
- pbolt.type = SYM_ZAP;
- pbolt.flavour = BEAM_FRAG; // sets it from pure damage to shrapnel (which is absorbed extra by armour)
- }
-
- if (stricmp(pbolt.beam_name, "great blast of cold") == 0)
- {
- seeMsg = "The blast explodes into a great storm of ice!";
- hearMsg = "You hear a raging storm!";
-
- strcpy(pbolt.beam_name, "ice storm");
- pbolt.damage.num = 6;
- pbolt.type = SYM_ZAP;
- pbolt.colour = WHITE;
- ex_size = 2 + (random2( pbolt.ench_power ) > 75);
- }
-
- if (stricmp(pbolt.beam_name, "ball of vapour") == 0)
- {
- seeMsg = "The ball expands into a vile cloud!";
- hearMsg = "You hear a gentle \'poof\'.";
- strcpy(pbolt.beam_name, "stinking cloud");
- }
-
- if (stricmp(pbolt.beam_name, "potion") == 0)
- {
- seeMsg = "The potion explodes!";
- hearMsg = "You hear an explosion!";
- strcpy(pbolt.beam_name, "cloud");
- }
-
- if (seeMsg == NULL)
- {
- seeMsg = "The beam explodes into a cloud of software bugs!";
- hearMsg = "You hear the sound of one hand clapping!";
- }
-
-
- if (!pbolt.is_tracer)
- {
- // check for see/hear/no msg
- if (see_grid(x,y) || (x == you.x_pos && y == you.y_pos))
- mpr(seeMsg);
- else
- {
- if (!(silenced(x,y) || silenced(you.x_pos, you.y_pos)))
- mpr(hearMsg, MSGCH_SOUND);
- else
- pbolt.msg_generated = false;
- }
- }
-
- pbolt.ex_size = ex_size;
- explosion( pbolt );
-} // end explosion1()
-
-
-#define MAX_EXPLOSION_RADIUS 9
-
-// explosion is considered to emanate from beam->target_x, target_y
-// and has a radius equal to ex_size. The explosion will respect
-// boundaries like walls, but go through/around statues/idols/etc.
-
-// for each cell affected by the explosion, affect() is called.
-
-void explosion( struct bolt &beam, bool hole_in_the_middle )
-{
- int r = beam.ex_size;
-
- // beam is now an explosion; set in_explosion_phase
- beam.in_explosion_phase = true;
-
-#if DEBUG_DIAGNOSTICS
- snprintf( info, INFO_SIZE,
- "explosion at (%d, %d) : t=%d c=%d f=%d hit=%d dam=%dd%d",
- beam.target_x, beam.target_y,
- beam.type, beam.colour, beam.flavour,
- beam.hit, beam.damage.num, beam.damage.size );
-
- mpr( info, MSGCH_DIAGNOSTICS );
-#endif
-
- // for now, we don't support explosions greater than 9 radius
- if (r > MAX_EXPLOSION_RADIUS)
- r = MAX_EXPLOSION_RADIUS;
-
- // make a noise
- noisy( 10 + 5*r, beam.target_x, beam.target_y );
-
- // set map to false
- for (int i=0; i<19; i++)
- {
- for (int j=0; j<19; j++)
- explode_map[i][j] = false;
- }
-
- // discover affected cells - recursion is your friend!
- // this is done to model an explosion's behaviour around
- // corners where a simple 'line of sight' isn't quite
- // enough. This might be slow for really big explosions,
- // as the recursion runs approximately as R^2
- explosion_map(beam, 0, 0, 0, 0, r);
-
- // go through affected cells, drawing effect and
- // calling affect() and affect_items() for each.
- // now, we get a bit fancy, drawing all radius 0
- // effects, then radius 1, radius 2, etc. It looks
- // a bit better that way.
-
- // turn buffering off
-#ifdef WIN32CONSOLE
- bool oldValue = true;
- if (!beam.is_tracer)
- oldValue = setBuffering(false);
-#endif
-
- // --------------------- begin boom ---------------
-
- bool drawing = true;
- for (int i = 0; i < 2; i++)
- {
- // do center -- but only if its affected
- if (!hole_in_the_middle)
- explosion_cell(beam, 0, 0, drawing);
-
- // do the rest of it
- for(int rad = 1; rad <= r; rad ++)
- {
- // do sides
- for (int ay = 1 - rad; ay <= rad - 1; ay += 1)
- {
- if (explode_map[-rad+9][ay+9])
- explosion_cell(beam, -rad, ay, drawing);
-
- if (explode_map[rad+9][ay+9])
- explosion_cell(beam, rad, ay, drawing);
- }
-
- // do top & bottom
- for (int ax = -rad; ax <= rad; ax += 1)
- {
- if (explode_map[ax+9][-rad+9])
- explosion_cell(beam, ax, -rad, drawing);
-
- if (explode_map[ax+9][rad+9])
- explosion_cell(beam, ax, rad, drawing);
- }
-
- // new-- delay after every 'ring' {gdl}
-#ifdef UNIX
- // If we don't refresh curses we won't
- // guarantee that the explosion is visible
- if (drawing)
- update_screen();
-#endif
- // only delay on real explosion
- if (!beam.is_tracer && drawing)
- delay(50);
- }
-
- drawing = false;
- }
-
- // ---------------- end boom --------------------------
-
-#ifdef WIN32CONSOLE
- if (!beam.is_tracer)
- setBuffering(oldValue);
-#endif
-
- // duplicate old behaviour - pause after entire explosion
- // has been drawn.
- if (!beam.is_tracer)
- more();
-}
-
-static void explosion_cell(struct bolt &beam, int x, int y, bool drawOnly)
-{
- bool random_beam = false;
- int realx = beam.target_x + x;
- int realy = beam.target_y + y;
-
- if (!drawOnly)
- {
- // random beams: randomize before affect
- if (beam.flavour == BEAM_RANDOM)
- {
- random_beam = true;
- beam.flavour = BEAM_FIRE + random2(7);
- }
-
- affect(beam, realx, realy);
-
- if (random_beam)
- beam.flavour = BEAM_RANDOM;
- }
-
- // early out for tracer
- if (beam.is_tracer)
- return;
-
- // now affect items
- if (!drawOnly)
- affect_items(beam, realx, realy);
-
- if (drawOnly)
- {
- int drawx = realx - you.x_pos + 18;
- int drawy = realy - you.y_pos + 9;
-
- if (see_grid(realx, realy) || (realx == you.x_pos && realy == you.y_pos))
- {
- // bounds check
- if (drawx > 8 && drawx < 26 && drawy > 0 && drawy < 18)
- {
- if (beam.colour == BLACK)
- textcolor(random_colour());
- else
- textcolor(beam.colour);
-
- gotoxy(drawx, drawy);
- putch('#');
- }
- }
- }
-}
-
-static void explosion_map( struct bolt &beam, int x, int y,
- int count, int dir, int r )
-{
- // 1. check to see out of range
- if (x * x + y * y > r * r + r)
- return;
-
- // 2. check count
- if (count > 10*r)
- return;
-
- // 3. check to see if we're blocked by something
- // specifically, we're blocked by WALLS. Not
- // statues, idols, etc.
- int dngn_feat = grd[beam.target_x + x][beam.target_y + y];
-
- // special case: explosion originates from rock/statue
- // (e.g. Lee's rapid deconstruction) - in this case, ignore
- // solid cells at the center of the explosion.
- if (dngn_feat < DNGN_GREEN_CRYSTAL_WALL || dngn_feat == DNGN_WAX_WALL)
- {
- if (!(x==0 && y==0))
- return;
- }
-
- // hmm, I think we're ok
- explode_map[x+9][y+9] = true;
-
- // now recurse in every direction except the one we
- // came from
- for(int i=0; i<4; i++)
- {
- if (i+1 != dir)
- {
- int cadd = 5;
- if (x * spreadx[i] < 0 || y * spready[i] < 0)
- cadd = 17;
-
- explosion_map( beam, x + spreadx[i], y + spready[i],
- count + cadd, opdir[i], r );
- }
- }
-}
-
-// returns true if the beam is harmful (ignoring monster
-// resists) -- mon is given for 'special' cases where,
-// for example, "Heal" might actually hurt undead, or
-// "Holy Word" being ignored by holy monsters, etc.
-//
-// only enchantments should need the actual monster type
-// to determine this; non-enchantments are pretty
-// straightforward.
-bool nasty_beam(struct monsters *mon, struct bolt &beam)
-{
- // take care of non-enchantments
- if (beam.beam_name[0] != '0')
- return (true);
-
- // now for some non-hurtful enchantments
-
- // degeneration / sleep
- if (beam.flavour == BEAM_DEGENERATE || beam.flavour == BEAM_SLEEP)
- return (mons_holiness(mon) == MH_NATURAL);
-
- // dispel undead / control undead
- if (beam.flavour == BEAM_DISPEL_UNDEAD || beam.flavour == BEAM_ENSLAVE_UNDEAD)
- return (mons_holiness(mon) == MH_UNDEAD);
-
- // pain/agony
- if (beam.flavour == BEAM_PAIN)
- return (!mons_res_negative_energy( mon ));
-
- // control demon
- if (beam.flavour == BEAM_ENSLAVE_DEMON)
- return (mons_holiness(mon) == MH_DEMONIC);
-
- // haste
- if (beam.flavour == BEAM_HASTE)
- return (false);
-
- // healing
- if (beam.flavour == BEAM_HEALING || beam.flavour == BEAM_INVISIBILITY)
- return (false);
-
- // everything else is considered nasty by everyone
- return (true);
-}