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