1 /*
2  *  File:       beam.cc
3  *  Summary:    Functions related to ranged attacks.
4  *  Written by: Linley Henzell
5  *
6  *  Change History (most recent first):
7  *
8  *   <7>    21mar2001    GDL    Replaced all FP arithmetic with integer*100 math
9  *   <6>    07jan2001    GDL    complete rewrite.
10  *   <5>    22July2000   GDL    allowed 'dummy' missiles from monsters
11  *   <4>    11/14/99     cdl    evade beams with random40(ev) vice random2(ev)
12  *                              all armour now protects against shrapnel
13  *   <3>     6/ 2/99     DML    Added enums
14  *   <2>     5/20/99     BWR    Added refreshs for curses
15  *   <1>     -/--/--     LRH    Created
16  */
17 
18 #include "AppHdr.h"
19 #include "beam.h"
20 
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 
25 #ifdef DOS
26 #include <dos.h>
27 #include <conio.h>
28 #endif
29 #if DEBUG_DIAGNOSTICS
30 #include <stdio.h>
31 #endif
32 
33 #include "externs.h"
34 
35 #include "cloud.h"
36 #include "effects.h"
37 #include "enum.h"
38 #include "it_use2.h"
39 #include "itemname.h"
40 #include "items.h"
41 #include "misc.h"
42 #include "monplace.h"
43 #include "monstuff.h"
44 #include "mon-util.h"
45 #include "mstuff2.h"
46 #include "ouch.h"
47 #include "player.h"
48 #include "religion.h"
49 #include "skills.h"
50 #include "spells1.h"
51 #include "spells3.h"
52 #include "spells4.h"
53 #include "stuff.h"
54 #include "view.h"
55 
56 #define BEAM_STOP       1000        // all beams stopped by subtracting this
57                                     // from remaining range
58 #define MON_RESIST      0           // monster resisted
59 #define MON_UNAFFECTED  1           // monster unaffected
60 #define MON_AFFECTED    2           // monster was unaffected
61 
62 extern FixedVector< char, NUM_STATUE_TYPES >  Visible_Statue;  // in acr.cc
63 
64 static int spreadx[] = { 0, 0, 1, -1 };
65 static int spready[] = { -1, 1, 0, 0 };
66 static int opdir[]   = { 2, 1, 4, 3 };
67 static FixedArray < bool, 19, 19 > explode_map;
68 
69 // helper functions (some of these, esp. affect(),  should probably
70 // be public):
71 static void sticky_flame_monster( int mn, bool source, int hurt_final );
72 static bool affectsWalls(struct bolt &beam);
73 static int affect(struct bolt &beam, int x, int y);
74 static bool isBouncy(struct bolt &beam);
75 static void beam_drop_object( struct bolt &beam, item_def *item, int x, int y );
76 static bool beam_term_on_target(struct bolt &beam);
77 static void beam_explodes(struct bolt &beam, int x, int y);
78 static int bounce(int &step1, int &step2, int w1, int w2, int &n1, int &n2,
79     int l1, int l2, int &t1, int &t2, bool topBlocked, bool sideBlocked);
80 static bool fuzzyLine(int nx, int ny, int &tx, int &ty, int lx, int ly,
81     int stepx, int stepy, bool roundX, bool roundY);
82 static int  affect_wall(struct bolt &beam, int x, int y);
83 static int  affect_place_clouds(struct bolt &beam, int x, int y);
84 static void affect_place_explosion_clouds(struct bolt &beam, int x, int y);
85 static int  affect_player(struct bolt &beam);
86 static void affect_items(struct bolt &beam, int x, int y);
87 static int  affect_monster(struct bolt &beam, struct monsters *mon);
88 static int  affect_monster_enchantment(struct bolt &beam, struct monsters *mon);
89 static int  range_used_on_hit(struct bolt &beam);
90 static void explosion1(struct bolt &pbolt);
91 static void explosion_map(struct bolt &beam, int x, int y,
92     int count, int dir, int r);
93 static void explosion_cell(struct bolt &beam, int x, int y, bool drawOnly);
94 
95 static void zappy(char z_type, int power, struct bolt &pbolt);
96 
zapping(char ztype,int power,struct bolt & pbolt)97 void zapping(char ztype, int power, struct bolt &pbolt)
98 {
99 
100 #if DEBUG_DIAGNOSTICS
101     snprintf( info, INFO_SIZE, "zapping:  power=%d", power );
102     mpr( info, MSGCH_DIAGNOSTICS );
103 #endif
104 
105     // GDL: note that rangeMax is set to 0, which means that max range is
106     // equal to range.  This is OK,  since rangeMax really only matters for
107     // stuff monsters throw/zap.
108 
109     // all of the following might be changed by zappy():
110     pbolt.range = 8 + random2(5);       // default for "0" beams (I think)
111     pbolt.rangeMax = 0;
112     pbolt.hit = 0;                      // default for "0" beams (I think)
113     pbolt.damage = dice_def( 1, 0 );    // default for "0" beams (I think)
114     pbolt.type = 0;                     // default for "0" beams
115     pbolt.flavour = BEAM_MAGIC;         // default for "0" beams
116     pbolt.ench_power = power;
117     pbolt.obviousEffect = false;
118     pbolt.isBeam = false;               // default for all beams.
119     pbolt.isTracer = false;             // default for all player beams
120     pbolt.thrower = KILL_YOU_MISSILE;   // missile from player
121     pbolt.aux_source = NULL;            // additional source info, unused
122 
123     // fill in the bolt structure
124     zappy( ztype, power, pbolt );
125 
126     if (ztype == ZAP_LIGHTNING && !silenced(you.x_pos, you.y_pos))
127         // needs to check silenced at other location, too {dlb}
128     {
129         mpr("You hear a mighty clap of thunder!");
130         noisy( 25, you.x_pos, you.y_pos );
131     }
132 
133     fire_beam(pbolt);
134 
135     return;
136 }                               // end zapping()
137 
calc_dice(int num_dice,int max_damage)138 dice_def calc_dice( int num_dice, int max_damage )
139 {
140     dice_def    ret( num_dice, 0 );
141 
142     if (num_dice <= 1)
143     {
144         ret.num  = 1;
145         ret.size = max_damage;
146     }
147     else if (max_damage <= num_dice)
148     {
149         ret.num  = max_damage;
150         ret.size = 1;
151     }
152     else
153     {
154         // Divied the damage among the dice, and add one
155         // occasionally to make up for the fractions. -- bwr
156         ret.size = max_damage / num_dice;
157         ret.size += (random2( num_dice ) < max_damage % num_dice);
158     }
159 
160     return (ret);
161 }
162 
163 // *do not* call this function directly (duh - it's static), need to
164 // see zapping() for default values not set within this function {dlb}
zappy(char z_type,int power,struct bolt & pbolt)165 static void zappy( char z_type, int power, struct bolt &pbolt )
166 {
167     int temp_rand = 0;          // probability determination {dlb}
168 
169     // Note: The incoming power is not linear in the case of spellcasting.
170     // The power curve currently allows for the character to reasonably
171     // get up to a power level of about a 100, but more than that will
172     // be very hard (and the maximum is 200).  The low level power caps
173     // provide the useful feature in that they allow for low level spells
174     // to have quick advancement, but don't cause them to obsolete the
175     // higher level spells. -- bwr
176     //
177     // I've added some example characters below to show how little
178     // people should be concerned about the power caps.
179     //
180     // The example characters are simplified to three stats:
181     //
182     // - Intelligence: This magifies power, its very useful.
183     //
184     // - Skills: This represents the character having Spellcasting
185     //   and the average of the component skills at this level.
186     //   Although, Spellcasting probably isn't quite as high as
187     //   other spell skills for a lot of characters, note that it
188     //   contributes much less to the total power (about 20%).
189     //
190     // - Enhancers:  These are equipment that the player can use to
191     //   apply additional magnifiers (x1.5) to power.  There are
192     //   also inhibitors that reduce power (/2.0), but we're not
193     //   concerned about those here.  Anyways, the character can
194     //   currently have up to 3 levels (for x1.5, x2.25, x3.375).
195     //   The lists below should help to point out the difficulty
196     //   and cost of getting more than one level of enhancement.
197     //
198     //   Here's a list of current magnifiers:
199     //
200     //   - rings of fire/cold
201     //   - staff of fire/cold/air/earth/poison/death/conjure/enchant/summon
202     //   - staff of Olgreb (poison)
203     //   - robe of the Archmagi (necro, conjure, enchant, summon)
204     //   - Mummy intrinsic (+1 necromancy at level 13, +2 at level 26)
205     //   - Necromutation (+1 to necromancy -- note: undead can't use this)
206     //   - Ring of Fire (+1 to fire)
207     //
208     //   The maximum enhancement, by school (but capped at 3):
209     //
210     //   - Necromancy:  4 (Mummies), 3 (others)
211     //   - Fire:        4
212     //   - Cold:        3
213     //   - Conjuration: 2
214     //   - Enchantment: 2
215     //   - Summoning:   2
216     //   - Air:         1
217     //   - Earth:       1
218     //   - Poison:      1
219     //   - Translocations, Transmigrations, Divinations intentionally 0
220 
221     switch (z_type)
222     {
223     // level 1
224     //
225     // This cap is to keep these easy and very cheap spells from
226     // becoming too powerful.
227     //
228     // Example characters with about 25 power:
229     //
230     // - int  5, skills 20, 0 enhancers
231     // - int  5, skills 14, 1 enhancer
232     // - int 10, skills 10, 0 enhancers
233     // - int 10, skills  7, 1 enhancers
234     // - int 15, skills  7, 0 enhancers
235     // - int 20, skills  6, 0 enhancers
236     case ZAP_STRIKING:
237     case ZAP_MAGIC_DARTS:
238     case ZAP_STING:
239     case ZAP_ELECTRICITY:
240     case ZAP_FLAME_TONGUE:
241     case ZAP_SMALL_SANDBLAST:
242     case ZAP_DISRUPTION:                // ench_power boosted below
243     case ZAP_PAIN:                      // ench_power boosted below
244         if (power > 25)
245             power = 25;
246         break;
247 
248     // level 2/3
249     //
250     // The following examples should make it clear that in the
251     // early game this cap is only limiting to serious spellcasters
252     // (they could easily reach the 20-10-0 example).
253     //
254     // Example characters with about 50 power:
255     //
256     // - int 10, skills 20, 0 enhancers
257     // - int 10, skills 14, 1 enhancer
258     // - int 15, skills 14, 0 enhancers
259     // - int 15, skills 10, 1 enhancer
260     // - int 20, skills 10, 0 enhancers
261     // - int 20, skills  7, 1 enhancer
262     // - int 25, skills  8, 0 enhancers
263     case ZAP_SANDBLAST:
264     case ZAP_FLAME:             // also ability (pow = lev * 2)
265     case ZAP_FROST:             // also ability (pow = lev * 2)
266     case ZAP_STONE_ARROW:
267         if (power > 50)
268             power = 50;
269         break;
270 
271     // Here are some examples that show that its fairly safe to assume
272     // that a high level character can easily have 75 power.
273     //
274     // Example characters with about 75 power:
275     //
276     // - int 10, skills 27, 1 enhancer
277     // - int 15, skills 27, 0 enhancers
278     // - int 15, skills 16, 1 enhancer
279     // - int 20, skills 20, 0 enhancers
280     // - int 20, skills 14, 1 enhancer
281     // - int 25, skills 16, 0 enhancers
282 
283     // level 4
284     //
285     // The following examples should make it clear that this is the
286     // effective maximum power.  Its not easy to get to 100 power,
287     // but 20-20-1 or 25-16-1 is certainly attainable by a high level
288     // spellcaster.  As you can see from the examples at 150 and 200,
289     // getting much power beyond this is very difficult.
290     //
291     // Level 3 and 4 spells cannot be overpowered.
292     //
293     // Example characters with about 100 power:
294     //
295     // - int 10, skills 27, 2 enhancers
296     // - int 15, skills 27, 1 enhancer
297     // - int 20, skills 20, 1 enhancer
298     // - int 25, skills 24, 0 enhancers
299     // - int 25, skills 16, 1 enhancer
300     case ZAP_MYSTIC_BLAST:
301     case ZAP_STICKY_FLAME:
302     case ZAP_ICE_BOLT:
303     case ZAP_DISPEL_UNDEAD:     // ench_power raised below
304         if (power > 100)
305             power = 100;
306         break;
307 
308     // levels 5-7
309     //
310     // These spells used to be capped, but its very hard to raise
311     // power over 100, and these examples should show that.
312     // Only the twinkiest of characters are expected to get to 150.
313     //
314     // Example characters with about 150 power:
315     //
316     // - int 15, skills 27, 3 enhancers (actually, only 146)
317     // - int 20, skills 27, 2 enhancers (actually, only 137)
318     // - int 20, skills 21, 3 enhancers
319     // - int 25, skills 26, 2 enhancers
320     // - int 30, skills 21, 2 enhancers
321     // - int 40, skills 24, 1 enhancer
322     // - int 70, skills 20, 0 enhancers
323     case ZAP_FIRE:
324     case ZAP_COLD:
325     case ZAP_VENOM_BOLT:
326     case ZAP_MAGMA:
327     case ZAP_AGONY:
328     case ZAP_LIGHTNING:                 // also invoc * 6 or lev * 2 (abils)
329     case ZAP_NEGATIVE_ENERGY:           // also ability (pow = lev * 6)
330     case ZAP_IRON_BOLT:
331     case ZAP_DISINTEGRATION:
332     case ZAP_FIREBALL:
333     case ZAP_ORB_OF_ELECTRICITY:
334     case ZAP_ORB_OF_FRAGMENTATION:
335     case ZAP_POISON_ARROW:
336         // if (power > 150)
337         //     power = 150;
338         break;
339 
340     // levels 8-9
341     //
342     // These spells are capped at 200 (which is the cap in calc_spell_power).
343     // As an example of how little of a cap that is, consider the fact
344     // that a 70-27-3 character has an uncapped power of 251.  Characters
345     // are never expected to get to this cap.
346     //
347     // Example characters with about 200 power:
348     //
349     // - int 30, skills 27, 3 enhancers (actually, only 190)
350     // - int 40, skills 27, 2 enhancers (actually, only 181)
351     // - int 40, skills 23, 3 enhancers
352     // - int 70, skills 27, 0 enhancers (actually, only 164)
353     // - int 70, skills 27, 1 enhancers (actually, only 194)
354     // - int 70, skills 20, 2 enhancers
355     // - int 70, skills 13, 3 enhancers
356     case ZAP_CRYSTAL_SPEAR:
357     case ZAP_HELLFIRE:
358     case ZAP_ICE_STORM:
359     case ZAP_CLEANSING_FLAME:
360         // if (power > 200)
361         //     power = 200;
362         break;
363 
364     // unlimited power (needs a good reason)
365     case ZAP_BONE_SHARDS:    // incoming power is modified for mass
366     case ZAP_BEAM_OF_ENERGY: // inaccuracy (only on staff, hardly hits)
367         break;
368 
369     // natural/mutant breath/spit powers (power ~= characer level)
370     case ZAP_SPIT_POISON:               // lev + mut * 5
371     case ZAP_BREATHE_FIRE:              // lev + mut * 4 + 12 (if dragonform)
372     case ZAP_BREATHE_FROST:             // lev
373     case ZAP_BREATHE_ACID:              // lev (or invoc * 3 from minor destr)
374     case ZAP_BREATHE_POISON:            // lev
375     case ZAP_BREATHE_POWER:             // lev
376     case ZAP_BREATHE_STEAM:             // lev
377         if (power > 50)
378             power = 50;
379         break;
380 
381     // enchantments and other resistable effects
382     case ZAP_SLOWING:
383     case ZAP_HASTING:
384     case ZAP_PARALYSIS:
385     case ZAP_BACKLIGHT:
386     case ZAP_SLEEP:
387     case ZAP_CONFUSION:
388     case ZAP_INVISIBILITY:
389     case ZAP_ENSLAVEMENT:
390     case ZAP_TELEPORTATION:
391     case ZAP_DIGGING:
392     case ZAP_POLYMORPH_OTHER:
393     case ZAP_DEGENERATION:
394     case ZAP_BANISHMENT:
395         // This is the only power that matters.  We magnify it apparently
396         // to get values that work better with magic resistance checks...
397         // those checks will scale down this value and max it out at 120.
398         pbolt.ench_power *= 3;
399         pbolt.ench_power /= 2;
400         break;
401 
402     // anything else we cap to 100
403     default:
404         if (power > 100)
405             power = 100;
406         break;
407     }
408 
409     // Note:  I'm only displaying the top damage and such here, that's
410     // because it's really not been known before (since the above caps
411     // didn't exist), so they were all pretty much unlimited before.
412     // Also note, that the high end damage occurs at the cap, only
413     // players that are that powerful can get that damage... and
414     // although these numbers might seem small, you should remember
415     // that Dragons in this game are 60-90 hp monsters, and very
416     // few monsters have more than 100 hp (and that 1d5 damage is
417     // still capable of taking a good sized chunk (and possibly killing)
418     // any monster you're likely to meet in the first three levels). -- bwr
419 
420     // Note: damage > 100 signals that "random2(damage - 100)" will be
421     // applied three times, which not only ups the damage but gives
422     // a more normal distribution.
423     switch (z_type)
424     {
425     case ZAP_STRIKING:                                  // cap 25
426         strcpy(pbolt.beam_name, "force bolt");
427         pbolt.colour = BLACK;
428         pbolt.range = 8 + random2(5);
429         pbolt.damage = dice_def( 1, 5 );                // dam: 5
430         pbolt.hit = 8 + power / 10;                     // 25: 10
431         pbolt.type = SYM_SPACE;
432         pbolt.flavour = BEAM_MMISSILE;                  // unresistable
433         pbolt.obviousEffect = true;
434         break;
435 
436     case ZAP_MAGIC_DARTS:                               // cap 25
437         strcpy(pbolt.beam_name, "magic dart");
438         pbolt.colour = LIGHTMAGENTA;
439         pbolt.range = random2(5) + 8;
440         pbolt.damage = dice_def( 1, 3 + power / 5 );    // 25: 1d8
441         pbolt.hit = 1500;                               // hits always
442         pbolt.type = SYM_ZAP;
443         pbolt.flavour = BEAM_MMISSILE;                  // unresistable
444         pbolt.obviousEffect = true;
445         break;
446 
447     case ZAP_STING:                                     // cap 25
448         strcpy(pbolt.beam_name, "sting");
449         pbolt.colour = GREEN;
450         pbolt.range = 8 + random2(5);
451         pbolt.damage = dice_def( 1, 3 + power / 5 );    // 25: 1d8
452         pbolt.hit = 8 + power / 5;                      // 25: 13
453         pbolt.type = SYM_ZAP;
454         pbolt.flavour = BEAM_POISON;                    // extra damage
455 
456         pbolt.obviousEffect = true;
457         break;
458 
459     case ZAP_ELECTRICITY:                               // cap 20
460         strcpy(pbolt.beam_name, "zap");
461         pbolt.colour = LIGHTCYAN;
462         pbolt.range = 6 + random2(8);                   // extended in beam
463         pbolt.damage = dice_def( 1, 3 + random2(power) / 2 ); // 25: 1d11
464         pbolt.hit = 8 + power / 7;                      // 25: 11
465         pbolt.type = SYM_ZAP;
466         pbolt.flavour = BEAM_ELECTRICITY;               // beams & reflects
467 
468         pbolt.obviousEffect = true;
469         pbolt.isBeam = true;
470         break;
471 
472     case ZAP_DISRUPTION:                                // cap 25
473         strcpy(pbolt.beam_name, "0");
474         pbolt.flavour = BEAM_DISINTEGRATION;
475         pbolt.range = 7 + random2(8);
476         pbolt.damage = dice_def( 1, 4 + power / 5 );    // 25: 1d9
477         pbolt.ench_power *= 3;
478         break;
479 
480     case ZAP_PAIN:                                      // cap 25
481         strcpy(pbolt.beam_name, "0");
482         pbolt.flavour = BEAM_PAIN;
483         pbolt.range = 7 + random2(8);
484         pbolt.damage = dice_def( 1, 4 + power / 5 );    // 25: 1d9
485         pbolt.ench_power *= 7;
486         pbolt.ench_power /= 2;
487         break;
488 
489     case ZAP_FLAME_TONGUE:                              // cap 25
490         strcpy(pbolt.beam_name, "flame");
491         pbolt.colour = RED;
492 
493         pbolt.range = 1 + random2(2) + random2(power) / 10;
494         if (pbolt.range > 4)
495             pbolt.range = 4;
496 
497         pbolt.damage = dice_def( 1, 8 + power / 4 );    // 25: 1d14
498         pbolt.hit = 7 + power / 6;                      // 25: 11
499         pbolt.type = SYM_BOLT;
500         pbolt.flavour = BEAM_FIRE;
501 
502         pbolt.obviousEffect = true;
503         break;
504 
505     case ZAP_SMALL_SANDBLAST:                           // cap 25
506         strcpy(pbolt.beam_name, "blast of ");
507 
508         temp_rand = random2(4);
509 
510         strcat(pbolt.beam_name, (temp_rand == 0) ? "dust" :
511                                 (temp_rand == 1) ? "dirt" :
512                                 (temp_rand == 2) ? "grit" : "sand");
513 
514         pbolt.colour = BROWN;
515         pbolt.range = (random2(power) > random2(30)) ? 2 : 1;
516         pbolt.damage = dice_def( 1, 8 + power / 4 );    // 25: 1d14
517         pbolt.hit = 8 + power / 5;                      // 25: 13
518         pbolt.type = SYM_BOLT;
519         pbolt.flavour = BEAM_FRAG;                      // extra AC resist
520 
521         pbolt.obviousEffect = true;
522         break;
523 
524     case ZAP_SANDBLAST:                                 // cap 50
525         strcpy(pbolt.beam_name, coinflip() ? "blast of rock" : "rocky blast");
526         pbolt.colour = BROWN;
527 
528         pbolt.range = 2 + random2(power) / 20;
529         if (pbolt.range > 4)
530             pbolt.range = 4;
531 
532         pbolt.damage = dice_def( 2, 4 + power / 3 );    // 25: 2d12
533         pbolt.hit = 13 + power / 10;                    // 25: 15
534         pbolt.type = SYM_BOLT;
535         pbolt.flavour = BEAM_FRAG;                      // extra AC resist
536 
537         pbolt.obviousEffect = true;
538         break;
539 
540     case ZAP_BONE_SHARDS:
541         strcpy(pbolt.beam_name, "spray of bone shards");
542         pbolt.colour = LIGHTGREY;
543         pbolt.range = 7 + random2(10);
544 
545         // Incoming power is highly dependant on mass (see spells3.cc).
546         // Basic function is power * 15 + mass...  with the largest
547         // available mass (3000) we get a power of 4500 at a power
548         // level of 100 (for 3d20).
549         pbolt.damage = dice_def( 3, 2 + (power / 250) );
550         pbolt.hit = 8 + (power / 100);                   // max hit: 53
551         pbolt.type = SYM_ZAP;
552         pbolt.flavour = BEAM_MAGIC;                      // unresisted
553 
554         pbolt.obviousEffect = true;
555         pbolt.isBeam = true;
556         break;
557 
558     case ZAP_FLAME:                                     // cap 50
559         strcpy(pbolt.beam_name, "puff of flame");
560         pbolt.colour = RED;
561         pbolt.range = 8 + random2(5);
562         pbolt.damage = dice_def( 2, 4 + power / 10 );   // 25: 2d6  50: 2d9
563         pbolt.hit = 8 + power / 10;                     // 25: 10   50: 13
564         pbolt.type = SYM_ZAP;
565         pbolt.flavour = BEAM_FIRE;
566 
567         pbolt.obviousEffect = true;
568         break;
569 
570     case ZAP_FROST:                                     // cap 50
571         strcpy(pbolt.beam_name, "puff of frost");
572         pbolt.colour = WHITE;
573         pbolt.range = 8 + random2(5);
574         pbolt.damage = dice_def( 2, 4 + power / 10 );   // 25: 2d6  50: 2d9
575         pbolt.hit = 8 + power / 10;                     // 50: 10   50: 13
576         pbolt.type = SYM_ZAP;
577         pbolt.flavour = BEAM_COLD;
578 
579         pbolt.obviousEffect = true;
580         break;
581 
582     case ZAP_STONE_ARROW:                               // cap 100
583         strcpy(pbolt.beam_name, "stone arrow");
584         pbolt.colour = LIGHTGREY;
585         pbolt.range = 8 + random2(5);
586         pbolt.damage = dice_def( 2, 4 + power / 8 );    // 25: 2d7  50: 2d10
587         pbolt.hit = 5 + power / 10;                     // 25: 6    50: 7
588         pbolt.type = SYM_MISSILE;
589         pbolt.flavour = BEAM_MMISSILE;                  // unresistable
590 
591         pbolt.obviousEffect = true;
592         break;
593 
594     case ZAP_STICKY_FLAME:                              // cap 100
595         strcpy(pbolt.beam_name, "sticky flame");        // extra damage
596         pbolt.colour = RED;
597         pbolt.range = 8 + random2(5);
598         pbolt.damage = dice_def( 2, 3 + power / 12 );   // 50: 2d7  100: 2d11
599         pbolt.hit = 11 + power / 10;                    // 50: 16   100: 21
600         pbolt.type = SYM_ZAP;
601         pbolt.flavour = BEAM_FIRE;
602 
603         pbolt.obviousEffect = true;
604         break;
605 
606     case ZAP_MYSTIC_BLAST:                              // cap 100
607         strcpy(pbolt.beam_name, "orb of energy");
608         pbolt.colour = LIGHTMAGENTA;
609         pbolt.range = 8 + random2(5);
610         pbolt.damage = calc_dice( 2, 15 + (power * 2) / 5 );
611         pbolt.hit = 10 + power / 7;                     // 50: 17   100: 24
612         pbolt.type = SYM_ZAP;
613         pbolt.flavour = BEAM_MMISSILE;                  // unresistable
614 
615         pbolt.obviousEffect = true;
616         break;
617 
618     case ZAP_ICE_BOLT:                                  // cap 100
619         strcpy(pbolt.beam_name, "bolt of ice");
620         pbolt.colour = WHITE;
621         pbolt.range = 8 + random2(5);
622         pbolt.damage = calc_dice( 3, 10 + power / 2 );
623         pbolt.hit = 9 + power / 12;                     // 50: 13   100: 17
624         pbolt.type = SYM_ZAP;
625         pbolt.flavour = BEAM_ICE;                       // half resistable
626         break;
627 
628     case ZAP_DISPEL_UNDEAD:                             // cap 100
629         strcpy(pbolt.beam_name, "0");
630         pbolt.flavour = BEAM_DISPEL_UNDEAD;
631         pbolt.range = 7 + random2(8);
632         pbolt.damage = calc_dice( 3, 20 + (power * 3) / 4 );
633         pbolt.ench_power *= 3;
634         pbolt.ench_power /= 2;
635         break;
636 
637     case ZAP_MAGMA:                                     // cap 150
638         strcpy(pbolt.beam_name, "bolt of magma");
639         pbolt.colour = RED;
640         pbolt.range = 5 + random2(4);
641         pbolt.damage = calc_dice( 4, 10 + (power * 3) / 5 );
642         pbolt.hit = 8 + power / 25;                     // 50: 10   100: 14
643         pbolt.type = SYM_ZAP;
644         pbolt.flavour = BEAM_LAVA;
645 
646         pbolt.obviousEffect = true;
647         pbolt.isBeam = true;
648         break;
649 
650     case ZAP_FIRE:                                      // cap 150
651         strcpy(pbolt.beam_name, "bolt of fire");
652         pbolt.colour = RED;
653         pbolt.range = 7 + random2(10);
654         pbolt.damage = calc_dice( 6, 20 + (power * 3) / 4 );
655         pbolt.hit = 10 + power / 25;                    // 50: 12   100: 14
656         pbolt.type = SYM_ZAP;
657         pbolt.flavour = BEAM_FIRE;
658 
659         pbolt.obviousEffect = true;
660         pbolt.isBeam = true;
661         break;
662 
663     case ZAP_COLD:                                      // cap 150
664         strcpy(pbolt.beam_name, "bolt of cold");
665         pbolt.colour = WHITE;
666         pbolt.range = 7 + random2(10);
667         pbolt.damage = calc_dice( 6, 20 + (power * 3) / 4 );
668         pbolt.hit = 10 + power / 25;                    // 50: 12   100: 14
669         pbolt.type = SYM_ZAP;
670         pbolt.flavour = BEAM_COLD;
671 
672         pbolt.obviousEffect = true;
673         pbolt.isBeam = true;
674         break;
675 
676     case ZAP_VENOM_BOLT:                                // cap 150
677         strcpy(pbolt.beam_name, "bolt of poison");
678         pbolt.colour = LIGHTGREEN;
679         pbolt.range = 8 + random2(10);
680         pbolt.damage = calc_dice( 4, 15 + power / 2 );
681         pbolt.hit = 8 + power / 20;                     // 50: 10   100: 13
682         pbolt.type = SYM_ZAP;
683         pbolt.flavour = BEAM_POISON;                    // extra damage
684 
685         pbolt.obviousEffect = true;
686         pbolt.isBeam = true;
687         break;
688 
689     case ZAP_NEGATIVE_ENERGY:                           // cap 150
690         strcpy(pbolt.beam_name, "bolt of negative energy");
691         pbolt.colour = DARKGREY;
692         pbolt.range = 7 + random2(10);
693         pbolt.damage = calc_dice( 4, 15 + (power * 3) / 5 );
694         pbolt.hit = 8 + power / 20;                     // 50: 10   100: 13
695         pbolt.type = SYM_ZAP;
696         pbolt.flavour = BEAM_NEG;                       // drains levels
697 
698         pbolt.obviousEffect = true;
699         pbolt.isBeam = true;
700         break;
701 
702     case ZAP_IRON_BOLT:                                 // cap 150
703         strcpy(pbolt.beam_name, "iron bolt");
704         pbolt.colour = LIGHTCYAN;
705         pbolt.range = 5 + random2(5);
706         pbolt.damage = calc_dice( 9, 15 + (power * 3) / 4 );
707         pbolt.hit = 7 + power / 15;                     // 50: 10   100: 13
708         pbolt.type = SYM_MISSILE;
709         pbolt.flavour = BEAM_MMISSILE;                  // unresistable
710         pbolt.obviousEffect = true;
711         break;
712 
713     case ZAP_POISON_ARROW:                              // cap 150
714         strcpy(pbolt.beam_name, "poison arrow");
715         pbolt.colour = LIGHTGREEN;
716         pbolt.range = 8 + random2(5);
717         pbolt.damage = calc_dice( 4, 15 + power );
718         pbolt.hit = 5 + power / 10;                     // 50: 10  100: 15
719         pbolt.type = SYM_MISSILE;
720         pbolt.flavour = BEAM_POISON_ARROW;              // extra damage
721         pbolt.obviousEffect = true;
722         break;
723 
724 
725     case ZAP_DISINTEGRATION:                            // cap 150
726         strcpy(pbolt.beam_name, "0");
727         pbolt.flavour = BEAM_DISINTEGRATION;
728         pbolt.range = 7 + random2(8);
729         pbolt.damage = calc_dice( 3, 15 + (power * 3) / 4 );
730         pbolt.ench_power *= 5;
731         pbolt.ench_power /= 2;
732         pbolt.isBeam = true;
733         break;
734 
735     case ZAP_LIGHTNING:                                 // cap 150
736         // also for breath (at pow = lev * 2; max dam: 33)
737         strcpy(pbolt.beam_name, "bolt of lightning");
738         pbolt.colour = LIGHTCYAN;
739         pbolt.range = 8 + random2(10);                  // extended in beam
740         pbolt.damage = calc_dice( 1, 10 + (power * 3) / 5 );
741         pbolt.hit = 7 + random2(power) / 20;            // 50: 7-9  100: 7-12
742         pbolt.type = SYM_ZAP;
743         pbolt.flavour = BEAM_ELECTRICITY;               // beams & reflects
744 
745         pbolt.obviousEffect = true;
746         pbolt.isBeam = true;
747         break;
748 
749     case ZAP_FIREBALL:                                  // cap 150
750         strcpy(pbolt.beam_name, "fireball");
751         pbolt.colour = RED;
752         pbolt.range = 8 + random2(5);
753         pbolt.damage = calc_dice( 3, 10 + power / 2 );
754         pbolt.hit = 40;                                 // hit: 40
755         pbolt.type = SYM_ZAP;
756         pbolt.flavour = BEAM_EXPLOSION;                 // fire
757         break;
758 
759     case ZAP_ORB_OF_ELECTRICITY:                        // cap 150
760         strcpy(pbolt.beam_name, "orb of electricity");
761         pbolt.colour = LIGHTBLUE;
762         pbolt.range = 9 + random2(12);
763         pbolt.damage = calc_dice( 1, 15 + (power * 4) / 5 );
764         pbolt.damage.num = 0;                    // only does explosion damage
765         pbolt.hit = 40;                                 // hit: 40
766         pbolt.type = SYM_ZAP;
767         pbolt.flavour = BEAM_ELECTRICITY;
768         break;
769 
770     case ZAP_ORB_OF_FRAGMENTATION:                      // cap 150
771         strcpy(pbolt.beam_name, "metal orb");
772         pbolt.colour = CYAN;
773         pbolt.range = 9 + random2(7);
774         pbolt.damage = calc_dice( 3, 30 + (power * 3) / 4 );
775         pbolt.hit = 20;                                 // hit: 20
776         pbolt.type = SYM_ZAP;
777         pbolt.flavour = BEAM_FRAG;                      // extra AC resist
778         break;
779 
780     case ZAP_CLEANSING_FLAME:                           // cap 200
781         strcpy(pbolt.beam_name, "golden flame");
782         pbolt.colour = YELLOW;
783         pbolt.range = 7 + random2(10);
784         pbolt.damage = calc_dice( 6, 30 + power );
785         pbolt.hit = 20;                                 // hit: 20
786         pbolt.type = SYM_ZAP;
787         pbolt.flavour = BEAM_HOLY;
788 
789         pbolt.obviousEffect = true;
790         pbolt.isBeam = true;
791         break;
792 
793     case ZAP_CRYSTAL_SPEAR:                             // cap 200
794         strcpy(pbolt.beam_name, "crystal spear");
795         pbolt.colour = WHITE;
796         pbolt.range = 7 + random2(10);
797         pbolt.damage = calc_dice( 12, 30 + (power * 4) / 3 );
798         pbolt.hit = 10 + power / 15;                    // 50: 13   100: 16
799         pbolt.type = SYM_MISSILE;
800         pbolt.flavour = BEAM_MMISSILE;                  // unresistable
801 
802         pbolt.obviousEffect = true;
803         break;
804 
805     case ZAP_HELLFIRE:                                  // cap 200
806         strcpy(pbolt.beam_name, "hellfire");
807         pbolt.colour = RED;
808         pbolt.range = 7 + random2(10);
809         pbolt.damage = calc_dice( 3, 10 + (power * 3) / 4 );
810         pbolt.hit = 20 + power / 10;                    // 50: 25   100: 30
811         pbolt.type = SYM_ZAP;
812         pbolt.flavour = BEAM_EXPLOSION;
813 
814         pbolt.obviousEffect = true;
815         pbolt.isBeam = true;
816         break;
817 
818     case ZAP_ICE_STORM:                                 // cap 200
819         strcpy(pbolt.beam_name, "great blast of cold");
820         pbolt.colour = BLUE;
821         pbolt.range = 9 + random2(5);
822         pbolt.damage = calc_dice( 6, 15 + power );
823         pbolt.damage.num = 0;                    // only does explosion damage
824         pbolt.hit = 20 + power / 10;                    // 50: 25   100: 30
825         pbolt.ench_power = power;                       // used for radius
826         pbolt.type = SYM_ZAP;
827         pbolt.flavour = BEAM_ICE;                       // half resisted
828         break;
829 
830     case ZAP_BEAM_OF_ENERGY:    // bolt of innacuracy
831         strcpy(pbolt.beam_name, "narrow beam of energy");
832         pbolt.colour = YELLOW;
833         pbolt.range = 7 + random2(10);
834         pbolt.damage = calc_dice( 12, 40 + (power * 3) / 2 );
835         pbolt.hit = 2;                                  // hit: 2 (very hard)
836         pbolt.type = SYM_ZAP;
837         pbolt.flavour = BEAM_ENERGY;                    // unresisted
838 
839         pbolt.obviousEffect = true;
840         pbolt.isBeam = true;
841         break;
842 
843     case ZAP_SPIT_POISON:       // cap 50
844         // max pow = lev + mut * 5 = 42
845         strcpy(pbolt.beam_name, "splash of poison");
846         pbolt.colour = GREEN;
847 
848         pbolt.range = 3 + random2( 1 + power / 2 );
849         if (pbolt.range > 9)
850             pbolt.range = 9;
851 
852         pbolt.damage = dice_def( 1, 4 + power / 2 );    // max dam: 25
853         pbolt.hit = 5 + random2( 1 + power / 3 );       // max hit: 19
854         pbolt.type = SYM_ZAP;
855         pbolt.flavour = BEAM_POISON;
856         pbolt.obviousEffect = true;
857         break;
858 
859     case ZAP_BREATHE_FIRE:      // cap 50
860         // max pow = lev + mut * 4 + 12 = 51 (capped to 50)
861         strcpy(pbolt.beam_name, "fiery breath");
862         pbolt.colour = RED;
863 
864         pbolt.range = 3 + random2( 1 + power / 2 );
865         if (pbolt.range > 9)
866             pbolt.range = 9;
867 
868         pbolt.damage = dice_def( 3, 4 + power / 3 );    // max dam: 60
869         pbolt.hit = 8 + random2( 1 + power / 3 );       // max hit: 25
870         pbolt.type = SYM_ZAP;
871         pbolt.flavour = BEAM_FIRE;
872 
873         pbolt.obviousEffect = true;
874         pbolt.isBeam = true;
875         break;
876 
877     case ZAP_BREATHE_FROST:     // cap 50
878         // max power = lev = 27
879         strcpy(pbolt.beam_name, "freezing breath");
880         pbolt.colour = WHITE;
881 
882         pbolt.range = 3 + random2( 1 + power / 2 );
883         if (pbolt.range > 9)
884             pbolt.range = 9;
885 
886         pbolt.damage = dice_def( 3, 4 + power / 3 );    // max dam: 39
887         pbolt.hit = 8 + random2( 1 + power / 3 );
888         pbolt.type = SYM_ZAP;
889         pbolt.flavour = BEAM_COLD;
890 
891         pbolt.obviousEffect = true;
892         pbolt.isBeam = true;
893         break;
894 
895     case ZAP_BREATHE_ACID:      // cap 50
896         // max power = lev for ability, 50 for minor destruction (max dam: 57)
897         strcpy(pbolt.beam_name, "acid");
898         pbolt.colour = YELLOW;
899 
900         pbolt.range = 3 + random2( 1 + power / 2 );
901         if (pbolt.range > 9)
902             pbolt.range = 9;
903 
904         pbolt.damage = dice_def( 3, 3 + power / 3 );    // max dam: 36
905         pbolt.hit = 5 + random2( 1 + power / 3 );
906         pbolt.type = SYM_ZAP;
907         pbolt.flavour = BEAM_ACID;
908 
909         pbolt.obviousEffect = true;
910         pbolt.isBeam = true;
911         break;
912 
913     case ZAP_BREATHE_POISON:    // leaves clouds of gas // cap 50
914         // max power = lev = 27
915         strcpy(pbolt.beam_name, "poison gas");
916         pbolt.colour = GREEN;
917 
918         pbolt.range = 3 + random2( 1 + power / 2 );
919         if (pbolt.range > 9)
920             pbolt.range = 9;
921 
922         pbolt.damage = dice_def( 3, 2 + power / 6 );    // max dam: 18
923         pbolt.hit = 6 + random2( 1 + power / 3 );
924         pbolt.type = SYM_ZAP;
925         pbolt.flavour = BEAM_POISON;
926 
927         pbolt.obviousEffect = true;
928         pbolt.isBeam = true;
929         break;
930 
931     case ZAP_BREATHE_POWER:     // cap 50
932         strcpy(pbolt.beam_name, "bolt of energy");
933         // max power = lev = 27
934 
935         pbolt.colour = BLUE;
936         if (random2(power) >= 8)
937             pbolt.colour = LIGHTBLUE;
938         if (random2(power) >= 12)
939             pbolt.colour = MAGENTA;
940         if (random2(power) >= 17)
941             pbolt.colour = LIGHTMAGENTA;
942 
943         pbolt.range = 6 + random2( 1 + power / 2 );
944         if (pbolt.range > 9)
945             pbolt.range = 9;
946 
947         pbolt.damage = dice_def( 3, 3 + power / 3 );    // max dam: 36
948         pbolt.hit = 5 + random2( 1 + power / 3 );
949         pbolt.type = SYM_ZAP;
950         pbolt.flavour = BEAM_MMISSILE;                  // unresistable
951 
952         pbolt.obviousEffect = true;
953         pbolt.isBeam = true;
954         break;
955 
956     case ZAP_BREATHE_STEAM:     // cap 50
957         // max power = lev = 27
958         strcpy(pbolt.beam_name, "ball of steam");
959         pbolt.colour = LIGHTGREY;
960 
961         pbolt.range = 6 + random2(5);
962         if (pbolt.range > 9)
963             pbolt.range = 9;
964 
965         pbolt.damage = dice_def( 3, 4 + power / 5 );    // max dam: 27
966         pbolt.hit = 10 + random2( 1 + power / 5 );
967         pbolt.type = SYM_ZAP;
968         pbolt.flavour = BEAM_FIRE;
969 
970         pbolt.obviousEffect = true;
971         pbolt.isBeam = true;
972         break;
973 
974     case ZAP_SLOWING:
975         strcpy(pbolt.beam_name, "0");
976         pbolt.flavour = BEAM_SLOW;
977         // pbolt.isBeam = true;
978         break;
979 
980     case ZAP_HASTING:
981         strcpy(pbolt.beam_name, "0");
982         pbolt.flavour = BEAM_HASTE;
983         // pbolt.isBeam = true;
984         break;
985 
986     case ZAP_PARALYSIS:
987         strcpy(pbolt.beam_name, "0");
988         pbolt.flavour = BEAM_PARALYSIS;
989         // pbolt.isBeam = true;
990         break;
991 
992     case ZAP_CONFUSION:
993         strcpy(pbolt.beam_name, "0");
994         pbolt.flavour = BEAM_CONFUSION;
995         // pbolt.isBeam = true;
996         break;
997 
998     case ZAP_INVISIBILITY:
999         strcpy(pbolt.beam_name, "0");
1000         pbolt.flavour = BEAM_INVISIBILITY;
1001         // pbolt.isBeam = true;
1002         break;
1003 
1004     case ZAP_HEALING:
1005         strcpy(pbolt.beam_name, "0");
1006         pbolt.flavour = BEAM_HEALING;
1007         pbolt.damage = dice_def( 1, 7 + power / 3 );
1008         // pbolt.isBeam = true;
1009         break;
1010 
1011     case ZAP_DIGGING:
1012         strcpy(pbolt.beam_name, "0");
1013         pbolt.flavour = BEAM_DIGGING;
1014         // not ordinary "0" beam range {dlb}
1015         pbolt.range = 3 + random2( power / 5 ) + random2(5);
1016         pbolt.isBeam = true;
1017         break;
1018 
1019     case ZAP_TELEPORTATION:
1020         strcpy(pbolt.beam_name, "0");
1021         pbolt.flavour = BEAM_TELEPORT;
1022         pbolt.range = 9 + random2(5);
1023         // pbolt.isBeam = true;
1024         break;
1025 
1026     case ZAP_POLYMORPH_OTHER:
1027         strcpy(pbolt.beam_name, "0");
1028         pbolt.flavour = BEAM_POLYMORPH;
1029         pbolt.range = 9 + random2(5);
1030         // pbolt.isBeam = true;
1031         break;
1032 
1033     case ZAP_ENSLAVEMENT:
1034         strcpy(pbolt.beam_name, "0");
1035         pbolt.flavour = BEAM_CHARM;
1036         pbolt.range = 7 + random2(5);
1037         // pbolt.isBeam = true;
1038         break;
1039 
1040     case ZAP_BANISHMENT:
1041         strcpy(pbolt.beam_name, "0");
1042         pbolt.flavour = BEAM_BANISH;
1043         pbolt.range = 7 + random2(5);
1044         // pbolt.isBeam = true;
1045         break;
1046 
1047     case ZAP_DEGENERATION:
1048         strcpy(pbolt.beam_name, "0");
1049         pbolt.flavour = BEAM_DEGENERATE;
1050         pbolt.range = 7 + random2(5);
1051         // pbolt.isBeam = true;
1052         break;
1053 
1054     case ZAP_ENSLAVE_UNDEAD:
1055         strcpy(pbolt.beam_name, "0");
1056         pbolt.flavour = BEAM_ENSLAVE_UNDEAD;
1057         pbolt.range = 7 + random2(5);
1058         // pbolt.isBeam = true;
1059         break;
1060 
1061     case ZAP_AGONY:
1062         strcpy(pbolt.beam_name, "0agony");
1063         pbolt.flavour = BEAM_PAIN;
1064         pbolt.range = 7 + random2(8);
1065         pbolt.ench_power *= 5;
1066         // pbolt.isBeam = true;
1067         break;
1068 
1069     case ZAP_CONTROL_DEMON:
1070         strcpy(pbolt.beam_name, "0");
1071         pbolt.flavour = BEAM_ENSLAVE_DEMON;
1072         pbolt.range = 7 + random2(5);
1073         pbolt.ench_power *= 3;
1074         pbolt.ench_power /= 2;
1075         // pbolt.isBeam = true;
1076         break;
1077 
1078     case ZAP_SLEEP:             //jmf: added
1079         strcpy(pbolt.beam_name, "0");
1080         pbolt.flavour = BEAM_SLEEP;
1081         pbolt.range = 7 + random2(5);
1082         // pbolt.isBeam = true;
1083         break;
1084 
1085     case ZAP_BACKLIGHT: //jmf: added
1086         strcpy(pbolt.beam_name, "0");
1087         pbolt.flavour = BEAM_BACKLIGHT;
1088         pbolt.colour = BLUE;
1089         pbolt.range = 7 + random2(5);
1090         // pbolt.isBeam = true;
1091         break;
1092 
1093     case ZAP_DEBUGGING_RAY:
1094         strcpy( pbolt.beam_name, "debugging ray" );
1095         pbolt.colour = random_colour();
1096         pbolt.range = 7 + random2(10);
1097         pbolt.damage = dice_def( 1500, 1 );             // dam: 1500
1098         pbolt.hit = 1500;                               // hit: 1500
1099         pbolt.type = SYM_DEBUG;
1100         pbolt.flavour = BEAM_MMISSILE;                  // unresistable
1101 
1102         pbolt.obviousEffect = true;
1103         break;
1104 
1105     default:
1106         strcpy(pbolt.beam_name, "buggy beam");
1107         pbolt.colour = random_colour();
1108         pbolt.range = 7 + random2(10);
1109         pbolt.damage = dice_def( 1, 0 );
1110         pbolt.hit = 60;
1111         pbolt.type = SYM_DEBUG;
1112         pbolt.flavour = BEAM_MMISSILE;                  // unresistable
1113 
1114         pbolt.obviousEffect = true;
1115         break;
1116     }                           // end of switch
1117 }                               // end zappy()
1118 
1119 /*  NEW (GDL):
1120  *  Now handles all beamed/thrown items and spells,  tracers, and their effects.
1121  *  item is used for items actually thrown/launched
1122  *
1123  *  if item is NULL,  there is no physical object being thrown that could
1124  *  land on the ground.
1125  */
1126 
1127 
1128 /*
1129  * Beam pseudo code:
1130  *
1131  * 1. Calculate stepx and stepy - algorithms depend on finding a step axis
1132  *    which results in a line of rise 1 or less (ie 45 degrees or less)
1133  * 2. Calculate range.  Tracers always have max range, otherwise the beam
1134  *    will have somewhere between range and rangeMax
1135  * 3. Loop tracing out the line:
1136  *      3a. Check for walls and wall affecting beams
1137  *      3b. If no valid move is found, try a fuzzy move
1138  *      3c. If no valid move is yet found, try bouncing
1139  *      3d. If no valid move or bounce is found, break
1140  *      4. Check for beam termination on target
1141  *      5. Affect the cell which the beam just moved into -> affect()
1142  *      6. Decrease remaining range appropriately
1143  *      7. Check for early out due to aimedAtFeet
1144  *      8. Draw the beam
1145  * 9. Drop an object where the beam 'landed'
1146  *10. Beams explode where the beam 'landed'
1147  *11. If no message generated yet, send "nothing happens" (enchantments only)
1148  *
1149  */
1150 
1151 
fire_beam(struct bolt & pbolt,item_def * item)1152 void fire_beam( struct bolt &pbolt, item_def *item )
1153 {
1154     int dx, dy;             // total delta between source & target
1155     int lx, ly;             // last affected x,y
1156     int stepx, stepy;       // x,y increment - FP
1157     int wx, wy;             // 'working' x,y - FP
1158     bool beamTerminate;     // has beam been 'stopped' by something?
1159     int nx, ny;             // test(new) x,y - FP
1160     int tx, ty;             // test(new) x,y - integer
1161     bool roundX, roundY;    // which to round?
1162     int rangeRemaining;
1163     bool fuzzyOK;           // fuzzification resulted in OK move
1164     bool sideBlocked, topBlocked, random_beam;
1165 
1166 #if DEBUG_DIAGNOSTICS
1167     snprintf( info, INFO_SIZE, "%s%s (%d,%d) to (%d,%d): ty=%d col=%d flav=%d hit=%d dam=%dd%d",
1168              (pbolt.isBeam) ? "beam" : "missile",
1169              (pbolt.isTracer) ? " tracer" : "",
1170              pbolt.source_x, pbolt.source_y,
1171              pbolt.target_x, pbolt.target_y,
1172              pbolt.type, pbolt.colour, pbolt.flavour,
1173              pbolt.hit, pbolt.damage.num, pbolt.damage.size );
1174 
1175     mpr( info, MSGCH_DIAGNOSTICS );
1176 #endif
1177 
1178     // init
1179     pbolt.aimedAtFeet = false;
1180     pbolt.msgGenerated = false;
1181     pbolt.isExplosion = false;
1182     roundY = false;
1183     roundX = false;
1184 
1185     // first, calculate beam step
1186     dx = pbolt.target_x - pbolt.source_x;
1187     dy = pbolt.target_y - pbolt.source_y;
1188 
1189     // check for aim at feet
1190     if (dx == 0 && dy == 0)
1191     {
1192         pbolt.aimedAtFeet = true;
1193         stepx = 0;
1194         stepy = 0;
1195         tx = pbolt.source_x;
1196         ty = pbolt.source_y;
1197     }
1198     else
1199     {
1200         if (abs(dx) >= abs(dy))
1201         {
1202             stepx = (dx > 0) ? 100 : -100;
1203             stepy = 100 * dy / (abs(dx));
1204             roundY = true;
1205         }
1206         else
1207         {
1208             stepy = (dy > 0) ? 100 : -100;
1209             stepx = 100 * dx / (abs(dy));
1210             roundX = true;
1211         }
1212     }
1213 
1214     // give chance for beam to affect one cell even if aimedAtFeet.
1215     beamTerminate = false;
1216     // setup working coords
1217     lx = pbolt.source_x;
1218     wx = 100 * lx;
1219     ly = pbolt.source_y;
1220     wy = 100 * ly;
1221     // setup range
1222     rangeRemaining = pbolt.range;
1223     if (pbolt.rangeMax > pbolt.range)
1224     {
1225         if (pbolt.isTracer)
1226             rangeRemaining = pbolt.rangeMax;
1227         else
1228             rangeRemaining += random2((pbolt.rangeMax - pbolt.range) + 1);
1229     }
1230 
1231     // before we start drawing the beam, turn buffering off
1232 #ifdef WIN32CONSOLE
1233     bool oldValue = true;
1234     if (!pbolt.isTracer)
1235         oldValue = setBuffering(false);
1236 #endif
1237 
1238     // cannot use source_x, source_y, target_x, target_y during
1239     // step algorithm due to bouncing.
1240 
1241     // now, one step at a time, try to move towards target.
1242     while(!beamTerminate)
1243     {
1244         nx = wx + stepx;
1245         ny = wy + stepy;
1246 
1247         if (roundY)
1248         {
1249             tx = nx / 100;
1250             ty = (ny + 50) / 100;
1251         }
1252         if (roundX)
1253         {
1254             ty = ny / 100;
1255             tx = (nx + 50) / 100;
1256         }
1257 
1258         // check that tx, ty are valid.  If not,  set to last
1259         // x,y and break.
1260         if (tx < 0 || tx >= GXM || ty < 0 || ty >= GYM)
1261         {
1262             tx = lx;
1263             ty = ly;
1264             break;
1265         }
1266 
1267         // see if tx, ty is blocked by something
1268         if (grd[tx][ty] < MINMOVE)
1269         {
1270             // first, check to see if this beam affects walls.
1271             if (affectsWalls(pbolt))
1272             {
1273                 // should we ever get a tracer with a wall-affecting
1274                 // beam (possible I suppose),  we'll quit tracing now.
1275                 if (!pbolt.isTracer)
1276                     rangeRemaining -= affect(pbolt, tx, ty);
1277 
1278                 // if it's still a wall, quit.
1279                 if (grd[tx][ty] < MINMOVE)
1280                 {
1281                     break;      // breaks from line tracing
1282                 }
1283             }
1284             else
1285             {
1286                 // BEGIN fuzzy line algorithm
1287                 fuzzyOK = fuzzyLine(nx,ny,tx,ty,lx,ly,stepx,stepy,roundX,roundY);
1288                 if (!fuzzyOK)
1289                 {
1290                     // BEGIN bounce case
1291                     if (!isBouncy(pbolt))
1292                     {
1293                         tx = lx;
1294                         ty = ly;
1295                         break;          // breaks from line tracing
1296                     }
1297 
1298                     sideBlocked = false;
1299                     topBlocked = false;
1300                     // BOUNCE -- guaranteed to return reasonable tx, ty.
1301                     // if it doesn't, we'll quit in the next if stmt anyway.
1302                     if (roundY)
1303                     {
1304                         if ( grd[lx + stepx / 100][ly] < MINMOVE)
1305                             sideBlocked = true;
1306 
1307                         if (dy != 0)
1308                         {
1309                             if ( grd[lx][ly + (stepy>0?1:-1)] < MINMOVE)
1310                                 topBlocked = true;
1311                         }
1312 
1313                         rangeRemaining -= bounce(stepx, stepy, wx, wy, nx, ny,
1314                             lx, ly, tx, ty, topBlocked, sideBlocked);
1315                     }
1316                     else
1317                     {
1318                         if ( grd[lx][ly + stepy / 100] < MINMOVE)
1319                             sideBlocked = true;
1320 
1321                         if (dx != 0)
1322                         {
1323                             if ( grd[lx + (stepx>0?1:-1)][ly] < MINMOVE)
1324                                 topBlocked = true;
1325                         }
1326 
1327                         rangeRemaining -= bounce(stepy, stepx, wy, wx, ny, nx,
1328                             ly, lx, ty, tx, topBlocked, sideBlocked);
1329                     }
1330                     // END bounce case - range check
1331                     if (rangeRemaining < 1)
1332                     {
1333                         tx = lx;
1334                         ty = ly;
1335                         break;
1336                     }
1337                 }
1338             } // end else - beam doesn't affect walls
1339         } // endif - is tx, ty wall?
1340 
1341         // at this point, if grd[tx][ty] is still a wall, we
1342         // couldn't find any path: bouncy, fuzzy, or not - so break.
1343         if (grd[tx][ty] < MINMOVE)
1344         {
1345             tx = lx;
1346             ty = ly;
1347             break;
1348         }
1349 
1350         // check for "target termination"
1351         // occurs when beam can be targetted at empty
1352         // cell (e.g. a mage wants an explosion to happen
1353         // between two monsters)
1354 
1355         // in this case,  don't affect the cell - players
1356         // /monsters have no chance to dodge or block such
1357         // a beam,  and we want to avoid silly messages.
1358         if (tx == pbolt.target_x && ty == pbolt.target_y)
1359             beamTerminate = beam_term_on_target(pbolt);
1360 
1361         // affect the cell,  except in the special case noted
1362         // above -- affect() will early out if something gets
1363         // hit and the beam is type 'term on target'.
1364         if (!beamTerminate)
1365         {
1366             // random beams: randomize before affect
1367             random_beam = false;
1368             if (pbolt.flavour == BEAM_RANDOM)
1369             {
1370                 random_beam = true;
1371                 pbolt.flavour = BEAM_FIRE + random2(7);
1372             }
1373 
1374             rangeRemaining -= affect(pbolt, tx, ty);
1375 
1376             if (random_beam)
1377                 pbolt.flavour = BEAM_RANDOM;
1378         }
1379 
1380         // always decrease range by 1
1381         rangeRemaining -= 1;
1382 
1383         // check for range termination
1384         if (rangeRemaining <= 0)
1385             beamTerminate = true;
1386 
1387         // special case - beam was aimed at feet
1388         if (pbolt.aimedAtFeet)
1389             beamTerminate = true;
1390 
1391         // actually draw the beam/missile/whatever,
1392         // if the player can see the cell.
1393         if (!pbolt.isTracer && pbolt.beam_name[0] != '0' && see_grid(tx,ty))
1394         {
1395             // we don't clean up the old position.
1396             // first, most people like seeing the full path,
1397             // and second, it is hard to do it right with
1398             // respect to killed monsters, cloud trails, etc.
1399 
1400             // draw new position
1401             int drawx = tx - you.x_pos + 18;
1402             int drawy = ty - you.y_pos + 9;
1403             // bounds check
1404             if (drawx > 8 && drawx < 26 && drawy > 0 && drawy < 18)
1405             {
1406                 if (pbolt.colour == BLACK)
1407                     textcolor(random_colour());
1408                 else
1409                     textcolor(pbolt.colour);
1410 
1411                 gotoxy(drawx, drawy);
1412                 putch(pbolt.type);
1413 
1414 #ifdef LINUX
1415                 // get curses to update the screen so we can see the beam
1416                 update_screen();
1417 #endif
1418 
1419                 delay(15);
1420 
1421 #ifdef MISSILE_TRAILS_OFF
1422                 if (!pbolt.isBeam || pbolt.beam_name[0] == '0')
1423                     viewwindow(1,false); // mv: added. It's not optimal but
1424                                          // is usually enough
1425 #endif
1426             }
1427 
1428         }
1429 
1430         // set some stuff up for the next iteration
1431         lx = tx;
1432         ly = ty;
1433 
1434         wx = nx;
1435         wy = ny;
1436 
1437     } // end- while !beamTerminate
1438 
1439     // the beam has finished,  and terminated at tx, ty
1440 
1441     // leave an object, if applicable
1442     if (item)
1443         beam_drop_object( pbolt, item, tx, ty );
1444 
1445     // check for explosion.  NOTE that for tracers, we have to make a copy
1446     // of target co'ords and then reset after calling this -- tracers should
1447     // never change any non-tracers fields in the beam structure. -- GDL
1448     int ox = pbolt.target_x;
1449     int oy = pbolt.target_y;
1450 
1451     beam_explodes(pbolt, tx, ty);
1452 
1453     if (pbolt.isTracer)
1454     {
1455         pbolt.target_x = ox;
1456         pbolt.target_y = oy;
1457     }
1458 
1459     // canned msg for enchantments that affected no-one
1460     if (pbolt.beam_name[0] == '0' && pbolt.flavour != BEAM_DIGGING)
1461     {
1462         if (!pbolt.isTracer && !pbolt.msgGenerated && !pbolt.obviousEffect)
1463             canned_msg(MSG_NOTHING_HAPPENS);
1464     }
1465 
1466     // that's it!
1467 #ifdef WIN32CONSOLE
1468     if (!pbolt.isTracer)
1469         setBuffering(oldValue);
1470 #endif
1471 }                               // end fire_beam();
1472 
1473 
1474 // returns damage taken by a monster from a "flavoured" (fire, ice, etc.)
1475 // attack -- damage from clouds and branded weapons handled elsewhere.
mons_adjust_flavoured(struct monsters * monster,struct bolt & pbolt,int hurted,bool doFlavouredEffects)1476 int mons_adjust_flavoured( struct monsters *monster, struct bolt &pbolt,
1477                            int hurted, bool doFlavouredEffects )
1478 {
1479     // if we're not doing flavored effects,  must be preliminary
1480     // damage check only;  do not print messages or apply any side
1481     // effects!
1482     int resist;
1483 
1484     switch (pbolt.flavour)
1485     {
1486     case BEAM_FIRE:
1487         resist = mons_res_fire(monster);
1488         if (resist > 1)
1489         {
1490             if (doFlavouredEffects)
1491                 simple_monster_message(monster, " appears unharmed.");
1492 
1493             hurted = 0;
1494         }
1495         else if (resist == 1)
1496         {
1497             if (doFlavouredEffects)
1498                 simple_monster_message(monster, " resists.");
1499 
1500             hurted /= 3;
1501         }
1502         else if (resist < 0)
1503         {
1504             if (monster->type == MONS_ICE_BEAST
1505                 || monster->type == MONS_SIMULACRUM_SMALL
1506                 || monster->type == MONS_SIMULACRUM_LARGE)
1507             {
1508                 if (doFlavouredEffects)
1509                     simple_monster_message(monster, " melts!");
1510             }
1511             else
1512             {
1513                 if (doFlavouredEffects)
1514                     simple_monster_message(monster, " is burned terribly!");
1515             }
1516 
1517             hurted *= 15;
1518             hurted /= 10;
1519         }
1520         break;
1521 
1522 
1523     case BEAM_COLD:
1524         resist = mons_res_cold(monster);
1525         if (resist > 1)
1526         {
1527             if (doFlavouredEffects)
1528                 simple_monster_message(monster, " appears unharmed.");
1529 
1530             hurted = 0;
1531         }
1532         else if (resist == 1)
1533         {
1534             if (doFlavouredEffects)
1535                 simple_monster_message(monster, " resists.");
1536 
1537             hurted /= 3;
1538         }
1539         else if (resist < 0)
1540         {
1541             if (doFlavouredEffects)
1542                 simple_monster_message(monster, " is frozen!");
1543 
1544             hurted *= 15;
1545             hurted /= 10;
1546         }
1547         break;
1548 
1549     case BEAM_ELECTRICITY:
1550         if (mons_res_elec(monster) > 0)
1551         {
1552             if (doFlavouredEffects)
1553                 simple_monster_message(monster, " appears unharmed.");
1554 
1555             hurted = 0;
1556         }
1557         break;
1558 
1559 
1560     case BEAM_POISON:
1561         if (mons_res_poison(monster) > 0)
1562         {
1563             if (doFlavouredEffects)
1564                 simple_monster_message( monster, " appears unharmed." );
1565 
1566             hurted = 0;
1567         }
1568         else if (doFlavouredEffects && !one_chance_in(3))
1569         {
1570             poison_monster( monster, YOU_KILL(pbolt.thrower) );
1571         }
1572         break;
1573 
1574     case BEAM_POISON_ARROW:
1575         if (mons_res_poison(monster) > 0)
1576         {
1577             if (doFlavouredEffects)
1578             {
1579                 simple_monster_message( monster, " partially resists." );
1580 
1581                 // Poison arrow can poison any living thing regardless of
1582                 // poison resistance. -- bwr
1583                 const int holy = mons_holiness( monster->type );
1584                 if (holy == MH_PLANT || holy == MH_NATURAL)
1585                     poison_monster( monster, YOU_KILL(pbolt.thrower), 2, true );
1586             }
1587 
1588             hurted /= 2;
1589         }
1590         else if (doFlavouredEffects)
1591         {
1592             poison_monster( monster, YOU_KILL(pbolt.thrower), 4 );
1593         }
1594         break;
1595 
1596     case BEAM_NEG:
1597         if (mons_res_negative_energy(monster) > 0)
1598         {
1599             if (doFlavouredEffects)
1600                 simple_monster_message(monster, " appears unharmed.");
1601 
1602             hurted = 0;
1603         }
1604         else
1605         {
1606             // early out for tracer/no side effects
1607             if (!doFlavouredEffects)
1608                 return (hurted);
1609 
1610             simple_monster_message(monster, " is drained.");
1611 
1612             if (one_chance_in(5))
1613                 monster->hit_dice--;
1614 
1615             monster->max_hit_points -= 2 + random2(3);
1616             monster->hit_points -= 2 + random2(3);
1617 
1618             if (monster->hit_points >= monster->max_hit_points)
1619                 monster->hit_points = monster->max_hit_points;
1620 
1621             if (monster->hit_dice < 1)
1622                 monster->hit_points = 0;
1623         }                       // end else
1624         break;
1625 
1626     case BEAM_HOLY:             // flame of cleansing
1627         if (mons_holiness(monster->type) == MH_NATURAL
1628             || mons_holiness(monster->type) == MH_NONLIVING
1629             || mons_holiness(monster->type) == MH_PLANT
1630             || mons_holiness(monster->type) == MH_HOLY)
1631         {
1632             if (doFlavouredEffects)
1633                 simple_monster_message(monster, " appears unharmed.");
1634 
1635             hurted = 0;
1636         }
1637         break;
1638 
1639     case BEAM_ICE:
1640         /* ice - about 50% of damage is cold, other 50% is impact and
1641            can't be resisted (except by AC, of course) */
1642         resist = mons_res_cold(monster);
1643         if (resist > 0)
1644         {
1645             if (doFlavouredEffects)
1646                 simple_monster_message(monster, " partially resists.");
1647 
1648             hurted /= 2;
1649         }
1650         else if (resist < 0)
1651         {
1652             if (doFlavouredEffects)
1653                 simple_monster_message(monster, " is frozen!");
1654 
1655             hurted *= 13;
1656             hurted /= 10;
1657         }
1658         break;
1659     }                           /* end of switch */
1660 
1661     if (pbolt.flavour == BEAM_LAVA)    //jmf: lava != hellfire
1662     {
1663         resist = mons_res_fire(monster);
1664         if (resist > 0)
1665         {
1666             if (doFlavouredEffects)
1667                 simple_monster_message(monster, " partially resists.");
1668 
1669             hurted /= 2;
1670         }
1671         else if (resist < 0)
1672         {
1673             if (monster->type == MONS_ICE_BEAST
1674                 || monster->type == MONS_SIMULACRUM_SMALL
1675                 || monster->type == MONS_SIMULACRUM_LARGE)
1676             {
1677                 if (doFlavouredEffects)
1678                     simple_monster_message(monster, " melts!");
1679             }
1680             else
1681             {
1682                 if (doFlavouredEffects)
1683                     simple_monster_message(monster, " is burned terribly!");
1684             }
1685 
1686             hurted *= 12;
1687             hurted /= 10;
1688         }
1689     }
1690     else if (stricmp(pbolt.beam_name, "hellfire") == 0)
1691     {
1692         resist = mons_res_fire(monster);
1693         if (resist > 2)
1694         {
1695             if (doFlavouredEffects)
1696                 simple_monster_message(monster, " appears unharmed.");
1697 
1698             hurted = 0;
1699         }
1700         else if (resist > 0)
1701         {
1702             if (doFlavouredEffects)
1703                 simple_monster_message(monster, " partially resists.");
1704 
1705             hurted /= 2;
1706         }
1707         else if (resist < 0)
1708         {
1709             if (monster->type == MONS_ICE_BEAST
1710                 || monster->type == MONS_SIMULACRUM_SMALL
1711                 || monster->type == MONS_SIMULACRUM_LARGE)
1712             {
1713                 if (doFlavouredEffects)
1714                     simple_monster_message(monster, " melts!");
1715             }
1716             else
1717             {
1718                 if (doFlavouredEffects)
1719                     simple_monster_message(monster, " is burned terribly!");
1720             }
1721 
1722             hurted *= 12;       /* hellfire */
1723             hurted /= 10;
1724         }
1725     }
1726 
1727     return (hurted);
1728 }                               // end mons_adjust_flavoured()
1729 
1730 
1731 // Enchants all monsters in player's sight.
mass_enchantment(int wh_enchant,int pow,int origin)1732 bool mass_enchantment( int wh_enchant, int pow, int origin )
1733 {
1734     int i;                      // loop variable {dlb}
1735     bool msgGenerated = false;
1736     struct monsters *monster;
1737 
1738     viewwindow(0, false);
1739 
1740     if (pow > 200)
1741         pow = 200;
1742 
1743     for (i = 0; i < MAX_MONSTERS; i++)
1744     {
1745         monster = &menv[i];
1746 
1747         if (monster->type == -1 || !mons_near(monster))
1748             continue;
1749 
1750         // assuming that the only mass charm is control undead:
1751         if (wh_enchant == ENCH_CHARM)
1752         {
1753             if (mons_friendly(monster))
1754                 continue;
1755 
1756             if (mons_holiness(monster->type) != MH_UNDEAD)
1757                 continue;
1758 
1759             if (check_mons_resist_magic( monster, pow ))
1760             {
1761                 simple_monster_message(monster, " resists.");
1762                 continue;
1763             }
1764         }
1765         else if (mons_holiness(monster->type) == MH_NATURAL)
1766         {
1767             if (check_mons_resist_magic( monster, pow ))
1768             {
1769                 simple_monster_message(monster, " resists.");
1770                 continue;
1771             }
1772         }
1773         else  // trying to enchant an unnatural creature doesn't work
1774         {
1775             simple_monster_message(monster, " is unaffected.");
1776             continue;
1777         }
1778 
1779         if (mons_has_ench(monster, wh_enchant))
1780             continue;
1781 
1782         if (mons_add_ench(monster, wh_enchant))
1783         {
1784             if (player_monster_visible( monster ))
1785             {
1786                 // turn message on
1787                 msgGenerated = true;
1788                 switch (wh_enchant)
1789                 {
1790                 case ENCH_FEAR:
1791                     simple_monster_message(monster,
1792                                            " looks frightened!");
1793                     break;
1794                 case ENCH_CONFUSION:
1795                     simple_monster_message(monster,
1796                                            " looks rather confused.");
1797                     break;
1798                 case ENCH_CHARM:
1799                     simple_monster_message(monster,
1800                                            " submits to your will.");
1801                     break;
1802                 default:
1803                     // oops, I guess not!
1804                     msgGenerated = false;
1805                 }
1806             }
1807 
1808             // extra check for fear (monster needs to reevaluate behaviour)
1809             if (wh_enchant == ENCH_FEAR)
1810                 behaviour_event( monster, ME_SCARE, origin );
1811         }
1812     }                           // end "for i"
1813 
1814     if (!msgGenerated)
1815         canned_msg(MSG_NOTHING_HAPPENS);
1816 
1817     return (msgGenerated);
1818 }                               // end mass_enchantmenet()
1819 
1820 /*
1821    Monster has probably failed save, now it gets enchanted somehow.
1822 
1823    returns MON_RESIST if monster is unaffected due to magic resist.
1824    returns MON_UNAFFECTED if monster is immune to enchantment
1825    returns MON_AFFECTED in all other cases (already enchanted, etc)
1826  */
mons_ench_f2(struct monsters * monster,struct bolt & pbolt)1827 int mons_ench_f2(struct monsters *monster, struct bolt &pbolt)
1828 {
1829     bool is_near = mons_near(monster);  // single caluclation permissible {dlb}
1830     char buff[ ITEMNAME_SIZE ];
1831 
1832     switch (pbolt.flavour)      /* put in magic resistance */
1833     {
1834     case BEAM_SLOW:         /* 0 = slow monster */
1835         // try to remove haste,  if monster is hasted
1836         if (mons_del_ench(monster, ENCH_HASTE))
1837         {
1838             if (simple_monster_message(monster, " is no longer moving quickly."))
1839                 pbolt.obviousEffect = true;
1840 
1841             return (MON_AFFECTED);
1842         }
1843 
1844         // not hasted,  slow it
1845         if (mons_add_ench(monster, ENCH_SLOW))
1846         {
1847             // put in an exception for fungi, plants and other things you won't
1848             // notice slow down.
1849             if (simple_monster_message(monster, " seems to slow down."))
1850                 pbolt.obviousEffect = true;
1851         }
1852         return (MON_AFFECTED);
1853 
1854     case BEAM_HASTE:                  // 1 = haste
1855         if (mons_del_ench(monster, ENCH_SLOW))
1856         {
1857             if (simple_monster_message(monster, " is no longer moving slowly."))
1858                 pbolt.obviousEffect = true;
1859 
1860             return (MON_AFFECTED);
1861         }
1862 
1863         // not slowed, haste it
1864         if (mons_add_ench(monster, ENCH_HASTE))
1865         {
1866             // put in an exception for fungi, plants and other things you won't
1867             // notice speed up.
1868             if (simple_monster_message(monster, " seems to speed up."))
1869                 pbolt.obviousEffect = true;
1870         }
1871         return (MON_AFFECTED);
1872 
1873     case BEAM_HEALING:         /* 2 = healing */
1874         if (heal_monster( monster, 5 + roll_dice( pbolt.damage ), false ))
1875         {
1876             if (monster->hit_points == monster->max_hit_points)
1877             {
1878                 if (simple_monster_message(monster,
1879                                         "'s wounds heal themselves!"))
1880                     pbolt.obviousEffect = true;
1881             }
1882             else
1883             {
1884                 if (simple_monster_message(monster, " is healed somewhat."))
1885                     pbolt.obviousEffect = true;
1886             }
1887         }
1888         return (MON_AFFECTED);
1889 
1890     case BEAM_PARALYSIS:                  /* 3 = paralysis */
1891         monster->speed_increment = 0;
1892 
1893         if (simple_monster_message(monster, " suddenly stops moving!"))
1894             pbolt.obviousEffect = true;
1895 
1896         if (grd[monster->x][monster->y] == DNGN_LAVA_X
1897             || grd[monster->x][monster->y] == DNGN_WATER_X)
1898         {
1899             if (mons_flies(monster) == 1)
1900             {
1901                 // don't worry about invisibility - you should be able to
1902                 // see if something has fallen into the lava
1903                 if (is_near)
1904                 {
1905                     strcpy(info, ptr_monam(monster, DESC_CAP_THE));
1906                     strcat(info, " falls into the ");
1907                     strcat(info, (grd[monster->x][monster->y] == DNGN_WATER_X)
1908                                 ? "water" : "lava");
1909                     strcat(info, "!");
1910                     mpr(info);
1911                 }
1912 
1913                 switch (pbolt.thrower)
1914                 {
1915                 case KILL_YOU:
1916                 case KILL_YOU_MISSILE:
1917                     monster_die(monster, KILL_YOU, pbolt.beam_source);
1918                     break;      /*  "    " */
1919 
1920                 case KILL_MON:
1921                 case KILL_MON_MISSILE:
1922                     monster_die(monster, KILL_MON_MISSILE, pbolt.beam_source);
1923                     break;      /* dragon breath &c */
1924                 }
1925             }
1926         }
1927         return (MON_AFFECTED);
1928 
1929     case BEAM_CONFUSION:                   /* 4 = confusion */
1930         if (mons_add_ench(monster, ENCH_CONFUSION))
1931         {
1932             // put in an exception for fungi, plants and other things you won't
1933             // notice becoming confused.
1934             if (simple_monster_message(monster, " appears confused."))
1935                 pbolt.obviousEffect = true;
1936         }
1937         return (MON_AFFECTED);
1938 
1939     case BEAM_INVISIBILITY:               /* 5 = invisibility */
1940         // Store the monster name before it becomes an "it" -- bwr
1941         strncpy( buff, ptr_monam( monster, DESC_CAP_THE ), sizeof(buff) );
1942 
1943         if (mons_add_ench(monster, ENCH_INVIS))
1944         {
1945             // Can't use simple_monster_message here, since it checks
1946             // for visibility of the monster (and its now invisible) -- bwr
1947             if (mons_near( monster ))
1948             {
1949                 snprintf( info, INFO_SIZE, "%s flickers %s",
1950                           buff, player_see_invis() ? "for a moment."
1951                                                    : "and vanishes!" );
1952                 mpr( info );
1953             }
1954 
1955             pbolt.obviousEffect = true;
1956         }
1957         return (MON_AFFECTED);
1958 
1959     case BEAM_CHARM:             /* 9 = charm */
1960         if (mons_add_ench(monster, ENCH_CHARM))
1961         {
1962             // put in an exception for fungi, plants and other things you won't
1963             // notice becoming charmed.
1964             if (simple_monster_message(monster, " is charmed."))
1965                 pbolt.obviousEffect = true;
1966         }
1967         return (MON_AFFECTED);
1968 
1969     default:
1970         break;
1971     }                           /* end of switch (beam_colour) */
1972 
1973     return (MON_AFFECTED);
1974 }                               // end mons_ench_f2()
1975 
1976 // actually poisons a monster (w/ message)
poison_monster(struct monsters * monster,bool fromPlayer,int levels,bool force)1977 void poison_monster( struct monsters *monster, bool fromPlayer, int levels,
1978                      bool force )
1979 {
1980     bool yourPoison = false;
1981     int ench = ENCH_NONE;
1982     int old_strength = 0;
1983 
1984     if (monster->type == -1)
1985         return;
1986 
1987     if (!force && mons_res_poison(monster) > 0)
1988         return;
1989 
1990     // who gets the credit if monster dies of poison?
1991     ench = mons_has_ench( monster, ENCH_POISON_I, ENCH_POISON_IV );
1992     if (ench != ENCH_NONE)
1993     {
1994         old_strength = ench - ENCH_POISON_I;
1995     }
1996     else
1997     {
1998         ench = mons_has_ench(monster, ENCH_YOUR_POISON_I, ENCH_YOUR_POISON_IV);
1999         if (ench != ENCH_NONE)
2000         {
2001             old_strength = ench - ENCH_YOUR_POISON_I;
2002             yourPoison = true;
2003         }
2004     }
2005 
2006     // delete old poison
2007     mons_del_ench( monster, ENCH_POISON_I, ENCH_POISON_IV, true );
2008     mons_del_ench( monster, ENCH_YOUR_POISON_I, ENCH_YOUR_POISON_IV, true );
2009 
2010     // Calculate new strength:
2011     int new_strength = old_strength + levels;
2012     if (new_strength > 3)
2013         new_strength = 3;
2014 
2015     // now, if player poisons the monster at ANY TIME, they should
2016     // get credit for the kill if the monster dies from poison.  This
2017     // really isn't that abusable -- GDL.
2018     if (fromPlayer || yourPoison)
2019         ench = ENCH_YOUR_POISON_I + new_strength;
2020     else
2021         ench = ENCH_POISON_I + new_strength;
2022 
2023     // actually do the poisoning
2024     // note: order important here
2025     if (mons_add_ench( monster, ench ) && new_strength > old_strength)
2026     {
2027         simple_monster_message( monster,
2028                                 (old_strength == 0) ? " looks ill."
2029                                                     : " looks even sicker." );
2030     }
2031 
2032     // finally, take care of deity preferences
2033     if (fromPlayer)
2034     {
2035         naughty(NAUGHTY_POISON, 5 + random2(3)); //jmf: TSO now hates poison
2036         done_good(GOOD_POISON, 5);      //jmf: had test god who liked poison
2037     }
2038 }                               // end poison_monster()
2039 
2040 // actually napalms a monster (w/ message)
sticky_flame_monster(int mn,bool fromPlayer,int levels)2041 void sticky_flame_monster( int mn, bool fromPlayer, int levels )
2042 {
2043     bool yourFlame = fromPlayer;
2044     int currentFlame;
2045     int currentStrength = 0;
2046 
2047     struct monsters *monster = &menv[mn];
2048 
2049     if (monster->type == -1)
2050         return;
2051 
2052     if (mons_res_fire(monster) > 0)
2053         return;
2054 
2055     // who gets the credit if monster dies of napalm?
2056     currentFlame = mons_has_ench( monster, ENCH_STICKY_FLAME_I,
2057                                            ENCH_STICKY_FLAME_IV );
2058 
2059     if (currentFlame != ENCH_NONE)
2060     {
2061         currentStrength = currentFlame - ENCH_STICKY_FLAME_I;
2062         yourFlame = false;
2063     }
2064     else
2065     {
2066         currentFlame = mons_has_ench( monster, ENCH_YOUR_STICKY_FLAME_I,
2067                                                ENCH_YOUR_STICKY_FLAME_IV );
2068 
2069         if (currentFlame != ENCH_NONE)
2070         {
2071             currentStrength = currentFlame - ENCH_YOUR_STICKY_FLAME_I;
2072             yourFlame = true;
2073         }
2074         else
2075             currentStrength = -1;           // no flame yet!
2076     }
2077 
2078     // delete old flame
2079     mons_del_ench( monster, ENCH_STICKY_FLAME_I, ENCH_STICKY_FLAME_IV, true );
2080     mons_del_ench( monster, ENCH_YOUR_STICKY_FLAME_I, ENCH_YOUR_STICKY_FLAME_IV,
2081                    true );
2082 
2083     // increase sticky flame strength,  cap at 3 (level is 0..3)
2084     currentStrength += levels;
2085 
2086     if (currentStrength > 3)
2087         currentStrength = 3;
2088 
2089     // now, if player flames the monster at ANY TIME, they should
2090     // get credit for the kill if the monster dies from napalm.  This
2091     // really isn't that abusable -- GDL.
2092     if (fromPlayer || yourFlame)
2093         currentStrength += ENCH_YOUR_STICKY_FLAME_I;
2094     else
2095         currentStrength += ENCH_STICKY_FLAME_I;
2096 
2097     // actually do flame
2098     if (mons_add_ench( monster, currentStrength ))
2099         simple_monster_message(monster, " is covered in liquid fire!");
2100 
2101 }                               // end sticky_flame_monster
2102 
2103 /*
2104  * Used by monsters in "planning" which spell to cast. Fires off a "tracer"
2105  * which tells the monster what it'll hit if it breathes/casts etc.
2106  *
2107  * The output from this tracer function is four variables in the beam struct:
2108  * fr_count, foe_count: a count of how many friends and foes will (probably)
2109  * be hit by this beam
2110  * fr_power, foe_power: a measure of how many 'friendly' hit dice it will
2111  *   affect,  and how many 'unfriendly' hit dice.
2112  *
2113  * note that beam properties must be set,  as the tracer will take them
2114  * into account,  as well as the monster's intelligence.
2115  *
2116  */
fire_tracer(struct monsters * monster,struct bolt & pbolt)2117 void fire_tracer(struct monsters *monster, struct bolt &pbolt)
2118 {
2119     // don't fiddle with any input parameters other than tracer stuff!
2120     pbolt.isTracer = true;
2121     pbolt.source_x = monster->x;    // always safe to do.
2122     pbolt.source_y = monster->y;
2123     pbolt.beam_source = monster_index(monster);
2124     pbolt.canSeeInvis = (mons_see_invis(monster) != 0);
2125     pbolt.smartMonster = (mons_intel(monster->type) == I_HIGH ||
2126                           mons_intel(monster->type) == I_NORMAL);
2127     pbolt.isFriendly = mons_friendly(monster);
2128 
2129     // init tracer variables
2130     pbolt.foe_count = pbolt.fr_count = 0;
2131     pbolt.foe_power = pbolt.fr_power = 0;
2132     pbolt.foeRatio = 80;        // default - see mons_should_fire()
2133 
2134     // foe ratio for summon gtr. demons & undead -- they may be
2135     // summoned, but they're hostile and would love nothing better
2136     // than to nuke the player and his minions
2137     if (monster->attitude != ATT_FRIENDLY)
2138         pbolt.foeRatio = 25;
2139 
2140     // fire!
2141     fire_beam(pbolt);
2142 
2143     // unset tracer flag (convenience)
2144     pbolt.isTracer = false;
2145 }                               // end tracer_f()
2146 
2147 
2148 /*
2149    When a mimic is hit by a ranged attack, it teleports away (the slow way)
2150    and changes its appearance - the appearance change is in monster_teleport
2151    in mstuff2.
2152  */
mimic_alert(struct monsters * mimic)2153 void mimic_alert(struct monsters *mimic)
2154 {
2155     if (mons_has_ench( mimic, ENCH_TP_I, ENCH_TP_IV ))
2156         return;
2157 
2158     monster_teleport( mimic, !one_chance_in(3) );
2159 }                               // end mimic_alert()
2160 
isBouncy(struct bolt & beam)2161 static bool isBouncy(struct bolt &beam)
2162 {
2163     // at present, only non-enchantment eletrcical beams bounce.
2164     if (beam.beam_name[0] != '0' && beam.flavour == BEAM_ELECTRICITY)
2165         return (true);
2166 
2167     return (false);
2168 }
2169 
beam_explodes(struct bolt & beam,int x,int y)2170 static void beam_explodes(struct bolt &beam, int x, int y)
2171 {
2172     int cloud_type;
2173 
2174     // this will be the last thing this beam does.. set target_x
2175     // and target_y to hold explosion co'ords.
2176 
2177     beam.target_x = x;
2178     beam.target_y = y;
2179 
2180     // generic explosion
2181     if (beam.flavour == BEAM_EXPLOSION || beam.flavour == BEAM_HOLY)
2182     {
2183         explosion1(beam);
2184         return;
2185     }
2186 
2187     if (beam.flavour >= BEAM_POTION_STINKING_CLOUD
2188         && beam.flavour <= BEAM_POTION_RANDOM)
2189     {
2190         switch (beam.flavour)
2191         {
2192         case BEAM_POTION_STINKING_CLOUD:
2193             beam.colour = GREEN;
2194             break;
2195 
2196         case BEAM_POTION_POISON:
2197             beam.colour = (coinflip() ? GREEN : LIGHTGREEN);
2198             break;
2199 
2200         case BEAM_POTION_MIASMA:
2201         case BEAM_POTION_BLACK_SMOKE:
2202             beam.colour = DARKGREY;
2203             break;
2204 
2205         case BEAM_POTION_STEAM:
2206             beam.colour = LIGHTGREY;
2207             break;
2208 
2209         case BEAM_POTION_FIRE:
2210             beam.colour = (coinflip() ? RED : LIGHTRED);
2211             break;
2212 
2213         case BEAM_POTION_COLD:
2214             beam.colour = (coinflip() ? BLUE : LIGHTBLUE);
2215             break;
2216 
2217         case BEAM_POTION_BLUE_SMOKE:
2218             beam.colour = LIGHTBLUE;
2219             break;
2220 
2221         case BEAM_POTION_PURP_SMOKE:
2222             beam.colour = MAGENTA;
2223             break;
2224 
2225         case BEAM_POTION_RANDOM:
2226         default:
2227             // Leave it the colour of the potion, the clouds will colour
2228             // themselves on the next refresh. -- bwr
2229             break;
2230         }
2231 
2232         explosion1(beam);
2233         return;
2234     }
2235 
2236 
2237     // cloud producer -- POISON BLAST
2238     if (strcmp(beam.beam_name, "blast of poison") == 0)
2239     {
2240         cloud_type = YOU_KILL(beam.thrower) ? CLOUD_POISON : CLOUD_POISON_MON;
2241         big_cloud( cloud_type, x, y, 0, 7 + random2(5) );
2242         return;
2243     }
2244 
2245     // cloud producer -- FOUL VAPOR (SWAMP DRAKE?)
2246     if (strcmp(beam.beam_name, "foul vapour") == 0)
2247     {
2248         cloud_type = YOU_KILL(beam.thrower) ? CLOUD_STINK : CLOUD_STINK_MON;
2249         big_cloud( cloud_type, x, y, 0, 9 );
2250         return;
2251     }
2252 
2253     // special cases - orbs & blasts of cold
2254     if (strcmp(beam.beam_name, "orb of electricity") == 0
2255         || strcmp(beam.beam_name, "metal orb") == 0
2256         || strcmp(beam.beam_name, "great blast of cold") == 0)
2257     {
2258         explosion1( beam );
2259         return;
2260     }
2261 
2262     // cloud producer only -- stinking cloud
2263     if (strcmp(beam.beam_name, "ball of vapour") == 0)
2264     {
2265         explosion1( beam );
2266         return;
2267     }
2268 }
2269 
beam_term_on_target(struct bolt & beam)2270 static bool beam_term_on_target(struct bolt &beam)
2271 {
2272 
2273     // generic - all explosion-type beams can be targetted at empty space,
2274     // and will explode there.  This semantic also means that a creature
2275     // in the target cell will have no chance to dodge or block,  so we
2276     // DON'T affect() the cell if this function returns true!
2277 
2278     if (beam.flavour == BEAM_EXPLOSION || beam.flavour == BEAM_HOLY)
2279         return (true);
2280 
2281     // POISON BLAST
2282     if (strcmp(beam.beam_name, "blast of poison") == 0)
2283         return (true);
2284 
2285     // FOUL VAPOR (SWAMP DRAKE)
2286     if (strcmp(beam.beam_name, "foul vapour") == 0)
2287         return (true);
2288 
2289     // STINKING CLOUD
2290     if (strcmp(beam.beam_name, "ball of vapour") == 0)
2291         return (true);
2292 
2293     return (false);
2294 }
2295 
beam_drop_object(struct bolt & beam,item_def * item,int x,int y)2296 static void beam_drop_object( struct bolt &beam, item_def *item, int x, int y )
2297 {
2298     ASSERT( item != NULL );
2299 
2300     // conditions: beam is missile and not tracer.
2301     if (beam.isTracer || beam.flavour != BEAM_MISSILE)
2302         return;
2303 
2304     if (YOU_KILL(beam.thrower) // ie if you threw it.
2305         && (grd[x][y] != DNGN_LAVA && grd[x][y] != DNGN_DEEP_WATER))
2306     {
2307         int chance;
2308 
2309         // Using Throwing skill as the fletching/ammo preserving skill. -- bwr
2310         switch (item->sub_type)
2311         {
2312         case MI_NEEDLE: chance = 6 + you.skills[SK_THROWING] / 6; break;
2313         case MI_STONE:  chance = 3 + you.skills[SK_THROWING] / 4; break;
2314         case MI_DART:   chance = 2 + you.skills[SK_THROWING] / 6; break;
2315         case MI_ARROW:  chance = 2 + you.skills[SK_THROWING] / 4; break;
2316         case MI_BOLT:   chance = 2 + you.skills[SK_THROWING] / 5; break;
2317 
2318         case MI_LARGE_ROCK:
2319         default:
2320             chance = 20;
2321             break;
2322         }
2323 
2324         if (item->base_type != OBJ_MISSILES || !one_chance_in(chance))
2325             copy_item_to_grid( *item, x, y, 1 );
2326     }
2327     else if (MON_KILL(beam.thrower) // monster threw it.
2328             && (grd[x][y] != DNGN_LAVA && grd[x][y] != DNGN_DEEP_WATER)
2329             && coinflip())
2330     {
2331         copy_item_to_grid( *item, x, y, 1 );
2332     }                           // if (thing_throw == 2) ...
2333 }
2334 
2335 // somewhat complicated BOUNCE function
2336 // returns # of times beam bounces during routine (usually 1)
2337 //
2338 // step 1 is always the step value from the stepping direction.
2339 #define B_HORZ      1
2340 #define B_VERT      2
2341 #define B_BOTH      3
2342 
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)2343 static int bounce(int &step1, int &step2, int w1, int w2, int &n1, int &n2,
2344     int l1, int l2, int &t1, int &t2, bool topBlocked, bool sideBlocked)
2345 {
2346     int bounceType = 0;
2347     int bounceCount = 1;
2348 
2349     if (topBlocked) bounceType = B_HORZ;
2350     if (sideBlocked) bounceType = B_VERT;
2351     if (topBlocked && sideBlocked)
2352     {
2353         // check for veritcal bounce only
2354         if ((w2 + step2 - 50)/100 == (w2 - 50)/100)
2355             bounceType = B_VERT;
2356         else
2357             bounceType = B_BOTH;
2358     }
2359 
2360     switch (bounceType)
2361     {
2362         case B_VERT:            // easiest
2363             n1 = w1;
2364             n2 = w2 + step2;
2365             step1 = -step1;
2366             t1 = n1 / 100;
2367             t2 = (n2 + 50)/100;
2368             // check top
2369             if (t2 != n2/100 && topBlocked)
2370                 t2 = n2/100;
2371             break;
2372         case B_HORZ:            // a little tricky
2373             if (step2 > 0)
2374                 n2 = (100 + 200*(w2/100)) - (w2 + step2);
2375             else
2376                 n2 = (100 + 200*((w2 - 50)/100)) - (w2 + step2);
2377             n1 = w1 + step1;
2378             t1 = n1 /100;
2379             t2 = (n2 + 50) / 100;
2380             step2 = -step2;
2381             break;
2382         case B_BOTH:
2383             // vertical:
2384             n1 = w1;
2385             t1 = l1;
2386             t2 = l2;
2387             // horizontal:
2388             if (step2 > 0)
2389                 n2 = (100 + 200*(w2/100)) - (w2 + step2);
2390             else
2391                 n2 = (100 + 200*((w2 - 50)/100)) - (w2 + step2);
2392             // reverse both directions
2393             step1 =- step1;
2394             step2 =- step2;
2395             bounceCount = 2;
2396             break;
2397         default:
2398             bounceCount = 0;
2399             break;
2400     }
2401 
2402     return (bounceCount);
2403 }
2404 
fuzzyLine(int nx,int ny,int & tx,int & ty,int lx,int ly,int stepx,int stepy,bool roundX,bool roundY)2405 static bool fuzzyLine(int nx, int ny, int &tx, int &ty, int lx, int ly,
2406     int stepx, int stepy, bool roundX, bool roundY)
2407 {
2408     bool fuzzyOK = false;
2409     int fx, fy;                 // fuzzy x,y
2410 
2411     // BEGIN fuzzy line algorithm
2412     fx = tx;
2413     fy = ty;
2414     if (roundY)
2415     {
2416         // try up
2417         fy = (ny + 100) / 100;
2418         // check for monotonic
2419         if (fy != ty && ((stepy>0 && fy >= ly)
2420             || (stepy<0 && fy <= ly)))
2421             fuzzyOK = true;
2422         // see if up try is blocked
2423         if (fuzzyOK && grd[tx][fy] < MINMOVE)
2424             fuzzyOK = false;
2425 
2426         // try down
2427         if (!fuzzyOK)
2428             fy = ny / 100;
2429         // check for monotonic
2430         if (fy != ty && ((stepy>0 && fy >= ly)
2431             || (stepy<0 && fy <= ly)))
2432             fuzzyOK = true;
2433         if (fuzzyOK && grd[tx][fy] < MINMOVE)
2434             fuzzyOK = false;
2435     }
2436     if (roundX)
2437     {
2438         // try up
2439         fx = (nx + 100) / 100;
2440         // check for monotonic
2441         if (fx != tx && ((stepx>0 && fx >= lx)
2442             || (stepx<0 && fx <= lx)))
2443             fuzzyOK = true;
2444         // see if up try is blocked
2445         if (fuzzyOK && grd[fx][ty] < MINMOVE)
2446             fuzzyOK = false;
2447 
2448         // try down
2449         if (!fuzzyOK)
2450             fx = nx / 100;
2451         // check for monotonic
2452         if (fx != tx && ((stepx>0 && fx >= lx)
2453             || (stepx<0 && fx <= lx)))
2454             fuzzyOK = true;
2455         if (fuzzyOK && grd[fx][ty] < MINMOVE)
2456             fuzzyOK = false;
2457     }
2458     // END fuzzy line algorithm
2459 
2460     if (fuzzyOK)
2461     {
2462         tx = fx;
2463         ty = fy;
2464     }
2465 
2466     return (fuzzyOK);
2467 }
2468 
2469 // affects a single cell.
2470 // returns the amount of extra range 'used up' by this beam
2471 // during the affectation.
2472 //
2473 // pseudo-code:
2474 //
2475 // 1. If wall, and wall affecting non-tracer, affect the wall.
2476 //  1b.  If for some reason the wall-affect didn't make it into
2477 //      a non-wall, return                      affect_wall()
2478 // 2. for non-tracers, produce cloud effects    affect_place_clouds()
2479 // 3. if cell holds player, affect player       affect_player()
2480 // 4. if cell holds monster, affect monster     affect_monster()
2481 // 5. return range used affectation.
2482 
affect(struct bolt & beam,int x,int y)2483 static int affect(struct bolt &beam, int x, int y)
2484 {
2485     // extra range used by hitting something
2486     int rangeUsed = 0;
2487 
2488     if (grd[x][y] < MINMOVE)
2489     {
2490         if (beam.isTracer)          // tracers always stop on walls.
2491             return (BEAM_STOP);
2492 
2493         if (affectsWalls(beam))
2494         {
2495             rangeUsed += affect_wall(beam, x, y);
2496         }
2497         // if it's still a wall,  quit - we can't do anything else to
2498         // a wall.  Otherwise effects (like clouds, etc) are still possible.
2499         if (grd[x][y] < MINMOVE)
2500             return (rangeUsed);
2501     }
2502 
2503     // grd[x][y] will NOT be a wall for the remainder of this function.
2504 
2505     // if not a tracer, place clouds
2506     if (!beam.isTracer)
2507         rangeUsed += affect_place_clouds(beam, x, y);
2508 
2509     // if player is at this location,  try to affect unless term_on_target
2510     if (x == you.x_pos && y == you.y_pos)
2511     {
2512         if (beam_term_on_target(beam) && !beam.isExplosion)
2513             return (BEAM_STOP);
2514 
2515         rangeUsed += affect_player(beam);
2516     }
2517 
2518     // if there is a monster at this location,  affect it
2519     // submerged monsters aren't really there -- bwr
2520     int mid = mgrd[x][y];
2521     if (mid != NON_MONSTER && !mons_has_ench( &menv[mid], ENCH_SUBMERGED ))
2522     {
2523         if (beam_term_on_target(beam) && !beam.isExplosion)
2524             return (BEAM_STOP);
2525 
2526         struct monsters* monster = &menv[mid];
2527         rangeUsed += affect_monster(beam, monster);
2528     }
2529 
2530     return (rangeUsed);
2531 }
2532 
affectsWalls(struct bolt & beam)2533 static bool affectsWalls(struct bolt &beam)
2534 {
2535     // don't know of any explosion that affects walls.  But change it here
2536     // if there is.
2537     if (beam.isExplosion)
2538         return (false);
2539 
2540     // digging
2541     if (beam.flavour == BEAM_DIGGING)
2542         return (true);
2543 
2544     // Isn't this much nicer than the hack to remove ice bolts, disrupt,
2545     // and needles (just because they were also coloured "white") -- bwr
2546     if (beam.flavour == BEAM_DISINTEGRATION && beam.damage.num >= 3)
2547         return (true);
2548 
2549     // eye of devestation?
2550     if (beam.flavour == BEAM_NUKE)
2551         return (true);
2552 
2553     return (false);
2554 }
2555 
2556 // return amount of extra range used up by affectation of this wall.
affect_wall(struct bolt & beam,int x,int y)2557 static int affect_wall(struct bolt &beam, int x, int y)
2558 {
2559     int rangeUsed = 0;
2560 
2561     // DIGGING
2562     if (beam.flavour == BEAM_DIGGING)
2563     {
2564         if (grd[x][y] == DNGN_STONE_WALL
2565             || grd[x][y] == DNGN_METAL_WALL
2566             || grd[x][y] == DNGN_PERMAROCK_WALL
2567             || x <= 5 || x >= (GXM - 5)
2568             || y <= 5 || y >= (GYM - 5))
2569         {
2570             return (0);
2571         }
2572 
2573         if (grd[x][y] == DNGN_ROCK_WALL)
2574         {
2575             grd[x][y] = DNGN_FLOOR;
2576 
2577             if (!beam.msgGenerated)
2578             {
2579                 if (!silenced(you.x_pos, you.y_pos))
2580                 {
2581                     mpr("You hear a grinding noise.");
2582                     beam.obviousEffect = true;
2583                 }
2584 
2585                 beam.msgGenerated = true;
2586             }
2587         }
2588 
2589         return (rangeUsed);
2590     }
2591     // END DIGGING EFFECT
2592 
2593     // NUKE / DISRUPT
2594     if (beam.flavour == BEAM_DISINTEGRATION || beam.flavour == BEAM_NUKE)
2595     {
2596         int targ_grid = grd[x][y];
2597 
2598         if ((targ_grid == DNGN_ROCK_WALL || targ_grid == DNGN_WAX_WALL)
2599              && !(x <= 6 || y <= 6 || x >= (GXM - 6) || y >= (GYM - 6)))
2600         {
2601             grd[ x ][ y ] = DNGN_FLOOR;
2602             if (!silenced(you.x_pos, you.y_pos))
2603             {
2604                 mpr("You hear a grinding noise.");
2605                 beam.obviousEffect = true;
2606             }
2607         }
2608 
2609         if (targ_grid == DNGN_ORCISH_IDOL || (targ_grid >= DNGN_SILVER_STATUE
2610                 && targ_grid <= DNGN_STATUE_39))
2611         {
2612             grd[x][y] = DNGN_FLOOR;
2613 
2614             if (!silenced(you.x_pos, you.y_pos))
2615             {
2616                 if (!see_grid( x, y ))
2617                     mpr("You hear a hideous screaming!");
2618                 else
2619                     mpr("The statue screams as its substance crumbles away!");
2620             }
2621             else
2622             {
2623                 if (see_grid(x,y))
2624                     mpr("The statue twists and shakes as its substance crumbles away!");
2625             }
2626 
2627             if (targ_grid == DNGN_SILVER_STATUE)
2628                 Visible_Statue[ STATUE_SILVER ] = 0;
2629             else if (targ_grid == DNGN_ORANGE_CRYSTAL_STATUE)
2630                 Visible_Statue[ STATUE_ORANGE_CRYSTAL ] = 0;
2631 
2632             beam.obviousEffect = 1;
2633         }
2634 
2635         return (BEAM_STOP);
2636     }
2637 
2638     return (rangeUsed);
2639 }
2640 
affect_place_clouds(struct bolt & beam,int x,int y)2641 static int affect_place_clouds(struct bolt &beam, int x, int y)
2642 {
2643     int cloud_type;
2644 
2645     if (beam.isExplosion)
2646     {
2647         affect_place_explosion_clouds( beam, x, y );
2648         return (0);       // return value irrelevant for explosions
2649     }
2650 
2651     // check for CLOUD HITS
2652     if (env.cgrid[x][y] != EMPTY_CLOUD)     // hit a cloud
2653     {
2654         // polymorph randomly changes clouds in its path
2655         if (beam.flavour == BEAM_POLYMORPH)
2656             env.cloud[ env.cgrid[x][y] ].type = 1 + random2(8);
2657 
2658         // now exit (all enchantments)
2659         if (beam.beam_name[0] == '0')
2660             return (0);
2661 
2662         int clouty = env.cgrid[x][y];
2663 
2664         // fire cancelling cold & vice versa
2665         if (((env.cloud[clouty].type == CLOUD_COLD
2666                 || env.cloud[clouty].type == CLOUD_COLD_MON)
2667             && (beam.flavour == BEAM_FIRE
2668                 || beam.flavour == BEAM_LAVA))
2669             || ((env.cloud[clouty].type == CLOUD_FIRE
2670                 || env.cloud[clouty].type == CLOUD_FIRE_MON)
2671             && beam.flavour == BEAM_COLD))
2672         {
2673             if (!silenced(x, y)
2674                 && !silenced(you.x_pos, you.y_pos))
2675             {
2676                 mpr("You hear a sizzling sound!");
2677             }
2678 
2679             delete_cloud( clouty );
2680             return (5);
2681         }
2682     }
2683 
2684     // POISON BLAST
2685     if (strcmp(beam.beam_name, "blast of poison") == 0)
2686     {
2687         cloud_type = YOU_KILL(beam.thrower) ? CLOUD_POISON : CLOUD_POISON_MON;
2688 
2689         place_cloud( cloud_type, x, y, random2(4) + 2 );
2690     }
2691 
2692     // FIRE/COLD over water/lava
2693     if ( (grd[x][y] == DNGN_LAVA && beam.flavour == BEAM_COLD)
2694         || ((grd[x][y] == DNGN_DEEP_WATER || grd[x][y] == DNGN_SHALLOW_WATER)
2695               && beam.flavour == BEAM_FIRE) )
2696     {
2697         cloud_type = YOU_KILL(beam.thrower) ? CLOUD_STEAM : CLOUD_STEAM_MON;
2698         place_cloud( cloud_type, x, y, 2 + random2(5) );
2699     }
2700 
2701     // ORB OF ENERGY
2702     if (strcmp(beam.beam_name, "orb of energy") == 0)
2703         place_cloud( CLOUD_PURP_SMOKE, x, y, random2(5) + 1 );
2704 
2705     // GREAT BLAST OF COLD
2706     if (strcmp(beam.beam_name, "great blast of cold") == 0)
2707         place_cloud( CLOUD_COLD, x, y, random2(5) + 3 );
2708 
2709 
2710     // BALL OF STEAM
2711     if (strcmp(beam.beam_name, "ball of steam") == 0)
2712     {
2713         cloud_type = YOU_KILL(beam.thrower) ? CLOUD_STEAM : CLOUD_STEAM_MON;
2714         place_cloud( cloud_type, x, y, random2(5) + 2 );
2715     }
2716 
2717     // STICKY FLAME
2718     if (strcmp(beam.beam_name, "sticky flame") == 0)
2719     {
2720         place_cloud( CLOUD_BLACK_SMOKE, x, y, random2(4) + 2 );
2721     }
2722 
2723     // POISON GAS
2724     if (strcmp(beam.beam_name, "poison gas") == 0)
2725     {
2726         cloud_type = YOU_KILL(beam.thrower) ? CLOUD_POISON : CLOUD_POISON_MON;
2727         place_cloud( cloud_type, x, y, random2(4) + 3 );
2728     }
2729 
2730     return (0);
2731 }
2732 
2733 // following two functions used with explosions:
affect_place_explosion_clouds(struct bolt & beam,int x,int y)2734 static void affect_place_explosion_clouds(struct bolt &beam, int x, int y)
2735 {
2736     int cloud_type;
2737     int duration;
2738 
2739     // first check: FIRE/COLD over water/lava
2740     if ( (grd[x][y] == DNGN_LAVA && beam.flavour == BEAM_COLD)
2741         || ((grd[x][y] == DNGN_DEEP_WATER || grd[x][y] == DNGN_SHALLOW_WATER)
2742               && beam.flavour == BEAM_FIRE) )
2743     {
2744         cloud_type = YOU_KILL(beam.thrower) ? CLOUD_STEAM : CLOUD_STEAM_MON;
2745         place_cloud( cloud_type, x, y, 2 + random2(5) );
2746         return;
2747     }
2748 
2749     if (beam.flavour >= BEAM_POTION_STINKING_CLOUD
2750         && beam.flavour <= BEAM_POTION_RANDOM)
2751     {
2752         duration = roll_dice( 2, 3 + beam.ench_power / 20 );
2753 
2754         switch (beam.flavour)
2755         {
2756         case BEAM_POTION_STINKING_CLOUD:
2757             cloud_type = CLOUD_STINK;
2758             break;
2759 
2760         case BEAM_POTION_POISON:
2761             cloud_type = CLOUD_POISON;
2762             break;
2763 
2764         case BEAM_POTION_MIASMA:
2765             cloud_type = CLOUD_MIASMA;
2766             break;
2767 
2768         case BEAM_POTION_BLACK_SMOKE:
2769             cloud_type = CLOUD_BLACK_SMOKE;
2770             break;
2771 
2772         case BEAM_POTION_FIRE:
2773             cloud_type = CLOUD_FIRE;
2774             break;
2775 
2776         case BEAM_POTION_COLD:
2777             cloud_type = CLOUD_COLD;
2778             break;
2779 
2780         case BEAM_POTION_BLUE_SMOKE:
2781             cloud_type = CLOUD_BLUE_SMOKE;
2782             break;
2783 
2784         case BEAM_POTION_PURP_SMOKE:
2785             cloud_type = CLOUD_PURP_SMOKE;
2786             break;
2787 
2788         case BEAM_POTION_RANDOM:
2789             switch (random2(10))
2790             {
2791             case 0:  cloud_type = CLOUD_FIRE;           break;
2792             case 1:  cloud_type = CLOUD_STINK;          break;
2793             case 2:  cloud_type = CLOUD_COLD;           break;
2794             case 3:  cloud_type = CLOUD_POISON;         break;
2795             case 4:  cloud_type = CLOUD_BLACK_SMOKE;    break;
2796             case 5:  cloud_type = CLOUD_BLUE_SMOKE;     break;
2797             case 6:  cloud_type = CLOUD_PURP_SMOKE;     break;
2798             default: cloud_type = CLOUD_STEAM;          break;
2799             }
2800             break;
2801 
2802         case BEAM_POTION_STEAM:
2803         default:
2804             cloud_type = CLOUD_STEAM;
2805             break;
2806         }
2807 
2808         place_cloud( cloud_type, x, y, duration );
2809     }
2810 
2811     // then check for more specific explosion cloud types.
2812     if (stricmp(beam.beam_name, "ice storm") == 0)
2813     {
2814         place_cloud( CLOUD_COLD, x, y, 2 + random2avg(5, 2) );
2815     }
2816 
2817     if (stricmp(beam.beam_name, "stinking cloud") == 0)
2818     {
2819         duration =  1 + random2(4) + random2( (beam.ench_power / 50) + 1 );
2820         place_cloud( CLOUD_STINK, x, y, duration );
2821     }
2822 
2823     if (strcmp(beam.beam_name, "great blast of fire") == 0)
2824     {
2825         duration = 1 + random2(5) + roll_dice( 2, beam.ench_power / 5 );
2826 
2827         if (duration > 20)
2828             duration = 20 + random2(4);
2829 
2830         place_cloud( CLOUD_FIRE, x, y, duration );
2831 
2832         if (grd[x][y] == DNGN_FLOOR && mgrd[x][y] == NON_MONSTER
2833             && one_chance_in(4))
2834         {
2835             mons_place( MONS_FIRE_VORTEX, BEH_HOSTILE, MHITNOT, true, x, y );
2836         }
2837     }
2838 }
2839 
affect_items(struct bolt & beam,int x,int y)2840 static void affect_items(struct bolt &beam, int x, int y)
2841 {
2842     char objs_vulnerable = -1;
2843 
2844     switch (beam.flavour)
2845     {
2846     case BEAM_FIRE:
2847     case BEAM_LAVA:
2848         objs_vulnerable = OBJ_SCROLLS;
2849         break;
2850     case BEAM_COLD:
2851         objs_vulnerable = OBJ_POTIONS;
2852         break;
2853     case BEAM_SPORE:
2854         objs_vulnerable = OBJ_FOOD;
2855         break;
2856     }
2857 
2858     if (stricmp(beam.beam_name, "hellfire") == 0)
2859         objs_vulnerable = OBJ_SCROLLS;
2860 
2861     if (igrd[x][y] != NON_ITEM)
2862     {
2863         if (objs_vulnerable != -1 &&
2864             mitm[igrd[x][y]].base_type == objs_vulnerable)
2865         {
2866             destroy_item( igrd[ x ][ y ] );
2867 
2868             if (objs_vulnerable == OBJ_SCROLLS && see_grid(x,y))
2869             {
2870                 mpr("You see a puff of smoke.");
2871             }
2872 
2873             if (objs_vulnerable == OBJ_POTIONS && !silenced(x,y)
2874                 && !silenced(you.x_pos, you.y_pos))
2875             {
2876                 mpr("You hear glass shatter.");
2877             }
2878         }
2879     }
2880 }
2881 
2882 // A little helper function to handle the calling of ouch()...
beam_ouch(int dam,struct bolt & beam)2883 static void beam_ouch( int dam, struct bolt &beam )
2884 {
2885     // The order of this is important.
2886     if (YOU_KILL( beam.thrower ) && !beam.aux_source)
2887     {
2888         ouch( dam, 0, KILLED_BY_TARGETTING );
2889     }
2890     else if (MON_KILL( beam.thrower ))
2891     {
2892         if (beam.flavour == BEAM_SPORE)
2893             ouch( dam, beam.beam_source, KILLED_BY_SPORE );
2894         else
2895             ouch( dam, beam.beam_source, KILLED_BY_BEAM, beam.aux_source );
2896     }
2897     else // KILL_MISC || (YOU_KILL && aux_source)
2898     {
2899         ouch( dam, beam.beam_source, KILLED_BY_WILD_MAGIC, beam.aux_source );
2900     }
2901 }
2902 
2903 // return amount of extra range used up by affectation of the player
affect_player(struct bolt & beam)2904 static int affect_player( struct bolt &beam )
2905 {
2906     int beamHit;
2907 
2908     // digging -- don't care.
2909     if (beam.flavour == BEAM_DIGGING)
2910         return (0);
2911 
2912     // check for tracer
2913     if (beam.isTracer)
2914     {
2915         // check can see player
2916         // XXX: note the cheat to allow for ME_ALERT to target the player...
2917         // replace this with a time since alert system, rather than just
2918         // peeking to see if the character is still there. -- bwr
2919         if (beam.canSeeInvis || !you.invis
2920             || (you.x_pos == beam.target_x && you.y_pos == beam.target_y))
2921         {
2922             if (beam.isFriendly)
2923             {
2924                 beam.fr_count += 1;
2925                 beam.fr_power += you.experience_level;
2926             }
2927             else
2928             {
2929                 beam.foe_count += 1;
2930                 beam.foe_power += you.experience_level;
2931             }
2932         }
2933         return (range_used_on_hit(beam));
2934     }
2935 
2936     // BEGIN real beam code
2937     beam.msgGenerated = true;
2938 
2939     // use beamHit,  NOT beam.hit,  for modification of tohit.. geez!
2940     beamHit = beam.hit;
2941 
2942     if (beam.beam_name[0] != '0')
2943     {
2944         if (!beam.isExplosion && !beam.aimedAtFeet)
2945         {
2946             // BEGIN BEAM/MISSILE
2947             int dodge = random2limit( player_evasion(), 40 )
2948                         + random2( you.dex ) / 3 - 2;
2949 
2950             if (beam.isBeam)
2951             {
2952                 // beams can be dodged
2953                 if (player_light_armour()
2954                     && !beam.aimedAtFeet && coinflip())
2955                 {
2956                     exercise(SK_DODGING, 1);
2957                 }
2958 
2959                 if (you.duration[DUR_REPEL_MISSILES]
2960                     || you.mutation[MUT_REPULSION_FIELD] == 3)
2961                 {
2962                     beamHit -= random2(beamHit / 2);
2963                 }
2964 
2965                 if (you.duration[DUR_DEFLECT_MISSILES])
2966                     beamHit = random2(beamHit / 3);
2967 
2968                 if (beamHit < dodge)
2969                 {
2970                     strcpy(info, "The ");
2971                     strcat(info, beam.beam_name);
2972                     strcat(info, " misses you.");
2973                     mpr(info);
2974                     return (0);           // no extra used by miss!
2975                 }
2976             }
2977             else
2978             {
2979                 // non-beams can be blocked or dodged
2980                 if (you.equip[EQ_SHIELD] != -1
2981                         && !beam.aimedAtFeet
2982                         && player_shield_class() > 0)
2983                 {
2984                     int exer = one_chance_in(3) ? 1 : 0;
2985                     const int hit = random2( beam.hit * 5
2986                                         + 10 * you.shield_blocks * you.shield_blocks );
2987 
2988                     const int block = random2(player_shield_class())
2989                                         + (random2(you.dex) / 5) - 1;
2990 
2991                     if (hit < block)
2992                     {
2993                         you.shield_blocks++;
2994                         snprintf( info, INFO_SIZE, "You block the %s.",
2995                                                     beam.beam_name );
2996                         mpr( info );
2997 
2998                         exercise( SK_SHIELDS, exer + 1 );
2999                         return (BEAM_STOP);
3000                     }
3001 
3002                     // some training just for the "attempt"
3003                     exercise( SK_SHIELDS, exer );
3004                 }
3005 
3006                 if (player_light_armour() && !beam.aimedAtFeet
3007                     && coinflip())
3008                     exercise(SK_DODGING, 1);
3009 
3010                 if (you.duration[DUR_REPEL_MISSILES]
3011                     || you.mutation[MUT_REPULSION_FIELD] == 3)
3012                 {
3013                     beamHit = random2(beamHit);
3014                 }
3015 
3016 
3017                 // miss message
3018                 if (beamHit < dodge || you.duration[DUR_DEFLECT_MISSILES])
3019                 {
3020                     strcpy(info, "The ");
3021                     strcat(info, beam.beam_name);
3022                     strcat(info, " misses you.");
3023                     return (0);
3024                 }
3025             }
3026         }
3027     }
3028     else
3029     {
3030         // BEGIN enchantment beam
3031         if (beam.flavour != BEAM_HASTE
3032             && beam.flavour != BEAM_INVISIBILITY
3033             && beam.flavour != BEAM_HEALING
3034             && ((beam.flavour != BEAM_TELEPORT && beam.flavour != BEAM_BANISH)
3035                 || !beam.aimedAtFeet)
3036             && you_resist_magic( beam.ench_power ))
3037         {
3038             canned_msg(MSG_YOU_RESIST);
3039             return (range_used_on_hit(beam));
3040         }
3041 
3042         // these colors are misapplied - see mons_ench_f2() {dlb}
3043         switch (beam.flavour)
3044         {
3045         case BEAM_SLOW:
3046             potion_effect( POT_SLOWING, beam.ench_power );
3047             beam.obviousEffect = true;
3048             break;     // slow
3049 
3050         case BEAM_HASTE:
3051             potion_effect( POT_SPEED, beam.ench_power );
3052             contaminate_player( 1 );
3053             beam.obviousEffect = true;
3054             break;     // haste
3055 
3056         case BEAM_HEALING:
3057             potion_effect( POT_HEAL_WOUNDS, beam.ench_power );
3058             beam.obviousEffect = true;
3059             break;     // heal (heal wounds potion eff)
3060 
3061         case BEAM_PARALYSIS:
3062             potion_effect( POT_PARALYSIS, beam.ench_power );
3063             beam.obviousEffect = true;
3064             break;     // paralysis
3065 
3066         case BEAM_CONFUSION:
3067             potion_effect( POT_CONFUSION, beam.ench_power );
3068             beam.obviousEffect = true;
3069             break;     // confusion
3070 
3071         case BEAM_INVISIBILITY:
3072             potion_effect( POT_INVISIBILITY, beam.ench_power );
3073             contaminate_player( 1 + random2(2) );
3074             beam.obviousEffect = true;
3075             break;     // invisibility
3076 
3077             // 6 is used by digging
3078 
3079         case BEAM_TELEPORT:
3080             you_teleport();
3081             beam.obviousEffect = true;
3082             break;
3083 
3084         case BEAM_POLYMORPH:
3085             mpr("This is polymorph other only!");
3086             beam.obviousEffect = true;
3087             break;
3088 
3089         case BEAM_CHARM:
3090             potion_effect( POT_CONFUSION, beam.ench_power );
3091             beam.obviousEffect = true;
3092             break;     // enslavement - confusion?
3093 
3094         case BEAM_BANISH:
3095             if (you.level_type == LEVEL_ABYSS)
3096             {
3097                 mpr("You feel trapped.");
3098                 break;
3099             }
3100             mpr("You are cast into the Abyss!");
3101             more();
3102             banished(DNGN_ENTER_ABYSS);
3103             beam.obviousEffect = true;
3104             break;     // banishment to the abyss
3105 
3106         case BEAM_PAIN:      // pain
3107             if (you.is_undead || you.mutation[MUT_TORMENT_RESISTANCE])
3108             {
3109                 mpr("You are unaffected.");
3110                 break;
3111             }
3112 
3113             mpr("Pain shoots through your body!");
3114 
3115             if (!beam.aux_source)
3116                 beam.aux_source = "by nerve-wracking pain";
3117 
3118             beam_ouch( roll_dice( beam.damage ), beam );
3119             beam.obviousEffect = true;
3120             break;
3121 
3122         case BEAM_DISPEL_UNDEAD:
3123             if (!you.is_undead)
3124             {
3125                 mpr("You are unaffected.");
3126                 break;
3127             }
3128 
3129             mpr( "You convulse!" );
3130 
3131             if (!beam.aux_source)
3132                 beam.aux_source = "by dispel undead";
3133 
3134             beam_ouch( roll_dice( beam.damage ), beam );
3135             beam.obviousEffect = true;
3136             break;
3137 
3138         case BEAM_DISINTEGRATION:
3139             mpr("You are blasted!");
3140 
3141             if (!beam.aux_source)
3142                 beam.aux_source = "disintegration bolt";
3143 
3144             beam_ouch( roll_dice( beam.damage ), beam );
3145             beam.obviousEffect = true;
3146             break;
3147 
3148         default:
3149             // _all_ enchantments should be enumerated here!
3150             mpr("Software bugs nibble your toes!");
3151             break;
3152         }               // end of switch (beam.colour)
3153 
3154         // regardless of affect, we need to know if this is a stopper
3155         // or not - it seems all of the above are.
3156         return (range_used_on_hit(beam));
3157 
3158         // END enchantment beam
3159     }
3160 
3161     // THE BEAM IS NOW GUARANTEED TO BE A NON-ENCHANTMENT WHICH HIT
3162 
3163     snprintf( info, INFO_SIZE, "The %s %s you!",
3164                 beam.beam_name, (beam.isExplosion ? "engulfs" : "hits") );
3165     mpr( info );
3166 
3167     int hurted = 0;
3168     int burn_power = (beam.isExplosion) ? 5 : ((beam.isBeam) ? 3 : 2);
3169 
3170     // Roll the damage
3171     hurted += roll_dice( beam.damage );
3172 
3173 #if DEBUG_DIAGNOSTICS
3174     int roll = hurted;
3175 #endif
3176 
3177     hurted -= random2( 1 + player_AC() );
3178 
3179 
3180     // shrapnel
3181     if (beam.flavour == BEAM_FRAG && !player_light_armour())
3182     {
3183         hurted -= random2( 1 + player_AC() );
3184         hurted -= random2( 1 + player_AC() );
3185     }
3186 
3187 #if DEBUG_DIAGNOSTICS
3188     snprintf( info, INFO_SIZE, "Player damage: rolled=%d; after AC=%d",
3189               roll, hurted );
3190 
3191     mpr( info, MSGCH_DIAGNOSTICS );
3192 #endif
3193 
3194     if (you.equip[EQ_BODY_ARMOUR] != -1)
3195     {
3196         if (!player_light_armour() && one_chance_in(4)
3197             && random2(1000) <= mass_item( you.inv[you.equip[EQ_BODY_ARMOUR]] ))
3198         {
3199             exercise( SK_ARMOUR, 1 );
3200         }
3201     }
3202 
3203     if (hurted < 0)
3204         hurted = 0;
3205 
3206     hurted = check_your_resists( hurted, beam.flavour );
3207 
3208     // poisoning
3209     if (strstr(beam.beam_name, "poison") != NULL
3210         && beam.flavour != BEAM_POISON
3211         && beam.flavour != BEAM_POISON_ARROW
3212         && !player_res_poison())
3213     {
3214         if (hurted || (strstr( beam.beam_name, "needle" ) != NULL
3215                         && random2(100) < 90 - (3 * player_AC())))
3216         {
3217             poison_player( 1 + random2(3) );
3218         }
3219     }
3220 
3221     // sticky flame
3222     if (strcmp(beam.beam_name, "sticky flame") == 0
3223         && (you.species != SP_MOTTLED_DRACONIAN
3224             || you.experience_level < 6))
3225     {
3226         if (!player_equip( EQ_BODY_ARMOUR, ARM_MOTTLED_DRAGON_ARMOUR ))
3227             you.duration[DUR_LIQUID_FLAMES] += random2avg(7, 3) + 1;
3228     }
3229 
3230     // simple cases for scroll burns
3231     if (beam.flavour == BEAM_LAVA || stricmp(beam.beam_name, "hellfire") == 0)
3232         scrolls_burn( burn_power, OBJ_SCROLLS );
3233 
3234     // more complex (geez..)
3235     if (beam.flavour == BEAM_FIRE && strcmp(beam.beam_name, "ball of steam") != 0)
3236         scrolls_burn( burn_power, OBJ_SCROLLS );
3237 
3238     // potions exploding
3239     if (beam.flavour == BEAM_COLD)
3240         scrolls_burn( burn_power, OBJ_POTIONS );
3241 
3242     if (beam.flavour == BEAM_ACID)
3243         splash_with_acid(5);
3244 
3245     // spore pops
3246     if (beam.isExplosion && beam.flavour == BEAM_SPORE)
3247         scrolls_burn( 2, OBJ_FOOD );
3248 
3249 #if DEBUG_DIAGNOSTICS
3250     snprintf( info, INFO_SIZE, "Damage: %d", hurted );
3251     mpr( info, MSGCH_DIAGNOSTICS );
3252 #endif
3253 
3254     beam_ouch( hurted, beam );
3255 
3256     return (range_used_on_hit( beam ));
3257 }
3258 
3259 // return amount of range used up by affectation of this monster
affect_monster(struct bolt & beam,struct monsters * mon)3260 static int  affect_monster(struct bolt &beam, struct monsters *mon)
3261 {
3262     int tid = mgrd[mon->x][mon->y];
3263     int hurt;
3264     int hurt_final;
3265 
3266     // digging -- don't care.
3267     if (beam.flavour == BEAM_DIGGING)
3268         return (0);
3269 
3270     // fire storm creates these, so we'll avoid affecting them
3271     if (strcmp(beam.beam_name, "great blast of fire") == 0
3272         && mon->type == MONS_FIRE_VORTEX)
3273     {
3274         return (0);
3275     }
3276 
3277     // check for tracer
3278     if (beam.isTracer)
3279     {
3280         // check can see other monster
3281         if (!beam.canSeeInvis && mons_has_ench(&menv[tid], ENCH_INVIS))
3282         {
3283             // can't see this monster, ignore it
3284             return 0;
3285         }
3286     }
3287 
3288     if (beam.beam_name[0] == '0')
3289     {
3290         if (beam.isTracer)
3291         {
3292             // enchant case -- enchantments always hit, so update target immed.
3293             if (beam.isFriendly ^ mons_friendly(mon))
3294             {
3295                 beam.foe_count += 1;
3296                 beam.foe_power += mons_power(tid);
3297             }
3298             else
3299             {
3300                 beam.fr_count += 1;
3301                 beam.fr_power += mons_power(tid);
3302             }
3303 
3304             return (range_used_on_hit(beam));
3305         }
3306 
3307         // BEGIN non-tracer enchantment
3308 
3309         // nasty enchantments will annoy the monster, and are considered
3310         // naughty (even if a monster might resist)
3311         if (nasty_beam(mon, beam))
3312         {
3313             if (mons_friendly(mon) && YOU_KILL(beam.thrower))
3314                 naughty(NAUGHTY_ATTACK_FRIEND, 5);
3315 
3316             behaviour_event( mon, ME_ANNOY,
3317                         MON_KILL(beam.thrower) ? beam.beam_source : MHITYOU );
3318         }
3319         else
3320         {
3321             behaviour_event( mon, ME_ALERT,
3322                         MON_KILL(beam.thrower) ? beam.beam_source : MHITYOU );
3323         }
3324 
3325         // !@#*( affect_monster_enchantment() has side-effects on
3326         // the beam structure which screw up range_used_on_hit(),
3327         // so call it now and store.
3328         int rangeUsed = range_used_on_hit(beam);
3329 
3330         // now do enchantment affect
3331         int ench_result = affect_monster_enchantment(beam, mon);
3332         switch(ench_result)
3333         {
3334             case MON_RESIST:
3335                 if (simple_monster_message(mon, " resists."))
3336                     beam.msgGenerated = true;
3337                 break;
3338             case MON_UNAFFECTED:
3339                 if (simple_monster_message(mon, " is unaffected."))
3340                     beam.msgGenerated = true;
3341                 break;
3342             default:
3343                 break;
3344         }
3345         return (rangeUsed);
3346 
3347         // END non-tracer enchantment
3348     }
3349 
3350 
3351     // BEGIN non-enchantment (could still be tracer)
3352     if (mons_has_ench( mon, ENCH_SUBMERGED ) && !beam.aimedAtFeet)
3353         return (0);                   // missed me!
3354 
3355     // we need to know how much the monster _would_ be hurt by this,  before
3356     // we decide if it actually hits.
3357 
3358     // Roll the damage:
3359     hurt = roll_dice( beam.damage );
3360 
3361     hurt_final = hurt;
3362 
3363     if (beam.isTracer)
3364         hurt_final -= mon->armour_class / 2;
3365     else
3366         hurt_final -= random2(1 + mon->armour_class);
3367 
3368     if (beam.flavour == BEAM_FRAG)
3369     {
3370         hurt_final -= random2(1 + mon->armour_class);
3371         hurt_final -= random2(1 + mon->armour_class);
3372     }
3373 
3374     if (hurt_final < 1)
3375     {
3376         hurt_final = 0;
3377     }
3378 
3379 #if DEBUG_DIAGNOSTICS
3380     const int old_hurt = hurt_final;
3381 #endif
3382 
3383     // check monster resists,  _without_ side effects (since the
3384     // beam/missile might yet miss!)
3385     hurt_final = mons_adjust_flavoured( mon, beam, hurt_final, false );
3386 
3387 #if DEBUG_DIAGNOSTICS
3388     if (!beam.isTracer)
3389     {
3390         snprintf( info, INFO_SIZE,
3391               "Monster: %s; Damage: pre-AC: %d; post-AC: %d; post-resist: %d",
3392                   ptr_monam( mon, DESC_PLAIN ), hurt, old_hurt, hurt_final );
3393 
3394         mpr( info, MSGCH_DIAGNOSTICS );
3395     }
3396 #endif
3397 
3398     // now,  we know how much this monster would (probably) be
3399     // hurt by this beam.
3400     if (beam.isTracer)
3401     {
3402         if (hurt_final != 0)
3403         {
3404             // monster could be hurt somewhat,  but only apply the
3405             // monster's power based on how badly it is affected.
3406             // For example,  if a fire giant (power 16) threw a
3407             // fireball at another fire giant,  and it only took
3408             // 1/3 damage,  then power of 5 would be applied to
3409             // foe_power or fr_power.
3410             if (beam.isFriendly ^ mons_friendly(mon))
3411             {
3412                 beam.foe_count += 1;
3413                 beam.foe_power += hurt_final * mons_power(tid) / hurt;
3414             }
3415             else
3416             {
3417                 beam.fr_count += 1;
3418                 beam.fr_power += hurt_final * mons_power(tid) / hurt;
3419             }
3420         }
3421         // either way, we could hit this monster, so return range used
3422         return (range_used_on_hit(beam));
3423     }
3424     // END non-enchantment (could still be tracer)
3425 
3426     // BEGIN real non-enchantment beam
3427 
3428     // player beams which hit friendly MIGHT annoy them and be considered
3429     // naughty if they do much damage (this is so as not to penalize
3430     // players that fling fireballs into a melee with fire elementals
3431     // on their side - the elementals won't give a sh*t,  after all)
3432 
3433     if (nasty_beam(mon, beam))
3434     {
3435         // could be naughty if it's your beam & the montster is friendly
3436         if (mons_friendly(mon) && YOU_KILL(beam.thrower))
3437         {
3438             // but did you do enough damage to piss them off?
3439             if (hurt_final > mon->hit_dice / 3)
3440             {
3441                 naughty(NAUGHTY_ATTACK_FRIEND, 5);
3442                 behaviour_event( mon, ME_ANNOY, MHITYOU );
3443             }
3444         }
3445         else
3446         {
3447             behaviour_event(mon, ME_ANNOY,
3448                     MON_KILL(beam.thrower) ? beam.beam_source : MHITYOU );
3449         }
3450     }
3451 
3452     // explosions always 'hit'
3453     if (!beam.isExplosion && beam.hit < random2(mon->evasion))
3454     {
3455         // if the PLAYER cannot see the monster, don't tell them anything!
3456         if (player_monster_visible( &menv[tid] ) && mons_near(mon))
3457         {
3458             strcpy(info, "The ");
3459             strcat(info, beam.beam_name);
3460             strcat(info, " misses ");
3461             strcat(info, ptr_monam(mon, DESC_NOCAP_THE));
3462             strcat(info, ".");
3463             mpr(info);
3464         }
3465         return (0);
3466     }
3467 
3468     // the beam hit.
3469     if (mons_near(mon))
3470     {
3471         strcpy(info, "The ");
3472         strcat(info, beam.beam_name);
3473         strcat(info, beam.isExplosion?" engulfs ":" hits ");
3474 
3475         if (player_monster_visible( &menv[tid] ))
3476             strcat(info, ptr_monam(mon, DESC_NOCAP_THE));
3477         else
3478             strcat(info, "something");
3479 
3480         strcat(info, ".");
3481         mpr(info);
3482     }
3483     else
3484     {
3485         // the player might hear something,
3486         // if _they_ fired a missile (not beam)
3487         if (!silenced(you.x_pos, you.y_pos) && beam.flavour == BEAM_MISSILE
3488                 && YOU_KILL(beam.thrower))
3489         {
3490             strcpy(info, "The ");
3491             strcat(info, beam.beam_name);
3492             strcat(info, " hits something.");
3493             mpr(info);
3494         }
3495     }
3496 
3497     // note that hurt_final was calculated above, so we don't need it again.
3498     // just need to apply flavoured specials (since we called with
3499     // doFlavouredEffects = false above)
3500     hurt_final = mons_adjust_flavoured(mon, beam, hurt_final);
3501 
3502     // now hurt monster
3503     hurt_monster( mon, hurt_final );
3504 
3505     int thrower = YOU_KILL(beam.thrower) ? KILL_YOU_MISSILE : KILL_MON_MISSILE;
3506 
3507     if (mon->hit_points < 1)
3508     {
3509         monster_die(mon, thrower, beam.beam_source);
3510     }
3511     else
3512     {
3513         if (thrower == KILL_YOU_MISSILE && mons_near(mon))
3514             print_wounds(mon);
3515 
3516         // sticky flame
3517         if (strcmp(beam.beam_name, "sticky flame") == 0)
3518         {
3519             int levels = 1 + random2( hurt_final ) / 2;
3520             if (levels > 4)
3521                 levels = 4;
3522 
3523             sticky_flame_monster( tid, YOU_KILL(beam.thrower), levels );
3524         }
3525 
3526 
3527         /* looks for missiles which aren't poison but
3528            are poison*ed* */
3529         if (strstr(beam.beam_name, "poison") != NULL
3530             && beam.flavour != BEAM_POISON
3531             && beam.flavour != BEAM_POISON_ARROW)
3532         {
3533             if (strstr(beam.beam_name, "needle") != NULL
3534                 && random2(100) < 90 - (3 * mon->armour_class))
3535             {
3536                 poison_monster( mon, YOU_KILL(beam.thrower), 2 );
3537             }
3538             else if (random2(hurt_final) - random2(mon->armour_class) > 0)
3539             {
3540                 poison_monster( mon, YOU_KILL(beam.thrower) );
3541             }
3542         }
3543 
3544         if (mons_is_mimic( mon->type ))
3545             mimic_alert(mon);
3546     }
3547 
3548     return (range_used_on_hit(beam));
3549 }
3550 
affect_monster_enchantment(struct bolt & beam,struct monsters * mon)3551 static int affect_monster_enchantment(struct bolt &beam, struct monsters *mon)
3552 {
3553     if (beam.flavour == BEAM_TELEPORT) // teleportation
3554     {
3555         if (check_mons_resist_magic( mon, beam.ench_power )
3556             && !beam.aimedAtFeet)
3557         {
3558             return (MON_RESIST);
3559         }
3560 
3561         if (simple_monster_message(mon, " looks slightly unstable."))
3562             beam.obviousEffect = true;
3563 
3564         monster_teleport(mon, false);
3565 
3566         return (MON_AFFECTED);
3567     }
3568 
3569     if (beam.flavour == BEAM_POLYMORPH)
3570     {
3571         if (mons_holiness( mon->type ) != MH_NATURAL)
3572             return (MON_UNAFFECTED);
3573 
3574         if (check_mons_resist_magic( mon, beam.ench_power ))
3575             return (MON_RESIST);
3576 
3577         if (monster_polymorph(mon, RANDOM_MONSTER, 100))
3578             beam.obviousEffect = true;
3579 
3580         return (MON_AFFECTED);
3581     }
3582 
3583     if (beam.flavour == BEAM_BANISH)
3584     {
3585         if (check_mons_resist_magic( mon, beam.ench_power ))
3586             return (MON_RESIST);
3587 
3588         if (you.level_type == LEVEL_ABYSS)
3589         {
3590             simple_monster_message(mon, " wobbles for a moment.");
3591         }
3592         else
3593             monster_die(mon, KILL_RESET, beam.beam_source);
3594 
3595         beam.obviousEffect = true;
3596         return (MON_AFFECTED);
3597     }
3598 
3599     if (beam.flavour == BEAM_DEGENERATE)
3600     {
3601         if (mons_holiness(mon->type) != MH_NATURAL
3602             || mon->type == MONS_PULSATING_LUMP)
3603         {
3604             return (MON_UNAFFECTED);
3605         }
3606 
3607         if (check_mons_resist_magic( mon, beam.ench_power ))
3608             return (MON_RESIST);
3609 
3610         if (monster_polymorph(mon, MONS_PULSATING_LUMP, 100))
3611             beam.obviousEffect = true;
3612 
3613         return (MON_AFFECTED);
3614     }
3615 
3616     if (beam.flavour == BEAM_DISPEL_UNDEAD)
3617     {
3618         if (mons_holiness(mon->type) != MH_UNDEAD)
3619             return (MON_UNAFFECTED);
3620 
3621         if (simple_monster_message(mon, " convulses!"))
3622             beam.obviousEffect = true;
3623 
3624         hurt_monster( mon, roll_dice( beam.damage ) );
3625 
3626         goto deathCheck;
3627     }
3628 
3629     if (beam.flavour == BEAM_ENSLAVE_UNDEAD
3630         && mons_holiness(mon->type) == MH_UNDEAD)
3631     {
3632 #if DEBUG_DIAGNOSTICS
3633         snprintf( info, INFO_SIZE, "HD: %d; pow: %d",
3634                   mon->hit_dice, beam.ench_power );
3635 
3636         mpr( info, MSGCH_DIAGNOSTICS );
3637 #endif
3638 
3639         if (check_mons_resist_magic( mon, beam.ench_power ))
3640             return (MON_RESIST);
3641 
3642         simple_monster_message(mon, " is enslaved.");
3643         beam.obviousEffect = true;
3644 
3645         // wow, permanent enslaving
3646         mon->attitude = ATT_FRIENDLY;
3647         return (MON_AFFECTED);
3648     }
3649 
3650     if (beam.flavour == BEAM_ENSLAVE_DEMON
3651         && mons_holiness(mon->type) == MH_DEMONIC)
3652     {
3653 #if DEBUG_DIAGNOSTICS
3654         snprintf( info, INFO_SIZE, "HD: %d; pow: %d",
3655                   mon->hit_dice, beam.ench_power );
3656 
3657         mpr( info, MSGCH_DIAGNOSTICS );
3658 #endif
3659 
3660         if (mon->hit_dice * 4 >= random2(beam.ench_power))
3661             return (MON_RESIST);
3662 
3663         simple_monster_message(mon, " is enslaved.");
3664         beam.obviousEffect = true;
3665 
3666         // wow, permanent enslaving
3667         mon->attitude = ATT_FRIENDLY;
3668         return (MON_AFFECTED);
3669     }
3670 
3671     //
3672     // Everything past this point must pass this magic resistance test.
3673     //
3674     // Using check_mons_resist_magic here since things like disintegrate
3675     // are beyond this point. -- bwr
3676     if (check_mons_resist_magic( mon, beam.ench_power )
3677         && beam.flavour != BEAM_HASTE
3678         && beam.flavour != BEAM_HEALING
3679         && beam.flavour != BEAM_INVISIBILITY)
3680     {
3681         return (MON_RESIST);
3682     }
3683 
3684     if (beam.flavour == BEAM_PAIN)      /* pain/agony */
3685     {
3686         if (mons_res_negative_energy( mon ))
3687             return (MON_UNAFFECTED);
3688 
3689         if (simple_monster_message(mon, " convulses in agony!"))
3690             beam.obviousEffect = true;
3691 
3692         if (strstr( beam.beam_name, "agony" ) != NULL)
3693         {
3694             // AGONY
3695             mon->hit_points = mon->hit_points / 2;
3696 
3697             if (mon->hit_points < 1)
3698                 mon->hit_points = 1;
3699         }
3700         else
3701         {
3702             // PAIN
3703             hurt_monster( mon, roll_dice( beam.damage ) );
3704         }
3705 
3706         goto deathCheck;
3707     }
3708 
3709     if (beam.flavour == BEAM_DISINTEGRATION)     /* disrupt/disintegrate */
3710     {
3711         if (simple_monster_message(mon, " is blasted."))
3712             beam.obviousEffect = true;
3713 
3714         hurt_monster( mon, roll_dice( beam.damage ) );
3715 
3716         goto deathCheck;
3717     }
3718 
3719 
3720     if (beam.flavour == BEAM_SLEEP)
3721     {
3722         if (mons_has_ench( mon, ENCH_SLEEP_WARY ))  // slept recently
3723             return (MON_RESIST);
3724 
3725         if (mons_holiness(mon->type) != MH_NATURAL) // no unnatural
3726             return (MON_UNAFFECTED);
3727 
3728         if (simple_monster_message(mon, " looks drowsy..."))
3729             beam.obviousEffect = true;
3730 
3731         mon->behaviour = BEH_SLEEP;
3732         mons_add_ench( mon, ENCH_SLEEP_WARY );
3733 
3734         return (MON_AFFECTED);
3735     }
3736 
3737     if (beam.flavour == BEAM_BACKLIGHT)
3738     {
3739         if (backlight_monsters(mon->x, mon->y, beam.hit, 0))
3740         {
3741             beam.obviousEffect = true;
3742             return (MON_AFFECTED);
3743         }
3744         return (MON_UNAFFECTED);
3745     }
3746 
3747     // everything else?
3748     return (mons_ench_f2(mon, beam));
3749 
3750 deathCheck:
3751 
3752     int thrower = KILL_YOU_MISSILE;
3753     if (MON_KILL(beam.thrower))
3754         thrower = KILL_MON_MISSILE;
3755 
3756     if (mon->hit_points < 1)
3757         monster_die(mon, thrower, beam.beam_source);
3758     else
3759     {
3760         print_wounds(mon);
3761 
3762         if (mons_is_mimic( mon->type ))
3763             mimic_alert(mon);
3764     }
3765 
3766     return (MON_AFFECTED);
3767 }
3768 
3769 
3770 // extra range used on hit
range_used_on_hit(struct bolt & beam)3771 static int  range_used_on_hit(struct bolt &beam)
3772 {
3773     // non-beams can only affect one thing (player/monster)
3774     if (!beam.isBeam)
3775         return (BEAM_STOP);
3776 
3777     // CHECK ENCHANTMENTS
3778     if (beam.beam_name[0] == '0')
3779     {
3780         switch(beam.flavour)
3781         {
3782         case BEAM_SLOW:
3783         case BEAM_HASTE:
3784         case BEAM_HEALING:
3785         case BEAM_PARALYSIS:
3786         case BEAM_CONFUSION:
3787         case BEAM_INVISIBILITY:
3788         case BEAM_TELEPORT:
3789         case BEAM_POLYMORPH:
3790         case BEAM_CHARM:
3791         case BEAM_BANISH:
3792         case BEAM_PAIN:
3793         case BEAM_DISINTEGRATION:
3794         case BEAM_DEGENERATE:
3795         case BEAM_DISPEL_UNDEAD:
3796         case BEAM_ENSLAVE_UNDEAD:
3797         case BEAM_ENSLAVE_DEMON:
3798         case BEAM_SLEEP:
3799         case BEAM_BACKLIGHT:
3800             return (BEAM_STOP);
3801         default:
3802             break;
3803         }
3804 
3805         return (0);
3806     }
3807 
3808     // hellfire stops for nobody!
3809     if (strcmp( beam.beam_name, "hellfire" ) == 0)
3810         return (0);
3811 
3812     // generic explosion
3813     if (beam.flavour == BEAM_EXPLOSION)
3814         return (BEAM_STOP);
3815 
3816     // plant spit
3817     if (beam.flavour == BEAM_ACID)
3818         return (BEAM_STOP);
3819 
3820     // lava doesn't go far, but it goes through most stuff
3821     if (beam.flavour == BEAM_LAVA)
3822         return (1);
3823 
3824     // If it isn't lightning, reduce range by a lot
3825     if (beam.flavour != BEAM_ELECTRICITY)
3826         return (random2(4) + 2);
3827 
3828     return (0);
3829 }
3830 
3831 /*
3832    Takes a bolt struct and refines it for use in the explosion function. Called
3833    from missile() and beam() in beam.cc. Explosions which do not follow from
3834    beams (eg scrolls of immolation) bypass this function.
3835  */
explosion1(struct bolt & pbolt)3836 static void explosion1(struct bolt &pbolt)
3837 {
3838     int ex_size = 1;
3839     // convenience
3840     int x = pbolt.target_x;
3841     int y = pbolt.target_y;
3842     const char *seeMsg = NULL;
3843     const char *hearMsg = NULL;
3844 
3845     // assume that the player can see/hear the explosion, or
3846     // gets burned by it anyway.  :)
3847     pbolt.msgGenerated = true;
3848 
3849     if (stricmp(pbolt.beam_name, "hellfire") == 0)
3850     {
3851         seeMsg = "The hellfire explodes!";
3852         hearMsg = "You hear a strangely unpleasant explosion.";
3853 
3854         pbolt.type = SYM_BURST;
3855         pbolt.flavour = BEAM_HELLFIRE;
3856     }
3857 
3858     if (stricmp(pbolt.beam_name, "golden flame") == 0)
3859     {
3860         seeMsg = "The flame explodes!";
3861         hearMsg = "You hear a strange explosion.";
3862 
3863         pbolt.type = SYM_BURST;
3864         pbolt.flavour = BEAM_HOLY;     // same as golden flame? [dlb]
3865     }
3866 
3867     if (stricmp(pbolt.beam_name, "fireball") == 0)
3868     {
3869         seeMsg = "The fireball explodes!";
3870         hearMsg = "You hear an explosion.";
3871 
3872         pbolt.type = SYM_BURST;
3873         pbolt.flavour = BEAM_FIRE;
3874         ex_size = 1;
3875     }
3876 
3877     if (stricmp(pbolt.beam_name, "orb of electricity") == 0)
3878     {
3879         seeMsg = "The orb of electricity explodes!";
3880         hearMsg = "You hear a clap of thunder!";
3881 
3882         pbolt.type = SYM_BURST;
3883         pbolt.flavour = BEAM_ELECTRICITY;
3884         pbolt.colour = LIGHTCYAN;
3885         pbolt.damage.num = 1;
3886         ex_size = 2;
3887     }
3888 
3889     if (stricmp(pbolt.beam_name, "orb of energy") == 0)
3890     {
3891         seeMsg = "The orb of energy explodes.";
3892         hearMsg = "You hear an explosion.";
3893     }
3894 
3895     if (stricmp(pbolt.beam_name, "metal orb") == 0)
3896     {
3897         seeMsg = "The orb explodes into a blast of deadly shrapnel!";
3898         hearMsg = "You hear an explosion!";
3899 
3900         strcpy(pbolt.beam_name, "blast of shrapnel");
3901         pbolt.type = SYM_ZAP;
3902         pbolt.flavour = BEAM_FRAG;     // sets it from pure damage to shrapnel (which is absorbed extra by armour)
3903     }
3904 
3905     if (stricmp(pbolt.beam_name, "great blast of cold") == 0)
3906     {
3907         seeMsg = "The blast explodes into a great storm of ice!";
3908         hearMsg = "You hear a raging storm!";
3909 
3910         strcpy(pbolt.beam_name, "ice storm");
3911         pbolt.damage.num = 6;
3912         pbolt.type = SYM_ZAP;
3913         pbolt.colour = WHITE;
3914         ex_size = 2 + (random2( pbolt.ench_power ) > 75);
3915     }
3916 
3917     if (stricmp(pbolt.beam_name, "ball of vapour") == 0)
3918     {
3919         seeMsg = "The ball expands into a vile cloud!";
3920         hearMsg = "You hear a gentle \'poof\'.";
3921         strcpy(pbolt.beam_name, "stinking cloud");
3922     }
3923 
3924     if (stricmp(pbolt.beam_name, "potion") == 0)
3925     {
3926         seeMsg = "The potion explodes!";
3927         hearMsg = "You hear an explosion!";
3928         strcpy(pbolt.beam_name, "cloud");
3929     }
3930 
3931     if (seeMsg == NULL)
3932     {
3933         seeMsg = "The beam explodes into a cloud of software bugs!";
3934         hearMsg = "You hear the sound of one hand clapping!";
3935     }
3936 
3937 
3938     if (!pbolt.isTracer)
3939     {
3940         // check for see/hear/no msg
3941         if (see_grid(x,y) || (x == you.x_pos && y == you.y_pos))
3942             mpr(seeMsg);
3943         else
3944         {
3945             if (!(silenced(x,y) || silenced(you.x_pos, you.y_pos)))
3946                 mpr(hearMsg);
3947             else
3948                 pbolt.msgGenerated = false;
3949         }
3950     }
3951 
3952     pbolt.ex_size = ex_size;
3953     explosion( pbolt );
3954 }                               // end explosion1()
3955 
3956 
3957 #define MAX_EXPLOSION_RADIUS 9
3958 
3959 // explosion is considered to emanate from beam->target_x, target_y
3960 // and has a radius equal to ex_size.  The explosion will respect
3961 // boundaries like walls,  but go through/around statues/idols/etc.
3962 
3963 // for each cell affected by the explosion, affect() is called.
3964 
explosion(struct bolt & beam,bool hole_in_the_middle)3965 void explosion( struct bolt &beam, bool hole_in_the_middle )
3966 {
3967     int r = beam.ex_size;
3968 
3969     // beam is now an explosion;  set isExplosion.
3970     beam.isExplosion = true;
3971 
3972 #if DEBUG_DIAGNOSTICS
3973     snprintf( info, INFO_SIZE,
3974               "explosion at (%d, %d) : t=%d c=%d f=%d hit=%d dam=%dd%d",
3975               beam.target_x, beam.target_y,
3976               beam.type, beam.colour, beam.flavour,
3977               beam.hit, beam.damage.num, beam.damage.size );
3978 
3979     mpr( info, MSGCH_DIAGNOSTICS );
3980 #endif
3981 
3982     // for now, we don't support explosions greater than 9 radius
3983     if (r > MAX_EXPLOSION_RADIUS)
3984         r = MAX_EXPLOSION_RADIUS;
3985 
3986     // make a noise
3987     noisy( 10 + 5*r, beam.target_x, beam.target_y );
3988 
3989     // set map to false
3990     for (int i=0; i<19; i++)
3991     {
3992         for (int j=0; j<19; j++)
3993             explode_map[i][j] = false;
3994     }
3995 
3996     // discover affected cells - recursion is your friend!
3997     // this is done to model an explosion's behaviour around
3998     // corners where a simple 'line of sight' isn't quite
3999     // enough.   This might be slow for really big explosions,
4000     // as the recursion runs approximately as R^2
4001     explosion_map(beam, 0, 0, 0, 0, r);
4002 
4003     // go through affected cells,  drawing effect and
4004     // calling affect() and affect_items() for each.
4005     // now, we get a bit fancy,  drawing all radius 0
4006     // effects, then radius 1, radius 2, etc.  It looks
4007     // a bit better that way.
4008 
4009     // turn buffering off
4010 #ifdef WIN32CONSOLE
4011     bool oldValue;
4012     if (!beam.isTracer)
4013         oldValue = setBuffering(false);
4014 #endif
4015 
4016     // --------------------- begin boom ---------------
4017 
4018     bool drawing = true;
4019     for (int i = 0; i < 2; i++)
4020     {
4021         // do center -- but only if its affected
4022         if (!hole_in_the_middle)
4023             explosion_cell(beam, 0, 0, drawing);
4024 
4025         // do the rest of it
4026         for(int rad = 1; rad <= r; rad ++)
4027         {
4028             // do sides
4029             for (int ay = 1 - rad; ay <= rad - 1; ay += 1)
4030             {
4031                 if (explode_map[-rad+9][ay+9])
4032                     explosion_cell(beam, -rad, ay, drawing);
4033 
4034                 if (explode_map[rad+9][ay+9])
4035                     explosion_cell(beam, rad, ay, drawing);
4036             }
4037 
4038             // do top & bottom
4039             for (int ax = -rad; ax <= rad; ax += 1)
4040             {
4041                 if (explode_map[ax+9][-rad+9])
4042                     explosion_cell(beam, ax, -rad, drawing);
4043 
4044                 if (explode_map[ax+9][rad+9])
4045                     explosion_cell(beam, ax, rad, drawing);
4046             }
4047 
4048             // new-- delay after every 'ring' {gdl}
4049 #ifdef LINUX
4050             // If we don't refresh curses we won't
4051             // guarantee that the explosion is visible
4052             if (drawing)
4053                 update_screen();
4054 #endif
4055             // only delay on real explosion
4056             if (!beam.isTracer && drawing)
4057                 delay(50);
4058         }
4059 
4060         drawing = false;
4061     }
4062 
4063     // ---------------- end boom --------------------------
4064 
4065 #ifdef WIN32CONSOLE
4066     if (!beam.isTracer)
4067         setBuffering(oldValue);
4068 #endif
4069 
4070     // duplicate old behaviour - pause after entire explosion
4071     // has been drawn.
4072     if (!beam.isTracer)
4073         more();
4074 }
4075 
explosion_cell(struct bolt & beam,int x,int y,bool drawOnly)4076 static void explosion_cell(struct bolt &beam, int x, int y, bool drawOnly)
4077 {
4078     bool random_beam = false;
4079     int realx = beam.target_x + x;
4080     int realy = beam.target_y + y;
4081 
4082     if (!drawOnly)
4083     {
4084         // random beams: randomize before affect
4085         if (beam.flavour == BEAM_RANDOM)
4086         {
4087             random_beam = true;
4088             beam.flavour = BEAM_FIRE + random2(7);
4089         }
4090 
4091         affect(beam, realx, realy);
4092 
4093         if (random_beam)
4094             beam.flavour = BEAM_RANDOM;
4095     }
4096 
4097     // early out for tracer
4098     if (beam.isTracer)
4099         return;
4100 
4101     // now affect items
4102     if (!drawOnly)
4103         affect_items(beam, realx, realy);
4104 
4105     if (drawOnly)
4106     {
4107         int drawx = realx - you.x_pos + 18;
4108         int drawy = realy - you.y_pos + 9;
4109 
4110         if (see_grid(realx, realy) || (realx == you.x_pos && realy == you.y_pos))
4111         {
4112             // bounds check
4113             if (drawx > 8 && drawx < 26 && drawy > 0 && drawy < 18)
4114             {
4115                 if (beam.colour == BLACK)
4116                     textcolor(random_colour());
4117                 else
4118                     textcolor(beam.colour);
4119 
4120                 gotoxy(drawx, drawy);
4121                 putch('#');
4122             }
4123         }
4124     }
4125 }
4126 
explosion_map(struct bolt & beam,int x,int y,int count,int dir,int r)4127 static void explosion_map( struct bolt &beam, int x, int y,
4128                            int count, int dir, int r )
4129 {
4130     // 1. check to see out of range
4131     if (x * x + y * y > r * r + r)
4132         return;
4133 
4134     // 2. check count
4135     if (count > 10*r)
4136         return;
4137 
4138     // 3. check to see if we're blocked by something
4139     //    specifically,  we're blocked by WALLS.  Not
4140     //    statues, idols, etc.
4141     int dngn_feat = grd[beam.target_x + x][beam.target_y + y];
4142 
4143     // special case: explosion originates from rock/statue
4144     // (e.g. Lee's rapid deconstruction) - in this case, ignore
4145     // solid cells at the center of the explosion.
4146     if (dngn_feat < DNGN_GREEN_CRYSTAL_WALL || dngn_feat == DNGN_WAX_WALL)
4147     {
4148         if (!(x==0 && y==0))
4149             return;
4150     }
4151 
4152     // hmm, I think we're ok
4153     explode_map[x+9][y+9] = true;
4154 
4155     // now recurse in every direction except the one we
4156     // came from
4157     for(int i=0; i<4; i++)
4158     {
4159         if (i+1 != dir)
4160         {
4161             int cadd = 5;
4162             if (x * spreadx[i] < 0 || y * spready[i] < 0)
4163                 cadd = 17;
4164 
4165             explosion_map( beam, x + spreadx[i], y + spready[i],
4166                            count + cadd, opdir[i], r );
4167         }
4168     }
4169 }
4170 
4171 // returns true if the beam is harmful (ignoring monster
4172 // resists) -- mon is given for 'special' cases where,
4173 // for example,  "Heal" might actually hurt undead, or
4174 // "Holy Word" being ignored by holy monsters,  etc.
4175 //
4176 // only enchantments should need the actual monster type
4177 // to determine this;  non-enchantments are pretty
4178 // straightforward.
nasty_beam(struct monsters * mon,struct bolt & beam)4179 bool nasty_beam(struct monsters *mon, struct bolt &beam)
4180 {
4181     // take care of non-enchantments
4182     if (beam.beam_name[0] != '0')
4183         return (true);
4184 
4185     // now for some non-hurtful enchantments
4186 
4187     // degeneration / sleep
4188     if (beam.flavour == BEAM_DEGENERATE || beam.flavour == BEAM_SLEEP)
4189         return (mons_holiness(mon->type) == MH_NATURAL);
4190 
4191     // dispel undead / control undead
4192     if (beam.flavour == BEAM_DISPEL_UNDEAD || beam.flavour == BEAM_ENSLAVE_UNDEAD)
4193         return (mons_holiness(mon->type) == MH_UNDEAD);
4194 
4195     // pain/agony
4196     if (beam.flavour == BEAM_PAIN)
4197         return (!mons_res_negative_energy( mon ));
4198 
4199     // control demon
4200     if (beam.flavour == BEAM_ENSLAVE_DEMON)
4201         return (mons_holiness(mon->type) == MH_DEMONIC);
4202 
4203     // haste
4204     if (beam.flavour == BEAM_HASTE)
4205         return (false);
4206 
4207     // healing
4208     if (beam.flavour == BEAM_HEALING || beam.flavour == BEAM_INVISIBILITY)
4209         return (false);
4210 
4211     // everything else is considered nasty by everyone
4212     return (true);
4213 }
4214