summaryrefslogtreecommitdiffstats
path: root/trunk/source/spells2.cc
diff options
context:
space:
mode:
authorpeterb12 <peterb12@c06c8d41-db1a-0410-9941-cceddc491573>2005-07-21 02:34:44 +0000
committerpeterb12 <peterb12@c06c8d41-db1a-0410-9941-cceddc491573>2005-07-21 02:34:44 +0000
commit673bdae75485d14f759af597c3c62b99601f9a43 (patch)
tree368103f29fe0ce5dcf98060d9b5faa04590085fb /trunk/source/spells2.cc
parent7e900be770db24b0405fd2162491c405a425873e (diff)
downloadcrawl-ref-673bdae75485d14f759af597c3c62b99601f9a43.tar.gz
crawl-ref-673bdae75485d14f759af597c3c62b99601f9a43.zip
Initial revision
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@3 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'trunk/source/spells2.cc')
-rw-r--r--trunk/source/spells2.cc1516
1 files changed, 1516 insertions, 0 deletions
diff --git a/trunk/source/spells2.cc b/trunk/source/spells2.cc
new file mode 100644
index 0000000000..588ade57c8
--- /dev/null
+++ b/trunk/source/spells2.cc
@@ -0,0 +1,1516 @@
+/*
+ * File: spells2.cc
+ * Summary: Implementations of some additional spells.
+ * Written by: Linley Henzell
+ *
+ * Change History (most recent first):
+ *
+ * <4> 03jan1999 jmf Changed summon_small_mammals so at
+ * higher levels it indeed summons in plural.
+ * Removed some IMHO unnecessary failure msgs.
+ * (from e.g. animate_dead).
+ * Added protection by special deities.
+ * <3> 10/11/99 BCR fixed range bug in burn_freeze,
+ * vamp_drain, and summon_elemental
+ * <2> 5/26/99 JDJ detect_items uses '~' instead of '*'.
+ * <1> -/--/-- LRH Created
+ */
+
+#include "AppHdr.h"
+#include "spells2.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "externs.h"
+
+#include "beam.h"
+#include "cloud.h"
+#include "direct.h"
+#include "effects.h"
+#include "itemname.h"
+#include "items.h"
+#include "misc.h"
+#include "monplace.h"
+#include "monstuff.h"
+#include "mon-util.h"
+#include "ouch.h"
+#include "player.h"
+#include "randart.h"
+#include "spells4.h"
+#include "spl-cast.h"
+#include "stuff.h"
+#include "view.h"
+#include "wpn-misc.h"
+
+int raise_corpse( int corps, int corx, int cory, int corps_beh,
+ int corps_hit, int actual );
+
+unsigned char detect_traps( int pow )
+{
+ unsigned char traps_found = 0;
+
+ if (pow > 50)
+ pow = 50;
+
+ const int range = 8 + random2(8) + pow;
+
+ for (int count_x = 0; count_x < MAX_TRAPS; count_x++)
+ {
+ const int etx = env.trap[ count_x ].x;
+ const int ety = env.trap[ count_x ].y;
+
+ // Used to just be visible screen:
+ // if (etx > you.x_pos - 15 && etx < you.x_pos + 15
+ // && ety > you.y_pos - 8 && ety < you.y_pos + 8)
+
+ if (grid_distance( you.x_pos, you.y_pos, etx, ety ) < range)
+ {
+ if (grd[ etx ][ ety ] == DNGN_UNDISCOVERED_TRAP)
+ {
+ traps_found++;
+
+ grd[ etx ][ ety ] = trap_category( env.trap[count_x].type );
+ env.map[etx - 1][ety - 1] = '^';
+ }
+ }
+ }
+
+ return (traps_found);
+} // end detect_traps()
+
+unsigned char detect_items( int pow )
+{
+ if (pow > 50)
+ pow = 50;
+
+ unsigned char items_found = 0;
+ const int map_radius = 8 + random2(8) + pow;
+
+ mpr("You detect items!");
+
+ for (int i = you.x_pos - map_radius; i < you.x_pos + map_radius; i++)
+ {
+ for (int j = you.y_pos - map_radius; j < you.y_pos + map_radius; j++)
+ {
+ if (i < 5 || j < 5 || i > (GXM - 5) || j > (GYM - 5))
+ continue;
+
+ if (igrd[i][j] != NON_ITEM)
+ {
+ env.map[i - 1][j - 1] = '~';
+ }
+ }
+ }
+
+ return (items_found);
+} // end detect_items()
+
+unsigned char detect_creatures( int pow )
+{
+ if (pow > 50)
+ pow = 50;
+
+ unsigned char creatures_found = 0;
+ const int map_radius = 8 + random2(8) + pow;
+
+ mpr("You detect creatures!");
+
+ for (int i = you.x_pos - map_radius; i < you.x_pos + map_radius; i++)
+ {
+ for (int j = you.y_pos - map_radius; j < you.y_pos + map_radius; j++)
+ {
+ if (i < 5 || j < 5 || i > (GXM - 5) || j > (GYM - 5))
+ continue;
+
+ if (mgrd[i][j] != NON_MONSTER)
+ {
+ struct monsters *mon = &menv[ mgrd[i][j] ];
+
+ env.map[i - 1][j - 1] = mons_char( mon->type );
+
+ // Assuming that highly intelligent spellcasters can
+ // detect scyring. -- bwr
+ if (mons_intel( mon->type ) == I_HIGH
+ && mons_flag( mon->type, M_SPELLCASTER ))
+ {
+ behaviour_event( mon, ME_DISTURB, MHITYOU,
+ you.x_pos, you.y_pos );
+ }
+ }
+ }
+ }
+
+ return (creatures_found);
+} // end detect_creatures()
+
+int corpse_rot(int power)
+{
+ UNUSED( power );
+
+ char adx = 0;
+ char ady = 0;
+
+ char minx = you.x_pos - 6;
+ char maxx = you.x_pos + 7;
+ char miny = you.y_pos - 6;
+ char maxy = you.y_pos + 6;
+ char xinc = 1;
+ char yinc = 1;
+
+ if (coinflip())
+ {
+ minx = you.x_pos + 6;
+ maxx = you.x_pos - 7;
+ xinc = -1;
+ }
+
+ if (coinflip())
+ {
+ miny = you.y_pos + 6;
+ maxy = you.y_pos - 7;
+ yinc = -1;
+ }
+
+ for (adx = minx; adx != maxx; adx += xinc)
+ {
+ if (adx == 7 || adx == -7)
+ return 0;
+
+ for (ady = miny; ady != maxy; ady += yinc)
+ {
+ if (see_grid(adx, ady))
+ {
+ if (igrd[adx][ady] == NON_ITEM
+ || env.cgrid[adx][ady] != EMPTY_CLOUD)
+ {
+ continue;
+ }
+
+ int objl = igrd[adx][ady];
+ int hrg = 0;
+
+ while (objl != NON_ITEM)
+ {
+ if (mitm[objl].base_type == OBJ_CORPSES
+ && mitm[objl].sub_type == CORPSE_BODY)
+ {
+ if (!mons_skeleton(mitm[objl].plus))
+ destroy_item(objl);
+ else
+ {
+ mitm[objl].sub_type = CORPSE_SKELETON;
+ mitm[objl].special = 200;
+ mitm[objl].colour = LIGHTGREY;
+ }
+
+ place_cloud(CLOUD_MIASMA, adx, ady,
+ 4 + random2avg(16, 3));
+
+ goto out_of_raise;
+ }
+ hrg = mitm[objl].link;
+ objl = hrg;
+ }
+
+ out_of_raise:
+ objl = 1;
+ }
+ }
+ }
+
+ if (you.species != SP_MUMMY) // josh declares mummies cannot smell {dlb}
+ mpr("You smell decay.");
+
+ // should make zombies decay into skeletons
+
+ return 0;
+} // end corpse_rot()
+
+int animate_dead( int power, int corps_beh, int corps_hit, int actual )
+{
+ UNUSED( power );
+
+ int adx = 0;
+ int ady = 0;
+
+ int minx = you.x_pos - 6;
+ int maxx = you.x_pos + 7;
+ int miny = you.y_pos - 6;
+ int maxy = you.y_pos + 6;
+ int xinc = 1;
+ int yinc = 1;
+
+ int number_raised = 0;
+
+ if (coinflip())
+ {
+ minx = you.x_pos + 6;
+ maxx = you.x_pos - 7;
+ xinc = -1;
+ }
+
+ if (coinflip())
+ {
+ miny = you.y_pos + 6;
+ maxy = you.y_pos - 7;
+ yinc = -1;
+ }
+
+ for (adx = minx; adx != maxx; adx += xinc)
+ {
+ if ((adx == 7) || (adx == -7))
+ return 0;
+
+ for (ady = miny; ady != maxy; ady += yinc)
+ {
+ if (see_grid(adx, ady))
+ {
+ if (igrd[adx][ady] != NON_ITEM)
+ {
+ int objl = igrd[adx][ady];
+ int hrg = 0;
+
+ //this searches all the items on the ground for a corpse
+ while (objl != NON_ITEM)
+ {
+ if (mitm[objl].base_type == OBJ_CORPSES)
+ {
+ number_raised += raise_corpse(objl, adx, ady,
+ corps_beh, corps_hit, actual);
+ break;
+ }
+
+ hrg = mitm[objl].link;
+ objl = hrg;
+ }
+
+ objl = 1;
+ }
+ }
+ }
+ }
+
+ if (actual == 0)
+ return number_raised;
+
+ if (number_raised > 0)
+ {
+ mpr("The dead are walking!");
+ //else
+ // mpr("The dark energy consumes the dead!"); - no, this
+ // means that no corpses were found. Better to say:
+ // mpr("You receive no reply.");
+ //jmf: Why do I have to get an uninformative message when some random
+ //jmf: monster fails to do something?
+ // IMHO there's too much noise already.
+ }
+
+ return number_raised;
+} // end animate_dead()
+
+int animate_a_corpse( int axps, int ayps, int corps_beh, int corps_hit,
+ int class_allowed )
+{
+ if (igrd[axps][ayps] == NON_ITEM)
+ return 0;
+ else if (mitm[igrd[axps][ayps]].base_type != OBJ_CORPSES)
+ return 0;
+ else if (class_allowed == CORPSE_SKELETON
+ && mitm[igrd[axps][ayps]].sub_type != CORPSE_SKELETON)
+ return 0;
+ else
+ if (raise_corpse( igrd[axps][ayps], axps, ayps,
+ corps_beh, corps_hit, 1 ) > 0)
+ {
+ mpr("The dead are walking!");
+ }
+
+ return 0;
+} // end animate_a_corpse()
+
+int raise_corpse( int corps, int corx, int cory,
+ int corps_beh, int corps_hit, int actual )
+{
+ int returnVal = 1;
+
+ if (!mons_zombie_size(mitm[corps].plus))
+ returnVal = 0;
+ else if (actual != 0)
+ {
+ int type;
+ if (mitm[corps].sub_type == CORPSE_BODY)
+ {
+ if (mons_zombie_size(mitm[corps].plus) == Z_SMALL)
+ type = MONS_ZOMBIE_SMALL;
+ else
+ type = MONS_ZOMBIE_LARGE;
+ }
+ else
+ {
+ if (mons_zombie_size(mitm[corps].plus) == Z_SMALL)
+ type = MONS_SKELETON_SMALL;
+ else
+ type = MONS_SKELETON_LARGE;
+ }
+
+ create_monster( type, 0, corps_beh, corx, cory, corps_hit,
+ mitm[corps].plus );
+
+ destroy_item(corps);
+ }
+
+ return returnVal;
+} // end raise_corpse()
+
+void cast_twisted(int power, int corps_beh, int corps_hit)
+{
+ int total_mass = 0;
+ int num_corpses = 0;
+ int type_resurr = MONS_ABOMINATION_SMALL;
+ char colour;
+
+ unsigned char rotted = 0;
+
+ if (igrd[you.x_pos][you.y_pos] == NON_ITEM)
+ {
+ mpr("There's nothing here!");
+ return;
+ }
+
+ int objl = igrd[you.x_pos][you.y_pos];
+ int next;
+
+ while (objl != NON_ITEM)
+ {
+ next = mitm[objl].link;
+
+ if (mitm[objl].base_type == OBJ_CORPSES
+ && mitm[objl].sub_type == CORPSE_BODY)
+ {
+ total_mass += mons_weight( mitm[objl].plus );
+
+ num_corpses++;
+ if (mitm[objl].special < 100)
+ rotted++;
+
+ destroy_item( objl );
+ }
+
+ objl = next;
+ }
+
+#if DEBUG_DIAGNOSTICS
+ snprintf( info, INFO_SIZE, "Mass for abomination: %d", total_mass);
+ mpr( info, MSGCH_DIAGNOSTICS );
+#endif
+
+ // This is what the old statement pretty much boils down to,
+ // the average will be approximately 10 * power (or about 1000
+ // at the practical maximum). That's the same as the mass
+ // of a hippogriff, a spiny frog, or a steam dragon. Thus,
+ // material components are far more important to this spell. -- bwr
+ total_mass += roll_dice( 20, power );
+
+#if DEBUG_DIAGNOSTICS
+ snprintf( info, INFO_SIZE, "Mass including power bonus: %d", total_mass);
+ mpr( info, MSGCH_DIAGNOSTICS );
+#endif
+
+ if (total_mass < 400 + roll_dice( 2, 500 )
+ || num_corpses < (coinflip() ? 3 : 2))
+ {
+ mpr("The spell fails.");
+ mpr("The corpses collapse into a pulpy mess.");
+ return;
+ }
+
+ if (total_mass > 500 + roll_dice( 3, 1000 ))
+ type_resurr = MONS_ABOMINATION_LARGE;
+
+ if (rotted == num_corpses)
+ colour = BROWN;
+ else if (rotted >= random2( num_corpses ))
+ colour = RED;
+ else
+ colour = LIGHTRED;
+
+ int mon = create_monster( type_resurr, 0, corps_beh, you.x_pos, you.y_pos,
+ corps_hit, colour );
+
+ if (mon == -1)
+ mpr("The corpses collapse into a pulpy mess.");
+ else
+ {
+ mpr("The heap of corpses melds into an agglomeration of writhing flesh!");
+ if (type_resurr == MONS_ABOMINATION_LARGE)
+ {
+ menv[mon].hit_dice = 8 + total_mass / ((colour == LIGHTRED) ? 500 :
+ (colour == RED) ? 1000
+ : 2500);
+
+ if (menv[mon].hit_dice > 30)
+ menv[mon].hit_dice = 30;
+
+ // XXX: No convenient way to get the hit dice size right now.
+ menv[mon].hit_points = hit_points( menv[mon].hit_dice, 2, 5 );
+ menv[mon].max_hit_points = menv[mon].hit_points;
+
+ if (colour == LIGHTRED)
+ menv[mon].armour_class += total_mass / 1000;
+ }
+ }
+} // end cast_twisted()
+
+bool brand_weapon(int which_brand, int power)
+{
+ int temp_rand; // probability determination {dlb}
+ int duration_affected = 0; //jmf: NB: now HOW LONG, not WHICH BRAND.
+
+ const int wpn = you.equip[EQ_WEAPON];
+
+ if (you.duration[DUR_WEAPON_BRAND])
+ return false;
+
+ if (wpn == -1)
+ return false;
+
+ if (you.inv[wpn].base_type != OBJ_WEAPONS
+ || launches_things(you.inv[wpn].sub_type))
+ {
+ return false;
+ }
+
+ if (is_fixed_artefact( you.inv[wpn] )
+ || is_random_artefact( you.inv[wpn] )
+ || get_weapon_brand( you.inv[wpn] ) != SPWPN_NORMAL )
+ {
+ return false;
+ }
+
+ char str_pass[ ITEMNAME_SIZE ];
+ in_name( wpn, DESC_CAP_YOUR, str_pass );
+ strcpy( info, str_pass );
+
+ const int wpn_type = damage_type( you.inv[wpn].base_type,
+ you.inv[wpn].sub_type );
+
+ switch (which_brand) // use SPECIAL_WEAPONS here?
+ {
+ case SPWPN_FLAMING:
+ strcat(info, " bursts into flame!");
+ duration_affected = 7;
+ break;
+
+ case SPWPN_FREEZING:
+ strcat(info, " glows blue.");
+ duration_affected = 7;
+ break;
+
+ case SPWPN_VENOM:
+ if (wpn_type == DVORP_CRUSHING)
+ return false;
+
+ strcat(info, " starts dripping with poison.");
+ duration_affected = 15;
+ break;
+
+ case SPWPN_DRAINING:
+ strcat(info, " crackles with unholy energy.");
+ duration_affected = 12;
+ break;
+
+ case SPWPN_VORPAL:
+ if (wpn_type != DVORP_SLICING)
+ return false;
+
+ strcat(info, " glows silver and looks extremely sharp.");
+ duration_affected = 10;
+ break;
+
+ case SPWPN_DISTORTION: //jmf: added for Warp Weapon
+ strcat(info, " seems to ");
+
+ temp_rand = random2(6);
+ strcat(info, (temp_rand == 0) ? "twist" :
+ (temp_rand == 1) ? "bend" :
+ (temp_rand == 2) ? "vibrate" :
+ (temp_rand == 3) ? "flex" :
+ (temp_rand == 4) ? "wobble"
+ : "twang");
+
+ strcat( info, coinflip() ? " oddly." : " strangely." );
+ duration_affected = 5;
+
+ // This brand is insanely powerful, this isn't even really
+ // a start to balancing it, but it needs something. -- bwr
+ miscast_effect(SPTYP_TRANSLOCATION, 9, 90, 100, "a distortion effect");
+ break;
+
+ case SPWPN_DUMMY_CRUSHING: //jmf: added for Maxwell's Silver Hammer
+ if (wpn_type != DVORP_CRUSHING)
+ return false;
+
+ which_brand = SPWPN_VORPAL;
+ strcat(info, " glows silver and feels heavier.");
+ duration_affected = 7;
+ break;
+ }
+
+ set_item_ego_type( you.inv[wpn], OBJ_WEAPONS, which_brand );
+
+ mpr(info);
+ you.wield_change = true;
+
+ int dur_change = duration_affected + roll_dice( 2, power );
+
+ you.duration[DUR_WEAPON_BRAND] += dur_change;
+
+ if (you.duration[DUR_WEAPON_BRAND] > 50)
+ you.duration[DUR_WEAPON_BRAND] = 50;
+
+ return true;
+} // end brand_weapon()
+
+bool restore_stat(unsigned char which_stat, bool suppress_msg)
+{
+ bool statRestored = false;
+
+ // a bit hackish, but cut me some slack, man! --
+ // besides, a little recursion never hurt anyone {dlb}:
+ if (which_stat == STAT_ALL)
+ {
+ for (unsigned char loopy = STAT_STRENGTH; loopy < NUM_STATS; loopy++)
+ {
+ if (restore_stat(loopy, suppress_msg) == true)
+ statRestored = true;
+ }
+ return statRestored; // early return {dlb}
+ }
+
+ // the real function begins here {dlb}:
+ char *ptr_stat = 0; // NULL {dlb}
+ char *ptr_stat_max = 0; // NULL {dlb}
+ char *ptr_redraw = 0; // NULL {dlb}
+
+ if (!suppress_msg)
+ strcpy(info, "You feel your ");
+
+ if (which_stat == STAT_RANDOM)
+ which_stat = random2(NUM_STATS);
+
+ switch (which_stat)
+ {
+ case STAT_STRENGTH:
+ if (!suppress_msg)
+ strcat(info, "strength");
+
+ ptr_stat = &you.strength;
+ ptr_stat_max = &you.max_strength;
+ ptr_redraw = &you.redraw_strength;
+ break;
+
+ case STAT_DEXTERITY:
+ if (!suppress_msg)
+ strcat(info, "dexterity");
+
+ ptr_stat = &you.dex;
+ ptr_stat_max = &you.max_dex;
+ ptr_redraw = &you.redraw_dexterity;
+ break;
+
+ case STAT_INTELLIGENCE:
+ if (!suppress_msg)
+ strcat(info, "intelligence");
+
+ ptr_stat = &you.intel;
+ ptr_stat_max = &you.max_intel;
+ ptr_redraw = &you.redraw_intelligence;
+ break;
+ }
+
+ if (*ptr_stat < *ptr_stat_max)
+ {
+ if (!suppress_msg)
+ {
+ strcat(info, " returning.");
+ mpr(info);
+ }
+
+ *ptr_stat = *ptr_stat_max;
+ *ptr_redraw = 1;
+ statRestored = true;
+
+ if (ptr_stat == &you.strength)
+ burden_change();
+ }
+
+ return statRestored;
+} // end restore_stat()
+
+void turn_undead(int pow)
+{
+ struct monsters *monster;
+
+ mpr("You attempt to repel the undead.");
+
+ for (int tu = 0; tu < MAX_MONSTERS; tu++)
+ {
+ monster = &menv[tu];
+
+ if (monster->type == -1 || !mons_near(monster))
+ continue;
+
+ // used to inflict random2(5) + (random2(pow) / 20) damage,
+ // in addition {dlb}
+ if (mons_holiness(monster->type) == MH_UNDEAD)
+ {
+ if (check_mons_resist_magic( monster, pow ))
+ {
+ simple_monster_message( monster, " resists." );
+ continue;
+ }
+
+ if (!mons_add_ench(monster, ENCH_FEAR))
+ continue;
+
+ simple_monster_message( monster, " is repelled!" );
+
+ //mv: must be here to work
+ behaviour_event( monster, ME_SCARE, MHITYOU );
+
+ // reduce power based on monster turned
+ pow -= monster->hit_dice * 3;
+ if (pow <= 0)
+ break;
+
+ } // end "if mons_holiness"
+ } // end "for tu"
+} // end turn_undead()
+
+void holy_word(int pow)
+{
+ struct monsters *monster;
+
+ mpr("You speak a Word of immense power!");
+
+ // doubt this will ever happen, but it's here as a safety -- bwr
+ if (pow > 300)
+ pow = 300;
+
+ for (int tu = 0; tu < MAX_MONSTERS; tu++)
+ {
+ monster = &menv[tu];
+
+ if (monster->type == -1 || !mons_near(monster))
+ continue;
+
+ if (mons_holiness(monster->type) == MH_UNDEAD
+ || mons_holiness(monster->type) == MH_DEMONIC)
+ {
+ simple_monster_message(monster, " convulses!");
+
+ hurt_monster( monster, roll_dice( 2, 15 ) + (random2(pow) / 3) );
+
+ if (monster->hit_points < 1)
+ {
+ monster_die(monster, KILL_YOU, 0);
+ continue;
+ }
+
+ if (monster->speed_increment >= 25)
+ monster->speed_increment -= 20;
+
+ mons_add_ench(monster, ENCH_FEAR);
+ } // end "if mons_holiness"
+ } // end "for tu"
+} // end holy_word()
+
+// poisonous light passes right through invisible players
+// and monsters, and so, they are unaffected by this spell --
+// assumes only you can cast this spell (or would want to)
+void cast_toxic_radiance(void)
+{
+ struct monsters *monster;
+
+ mpr("You radiate a sickly green light!");
+
+ show_green = GREEN;
+ viewwindow(1, false);
+ more();
+ mesclr();
+
+ // determine whether the player is hit by the radiance: {dlb}
+ if (you.invis)
+ {
+ mpr("The light passes straight through your body.");
+ }
+ else if (!player_res_poison())
+ {
+ mpr("You feel rather sick.");
+ poison_player(2);
+ }
+
+ // determine which monsters are hit by the radiance: {dlb}
+ for (int toxy = 0; toxy < MAX_MONSTERS; toxy++)
+ {
+ monster = &menv[toxy];
+
+ if (monster->type != -1 && mons_near(monster))
+ {
+ if (!mons_has_ench(monster, ENCH_INVIS))
+ {
+ poison_monster(monster, true);
+
+ if (coinflip()) // 50-50 chance for a "double hit" {dlb}
+ poison_monster(monster, true);
+
+ }
+ else if (player_see_invis())
+ {
+ // message player re:"miss" where appropriate {dlb}
+ strcpy(info, "The light passes through ");
+ strcat(info, ptr_monam( monster, DESC_NOCAP_THE ));
+ strcat(info, ".");
+ mpr(info);
+ }
+ }
+ }
+} // end cast_toxic_radiance()
+
+void cast_refrigeration(int pow)
+{
+ struct monsters *monster = 0; // NULL {dlb}
+ int hurted = 0;
+ struct bolt beam;
+ int toxy;
+
+ beam.flavour = BEAM_COLD;
+
+ const dice_def dam_dice( 3, 5 + pow / 10 );
+
+ mpr("The heat is drained from your surroundings.");
+
+ show_green = LIGHTCYAN;
+ viewwindow(1, false);
+ more();
+ mesclr();
+
+ // Do the player:
+ hurted = roll_dice( dam_dice );
+ hurted = check_your_resists( hurted, beam.flavour );
+
+ if (hurted > 0)
+ {
+ mpr("You feel very cold.");
+ ouch( hurted, 0, KILLED_BY_FREEZING );
+
+ // Note: this used to be 12!... and it was also applied even if
+ // the player didn't take damage from the cold, so we're being
+ // a lot nicer now. -- bwr
+ scrolls_burn( 5, OBJ_POTIONS );
+ }
+
+ // Now do the monsters:
+ for (toxy = 0; toxy < MAX_MONSTERS; toxy++)
+ {
+ monster = &menv[toxy];
+
+ if (monster->type == -1)
+ continue;
+
+ if (mons_near(monster))
+ {
+ snprintf( info, INFO_SIZE, "You freeze %s.",
+ ptr_monam( monster, DESC_NOCAP_THE ));
+
+ mpr(info);
+
+ hurted = roll_dice( dam_dice );
+ hurted = mons_adjust_flavoured( monster, beam, hurted );
+
+ if (hurted > 0)
+ {
+ hurt_monster( monster, hurted );
+
+ if (monster->hit_points < 1)
+ monster_die(monster, KILL_YOU, 0);
+ else
+ {
+ print_wounds(monster);
+
+ //jmf: "slow snakes" finally available
+ if (mons_flag( monster->type, M_COLD_BLOOD ) && coinflip())
+ mons_add_ench(monster, ENCH_SLOW);
+ }
+ }
+ }
+ }
+} // end cast_refrigeration()
+
+void drain_life(int pow)
+{
+ int hp_gain = 0;
+ int hurted = 0;
+ struct monsters *monster = 0; // NULL {dlb}
+
+ mpr("You draw life from your surroundings.");
+
+ // Incoming power to this function is skill in INVOCATIONS, so
+ // we'll add an assert here to warn anyone who tries to use
+ // this function with spell level power.
+ ASSERT( pow <= 27 );
+
+ show_green = DARKGREY;
+ viewwindow(1, false);
+ more();
+ mesclr();
+
+ for (int toxy = 0; toxy < MAX_MONSTERS; toxy++)
+ {
+ monster = &menv[toxy];
+
+ if (monster->type == -1)
+ continue;
+
+ if (mons_holiness( monster->type ) != MH_NATURAL)
+ continue;
+
+ if (mons_res_negative_energy( monster ))
+ continue;
+
+ if (mons_near(monster))
+ {
+ strcpy(info, "You draw life from ");
+ strcat(info, ptr_monam( monster, DESC_NOCAP_THE ));
+ strcat(info, ".");
+ mpr(info);
+
+ hurted = 3 + random2(7) + random2(pow);
+
+ hurt_monster(monster, hurted);
+
+ hp_gain += hurted;
+
+ if (monster->hit_points < 1)
+ monster_die(monster, KILL_YOU, 0);
+ else
+ print_wounds(monster);
+ }
+ }
+
+ hp_gain /= 2;
+
+ if (hp_gain > pow * 2)
+ hp_gain = pow * 2;
+
+ if (hp_gain)
+ {
+ mpr( "You feel life flooding into your body." );
+ inc_hp( hp_gain, false );
+ }
+} // end drain_life()
+
+int vampiric_drain(int pow)
+{
+ int inflicted = 0;
+ int mgr = 0;
+ struct monsters *monster = 0; // NULL
+ struct dist vmove;
+
+ dirc:
+ mpr("Which direction?", MSGCH_PROMPT);
+ direction( vmove, DIR_DIR, TARG_ENEMY );
+
+ if (!vmove.isValid)
+ {
+ canned_msg(MSG_SPELL_FIZZLES);
+ return -1;
+ }
+
+ mgr = mgrd[you.x_pos + vmove.dx][you.y_pos + vmove.dy];
+
+ if (vmove.dx == 0 && vmove.dy == 0)
+ {
+ mpr("You can't do that.");
+ goto dirc;
+ }
+
+ if (mgr == NON_MONSTER)
+ {
+ mpr("There isn't anything there!");
+ return -1;
+ }
+
+ monster = &menv[mgr];
+
+ const int holy = mons_holiness(monster->type);
+
+ if (holy == MH_UNDEAD || holy == MH_DEMONIC)
+ {
+ mpr("Aaaarggghhhhh!");
+ dec_hp(random2avg(39, 2) + 10, false);
+ return -1;
+ }
+
+ if (mons_res_negative_energy( monster ))
+ {
+ canned_msg(MSG_NOTHING_HAPPENS);
+ return -1;
+ }
+
+ // The practical maximum of this is about 25 (pow @ 100). -- bwr
+ inflicted = 3 + random2avg( 9, 2 ) + random2(pow) / 7;
+
+ if (inflicted >= monster->hit_points)
+ inflicted = monster->hit_points;
+
+ if (inflicted >= you.hp_max - you.hp)
+ inflicted = you.hp_max - you.hp;
+
+ if (inflicted == 0)
+ {
+ canned_msg(MSG_NOTHING_HAPPENS);
+ return -1;
+ }
+
+ hurt_monster(monster, inflicted);
+
+ strcpy(info, "You feel life coursing from ");
+ strcat(info, ptr_monam( monster, DESC_NOCAP_THE ));
+ strcat(info, " into your body!");
+ mpr(info);
+
+ print_wounds(monster);
+
+ if (monster->hit_points < 1)
+ monster_die(monster, KILL_YOU, 0);
+
+ inc_hp(inflicted / 2, false);
+
+ return 1;
+} // end vampiric_drain()
+
+// Note: this function is currently only used for Freeze. -- bwr
+char burn_freeze(int pow, char flavour)
+{
+ int mgr = NON_MONSTER;
+ struct monsters *monster = 0; // NULL {dlb}
+ struct dist bmove;
+
+ if (pow > 25)
+ pow = 25;
+
+ while (mgr == NON_MONSTER)
+ {
+ mpr("Which direction?", MSGCH_PROMPT);
+ direction( bmove, DIR_DIR, TARG_ENEMY );
+
+ if (!bmove.isValid)
+ {
+ canned_msg(MSG_SPELL_FIZZLES);
+ return -1;
+ }
+
+ if (bmove.isMe)
+ {
+ canned_msg(MSG_UNTHINKING_ACT);
+ return -1;
+ }
+
+ mgr = mgrd[you.x_pos + bmove.dx][you.y_pos + bmove.dy];
+
+ // Yes, this is strange, but it does maintain the original behaviour
+ if (mgr == NON_MONSTER)
+ {
+ mpr("There isn't anything close enough!");
+ return -1;
+ }
+ }
+
+ monster = &menv[mgr];
+
+ strcpy(info, "You ");
+ strcat(info, (flavour == BEAM_FIRE) ? "burn" :
+ (flavour == BEAM_COLD) ? "freeze" :
+ (flavour == BEAM_MISSILE) ? "crush" :
+ (flavour == BEAM_ELECTRICITY) ? "zap"
+ : "______");
+
+ strcat(info, " ");
+ strcat(info, ptr_monam( monster, DESC_NOCAP_THE ));
+ strcat(info, ".");
+ mpr(info);
+
+ int hurted = roll_dice( 1, 3 + pow / 3 );
+
+ struct bolt beam;
+
+ beam.flavour = flavour;
+
+ if (flavour != BEAM_MISSILE)
+ hurted = mons_adjust_flavoured(monster, beam, hurted);
+
+ if (hurted)
+ {
+ hurt_monster(monster, hurted);
+
+ if (monster->hit_points < 1)
+ monster_die(monster, KILL_YOU, 0);
+ else
+ {
+ print_wounds(monster);
+
+ if (flavour == BEAM_COLD)
+ {
+ if (mons_flag( monster->type, M_COLD_BLOOD ) && coinflip())
+ mons_add_ench(monster, ENCH_SLOW);
+
+ const int cold_res = mons_res_cold( monster );
+ if (cold_res <= 0)
+ {
+ const int stun = (1 - cold_res) * random2( 2 + pow / 5 );
+ monster->speed_increment -= stun;
+ }
+ }
+ }
+ }
+
+ return 1;
+} // end burn_freeze()
+
+// 'unfriendly' is percentage chance summoned elemental goes
+// postal on the caster (after taking into account
+// chance of that happening to unskilled casters
+// anyway)
+int summon_elemental(int pow, unsigned char restricted_type,
+ unsigned char unfriendly)
+{
+ int type_summoned = MONS_PROGRAM_BUG; // error trapping {dlb}
+ char summ_success = 0;
+ struct dist smove;
+
+ int dir_x;
+ int dir_y;
+ int targ_x;
+ int targ_y;
+
+ int numsc = ENCH_ABJ_II + (random2(pow) / 5);
+
+ if (numsc > ENCH_ABJ_VI)
+ numsc = ENCH_ABJ_VI;
+
+ for (;;)
+ {
+ mpr("Summon from material in which direction?", MSGCH_PROMPT);
+
+ direction( smove, DIR_DIR );
+
+ if (!smove.isValid)
+ {
+ canned_msg(MSG_NOTHING_HAPPENS);
+ return (-1);
+ }
+
+ dir_x = smove.dx;
+ dir_y = smove.dy;
+ targ_x = you.x_pos + dir_x;
+ targ_y = you.y_pos + dir_y;
+
+ if (mgrd[ targ_x ][ targ_y ] != NON_MONSTER)
+ mpr("Not there!");
+ else if (dir_x == 0 && dir_y == 0)
+ mpr("You can't summon an elemental from yourself!");
+ else
+ break;
+ }
+
+ if (grd[ targ_x ][ targ_y ] == DNGN_ROCK_WALL
+ && (restricted_type == 0 || restricted_type == MONS_EARTH_ELEMENTAL))
+ {
+ type_summoned = MONS_EARTH_ELEMENTAL;
+
+ if (targ_x > 6 && targ_x < 74 && targ_y > 6 && targ_y < 64)
+ grd[ targ_x ][ targ_y ] = DNGN_FLOOR;
+ }
+ else if ((env.cgrid[ targ_x ][ targ_y ] != EMPTY_CLOUD
+ && (env.cloud[env.cgrid[ targ_x ][ targ_y ]].type == CLOUD_FIRE
+ || env.cloud[env.cgrid[ targ_x ][ targ_y ]].type == CLOUD_FIRE_MON))
+ && (restricted_type == 0 || restricted_type == MONS_FIRE_ELEMENTAL))
+ {
+ type_summoned = MONS_FIRE_ELEMENTAL;
+ delete_cloud( env.cgrid[ targ_x ][ targ_y ] );
+ }
+ else if ((grd[ targ_x ][ targ_y ] == DNGN_LAVA)
+ && (restricted_type == 0 || restricted_type == MONS_FIRE_ELEMENTAL))
+ {
+ type_summoned = MONS_FIRE_ELEMENTAL;
+ }
+ else if ((grd[ targ_x ][ targ_y ] == DNGN_DEEP_WATER
+ || grd[ targ_x ][ targ_y ] == DNGN_SHALLOW_WATER
+ || grd[ targ_x ][ targ_y ] == DNGN_BLUE_FOUNTAIN)
+ && (restricted_type == 0 || restricted_type == MONS_WATER_ELEMENTAL))
+ {
+ type_summoned = MONS_WATER_ELEMENTAL;
+ }
+ else if ((grd[ targ_x ][ targ_y ] >= DNGN_FLOOR
+ && env.cgrid[ targ_x ][ targ_y ] == EMPTY_CLOUD)
+ && (restricted_type == 0 || restricted_type == MONS_AIR_ELEMENTAL))
+ {
+ type_summoned = MONS_AIR_ELEMENTAL;
+ }
+
+ // found something to summon
+ if (type_summoned == MONS_PROGRAM_BUG)
+ {
+ canned_msg(MSG_NOTHING_HAPPENS);
+ return (-1);
+ }
+
+ // silly - ice for water? 15jan2000 {dlb}
+ // little change here to help with the above... and differentiate
+ // elements a bit... {bwr}
+ // - Water elementals are now harder to be made reliably friendly
+ // - Air elementals are harder because they're more dynamic/dangerous
+ // - Earth elementals are more static and easy to tame (as before)
+ // - Fire elementals fall in between the two (10 is still fairly easy)
+ if ((type_summoned == MONS_FIRE_ELEMENTAL
+ && random2(10) >= you.skills[SK_FIRE_MAGIC])
+
+ || (type_summoned == MONS_WATER_ELEMENTAL
+ && random2((you.species == SP_MERFOLK) ? 5 : 15)
+ >= you.skills[SK_ICE_MAGIC])
+
+ || (type_summoned == MONS_AIR_ELEMENTAL
+ && random2(15) >= you.skills[SK_AIR_MAGIC])
+
+ || (type_summoned == MONS_EARTH_ELEMENTAL
+ && random2(5) >= you.skills[SK_EARTH_MAGIC])
+
+ || random2(100) < unfriendly)
+ {
+ summ_success = create_monster( type_summoned, numsc, BEH_HOSTILE,
+ targ_x, targ_y, MHITYOU, 250 );
+
+ if (summ_success >= 0)
+ mpr( "The elemental doesn't seem to appreciate being summoned." );
+ }
+ else
+ {
+ summ_success = create_monster( type_summoned, numsc, BEH_FRIENDLY,
+ targ_x, targ_y, you.pet_target, 250 );
+ }
+
+ return (summ_success >= 0);
+} // end summon_elemental()
+
+//jmf: beefed up higher-level casting of this (formerly lame) spell
+void summon_small_mammals(int pow)
+{
+ int thing_called = MONS_PROGRAM_BUG; // error trapping{dlb}
+
+ int pow_spent = 0;
+ int pow_left = pow + 1;
+ int summoned = 0;
+ int summoned_max = pow / 16;
+
+ if (summoned_max > 5)
+ summoned_max = 5;
+ if (summoned_max < 1)
+ summoned_max = 1;
+
+ while (pow_left > 0 && summoned < summoned_max)
+ {
+ summoned++;
+ pow_spent = 1 + random2(pow_left);
+ pow_left -= pow_spent;
+
+ switch (pow_spent)
+ {
+ case 75:
+ case 74:
+ case 38:
+ thing_called = MONS_ORANGE_RAT;
+ break;
+
+ case 65:
+ case 64:
+ case 63:
+ case 27:
+ case 26:
+ case 25:
+ thing_called = MONS_GREEN_RAT;
+ break;
+
+ case 57:
+ case 56:
+ case 55:
+ case 54:
+ case 53:
+ case 52:
+ case 20:
+ case 18:
+ case 16:
+ case 14:
+ case 12:
+ case 10:
+ thing_called = coinflip() ? MONS_QUOKKA : MONS_GREY_RAT;
+ break;
+
+ default:
+ thing_called = coinflip() ? MONS_GIANT_BAT : MONS_RAT;
+ break;
+ }
+
+ create_monster( thing_called, ENCH_ABJ_III, BEH_FRIENDLY,
+ you.x_pos, you.y_pos, you.pet_target, 250 );
+ }
+} // end summon_small_mammals()
+
+void summon_scorpions(int pow)
+{
+ int numsc = 1 + random2(pow) / 10 + random2(pow) / 10;
+
+ numsc = stepdown_value(numsc, 2, 2, 6, 8); //see stuff.cc - 12jan2000 {dlb}
+
+ for (int scount = 0; scount < numsc; scount++)
+ {
+ if (random2(pow) <= 3)
+ {
+ if (create_monster( MONS_SCORPION, ENCH_ABJ_III, BEH_HOSTILE,
+ you.x_pos, you.y_pos, MHITYOU, 250 ) != -1)
+ {
+ mpr("A scorpion appears. It doesn't look very happy.");
+ }
+ }
+ else
+ {
+ if (create_monster( MONS_SCORPION, ENCH_ABJ_III, BEH_FRIENDLY,
+ you.x_pos, you.y_pos,
+ you.pet_target, 250 ) != -1)
+ {
+ mpr("A scorpion appears.");
+ }
+ }
+ }
+} // end summon_scorpions()
+
+void summon_ice_beast_etc(int pow, int ibc)
+{
+ int numsc = ENCH_ABJ_II + (random2(pow) / 4);
+ int beha = BEH_FRIENDLY;
+
+ if (numsc > ENCH_ABJ_VI)
+ numsc = ENCH_ABJ_VI;
+
+ switch (ibc)
+ {
+ case MONS_ICE_BEAST:
+ mpr("A chill wind blows around you.");
+ break;
+
+ case MONS_IMP:
+ mpr("A beastly little devil appears in a puff of flame.");
+ break;
+
+ case MONS_WHITE_IMP:
+ mpr("A beastly little devil appears in a puff of frigid air.");
+ break;
+
+ case MONS_SHADOW_IMP:
+ mpr("A shadowy apparition takes form in the air.");
+ break;
+
+ case MONS_ANGEL:
+ mpr("You open a gate to the realm of Zin!");
+ break;
+
+ case MONS_DAEVA:
+ mpr("You are momentarily dazzled by a brilliant golden light.");
+ break;
+
+ default:
+ mpr("A demon appears!");
+ if (random2(pow) < 4)
+ {
+ beha = BEH_HOSTILE;
+ mpr("It doesn't look very happy.");
+ }
+ break;
+
+ }
+
+ create_monster( ibc, numsc, beha, you.x_pos, you.y_pos, MHITYOU, 250 );
+} // end summon_ice_beast_etc()
+
+bool summon_swarm( int pow, bool unfriendly, bool god_gift )
+{
+ int thing_called = MONS_PROGRAM_BUG; // error trapping {dlb}
+ int numsc = 2 + random2(pow) / 10 + random2(pow) / 25;
+ bool summoned = false;
+
+ // see stuff.cc - 12jan2000 {dlb}
+ numsc = stepdown_value( numsc, 2, 2, 6, 8 );
+
+ for (int scount = 0; scount < numsc; scount++)
+ {
+ switch (random2(14))
+ {
+ case 0:
+ case 1: // prototypical swarming creature {dlb}
+ thing_called = MONS_KILLER_BEE;
+ break;
+
+ case 2: // comment said "larva", code read scorpion {dlb}
+ thing_called = MONS_SCORPION;
+ break; // think: "The Arrival" {dlb}
+
+ case 3: //jmf: technically not insects but still cool
+ thing_called = MONS_WORM;
+ break; // but worms kinda "swarm" so s'ok {dlb}
+
+ case 4: // comment read "larva", code was for scorpion
+ thing_called = MONS_GIANT_MOSQUITO;
+ break; // changed into giant mosquito 12jan2000 {dlb}
+
+ case 5: // think: scarabs in "The Mummy" {dlb}
+ thing_called = MONS_GIANT_BEETLE;
+ break;
+
+ case 6: //jmf: blowfly instead of queen bee
+ thing_called = MONS_GIANT_BLOWFLY;
+ break;
+
+ // queen bee added if more than x bees in swarm? {dlb}
+ // the above would require code rewrite - worth it? {dlb}
+
+ case 8: //jmf: changed to red wasp; was wolf spider
+ thing_called = MONS_WOLF_SPIDER; //jmf: spiders aren't insects
+ break; // think: "Kingdom of the Spiders" {dlb}
+ // not just insects!!! - changed back {dlb}
+
+ case 9:
+ thing_called = MONS_BUTTERFLY; // comic relief? {dlb}
+ break;
+
+ case 10: // change into some kind of snake -- {dlb}
+ thing_called = MONS_YELLOW_WASP; // do wasps swarm? {dlb}
+ break; // think: "Indiana Jones" and snakepit? {dlb}
+
+ default: // 3 in 14 chance, 12jan2000 {dlb}
+ thing_called = MONS_GIANT_ANT;
+ break;
+ } // end switch
+
+ int behaviour = BEH_HOSTILE; // default to unfriendly
+
+ // Note: friendly, non-god_gift means spell.
+ if (god_gift)
+ behaviour = BEH_GOD_GIFT;
+ else if (!unfriendly && random2(pow) > 7)
+ behaviour = BEH_FRIENDLY;
+
+ if (create_monster( thing_called, ENCH_ABJ_III, behaviour,
+ you.x_pos, you.y_pos, MHITYOU, 250 ))
+ {
+ summoned = true;
+ }
+ }
+
+ return (summoned);
+} // end summon_swarm()
+
+void summon_undead(int pow)
+{
+ int temp_rand = 0;
+ int thing_called = MONS_PROGRAM_BUG; // error trapping {dlb}
+
+ int numsc = 1 + random2(pow) / 30 + random2(pow) / 30;
+ numsc = stepdown_value(numsc, 2, 2, 6, 8); //see stuff.cc {dlb}
+
+ mpr("You call on the undead to aid you!");
+
+ for (int scount = 0; scount < numsc; scount++)
+ {
+ temp_rand = random2(25);
+
+ thing_called = ((temp_rand > 8) ? MONS_WRAITH : // 64%
+ (temp_rand > 3) ? MONS_SPECTRAL_WARRIOR // 20%
+ : MONS_FREEZING_WRAITH); // 16%
+
+ if (random2(pow) < 6)
+ {
+ if (create_monster( thing_called, ENCH_ABJ_V, BEH_HOSTILE,
+ you.x_pos, you.y_pos, MHITYOU, 250 ) != -1)
+ {
+ mpr("You sense a hostile presence.");
+ }
+ }
+ else
+ {
+ if (create_monster( thing_called, ENCH_ABJ_V, BEH_FRIENDLY,
+ you.x_pos, you.y_pos, you.pet_target, 250 ) != -1)
+ {
+ mpr("An insubstantial figure forms in the air.");
+ }
+ }
+ } // end for loop
+
+ //jmf: Kiku sometimes deflects this
+ if (!you.is_undead
+ && !(you.religion == GOD_KIKUBAAQUDGHA
+ && (!player_under_penance()
+ && you.piety >= 100 && random2(200) <= you.piety)))
+ {
+ disease_player( 25 + random2(50) );
+ }
+} // end summon_undead()
+
+void summon_things( int pow )
+{
+ int big_things = 0;
+ int numsc = 2 + (random2(pow) / 10) + (random2(pow) / 10);
+
+ if (one_chance_in(3) && !lose_stat( STAT_INTELLIGENCE, 1, true ))
+ mpr("Your call goes unanswered.");
+ else
+ {
+ numsc = stepdown_value( numsc, 2, 2, 6, -1 );
+
+ while (numsc > 2)
+ {
+ if (one_chance_in(4))
+ break;
+
+ numsc -= 2;
+ big_things++;
+ }
+
+ if (numsc > 8)
+ numsc = 8;
+
+ if (big_things > 8)
+ big_things = 8;
+
+ while (big_things > 0)
+ {
+ create_monster( MONS_TENTACLED_MONSTROSITY, 0, BEH_FRIENDLY,
+ you.x_pos, you.y_pos, you.pet_target, 250 );
+ big_things--;
+ }
+
+ while (numsc > 0)
+ {
+ create_monster( MONS_ABOMINATION_LARGE, 0, BEH_FRIENDLY,
+ you.x_pos, you.y_pos, you.pet_target, 250 );
+ numsc--;
+ }
+
+ snprintf( info, INFO_SIZE, "Some Thing%s answered your call!",
+ (numsc + big_things > 1) ? "s" : "" );
+
+ mpr(info);
+ }
+
+ return;
+}