diff options
Diffstat (limited to 'stone_soup/crawl-ref/source/spells4.cc')
-rw-r--r-- | stone_soup/crawl-ref/source/spells4.cc | 3155 |
1 files changed, 0 insertions, 3155 deletions
diff --git a/stone_soup/crawl-ref/source/spells4.cc b/stone_soup/crawl-ref/source/spells4.cc deleted file mode 100644 index 076be063a6..0000000000 --- a/stone_soup/crawl-ref/source/spells4.cc +++ /dev/null @@ -1,3155 +0,0 @@ -/* - * File: spells4.cc - * Summary: new spells, focusing on transmigration, divination and - * other neglected areas of Crawl magic ;^) - * Written by: Copyleft Josh Fishman 1999-2000, All Rights Preserved - * - * Change History (most recent first): - * - * <2> 29jul2000 jdj Made a zillion functions static. - * <1> 06jan2000 jmf Created - */ - -#include "AppHdr.h" - -#include <string> -#include <stdio.h> - -#include "externs.h" - -#include "abyss.h" -#include "beam.h" -#include "cloud.h" -#include "debug.h" -#include "delay.h" -#include "describe.h" -#include "direct.h" -#include "dungeon.h" -#include "effects.h" -#include "it_use2.h" -#include "itemname.h" -#include "itemprop.h" -#include "items.h" -#include "invent.h" -#include "misc.h" -#include "monplace.h" -#include "monstuff.h" -#include "mon-util.h" -#include "mstuff2.h" -#include "ouch.h" -#include "player.h" -#include "randart.h" -#include "religion.h" -#include "skills.h" -#include "spells1.h" -#include "spells4.h" -#include "spl-cast.h" -#include "spl-util.h" -#include "stuff.h" -#include "view.h" - -enum DEBRIS // jmf: add for shatter, dig, and Giants to throw -{ - DEBRIS_METAL, // 0 - DEBRIS_ROCK, - DEBRIS_STONE, - DEBRIS_WOOD, - DEBRIS_CRYSTAL, - NUM_DEBRIS -}; // jmf: ...and I'll actually implement the items Real Soon Now... - -// static int make_a_random_cloud(int x, int y, int pow, int ctype); -static int make_a_rot_cloud(int x, int y, int pow, int ctype); -static int quadrant_blink(int x, int y, int pow, int garbage); - -//void cast_animate_golem(int pow); // see actual function for reasoning {dlb} -//void cast_detect_magic(int pow); //jmf: as above... -//void cast_eringyas_surprising_bouquet(int powc); -void do_monster_rot(int mon); - -//jmf: FIXME: put somewhere else (misc.cc?) -// A feeble attempt at Nethack-like completeness for cute messages. -const char *your_hand( bool plural ) -{ - static char hand_buff[80]; - - switch (you.attribute[ATTR_TRANSFORMATION]) - { - default: - mpr("ERROR: unknown transformation in your_hand() (spells4.cc)"); - case TRAN_NONE: - case TRAN_STATUE: - if (you.species == SP_TROLL || you.species == SP_GHOUL) - { - strcpy(hand_buff, "claw"); - break; - } - // or fall-through - case TRAN_ICE_BEAST: - case TRAN_LICH: - strcpy(hand_buff, "hand"); - break; - case TRAN_SPIDER: - strcpy(hand_buff, "front leg"); - break; - case TRAN_SERPENT_OF_HELL: - case TRAN_DRAGON: - strcpy(hand_buff, "foreclaw"); - break; - case TRAN_BLADE_HANDS: - strcpy(hand_buff, "scythe-like blade"); - break; - case TRAN_AIR: - strcpy(hand_buff, "misty tendril"); - break; - } - - if (plural) - strcat(hand_buff, "s"); - - return (hand_buff); -} - -// I need to make some debris for metal, crystal and stone. -// They could go in OBJ_MISSILES, but I think I'd rather move -// MI_LARGE_ROCK into OBJ_DEBRIS and code giants to throw any -// OBJ_DEBRIS they get their meaty mits on. -static void place_debris(int x, int y, int debris_type) -{ -#ifdef USE_DEBRIS_CODE - switch (debris_type) - { - // hate to say this, but the first parameter only allows specific quantity - // for *food* and nothing else -- and I would hate to see that parameter - // (force_unique) abused any more than it already has been ... {dlb}: - case DEBRIS_STONE: - large = items( random2(3), OBJ_MISSILES, MI_LARGE_ROCK, true, 1, 250 ); - small = items( 3 + random2(6) + random2(6) + random2(6), - OBJ_MISSILES, MI_STONE, true, 1, 250 ); - break; - case DEBRIS_METAL: - case DEBRIS_WOOD: - case DEBRIS_CRYSTAL: - break; - } - - if (small != NON_ITEM) - move_item_to_grid( &small, x, y ); - - if (large != NON_ITEM) - move_item_to_grid( &large, x, y ); - -#else - UNUSED( x ); - UNUSED( y ); - UNUSED( debris_type ); - return; -#endif -} // end place_debris() - -// just to avoid typing this over and over -// now returns true if monster died -- bwr -inline bool player_hurt_monster(int monster, int damage) -{ - ASSERT( monster != NON_MONSTER ); - - if (damage > 0) - { - hurt_monster( &menv[monster], damage ); - - if (menv[monster].hit_points > 0) - print_wounds( &menv[monster] ); - else - { - monster_die( &menv[monster], KILL_YOU, 0 ); - return (true); - } - } - - return (false); -} // end player_hurt_monster() - - -// Here begin the actual spells: -static int shatter_monsters(int x, int y, int pow, int garbage) -{ - UNUSED( garbage ); - - dice_def dam_dice( 0, 5 + pow / 4 ); // number of dice set below - const int monster = mgrd[x][y]; - - if (monster == NON_MONSTER) - return (0); - - // Removed a lot of silly monsters down here... people, just because - // it says ice, rock, or iron in the name doesn't mean it's actually - // made out of the substance. -- bwr - switch (menv[monster].type) - { - case MONS_ICE_BEAST: // 3/2 damage - case MONS_SIMULACRUM_SMALL: - case MONS_SIMULACRUM_LARGE: - dam_dice.num = 4; - break; - - case MONS_SKELETON_SMALL: // double damage - case MONS_SKELETON_LARGE: - case MONS_CURSE_SKULL: - case MONS_CLAY_GOLEM: - case MONS_STONE_GOLEM: - case MONS_IRON_GOLEM: - case MONS_CRYSTAL_GOLEM: - case MONS_EARTH_ELEMENTAL: - case MONS_GARGOYLE: - case MONS_SKELETAL_DRAGON: - case MONS_SKELETAL_WARRIOR: - dam_dice.num = 6; - break; - - case MONS_VAPOUR: - case MONS_INSUBSTANTIAL_WISP: - case MONS_AIR_ELEMENTAL: - case MONS_FIRE_ELEMENTAL: - case MONS_WATER_ELEMENTAL: - case MONS_SPECTRAL_WARRIOR: - case MONS_FREEZING_WRAITH: - case MONS_WRAITH: - case MONS_PHANTOM: - case MONS_PLAYER_GHOST: - case MONS_SHADOW: - case MONS_HUNGRY_GHOST: - case MONS_FLAYED_GHOST: - case MONS_SMOKE_DEMON: //jmf: I hate these bastards... - dam_dice.num = 0; - break; - - case MONS_PULSATING_LUMP: - case MONS_JELLY: - case MONS_SLIME_CREATURE: - case MONS_BROWN_OOZE: - case MONS_AZURE_JELLY: - case MONS_DEATH_OOZE: - case MONS_ACID_BLOB: - case MONS_ROYAL_JELLY: - case MONS_OOZE: - case MONS_SPECTRAL_THING: - case MONS_JELLYFISH: - dam_dice.num = 1; - dam_dice.size /= 2; - break; - - case MONS_DANCING_WEAPON: // flies, but earth based - case MONS_MOLTEN_GARGOYLE: - case MONS_QUICKSILVER_DRAGON: - // Soft, earth creatures... would normally resist to 1 die, but - // are sensitive to this spell. -- bwr - dam_dice.num = 2; - break; - - default: // normal damage - if (mons_flies( &menv[monster] )) - dam_dice.num = 1; - else - dam_dice.num = 3; - break; - } - - int damage = roll_dice( dam_dice ) - random2( menv[monster].armour_class ); - - if (damage > 0) - player_hurt_monster( monster, damage ); - else - damage = 0; - - return (damage); -} // end shatter_monsters() - -static int shatter_items(int x, int y, int pow, int garbage) -{ - UNUSED( pow ); - UNUSED( garbage ); - - int broke_stuff = 0, next, obj = igrd[x][y]; - - if (obj == NON_ITEM) - return 0; - - while (obj != NON_ITEM) - { - next = mitm[obj].link; - - switch (mitm[obj].base_type) - { - case OBJ_POTIONS: - if (!one_chance_in(10)) - { - broke_stuff++; - destroy_item(obj); - } - break; - - default: - break; - } - - obj = next; - } - - if (broke_stuff) - { - if (!silenced(x, y) && !silenced(you.x_pos, you.y_pos)) - mpr("You hear glass break.", MSGCH_SOUND); - - return 1; - } - - return 0; -} // end shatter_items() - -static int shatter_walls(int x, int y, int pow, int garbage) -{ - UNUSED( garbage ); - - int chance = 0; - int stuff = 0; - - // if not in-bounds then we can't really shatter it -- bwr - if (x <= 5 || x >= GXM - 5 || y <= 5 || y >= GYM - 5) - return (0); - - switch (grd[x][y]) - { - case DNGN_SECRET_DOOR: - if (see_grid(x, y)) - mpr("A secret door shatters!"); - grd[x][y] = DNGN_FLOOR; - stuff = DEBRIS_WOOD; - chance = 100; - break; - - case DNGN_CLOSED_DOOR: - case DNGN_OPEN_DOOR: - if (see_grid(x, y)) - mpr("A door shatters!"); - grd[x][y] = DNGN_FLOOR; - stuff = DEBRIS_WOOD; - chance = 100; - break; - - case DNGN_METAL_WALL: - case DNGN_SILVER_STATUE: - stuff = DEBRIS_METAL; - chance = pow / 10; - break; - - case DNGN_ORCISH_IDOL: - case DNGN_GRANITE_STATUE: - chance = 50; - stuff = DEBRIS_STONE; - break; - - case DNGN_STONE_WALL: - chance = pow / 6; - stuff = DEBRIS_STONE; - break; - - case DNGN_ROCK_WALL: - chance = pow / 4; - stuff = DEBRIS_ROCK; - break; - - case DNGN_ORANGE_CRYSTAL_STATUE: - chance = pow / 6; - stuff = DEBRIS_CRYSTAL; - break; - - case DNGN_GREEN_CRYSTAL_WALL: - chance = 50; - stuff = DEBRIS_CRYSTAL; - break; - - default: - break; - } - - if (stuff && random2(100) < chance) - { - if (!silenced( x, y )) - noisy( 30, x, y ); - - grd[x][y] = DNGN_FLOOR; - place_debris(x, y, stuff); - return (1); - } - - return (0); -} // end shatter_walls() - -void cast_shatter(int pow) -{ - int damage = 0; - const bool sil = silenced( you.x_pos, you.y_pos ); - - if (!sil) - noisy( 30, you.x_pos, you.y_pos ); - - snprintf(info, INFO_SIZE, "The dungeon %s!", (sil ? "shakes" : "rumbles")); - mpr(info, (sil? MSGCH_PLAIN : MSGCH_SOUND)); - - switch (you.attribute[ATTR_TRANSFORMATION]) - { - case TRAN_NONE: - case TRAN_SPIDER: - case TRAN_LICH: - case TRAN_DRAGON: - case TRAN_AIR: - case TRAN_SERPENT_OF_HELL: - break; - - case TRAN_STATUE: // full damage - damage = 15 + random2avg( (pow / 5), 4 ); - break; - - case TRAN_ICE_BEAST: // 1/2 damage - damage = 10 + random2avg( (pow / 5), 4 ) / 2; - break; - - case TRAN_BLADE_HANDS: // 2d3 damage - mpr("Your scythe-like blades vibrate painfully!"); - damage = 2 + random2avg(5, 2); - break; - - default: - mpr("cast_shatter(): unknown transformation in spells4.cc"); - } - - if (damage) - ouch(damage, 0, KILLED_BY_TARGETTING); - - int rad = 3 + (you.skills[SK_EARTH_MAGIC] / 5); - - apply_area_within_radius(shatter_items, you.x_pos, you.y_pos, pow, rad, 0); - apply_area_within_radius(shatter_monsters, you.x_pos, you.y_pos, pow, rad, 0); - int dest = apply_area_within_radius( shatter_walls, you.x_pos, you.y_pos, - pow, rad, 0 ); - - if (dest && !sil) - mpr("Ka-crash!", MSGCH_SOUND); -} // end cast_shatter() - -// cast_forescry: raises evasion (by 8 currently) via divination -void cast_forescry(int pow) -{ - if (!you.duration[DUR_FORESCRY]) - mpr("You begin to receive glimpses of the immediate future..."); - - you.duration[DUR_FORESCRY] += 5 + random2(pow); - - if (you.duration[DUR_FORESCRY] > 30) - you.duration[DUR_FORESCRY] = 30; - - you.redraw_evasion = 1; -} // end cast_forescry() - -void cast_see_invisible(int pow) -{ - if (player_see_invis()) - mpr("Nothing seems to happen."); - else - mpr("Your vision seems to sharpen."); - - // no message if you already are under the spell - you.duration[DUR_SEE_INVISIBLE] += 10 + random2(2 + (pow / 2)); - - if (you.duration[DUR_SEE_INVISIBLE] > 100) - you.duration[DUR_SEE_INVISIBLE] = 100; -} // end cast_see_invisible() - -#if 0 -// FIXME: This would be kinda cool if implemented right. -// The idea is that, like detect_secret_doors, the spell gathers all -// sorts of information about a thing and then tells the caster a few -// cryptic hints. So for a (+3,+5) Mace of Flaming, one might detect -// "enchantment and heat", but for a cursed ring of hunger, one might -// detect "enchantment and ice" (since it gives you a 'deathly cold' -// feeling when you put it on) or "necromancy" (since it's evil). -// A weapon of Divine Wrath and a randart that makes you angry might -// both give similar messages. The key would be to not tell more than -// hints about whether an item is benign or cursed, but give info -// on how strong its enchantment is (and therefore how valuable it -// probably is). -static void cast_detect_magic(int pow) -{ - struct dist bmove; - int x, y; - int monster = 0, item = 0, next; //int max; - FixedVector < int, NUM_SPELL_TYPES > found; - int strong = 0; // int curse = 0; - - for (next = 0; next < NUM_SPELL_TYPES; next++) - { - found[next] = 0; - } - - mpr("Which direction?", MSGCH_PROMPT); - direction( bmove, DIR_DIR ); - - if (!bmove.isValid) - { - canned_msg(MSG_SPELL_FIZZLES); - return; - } - - if (bmove.dx == 0 && bmove.dy == 0) - { - mpr("You detect a divination in progress."); - return; - } - - x = you.x_pos + bmove.dx; - y = you.y_pos + bmove.dy; - - monster = mgrd[x][y]; - if (monster == NON_MONSTER) - goto do_items; - else - goto all_done; - - do_items: - item = igrd[x][y]; - - if (item == NON_ITEM) - goto all_done; - - while (item != NON_ITEM) - { - next = mitm[item].link; - if (is_dumpable_artifact - (mitm[item].base_type, mitm[item].sub_type, mitm[item].plus, - mitm[item].plus2, mitm[item].special, 0, 0)) - { - strong++; - //FIXME: do checks for randart properties - } - else - { - switch (mitm[item].base_type) - { - case OBJ_WEAPONS: - found[SPTYP_ENCHANTMENT] += (mitm[item].plus > 50); - found[SPTYP_ENCHANTMENT] += (mitm[item].plus2 > 50); - break; - - case OBJ_MISSILES: - found[SPTYP_ENCHANTMENT] += (mitm[item].plus > 50); - found[SPTYP_ENCHANTMENT] += (mitm[item].plus2 > 50); - break; - - case OBJ_ARMOUR: - found[SPTYP_ENCHANTMENT] += mitm[item].plus; - } - } - } - - all_done: - if (monster) - { - mpr("You detect a morphogenic field, such as a monster might have."); - } - if (strong) - { - mpr("You detect very strong enchantments."); - return; - } - else - { - //FIXME: - } - return; -} -#endif - -// The description idea was okay, but this spell just isn't that exciting. -// So I'm converting it to the more practical expose secret doors. -- bwr -void cast_detect_secret_doors(int pow) -{ - int found = 0; - - for (int x = you.x_pos - 8; x <= you.x_pos + 8; x++) - { - for (int y = you.y_pos - 8; y <= you.y_pos + 8; y++) - { - if (x < 5 || x > GXM - 5 || y < 5 || y > GYM - 5) - continue; - - if (!see_grid(x, y)) - continue; - - if (grd[x][y] == DNGN_SECRET_DOOR && random2(pow) > random2(15)) - { - grd[x][y] = DNGN_CLOSED_DOOR; - found++; - } - } - } - - if (found) - { - redraw_screen(); - - snprintf( info, INFO_SIZE, "You detect %s secret door%s.", - (found > 1) ? "some" : "a", (found > 1) ? "s" : "" ); - mpr( info ); - } -} // end cast_detect_secret_doors() - -void cast_summon_butterflies(int pow) -{ - // explicitly limiting the number - int num = 4 + random2(3) + random2( pow ) / 10; - if (num > 16) - num = 16; - - for (int scount = 1; scount < num; scount++) - { - create_monster( MONS_BUTTERFLY, ENCH_ABJ_III, BEH_FRIENDLY, - you.x_pos, you.y_pos, MHITYOU, 250 ); - } -} - -void cast_summon_large_mammal(int pow) -{ - int mon; - int temp_rand = random2(pow); - - if (temp_rand < 10) - mon = MONS_JACKAL; - else if (temp_rand < 15) - mon = MONS_HOUND; - else - { - switch (temp_rand % 7) - { - case 0: - if (you.species == SP_HILL_ORC && one_chance_in(3)) - mon = MONS_WARG; - else - mon = MONS_WOLF; - break; - case 1: - case 2: - mon = MONS_WAR_DOG; - break; - case 3: - case 4: - mon = MONS_HOUND; - break; - default: - mon = MONS_JACKAL; - break; - } - } - - create_monster( mon, ENCH_ABJ_III, BEH_FRIENDLY, you.x_pos, you.y_pos, - you.pet_target, 250 ); -} - -void cast_sticks_to_snakes(int pow) -{ - int mon, i, behaviour; - - int how_many = 0; - - int max = 1 + random2( 1 + you.skills[SK_TRANSMIGRATION] ) / 4; - - int dur = ENCH_ABJ_III + random2(pow) / 20; - if (dur > ENCH_ABJ_V) - dur = ENCH_ABJ_V; - - const int weapon = you.equip[EQ_WEAPON]; - - if (weapon == -1) - { - snprintf( info, INFO_SIZE, "Your %s feel slithery!", your_hand(true)); - mpr(info); - return; - } - - behaviour = item_cursed( you.inv[ weapon ] ) ? BEH_HOSTILE - : BEH_FRIENDLY; - - if ((you.inv[ weapon ].base_type == OBJ_MISSILES - && (you.inv[ weapon ].sub_type == MI_ARROW))) - { - if (you.inv[ weapon ].quantity < max) - max = you.inv[ weapon ].quantity; - - for (i = 0; i <= max; i++) - { - //jmf: perhaps also check for poison ammo? - if (pow > 50 || (pow > 25 && one_chance_in(3))) - mon = MONS_SNAKE; - else - mon = MONS_SMALL_SNAKE; - - if (create_monster( mon, dur, behaviour, you.x_pos, you.y_pos, - MHITYOU, 250 ) != -1) - { - how_many++; - } - } - } - - if (you.inv[ weapon ].base_type == OBJ_WEAPONS - && (you.inv[ weapon ].sub_type == WPN_CLUB - || you.inv[ weapon ].sub_type == WPN_SPEAR - || you.inv[ weapon ].sub_type == WPN_QUARTERSTAFF - || you.inv[ weapon ].sub_type == WPN_SCYTHE - || you.inv[ weapon ].sub_type == WPN_GIANT_CLUB - || you.inv[ weapon ].sub_type == WPN_GIANT_SPIKED_CLUB - || you.inv[ weapon ].sub_type == WPN_BOW - || you.inv[ weapon ].sub_type == WPN_LONGBOW - || you.inv[ weapon ].sub_type == WPN_ANCUS - || you.inv[ weapon ].sub_type == WPN_HALBERD - || you.inv[ weapon ].sub_type == WPN_GLAIVE - || you.inv[ weapon ].sub_type == WPN_BLOWGUN)) - { - how_many = 1; - - // Upsizing Snakes to Brown Snakes as the base class for using - // the really big sticks (so bonus applies really only to trolls, - // ogres, and most importantly ogre magi). Still it's unlikely - // any character is strong enough to bother lugging a few of - // these around. -- bwr - if (item_mass( you.inv[ weapon ] ) < 500) - mon = MONS_SNAKE; - else - mon = MONS_BROWN_SNAKE; - - if (pow > 90 && one_chance_in(3)) - mon = MONS_GREY_SNAKE; - - if (pow > 70 && one_chance_in(3)) - mon = MONS_BLACK_SNAKE; - - if (pow > 40 && one_chance_in(3)) - mon = MONS_YELLOW_SNAKE; - - if (pow > 20 && one_chance_in(3)) - mon = MONS_BROWN_SNAKE; - - create_monster(mon, dur, behaviour, you.x_pos, you.y_pos, MHITYOU, 250); - } - -#ifdef USE_DEBRIS_CODE - if (you.inv[ weapon ].base_type == OBJ_DEBRIS - && (you.inv[ weapon ].sub_type == DEBRIS_WOOD)) - { - // this is how you get multiple big snakes - how_many = 1; - mpr("FIXME: implement OBJ_DEBRIS conversion! (spells4.cc)"); - } -#endif // USE_DEBRIS_CODE - - if (how_many > you.inv[you.equip[EQ_WEAPON]].quantity) - how_many = you.inv[you.equip[EQ_WEAPON]].quantity; - - if (how_many) - { - dec_inv_item_quantity( you.equip[EQ_WEAPON], how_many ); - - snprintf( info, INFO_SIZE, "You create %s snake%s!", - how_many > 1 ? "some" : "a", how_many > 1 ? "s" : ""); - } - else - { - snprintf( info, INFO_SIZE, "Your %s feel slithery!", your_hand(true)); - } - - mpr(info); - return; -} // end cast_sticks_to_snakes() - -void cast_summon_dragon(int pow) -{ - int happy; - - // Removed the chance of multiple dragons... one should be more - // than enough, and if it isn't, the player can cast again... - // especially since these aren't on the Abjuration plan... they'll - // last until they die (maybe that should be changed, but this is - // a very high level spell so it might be okay). -- bwr - happy = (random2(pow) > 5); - - if (create_monster( MONS_DRAGON, ENCH_ABJ_III, - (happy ? BEH_FRIENDLY : BEH_HOSTILE), - you.x_pos, you.y_pos, MHITYOU, 250 ) != -1) - { - strcpy(info, "A dragon appears."); - - if (!happy) - strcat(info, " It doesn't look very happy."); - } - else - strcpy(info, "Nothing happens."); - - mpr(info); -} // end cast_summon_dragon() - -void cast_conjure_ball_lightning( int pow ) -{ - int num = 3 + random2( 2 + pow / 50 ); - - // but restricted so that the situation doesn't get too gross. - // Each of these will explode for 3d20 damage. -- bwr - if (num > 8) - num = 8; - - bool summoned = false; - - for (int i = 0; i < num; i++) - { - int tx = -1, ty = -1; - - for (int j = 0; j < 10; j++) - { - if (!random_near_space( you.x_pos, you.y_pos, tx, ty, true, true) - && distance( you.x_pos, you.y_pos, tx, ty ) <= 5) - { - break; - } - } - - // if we fail, we'll try the ol' summon next to player trick. - if (tx == -1 || ty == -1) - { - tx = you.x_pos; - ty = you.y_pos; - } - - int mon = mons_place( MONS_BALL_LIGHTNING, BEH_FRIENDLY, MHITNOT, - true, tx, ty ); - - // int mon = create_monster( MONS_BALL_LIGHTNING, 0, BEH_FRIENDLY, - // tx, ty, MHITNOT, 250 ); - - if (mon != -1) - { - mons_add_ench( &menv[mon], ENCH_SHORT_LIVED ); - summoned = true; - } - } - - if (summoned) - mpr( "You create some ball lightning!" ); - else - canned_msg( MSG_NOTHING_HAPPENS ); -} - -static int sleep_monsters(int x, int y, int pow, int garbage) -{ - UNUSED( garbage ); - int mnstr = mgrd[x][y]; - - if (mnstr == NON_MONSTER) return 0; - if (mons_holiness(&menv[mnstr]) != MH_NATURAL) return 0; - if (check_mons_resist_magic( &menv[mnstr], pow )) return 0; - - // Why shouldn't we be able to sleep friendly monsters? -- bwr - // if (mons_friendly( &menv[mnstr] )) return 0; - - //jmf: now that sleep == hibernation: - if (mons_res_cold( &menv[mnstr] ) > 0 && coinflip()) return 0; - if (mons_has_ench( &menv[mnstr], ENCH_SLEEP_WARY )) return 0; - - menv[mnstr].behaviour = BEH_SLEEP; - mons_add_ench( &menv[mnstr], ENCH_SLEEP_WARY ); - - if (mons_class_flag( menv[mnstr].type, M_COLD_BLOOD ) && coinflip()) - mons_add_ench( &menv[mnstr], ENCH_SLOW ); - - return 1; -} // end sleep_monsters() - -void cast_mass_sleep(int pow) -{ - apply_area_visible(sleep_monsters, pow); -} // end cast_mass_sleep() - -static int tame_beast_monsters(int x, int y, int pow, int garbage) -{ - UNUSED( garbage ); - int which_mons = mgrd[x][y]; - - if (which_mons == NON_MONSTER) return 0; - - struct monsters *monster = &menv[which_mons]; - - if (mons_holiness(monster) != MH_NATURAL) return 0; - if (mons_intel_type(monster->type) != I_ANIMAL) return 0; - if (mons_friendly(monster)) return 0; - - // 50% bonus for dogs, add cats if they get implemented - if (monster->type == MONS_HOUND || monster->type == MONS_WAR_DOG - || monster->type == MONS_BLACK_BEAR) - { - pow += (pow / 2); - } - - if (you.species == SP_HILL_ORC && monster->type == MONS_WARG) - pow += (pow / 2); - - if (check_mons_resist_magic(monster, pow)) - return 0; - - // I'd like to make the monsters affected permanently, but that's - // pretty powerful. Maybe a small (pow/10) chance of being permanently - // tamed, large chance of just being enslaved. - simple_monster_message(monster, " is tamed!"); - - if (random2(100) < random2(pow / 10)) - monster->attitude = ATT_FRIENDLY; // permanent, right? - else - mons_add_ench(monster, ENCH_CHARM); - - return 1; -} // end tame_beast_monsters() - -void cast_tame_beasts(int pow) -{ - apply_area_visible(tame_beast_monsters, pow); -} // end cast_tame_beasts() - -static int ignite_poison_objects(int x, int y, int pow, int garbage) -{ - UNUSED( pow ); - UNUSED( garbage ); - - int obj = igrd[x][y], next, strength = 0; - - if (obj == NON_ITEM) - return (0); - - while (obj != NON_ITEM) - { - next = mitm[obj].link; - if (mitm[obj].base_type == OBJ_POTIONS) - { - switch (mitm[obj].sub_type) - { - // intentional fall-through all the way down - case POT_STRONG_POISON: - strength += 20; - case POT_DEGENERATION: - strength += 10; - case POT_POISON: - strength += 10; - destroy_item(obj); - default: - break; - } - } - - // FIXME: impliment burning poisoned ammo - // else if ( it's ammo that's poisoned) { - // strength += number_of_ammo; - // destroy_item(ammo); - // } - obj = next; - } - - if (strength > 0) - place_cloud(CLOUD_FIRE, x, y, strength + roll_dice(3, strength / 4) ); - - return (strength); -} // end ignite_poison_objects() - -static int ignite_poison_clouds( int x, int y, int pow, int garbage ) -{ - UNUSED( pow ); - UNUSED( garbage ); - - bool did_anything = false; - - const int cloud = env.cgrid[x][y]; - - if (cloud != EMPTY_CLOUD) - { - if (env.cloud[ cloud ].type == CLOUD_STINK - || env.cloud[ cloud ].type == CLOUD_STINK_MON) - { - did_anything = true; - env.cloud[ cloud ].type = CLOUD_FIRE; - - env.cloud[ cloud ].decay /= 2; - - if (env.cloud[ cloud ].decay < 1) - env.cloud[ cloud ].decay = 1; - } - else if (env.cloud[ cloud ].type == CLOUD_POISON - || env.cloud[ cloud ].type == CLOUD_POISON_MON) - { - did_anything = true; - env.cloud[ cloud ].type = CLOUD_FIRE; - } - } - - return ((int) did_anything); -} // end ignite_poison_clouds() - -static int ignite_poison_monsters(int x, int y, int pow, int garbage) -{ - UNUSED( garbage ); - - struct bolt beam; - beam.flavour = BEAM_FIRE; // this is dumb, only used for adjust! - - dice_def dam_dice( 0, 5 + pow / 7 ); // dice added below if applicable - - const int mon_index = mgrd[x][y]; - if (mon_index == NON_MONSTER) - return (0); - - struct monsters *const mon = &menv[ mon_index ]; - - // Monsters which have poison corpses or poisonous attacks: - if (mons_corpse_effect( mon->type ) == CE_POISONOUS - || mon->type == MONS_GIANT_ANT - || mon->type == MONS_SMALL_SNAKE - || mon->type == MONS_SNAKE - || mon->type == MONS_JELLYFISH - || mons_is_mimic( mon->type )) - { - dam_dice.num = 3; - } - - // Monsters which are poisoned: - int strength = 0; - - // first check for player poison: - int ench = mons_has_ench( mon, ENCH_YOUR_POISON_I, ENCH_YOUR_POISON_IV ); - if (ench != ENCH_NONE) - strength += ench - ENCH_YOUR_POISON_I + 1; - - // ... now monster poison: - ench = mons_has_ench( mon, ENCH_POISON_I, ENCH_POISON_IV ); - if (ench != ENCH_NONE) - strength += ench - ENCH_POISON_I + 1; - - // strength is now the sum of both poison types (although only - // one should actually be present at a given time): - dam_dice.num += strength; - - int damage = roll_dice( dam_dice ); - if (damage > 0) - { - damage = mons_adjust_flavoured( mon, beam, damage ); - -#if DEBUG_DIAGNOSTICS - snprintf( info, INFO_SIZE, "Dice: %dd%d; Damage: %d", - dam_dice.num, dam_dice.size, damage ); - - mpr( info, MSGCH_DIAGNOSTICS ); -#endif - - if (!player_hurt_monster( mon_index, damage )) - { - // Monster survived, remove any poison. - mons_del_ench( mon, ENCH_POISON_I, ENCH_POISON_IV ); - mons_del_ench( mon, ENCH_YOUR_POISON_I, ENCH_YOUR_POISON_IV ); - } - - return (1); - } - - return (0); -} - -void cast_ignite_poison(int pow) -{ - int damage = 0, strength = 0, pcount = 0, acount = 0, totalstrength = 0; - char item; - bool wasWielding = false; - char str_pass[ ITEMNAME_SIZE ]; - - // temp weapon of venom => temp fire brand - const int wpn = you.equip[EQ_WEAPON]; - - if (wpn != -1 - && you.duration[DUR_WEAPON_BRAND] - && get_weapon_brand( you.inv[wpn] ) == SPWPN_VENOM) - { - if (set_item_ego_type( you.inv[wpn], OBJ_WEAPONS, SPWPN_FLAMING )) - { - in_name( wpn, DESC_CAP_YOUR, str_pass ); - strcpy( info, str_pass ); - strcat( info, " bursts into flame!" ); - mpr(info); - - you.wield_change = true; - you.duration[DUR_WEAPON_BRAND] += 1 + you.duration[DUR_WEAPON_BRAND] / 2; - if (you.duration[DUR_WEAPON_BRAND] > 80) - you.duration[DUR_WEAPON_BRAND] = 80; - } - } - - totalstrength = 0; - - for (item = 0; item < ENDOFPACK; item++) - { - if (!you.inv[item].quantity) - continue; - - strength = 0; - - if (you.inv[item].base_type == OBJ_MISSILES) - { - if (you.inv[item].special == 3) - { // burn poison ammo - strength = you.inv[item].quantity; - acount += you.inv[item].quantity; - } - } - - if (you.inv[item].base_type == OBJ_POTIONS) - { - switch (you.inv[item].sub_type) - { - case POT_STRONG_POISON: - strength += 20 * you.inv[item].quantity; - break; - case POT_DEGENERATION: - case POT_POISON: - strength += 10 * you.inv[item].quantity; - break; - default: - break; - } // end switch - - if (strength) - pcount += you.inv[item].quantity; - } - - if (strength) - { - you.inv[item].quantity = 0; - if (item == you.equip[EQ_WEAPON]) - { - you.equip[EQ_WEAPON] = -1; - wasWielding = true; - } - } - - totalstrength += strength; - } - - if (acount > 0) - mpr("Some ammo you are carrying burns!"); - - if (pcount > 0) - { - snprintf( info, INFO_SIZE, "%s potion%s you are carrying explode%s!", - pcount > 1 ? "Some" : "A", - pcount > 1 ? "s" : "", - pcount > 1 ? "" : "s"); - mpr(info); - } - - if (wasWielding == true) - canned_msg( MSG_EMPTY_HANDED ); - - if (totalstrength) - { - place_cloud(CLOUD_FIRE, you.x_pos, you.y_pos, - random2(totalstrength / 4 + 1) + random2(totalstrength / 4 + 1) + - random2(totalstrength / 4 + 1) + random2(totalstrength / 4 + 1) + 1); - } - - // player is poisonous - if (you.mutation[MUT_SPIT_POISON] || you.mutation[MUT_STINGER] - || you.attribute[ATTR_TRANSFORMATION] == TRAN_SPIDER // poison attack - || (!player_is_shapechanged() - && (you.species == SP_GREEN_DRACONIAN // poison breath - || you.species == SP_KOBOLD // poisonous corpse - || you.species == SP_NAGA))) // spit poison - { - damage = roll_dice( 3, 5 + pow / 7 ); - } - - // player is poisoned - damage += roll_dice( you.poison, 6 ); - - if (damage) - { - const int resist = player_res_fire(); - - if (resist > 0) - { - mpr("You feel like your blood is boiling!"); - damage = damage / 3; - } - else if (resist < 0) - { - damage *= 3; - mpr("The poison in your system burns terribly!"); - } - else - { - mpr("The poison in your system burns!"); - } - - ouch( damage, 0, KILLED_BY_TARGETTING ); - - if (you.poison > 0) - { - mpr( "You feel that the poison has left your system." ); - you.poison = 0; - } - } - - apply_area_visible(ignite_poison_clouds, pow); - apply_area_visible(ignite_poison_objects, pow); - apply_area_visible(ignite_poison_monsters, pow); -} // end cast_ignite_poison() - -void cast_silence(int pow) -{ - if (!you.attribute[ATTR_WAS_SILENCED]) - mpr("A profound silence engulfs you."); - - you.attribute[ATTR_WAS_SILENCED] = 1; - - you.duration[DUR_SILENCE] += 10 + random2avg( pow, 2 ); - - if (you.duration[DUR_SILENCE] > 100) - you.duration[DUR_SILENCE] = 100; -} // end cast_silence() - - -/* ****************************************************************** -// no hooks for this anywhere {dlb}: - -void cast_animate_golem(int pow) -{ - // must have more than 20 max_hitpoints - - // must be wielding a Scroll of Paper (for chem) - - // must be standing on a pile of <foo> (for foo in: wood, metal, rock, stone) - - // Will cost you 5-10% of max_hitpoints, or 20 + some, whichever is more - mpr("You imbue the inanimate form with a portion of your life force."); - - naughty(NAUGHTY_CREATED_LIFE, 10); -} - -****************************************************************** */ - -static int discharge_monsters( int x, int y, int pow, int garbage ) -{ - UNUSED( garbage ); - - const int mon = mgrd[x][y]; - int damage = 0; - - struct bolt beam; - beam.flavour = BEAM_ELECTRICITY; // used for mons_adjust_flavoured - - if (x == you.x_pos && y == you.y_pos) - { - mpr( "You are struck by lightning." ); - damage = 3 + random2( 5 + pow / 10 ); - damage = check_your_resists( damage, BEAM_ELECTRICITY ); - ouch( damage, 0, KILLED_BY_WILD_MAGIC ); - } - else if (mon == NON_MONSTER) - return (0); - else if (mons_res_elec(&menv[mon]) > 0 || mons_flies(&menv[mon])) - return (0); - else - { - damage = 3 + random2( 5 + pow / 10 ); - damage = mons_adjust_flavoured( &menv[mon], beam, damage ); - - if (damage) - { - strcpy( info, ptr_monam( &(menv[mon]), DESC_CAP_THE ) ); - strcat( info, " is struck by lightning." ); - mpr( info ); - - player_hurt_monster( mon, damage ); - } - } - - // Recursion to give us chain-lightning -- bwr - // Low power slight chance added for low power characters -- bwr - if ((pow >= 10 && !one_chance_in(3)) || (pow >= 3 && one_chance_in(10))) - { - mpr( "The lightning arcs!" ); - pow /= (coinflip() ? 2 : 3); - damage += apply_random_around_square( discharge_monsters, x, y, - true, pow, 1 ); - } - else if (damage > 0) - { - // Only printed if we did damage, so that the messages in - // cast_discharge() are clean. -- bwr - mpr( "The lightning grounds out." ); - } - - return (damage); -} // end discharge_monsters() - -void cast_discharge( int pow ) -{ - int num_targs = 1 + random2( 1 + pow / 25 ); - int dam; - - dam = apply_random_around_square( discharge_monsters, you.x_pos, you.y_pos, - true, pow, num_targs ); - -#if DEBUG_DIAGNOSTICS - snprintf( info, INFO_SIZE, "Arcs: %d Damage: %d", num_targs, dam ); - mpr( info, MSGCH_DIAGNOSTICS ); -#endif - - if (dam == 0) - { - if (coinflip()) - mpr("The air around you crackles with electrical energy."); - else - { - bool plural = coinflip(); - snprintf( info, INFO_SIZE, "%s blue arc%s ground%s harmlessly %s you.", - plural ? "Some" : "A", - plural ? "s" : "", - plural ? " themselves" : "s itself", - plural ? "around" : (coinflip() ? "beside" : - coinflip() ? "behind" : "before") - ); - - mpr(info); - } - } -} // end cast_discharge() - -// NB: this must be checked against the same effects -// in fight.cc for all forms of attack !!! {dlb} -// This function should be currently unused (the effect is too powerful) -static int distortion_monsters(int x, int y, int pow, int message) -{ - int specdam = 0; - int monster_attacked = mgrd[x][y]; - - if (monster_attacked == NON_MONSTER) - return 0; - - struct monsters *defender = &menv[monster_attacked]; - - if (pow > 100) - pow = 100; - - if (x == you.x_pos && y == you.y_pos) - { - if (you.skills[SK_TRANSLOCATIONS] < random2(8)) - { - miscast_effect( SPTYP_TRANSLOCATION, pow / 9 + 1, pow, 100, - "a distortion effect" ); - } - else - { - miscast_effect( SPTYP_TRANSLOCATION, 1, 1, 100, - "a distortion effect" ); - } - - return 1; - } - - if (defender->type == MONS_BLINK_FROG) // any others resist? - { - int hp = defender->hit_points; - int max_hp = defender->max_hit_points; - - mpr("The blink frog basks in the translocular energy."); - - if (hp < max_hp) - hp += 1 + random2(1 + pow / 4) + random2(1 + pow / 7); - - if (hp > max_hp) - hp = max_hp; - - defender->hit_points = hp; - return 1; - } - else if (coinflip()) - { - strcpy(info, "Space bends around "); - strcat(info, ptr_monam(defender, DESC_NOCAP_THE)); - strcat(info, "."); - mpr(info); - specdam += 1 + random2avg( 7, 2 ) + random2(pow) / 40; - } - else if (coinflip()) - { - strcpy(info, "Space warps horribly around "); - strcat(info, ptr_monam( defender, DESC_NOCAP_THE )); - strcat(info, "!"); - mpr(info); - - specdam += 3 + random2avg( 12, 2 ) + random2(pow) / 25; - } - else if (one_chance_in(3)) - { - monster_blink(defender); - return 1; - } - else if (one_chance_in(3)) - { - monster_teleport(defender, coinflip()); - return 1; - } - else if (one_chance_in(3)) - { - monster_die(defender, KILL_RESET, 0); - return 1; - } - else if (message) - { - mpr("Nothing seems to happen."); - return 1; - } - - player_hurt_monster(monster_attacked, specdam); - - return (specdam); -} // end distortion_monsters() - -void cast_bend(int pow) -{ - apply_one_neighbouring_square( distortion_monsters, pow ); -} // end cast_bend() - -// Really this is just applying the best of Band/Warp weapon/Warp field -// into a spell that gives the "make monsters go away" benefit without -// the insane damage potential. -- bwr -int disperse_monsters(int x, int y, int pow, int message) -{ - UNUSED( message ); - - const int monster_attacked = mgrd[x][y]; - - if (monster_attacked == NON_MONSTER) - return 0; - - struct monsters *defender = &menv[monster_attacked]; - - if (defender->type == MONS_BLINK_FROG) - { - simple_monster_message(defender, " resists."); - return 1; - } - else if (check_mons_resist_magic(defender, pow)) - { - if (coinflip()) - { - simple_monster_message(defender, " partially resists."); - monster_blink(defender); - } - else - simple_monster_message(defender, " resists."); - - return 1; - } - else - { - monster_teleport( defender, true ); - return 1; - } - - return 0; -} - -void cast_dispersal(int pow) -{ - if (apply_area_around_square( disperse_monsters, - you.x_pos, you.y_pos, pow ) == 0) - { - mpr( "There is a brief shimmering in the air around you." ); - } -} - -static int spell_swap_func(int x, int y, int pow, int message) -{ - UNUSED( message ); - - int monster_attacked = mgrd[x][y]; - - if (monster_attacked == NON_MONSTER) - return 0; - - struct monsters *defender = &menv[monster_attacked]; - - if (defender->type == MONS_BLINK_FROG - || check_mons_resist_magic( defender, pow )) - { - simple_monster_message( defender, " resists." ); - } - else - { - // Swap doesn't seem to actually swap, but just sets the - // monster's location equal to the players... this being because - // the acr.cc call is going to move the player afterwards (for - // the regular friendly monster swap). So we'll go through - // standard swap procedure here... since we really want to apply - // the same swap_places function as with friendly monsters... - // see note over there. -- bwr - int old_x = defender->x; - int old_y = defender->y; - - if (swap_places( defender )) - { - you.x_pos = old_x; - you.y_pos = old_y; - } - } - - return 1; -} - -void cast_swap(int pow) -{ - apply_one_neighbouring_square( spell_swap_func, pow ); -} - -static int make_a_rot_cloud(int x, int y, int pow, int ctype) -{ - int next = 0, obj = mgrd[x][y]; - - if (obj == NON_MONSTER) - return 0; - - while (obj != NON_ITEM) - { - next = mitm[obj].link; - - if (mitm[obj].base_type == OBJ_CORPSES - && mitm[obj].sub_type == CORPSE_BODY) - { - if (!mons_skeleton(mitm[obj].plus)) - destroy_item(obj); - else - { - mitm[obj].sub_type = CORPSE_SKELETON; - mitm[obj].special = 200; - mitm[obj].colour = LIGHTGREY; - } - - place_cloud(ctype, x, y, - (3 + random2(pow / 4) + random2(pow / 4) + - random2(pow / 4))); - return 1; - } - - obj = next; - } - - return 0; -} // end make_a_rot_cloud() - -int make_a_normal_cloud(int x, int y, int pow, int ctype) -{ - place_cloud( ctype, x, y, - (3 + random2(pow / 4) + random2(pow / 4) + random2(pow / 4)) ); - - return 1; -} // end make_a_normal_cloud() - -#if 0 - -static int make_a_random_cloud(int x, int y, int pow, int ctype) -{ - if (ctype == CLOUD_NONE) - ctype = CLOUD_BLACK_SMOKE; - - unsigned char cloud_material; - - switch (random2(9)) - { - case 0: - cloud_material = CLOUD_FIRE; - break; - case 1: - cloud_material = CLOUD_STINK; - break; - case 2: - cloud_material = CLOUD_COLD; - break; - case 3: - cloud_material = CLOUD_POISON; - break; - case 4: - cloud_material = CLOUD_BLUE_SMOKE; - break; - case 5: - cloud_material = CLOUD_STEAM; - break; - case 6: - cloud_material = CLOUD_PURP_SMOKE; - break; - default: - cloud_material = ctype; - break; - } - - // that last bit is equivalent to "random2(pow/4) + random2(pow/4) - // + random2(pow/4)" {dlb} - // can you see the pattern? {dlb} - place_cloud(cloud_material, x, y, 3 + random2avg(3 * (pow / 4) - 2, 3)); - - return 1; -} // end make_a_random_cloud() - -#endif - -static int passwall(int x, int y, int pow, int garbage) -{ - UNUSED( garbage ); - - char dx, dy, nx = x, ny = y; - int howdeep = 0; - bool done = false; - int shallow = 1 + (you.skills[SK_EARTH_MAGIC] / 8); - - // allow statues as entry points? - if (grd[x][y] != DNGN_ROCK_WALL) - // Irony: you can start on a secret door but not a door. - // Worked stone walls are out, they're not diggable and - // are used for impassable walls... I'm not sure we should - // even allow statues (should be contiguous rock) -- bwr - { - mpr("That's not a passable wall."); - return 0; - } - - dx = x - you.x_pos; - dy = y - you.y_pos; - - while (!done) - { - // I'm trying to figure proper borders out {dlb} - // FIXME: dungeon border? - if (nx > (GXM - 1) || ny > (GYM - 1) || nx < 2 || ny < 2) - { - mpr("You sense an overwhelming volume of rock."); - return 0; - } - - switch (grd[nx][ny]) - { - default: - done = true; - break; - case DNGN_ROCK_WALL: - case DNGN_ORCISH_IDOL: - case DNGN_GRANITE_STATUE: - case DNGN_SECRET_DOOR: - nx += dx; - ny += dy; - howdeep++; - break; - } - } - - int range = shallow + random2(pow) / 25; - - if (howdeep > shallow) - { - mpr("This rock feels deep."); - - if (yesno("Try anyway?")) - { - if (howdeep > range) - { - ouch(1 + you.hp, 0, KILLED_BY_PETRIFICATION); - //jmf: not return; if wizard, successful transport is option - } - } - else - { - if (one_chance_in(30)) - mpr("Wuss."); - else - canned_msg(MSG_OK); - return 1; - } - } - - // Note that the delay was (1 + howdeep * 2), but now that the - // delay is stopped when the player is attacked it can be much - // shorter since its harder to use for quick escapes. -- bwr - start_delay( DELAY_PASSWALL, 2 + howdeep, nx, ny ); - - return 1; -} // end passwall() - -void cast_passwall(int pow) -{ - apply_one_neighbouring_square(passwall, pow); -} // end cast_passwall() - -static int intoxicate_monsters(int x, int y, int pow, int garbage) -{ - UNUSED( pow ); - UNUSED( garbage ); - - int mon = mgrd[x][y]; - - if (mon == NON_MONSTER) - return 0; - if (mons_intel(menv[mon].type) < I_NORMAL) - return 0; - if (mons_holiness(&menv[mon]) != MH_NATURAL) - return 0; - if (mons_res_poison(&menv[mon]) > 0) - return 0; - - mons_add_ench(&menv[mon], ENCH_CONFUSION); - return 1; -} // end intoxicate_monsters() - -void cast_intoxicate(int pow) -{ - potion_effect( POT_CONFUSION, 10 + (100 - pow) / 10); - - if (one_chance_in(20) && lose_stat( STAT_INTELLIGENCE, 1 + random2(3) )) - mpr("Your head spins!"); - - apply_area_visible(intoxicate_monsters, pow); -} // end cast_intoxicate() - -// intended as a high-level Elven (a)bility -static int glamour_monsters(int x, int y, int pow, int garbage) -{ - UNUSED( garbage ); - - int mon = mgrd[x][y]; - - // Power in this function is already limited by a function of - // experience level (10 + level / 2) since it's only an ability, - // never an actual spell. -- bwr - - if (mon == NON_MONSTER) - return (0); - - if (one_chance_in(5)) - return (0); - - if (mons_intel(menv[mon].type) < I_NORMAL) - return (0); - - if (mons_class_holiness(mon) != MH_NATURAL) - return (0); - - if (!mons_is_humanoid( menv[mon].type )) - return (0); - - const char show_char = mons_char( menv[mon].type ); - - // gargoyles are immune. - if (menv[mon].type == MONS_GARGOYLE - || menv[mon].type == MONS_METAL_GARGOYLE - || menv[mon].type == MONS_MOLTEN_GARGOYLE) - { - return (0); - } - - // orcs resist thru hatred of elves - // elves resist cause they're elves - // boggarts are malevolent highly magical wee-folk - if (show_char == 'o' || show_char == 'e' || menv[mon].type == MONS_BOGGART) - pow = (pow / 2) + 1; - - if (check_mons_resist_magic(&menv[mon], pow)) - return (0); - - switch (random2(6)) - { - case 0: - mons_add_ench(&menv[mon], ENCH_FEAR); - break; - case 1: - case 4: - mons_add_ench(&menv[mon], ENCH_CONFUSION); - break; - case 2: - case 5: - mons_add_ench(&menv[mon], ENCH_CHARM); - break; - case 3: - menv[mon].behaviour = BEH_SLEEP; - break; - } - - // why no, there's no message as to which effect happened >:^) - if (!one_chance_in(4)) - { - strcpy(info, ptr_monam( &(menv[mon]), DESC_CAP_THE)); - - switch (random2(4)) - { - case 0: - strcat(info, " looks dazed."); - break; - case 1: - strcat(info, " blinks several times."); - break; - case 2: - strcat(info, " rubs its eye"); - if (menv[mon].type != MONS_CYCLOPS) - strcat(info, "s"); - strcat(info, "."); - break; - case 4: - strcat(info, " tilts its head."); - break; - } - - mpr(info); - } - - return (1); -} // end glamour_monsters() - -void cast_glamour(int pow) -{ - apply_area_visible(glamour_monsters, pow); -} // end cast_glamour() - -bool backlight_monsters(int x, int y, int pow, int garbage) -{ - UNUSED( pow ); - UNUSED( garbage ); - - int mon = mgrd[x][y]; - - if (mon == NON_MONSTER) - return (false); - - switch (menv[mon].type) - { - //case MONS_INSUBSTANTIAL_WISP: //jmf: I'm not sure if these glow or not - //case MONS_VAPOUR: - case MONS_UNSEEN_HORROR: // consider making this visible? probably not. - return (false); - - case MONS_FIRE_VORTEX: - case MONS_ANGEL: - case MONS_FIEND: - case MONS_SHADOW: - case MONS_EFREET: - case MONS_HELLION: - case MONS_GLOWING_SHAPESHIFTER: - case MONS_FIRE_ELEMENTAL: - case MONS_AIR_ELEMENTAL: - case MONS_SHADOW_FIEND: - case MONS_SPECTRAL_WARRIOR: - case MONS_ORANGE_RAT: - case MONS_BALRUG: - case MONS_SPATIAL_VORTEX: - case MONS_PIT_FIEND: - case MONS_SHINING_EYE: - case MONS_DAEVA: - case MONS_SPECTRAL_THING: - case MONS_ORB_OF_FIRE: - case MONS_EYE_OF_DEVASTATION: - return (false); // already glowing or invisible - default: - break; - } - - int lvl = mons_has_ench( &menv[mon], ENCH_BACKLIGHT_I, ENCH_BACKLIGHT_IV ); - - if (lvl == ENCH_NONE) - simple_monster_message( &menv[mon], " is outlined in light." ); - else if (lvl == ENCH_BACKLIGHT_IV) - simple_monster_message( &menv[mon], " glows brighter for a moment." ); - else - { - // remove old level - mons_del_ench( &menv[mon], ENCH_BACKLIGHT_I, ENCH_BACKLIGHT_III, true ); - simple_monster_message( &menv[mon], " glows brighter." ); - } - - // this enchantment wipes out invisibility (neat) - mons_del_ench( &menv[mon], ENCH_INVIS ); - mons_add_ench( &menv[mon], ENCH_BACKLIGHT_IV ); - - return (true); -} // end backlight_monsters() - -void cast_evaporate(int pow) -{ - // experimenting with allowing the potion to be thrown... we're - // still making it have to be "in hands" at this point. -- bwr - struct dist spelld; - struct bolt beem; - - const int potion = prompt_invent_item( "Throw which potion?", OBJ_POTIONS ); - - if (potion == -1) - { - snprintf( info, INFO_SIZE, "Wisps of steam play over your %s!", - your_hand(true) ); - - mpr(info); - return; - } - else if (you.inv[potion].base_type != OBJ_POTIONS) - { - mpr( "This spell works only on potions!" ); - canned_msg(MSG_SPELL_FIZZLES); - return; - } - - mpr( STD_DIRECTION_PROMPT, MSGCH_PROMPT ); - - message_current_target(); - - direction( spelld, DIR_NONE, TARG_ENEMY ); - - if (!spelld.isValid) - { - canned_msg(MSG_SPELL_FIZZLES); - return; - } - - beem.target_x = spelld.tx; - beem.target_y = spelld.ty; - - beem.source_x = you.x_pos; - beem.source_y = you.y_pos; - - strcpy( beem.beam_name, "potion" ); - beem.colour = you.inv[potion].colour; - beem.range = 9; - beem.rangeMax = 9; - beem.type = SYM_FLASK; - beem.beam_source = MHITYOU; - beem.thrower = KILL_YOU_MISSILE; - beem.aux_source = NULL; - beem.is_beam = false; - beem.is_tracer = false; - - beem.hit = you.dex / 2 + roll_dice( 2, you.skills[SK_RANGED_COMBAT] / 2 + 1 ); - beem.damage = dice_def( 1, 0 ); // no damage, just producing clouds - beem.ench_power = pow; // used for duration only? - - beem.flavour = BEAM_POTION_STINKING_CLOUD; - - switch (you.inv[potion].sub_type) - { - case POT_STRONG_POISON: - beem.flavour = BEAM_POTION_POISON; - beem.ench_power *= 2; - break; - - case POT_DEGENERATION: - beem.flavour = (coinflip() ? BEAM_POTION_POISON : BEAM_POTION_MIASMA); - beem.ench_power *= 2; - break; - - case POT_POISON: - beem.flavour = BEAM_POTION_POISON; - break; - - case POT_DECAY: - beem.flavour = BEAM_POTION_MIASMA; - beem.ench_power *= 2; - break; - - case POT_PARALYSIS: - beem.ench_power *= 2; - // fall through - case POT_CONFUSION: - case POT_SLOWING: - beem.flavour = BEAM_POTION_STINKING_CLOUD; - break; - - case POT_WATER: - case POT_PORRIDGE: - beem.flavour = BEAM_POTION_STEAM; - break; - - case POT_BERSERK_RAGE: - beem.flavour = (coinflip() ? BEAM_POTION_FIRE : BEAM_POTION_STEAM); - break; - - case POT_MUTATION: - case POT_GAIN_STRENGTH: - case POT_GAIN_DEXTERITY: - case POT_GAIN_INTELLIGENCE: - case POT_EXPERIENCE: - case POT_MAGIC: - switch (random2(5)) - { - case 0: beem.flavour = BEAM_POTION_FIRE; break; - case 1: beem.flavour = BEAM_POTION_COLD; break; - case 2: beem.flavour = BEAM_POTION_POISON; break; - case 3: beem.flavour = BEAM_POTION_MIASMA; break; - default: beem.flavour = BEAM_POTION_RANDOM; break; - } - break; - - default: - switch (random2(12)) - { - case 0: beem.flavour = BEAM_POTION_FIRE; break; - case 1: beem.flavour = BEAM_POTION_STINKING_CLOUD; break; - case 2: beem.flavour = BEAM_POTION_COLD; break; - case 3: beem.flavour = BEAM_POTION_POISON; break; - case 4: beem.flavour = BEAM_POTION_RANDOM; break; - case 5: beem.flavour = BEAM_POTION_BLUE_SMOKE; break; - case 6: beem.flavour = BEAM_POTION_BLACK_SMOKE; break; - case 7: beem.flavour = BEAM_POTION_PURP_SMOKE; break; - default: beem.flavour = BEAM_POTION_STEAM; break; - } - break; - } - - if (coinflip()) - exercise( SK_RANGED_COMBAT, 1 ); - - fire_beam(beem); - - // both old and new code use up a potion: - dec_inv_item_quantity( potion, 1 ); - - return; -} // end cast_evaporate() - -// The intent of this spell isn't to produce helpful potions -// for drinking, but rather to provide ammo for the Evaporate -// spell out of corpses, thus potentially making it useful. -// Producing helpful potions would break game balance here... -// and producing more than one potion from a corpse, or not -// using up the corpse might also lead to game balance problems. -- bwr -void cast_fulsome_distillation( int powc ) -{ - char str_pass[ ITEMNAME_SIZE ]; - - if (powc > 50) - powc = 50; - - int corpse = -1; - - // Search items at the players location for corpses. - // XXX: Turn this into a separate function and merge with - // the messes over in butchery, animating, and maybe even - // item pickup from stacks (which would make it easier to - // create a floor stack menu system later) -- bwr - for (int curr_item = igrd[you.x_pos][you.y_pos]; - curr_item != NON_ITEM; - curr_item = mitm[curr_item].link) - { - if (mitm[curr_item].base_type == OBJ_CORPSES - && mitm[curr_item].sub_type == CORPSE_BODY) - { - it_name( curr_item, DESC_NOCAP_THE, str_pass ); - snprintf( info, INFO_SIZE, "Distill a potion from %s?", str_pass ); - - if (yesno( info, true, 0, false )) - { - corpse = curr_item; - break; - } - } - } - - if (corpse == -1) - { - canned_msg(MSG_SPELL_FIZZLES); - return; - } - - const bool rotten = (mitm[corpse].special < 100); - const bool big_monster = (mons_type_hit_dice( mitm[corpse].plus ) >= 5); - const bool power_up = (rotten && big_monster); - - int potion_type = POT_WATER; - - switch (mitm[corpse].plus) - { - case MONS_GIANT_BAT: // extracting batty behaviour : 1 - case MONS_UNSEEN_HORROR: // extracting batty behaviour : 7 - case MONS_GIANT_BLOWFLY: // extracting batty behaviour : 5 - potion_type = POT_CONFUSION; - break; - - case MONS_RED_WASP: // paralysis attack : 8 - case MONS_YELLOW_WASP: // paralysis attack : 4 - potion_type = POT_PARALYSIS; - break; - - case MONS_SNAKE: // clean meat, but poisonous attack : 2 - case MONS_GIANT_ANT: // clean meat, but poisonous attack : 3 - potion_type = (power_up ? POT_POISON : POT_CONFUSION); - break; - - case MONS_ORANGE_RAT: // poisonous meat, but draining attack : 3 - potion_type = (power_up ? POT_DECAY : POT_POISON); - break; - - case MONS_SPINY_WORM: // 12 - potion_type = (power_up ? POT_DECAY : POT_STRONG_POISON); - break; - - default: - switch (mons_corpse_effect( mitm[corpse].plus )) - { - case CE_CLEAN: - potion_type = (power_up ? POT_CONFUSION : POT_WATER); - break; - - case CE_CONTAMINATED: - potion_type = (power_up ? POT_DEGENERATION : POT_POISON); - break; - - case CE_POISONOUS: - potion_type = (power_up ? POT_STRONG_POISON : POT_POISON); - break; - - case CE_MUTAGEN_RANDOM: - case CE_MUTAGEN_GOOD: // unused - case CE_RANDOM: // unused - potion_type = POT_MUTATION; - break; - - case CE_MUTAGEN_BAD: // unused - case CE_ROTTEN: // actually this only occurs via mangling - case CE_HCL: // necrophage - potion_type = (power_up ? POT_DECAY : POT_STRONG_POISON); - break; - - case CE_NOCORPSE: // shouldn't occur - default: - break; - } - break; - } - - // If not powerful enough, we downgrade the potion - if (random2(50) > powc + 10 * rotten) - { - switch (potion_type) - { - case POT_DECAY: - case POT_DEGENERATION: - case POT_STRONG_POISON: - potion_type = POT_POISON; - break; - - case POT_MUTATION: - case POT_POISON: - potion_type = POT_CONFUSION; - break; - - case POT_PARALYSIS: - potion_type = POT_SLOWING; - break; - - case POT_CONFUSION: - case POT_SLOWING: - default: - potion_type = POT_WATER; - break; - } - } - - // We borrow the corpse's object to make our potion: - mitm[corpse].base_type = OBJ_POTIONS; - mitm[corpse].sub_type = potion_type; - mitm[corpse].quantity = 1; - mitm[corpse].plus = 0; - mitm[corpse].plus2 = 0; - item_colour( mitm[corpse] ); // sets special as well - - it_name( corpse, DESC_NOCAP_A, str_pass ); - snprintf( info, INFO_SIZE, "You extract %s from the corpse.", - str_pass ); - mpr( info ); - - // try to move the potion to the player (for convenience) - if (move_item_to_player( corpse, 1 ) != 1) - { - mpr( "Unfortunately, you can't carry it right now!" ); - } -} - -void make_shuggoth(int x, int y, int hp) -{ - int mon = create_monster( MONS_SHUGGOTH, 100 + random2avg(58, 3), - BEH_HOSTILE, x, y, MHITNOT, 250 ); - - if (mon != -1) - { - menv[mon].hit_points = hp; - menv[mon].max_hit_points = hp; - } - - return; -} // end make_shuggoth() - -static int rot_living(int x, int y, int pow, int message) -{ - UNUSED( message ); - - int mon = mgrd[x][y]; - int ench; - - if (mon == NON_MONSTER) - return 0; - - if (mons_holiness(&menv[mon]) != MH_NATURAL) - return 0; - - if (check_mons_resist_magic(&menv[mon], pow)) - return 0; - - ench = ((random2(pow) + random2(pow) + random2(pow) + random2(pow)) / 4); - - if (ench >= 50) - ench = ENCH_YOUR_ROT_IV; - else if (ench >= 35) - ench = ENCH_YOUR_ROT_III; - else if (ench >= 20) - ench = ENCH_YOUR_ROT_II; - else - ench = ENCH_YOUR_ROT_I; - - mons_add_ench(&menv[mon], ench); - - return 1; -} // end rot_living() - -static int rot_undead(int x, int y, int pow, int garbage) -{ - UNUSED( garbage ); - - int mon = mgrd[x][y]; - int ench; - - if (mon == NON_MONSTER) - return 0; - - if (mons_holiness(&menv[mon]) != MH_UNDEAD) - return 0; - - if (check_mons_resist_magic(&menv[mon], pow)) - return 0; - - // this does not make sense -- player mummies are - // immune to rotting (or have been) -- so what is - // the schema in use here to determine rotting??? {dlb} - - //jmf: up for discussion. it is clearly unfair to - // rot player mummies. - // the `shcema' here is: corporeal non-player undead - // rot, discorporeal undead don't rot. if you wanna - // insist that monsters get the same treatment as - // players, I demand my player mummies get to worship - // the evil mummy & orc god. - switch (menv[mon].type) - { - case MONS_NECROPHAGE: - case MONS_ZOMBIE_SMALL: - case MONS_LICH: - case MONS_MUMMY: - case MONS_VAMPIRE: - case MONS_ZOMBIE_LARGE: - case MONS_WIGHT: - case MONS_GHOUL: - case MONS_BORIS: - case MONS_ANCIENT_LICH: - case MONS_VAMPIRE_KNIGHT: - case MONS_VAMPIRE_MAGE: - case MONS_GUARDIAN_MUMMY: - case MONS_GREATER_MUMMY: - case MONS_MUMMY_PRIEST: - break; - case MONS_ROTTING_HULK: - default: - return 0; // immune (no flesh) or already rotting - } - - ench = ((random2(pow) + random2(pow) + random2(pow) + random2(pow)) / 4); - - if (ench >= 50) - ench = ENCH_YOUR_ROT_IV; - else if (ench >= 35) - ench = ENCH_YOUR_ROT_III; - else if (ench >= 20) - ench = ENCH_YOUR_ROT_II; - else - ench = ENCH_YOUR_ROT_I; - - mons_add_ench(&menv[mon], ench); - - return 1; -} // end rot_undead() - -static int rot_corpses(int x, int y, int pow, int garbage) -{ - UNUSED( garbage ); - - return make_a_rot_cloud(x, y, pow, CLOUD_MIASMA); -} // end rot_corpses() - -void cast_rotting(int pow) -{ - apply_area_visible(rot_living, pow); - apply_area_visible(rot_undead, pow); - apply_area_visible(rot_corpses, pow); - return; -} // end cast_rotting() - -void do_monster_rot(int mon) -{ - int damage = 1 + random2(3); - - if (mons_holiness(&menv[mon]) == MH_UNDEAD && random2(5)) - { - apply_area_cloud(make_a_normal_cloud, menv[mon].x, menv[mon].y, - 10, 1, CLOUD_MIASMA); - } - - player_hurt_monster( mon, damage ); - return; -} // end do_monster_rot() - -static int snake_charm_monsters(int x, int y, int pow, int message) -{ - UNUSED( message ); - - int mon = mgrd[x][y]; - - if (mon == NON_MONSTER) return 0; - if (mons_friendly(&menv[mon])) return 0; - if (one_chance_in(4)) return 0; - if (mons_char(menv[mon].type) != 'S') return 0; - if (check_mons_resist_magic(&menv[mon], pow)) return 0; - - menv[mon].attitude = ATT_FRIENDLY; - snprintf( info, INFO_SIZE, "%s sways back and forth.", ptr_monam( &(menv[mon]), DESC_CAP_THE )); - mpr(info); - - return 1; -} - -void cast_snake_charm(int pow) -{ - // powc = (you.experience_level * 2) + (you.skills[SK_INVOCATIONS] * 3); - apply_one_neighbouring_square(snake_charm_monsters, pow); -} - -void cast_fragmentation(int pow) // jmf: ripped idea from airstrike -{ - struct dist beam; - struct bolt blast; - int debris = 0; - int trap; - bool explode = false; - bool hole = true; - const char *what = NULL; - - mpr("Fragment what (e.g. a wall)?", MSGCH_PROMPT); - direction( beam, DIR_TARGET, TARG_ENEMY ); - - if (!beam.isValid) - { - canned_msg(MSG_SPELL_FIZZLES); - return; - } - - //FIXME: if (player typed '>' to attack floor) goto do_terrain; - blast.beam_source = MHITYOU; - blast.thrower = KILL_YOU; - blast.aux_source = NULL; - blast.ex_size = 1; // default - blast.type = '#'; - blast.colour = 0; - blast.target_x = beam.tx; - blast.target_y = beam.ty; - blast.is_tracer = false; - blast.flavour = BEAM_FRAG; - - // Number of dice vary... 3 is easy/common, but it can get as high as 6. - blast.damage = dice_def( 0, 5 + pow / 10 ); - - const int grid = grd[beam.tx][beam.ty]; - const int mon = mgrd[beam.tx][beam.ty]; - - const bool okay_to_dest = ((beam.tx > 5 && beam.tx < GXM - 5) - && (beam.ty > 5 && beam.ty < GYM - 5)); - - if (mon != NON_MONSTER) - { - // This needs its own hand_buff... we also need to do it first - // in case the target dies. -- bwr - char explode_msg[80]; - - snprintf( explode_msg, sizeof( explode_msg ), "%s explodes!", - ptr_monam( &(menv[mon]), DESC_CAP_THE ) ); - - switch (menv[mon].type) - { - case MONS_ICE_BEAST: // blast of ice fragments - case MONS_SIMULACRUM_SMALL: - case MONS_SIMULACRUM_LARGE: - explode = true; - strcpy(blast.beam_name, "icy blast"); - blast.colour = WHITE; - blast.damage.num = 2; - blast.flavour = BEAM_ICE; - if (player_hurt_monster(mon, roll_dice( blast.damage ))) - blast.damage.num += 1; - break; - - case MONS_FLYING_SKULL: - case MONS_SKELETON_SMALL: - case MONS_SKELETON_LARGE: // blast of bone - explode = true; - - snprintf( info, INFO_SIZE, "The sk%s explodes into sharp fragments of bone!", - (menv[mon].type == MONS_FLYING_SKULL) ? "ull" : "eleton"); - - strcpy(blast.beam_name, "blast of bone shards"); - - blast.colour = LIGHTGREY; - - if (random2(50) < (pow / 5)) // potential insta-kill - { - monster_die(&menv[mon], KILL_YOU, 0); - blast.damage.num = 4; - } - else - { - blast.damage.num = 2; - if (player_hurt_monster(mon, roll_dice( blast.damage ))) - blast.damage.num = 4; - } - goto all_done; // i.e. no "Foo Explodes!" - - case MONS_WOOD_GOLEM: - explode = false; - simple_monster_message(&menv[mon], " shudders violently!"); - - // We use blast.damage not only for inflicting damage here, - // but so that later on we'll know that the spell didn't - // fizzle (since we don't actually explode wood golems). -- bwr - blast.damage.num = 2; - player_hurt_monster( mon, roll_dice( blast.damage ) ); - break; - - case MONS_IRON_GOLEM: - case MONS_METAL_GARGOYLE: - explode = true; - strcpy( blast.beam_name, "blast of metal fragments" ); - blast.colour = CYAN; - blast.damage.num = 4; - if (player_hurt_monster(mon, roll_dice( blast.damage ))) - blast.damage.num += 2; - break; - - case MONS_CLAY_GOLEM: // assume baked clay and not wet loam - case MONS_STONE_GOLEM: - case MONS_EARTH_ELEMENTAL: - case MONS_GARGOYLE: - explode = true; - blast.ex_size = 2; - strcpy(blast.beam_name, "blast of rock fragments"); - blast.colour = BROWN; - blast.damage.num = 3; - if (player_hurt_monster(mon, roll_dice( blast.damage ))) - blast.damage.num += 1; - break; - - case MONS_CRYSTAL_GOLEM: - explode = true; - blast.ex_size = 2; - strcpy(blast.beam_name, "blast of crystal shards"); - blast.colour = WHITE; - blast.damage.num = 4; - if (player_hurt_monster(mon, roll_dice( blast.damage ))) - blast.damage.num += 2; - break; - - default: - blast.damage.num = 1; // to mark that a monster was targetted - - // Yes, this spell does lousy damage if the - // monster isn't susceptable. -- bwr - player_hurt_monster( mon, roll_dice( 1, 5 + pow / 25 ) ); - goto do_terrain; - } - - mpr( explode_msg ); - goto all_done; - } - - do_terrain: - // FIXME: do nothing in Abyss & Pandemonium? - - switch (grid) - { - // - // Stone and rock terrain - // - case DNGN_ROCK_WALL: - case DNGN_SECRET_DOOR: - blast.colour = env.rock_colour; - // fall-through - case DNGN_STONE_WALL: - what = "wall"; - if (player_in_branch( BRANCH_HALL_OF_ZOT )) - blast.colour = env.rock_colour; - // fall-through - case DNGN_ORCISH_IDOL: - if (what == NULL) - what = "stone idol"; - if (blast.colour == 0) - blast.colour = DARKGREY; - // fall-through - case DNGN_GRANITE_STATUE: // normal rock -- big explosion - if (what == NULL) - what = "statue"; - - explode = true; - - strcpy(blast.beam_name, "blast of rock fragments"); - blast.damage.num = 3; - if (blast.colour == 0) - blast.colour = LIGHTGREY; - - if (okay_to_dest - && (grid == DNGN_ORCISH_IDOL - || grid == DNGN_GRANITE_STATUE - || (pow >= 40 && grid == DNGN_ROCK_WALL && one_chance_in(3)) - || (pow >= 60 && grid == DNGN_STONE_WALL && one_chance_in(10)))) - { - // terrain blew up real good: - blast.ex_size = 2; - grd[beam.tx][beam.ty] = DNGN_FLOOR; - debris = DEBRIS_ROCK; - } - break; - - // - // Metal -- small but nasty explosion - // - - case DNGN_METAL_WALL: - what = "metal wall"; - blast.colour = CYAN; - // fallthru - case DNGN_SILVER_STATUE: - if (what == NULL) - { - what = "silver statue"; - blast.colour = WHITE; - } - - explode = true; - strcpy( blast.beam_name, "blast of metal fragments" ); - blast.damage.num = 4; - - if (okay_to_dest && pow >= 80 && random2(500) < pow / 5) - { - blast.damage.num += 2; - grd[beam.tx][beam.ty] = DNGN_FLOOR; - debris = DEBRIS_METAL; - } - break; - - // - // Crystal - // - - case DNGN_GREEN_CRYSTAL_WALL: // crystal -- large & nasty explosion - what = "crystal wall"; - blast.colour = GREEN; - // fallthru - case DNGN_ORANGE_CRYSTAL_STATUE: - if (what == NULL) - { - what = "crystal statue"; - blast.colour = LIGHTRED; //jmf: == orange, right? - } - - explode = true; - blast.ex_size = 2; - strcpy(blast.beam_name, "blast of crystal shards"); - blast.damage.num = 5; - - if (okay_to_dest - && ((grid == DNGN_GREEN_CRYSTAL_WALL && coinflip()) - || (grid == DNGN_ORANGE_CRYSTAL_STATUE - && pow >= 50 && one_chance_in(10)))) - { - blast.ex_size = coinflip() ? 3 : 2; - grd[beam.tx][beam.ty] = DNGN_FLOOR; - debris = DEBRIS_CRYSTAL; - } - break; - - // - // Traps - // - - case DNGN_UNDISCOVERED_TRAP: - case DNGN_TRAP_MECHANICAL: - trap = trap_at_xy( beam.tx, beam.ty ); - if (trap != -1 - && trap_category( env.trap[trap].type ) != DNGN_TRAP_MECHANICAL) - { - // non-mechanical traps don't explode with this spell -- bwr - break; - } - - // undiscovered traps appear as exploding from the floor -- bwr - what = ((grid == DNGN_UNDISCOVERED_TRAP) ? "floor" : "trap"); - - explode = true; - hole = false; // to hit monsters standing on traps - strcpy( blast.beam_name, "blast of fragments" ); - blast.colour = env.floor_colour; // in order to blend in - blast.damage.num = 2; - - // Exploded traps are nonfunctional, ammo is also ruined -- bwr - if (okay_to_dest) - { - grd[beam.tx][beam.ty] = DNGN_FLOOR; - env.trap[trap].type = TRAP_UNASSIGNED; - } - break; - - // - // Stone doors and arches - // - - case DNGN_OPEN_DOOR: - case DNGN_CLOSED_DOOR: - // Doors always blow up, stone arches never do (would cause problems) - if (okay_to_dest) - grd[beam.tx][beam.ty] = DNGN_FLOOR; - - // fall-through - case DNGN_STONE_ARCH: // floor -- small explosion - explode = true; - hole = false; // to hit monsters standing on doors - strcpy( blast.beam_name, "blast of rock fragments" ); - blast.colour = LIGHTGREY; - blast.damage.num = 2; - break; - - // - // Permarock and floor are unaffected -- bwr - // - case DNGN_PERMAROCK_WALL: - case DNGN_FLOOR: - explode = false; - snprintf( info, INFO_SIZE, "%s seems to be unnaturally hard.", - (grid == DNGN_PERMAROCK_WALL) ? "That wall" - : "The dungeon floor" ); - explode = false; - break; - - case DNGN_TRAP_III: // What are these? Should they explode? -- bwr - default: - // FIXME: cute message for water? - break; - } - - all_done: - if (explode && blast.damage.num > 0) - { - if (what != NULL) - { - snprintf( info, INFO_SIZE, "The %s explodes!", what); - mpr(info); - } - - explosion( blast, hole ); - } - else if (blast.damage.num == 0) - { - // if damage dice are zero we assume that nothing happened at all. - canned_msg(MSG_SPELL_FIZZLES); - } - - if (debris) - place_debris(beam.tx, beam.ty, debris); -} // end cast_fragmentation() - -void cast_twist(int pow) -{ - struct dist targ; - struct bolt tmp; // used, but ignored - - // level one power cap -- bwr - if (pow > 25) - pow = 25; - - // Get target, using DIR_TARGET for targetting only, - // since we don't use fire_beam() for this spell. - if (spell_direction(targ, tmp, DIR_TARGET) == -1) - return; - - const int mons = mgrd[ targ.tx ][ targ.ty ]; - - // anything there? - if (mons == NON_MONSTER || targ.isMe) - { - mpr("There is no monster there!"); - return; - } - - // Monster can magically save vs attack. - if (check_mons_resist_magic( &menv[ mons ], pow * 2 )) - { - simple_monster_message( &menv[ mons ], " resists." ); - return; - } - - // Roll the damage... this spell is pretty low on damage, because - // it can target any monster in LOS (high utility). This is - // similar to the damage done by Magic Dart (although, the - // distribution is much more uniform). -- bwr - int damage = 1 + random2( 3 + pow / 5 ); - - // Inflict the damage - player_hurt_monster( mons, damage ); - return; -} // end cast_twist() - -// -// This version of far strike is a bit too creative for level one, in -// order to make it work we needed to put a lot of restrictions on it -// (like the damage limitation), which wouldn't be necessary if it were -// a higher level spell. This code might come back as a high level -// translocation spell later (maybe even with special effects if it's -// using some of Josh's ideas about occasionally losing the weapon). -// Would potentially make a good high-level, second book Warper spell -// (since Translocations is a utility school, it should be higher level -// that usual... especially if it turns into a flavoured smiting spell). -// This can all wait until after the next release (as it would be better -// to have a proper way to do a single weapon strike here (you_attack -// does far more than we need or want here)). --bwr -// - -void cast_far_strike(int pow) -{ - struct dist targ; - struct bolt tmp; // used, but ignored - - // Get target, using DIR_TARGET for targetting only, - // since we don't use fire_beam() for this spell. - if (spell_direction(targ, tmp, DIR_TARGET) == -1) - return; - - // Get the target monster... - if (mgrd[targ.tx][targ.ty] == NON_MONSTER - || targ.isMe) - { - mpr("There is no monster there!"); - return; - } - - // Start with weapon base damage... - const int weapon = you.equip[ EQ_WEAPON ]; - - int damage = 3; // default unarmed damage - int speed = 10; // default unarmed time - - if (weapon != -1) // if not unarmed - { - // look up the damage base - if (you.inv[ weapon ].base_type == OBJ_WEAPONS) - { - damage = property( you.inv[ weapon ], PWPN_DAMAGE ); - speed = property( you.inv[ weapon ], PWPN_SPEED ); - - if (get_weapon_brand( you.inv[ weapon ] ) == SPWPN_SPEED) - { - speed *= 5; - speed /= 10; - } - } - else if (item_is_staff( you.inv[ weapon ] )) - { - damage = property( you.inv[ weapon ], PWPN_DAMAGE ); - speed = property( you.inv[ weapon ], PWPN_SPEED ); - } - } - - // Because we're casting a spell (and don't want to make this level - // one spell too good), we're not applying skill speed bonuses and at - // the very least guaranteeing one full turn (speed == 10) like the - // other spells (if any thing else related to speed is changed, at - // least leave this right before the application to you.time_taken). - // Leaving skill out of the speed bonus is an important part of - // keeping this spell from becoming a "better than actual melee" - // spell... although, it's fine if that's the case for early Warpers, - // Fighter types, and such that pick up this trivial first level spell, - // shouldn't be using it instead of melee (but rather as an accessory - // long range plinker). Therefore, we tone things down to try and - // guarantee that the spell is never begins to approach real combat - // (although the magic resistance check might end up with a higher - // hit rate than attacking against EV for high level Warpers). -- bwr - if (speed < 10) - speed = 10; - - you.time_taken *= speed; - you.time_taken /= 10; - - // Apply strength only to damage (since we're only interested in - // force here, not finesse... the dex/to-hit part of combat is - // instead handled via magical ability). This part could probably - // just be removed, as it's unlikely to make any real difference... - // if it is, the Warper stats in newgame.cc should be changed back - // to the standard 6 int-4 dex of spellcasters. -- bwr - int dammod = 78; - const int dam_stat_val = you.strength; - - if (dam_stat_val > 11) - dammod += (random2(dam_stat_val - 11) * 2); - else if (dam_stat_val < 9) - dammod -= (random2(9 - dam_stat_val) * 3); - - damage *= dammod; - damage /= 78; - - struct monsters *monster = &menv[ mgrd[targ.tx][targ.ty] ]; - - // apply monster's AC - if (monster->armour_class > 0) - damage -= random2( 1 + monster->armour_class ); - -#if 0 - // Removing damage limiter since it's categorized at level 4 right now. - - // Force transmitted is limited by skill... - const int limit = (you.skills[SK_TRANSLOCATIONS] + 1) / 2 + 3; - if (damage > limit) - damage = limit; -#endif - - // Roll the damage... - damage = 1 + random2( damage ); - - // Monster can magically save vs attack (this could be replaced or - // augmented with an EV check). - if (check_mons_resist_magic( monster, pow * 2 )) - { - simple_monster_message( monster, " resists." ); - return; - } - - // Inflict the damage - hurt_monster( monster, damage ); - if (monster->hit_points < 1) - monster_die( monster, KILL_YOU, 0 ); - else - print_wounds( monster ); - - return; -} // end cast_far_strike() - -void cast_apportation(int pow) -{ - struct dist beam; - - mpr("Pull items from where?"); - - direction( beam, DIR_TARGET ); - - if (!beam.isValid) - { - canned_msg(MSG_SPELL_FIZZLES); - return; - } - - // it's already here! - if (beam.isMe) - { - mpr( "That's just silly." ); - return; - } - - // Protect the player from destroying the item - const int grid = grd[ you.x_pos ][ you.y_pos ]; - - if (grid == DNGN_LAVA || grid == DNGN_DEEP_WATER) - { - mpr( "That would be silly while over this terrain!" ); - return; - } - - // If this is ever changed to allow moving objects that can't - // be seen, it should at least only allow moving from squares - // that have been phyisically (and maybe magically) seen and - // should probably have a range check as well. In these cases - // the spell should probably be upped to at least two, or three - // if magic mapped squares are allowed. Right now it's okay - // at one... it has a few uses, but you still have to get line - // of sight to the object first so it will only help a little - // with snatching runes or the orb (although it can be quite - // useful for getting items out of statue rooms or the abyss). -- bwr - if (!see_grid( beam.tx, beam.ty )) - { - mpr( "You cannot see there!" ); - return; - } - - // Let's look at the top item in that square... - const int item = igrd[ beam.tx ][ beam.ty ]; - if (item == NON_ITEM) - { - const int mon = mgrd[ beam.tx ][ beam.ty ]; - - if (mon == NON_MONSTER) - mpr( "There are no items there." ); - else if (mons_is_mimic( menv[ mon ].type )) - { - snprintf( info, INFO_SIZE, "%s twitches.", - ptr_monam( &(menv[ mon ]), DESC_CAP_THE ) ); - mpr( info ); - } - else - mpr( "This spell does not work on creatures." ); - - return; - } - - // mass of one unit - const int unit_mass = item_mass( mitm[ item ] ); - // assume we can pull everything - int max_units = mitm[ item ].quantity; - - // item has mass: might not move all of them - if (unit_mass > 0) - { - const int max_mass = pow * 30 + random2( pow * 20 ); - - // most units our power level will allow - max_units = max_mass / unit_mass; - } - - if (max_units <= 0) - { - mpr( "The mass is resisting your pull." ); - return; - } - - // Failure should never really happen after all the above checking, - // but we'll handle it anyways... - if (move_top_item( beam.tx, beam.ty, you.x_pos, you.y_pos )) - { - if (max_units < mitm[ item ].quantity) - { - mitm[ item ].quantity = max_units; - mpr( "You feel that some mass got lost in the cosmic void." ); - } - else - { - mpr( "Yoink!" ); - snprintf( info, INFO_SIZE, "You pull the item%s to yourself.", - (mitm[ item ].quantity > 1) ? "s" : "" ); - mpr( info ); - } - } - else - mpr( "The spell fails." ); -} - -void cast_sandblast(int pow) -{ - bool big = true; - struct dist spd; - struct bolt beam; - - // this type of power manipulation should be done with the others, - // currently over in it_use2.cc (ack) -- bwr - // int hurt = 2 + random2(5) + random2(4) + random2(pow) / 20; - - big = false; - - if (you.equip[EQ_WEAPON] != -1) - { - int wep = you.equip[EQ_WEAPON]; - if (you.inv[wep].base_type == OBJ_MISSILES - && (you.inv[wep].sub_type == MI_STONE || you.inv[wep].sub_type == MI_LARGE_ROCK)) - big = true; - } - - if (spell_direction(spd, beam) == -1) - return; - - if (spd.isMe) - { - canned_msg(MSG_UNTHINKING_ACT); - return; - } - - if (big) - { - dec_inv_item_quantity( you.equip[EQ_WEAPON], 1 ); - zapping(ZAP_SANDBLAST, pow, beam); - } - else - { - zapping(ZAP_SMALL_SANDBLAST, pow, beam); - } -} // end cast_sandblast() - -void cast_condensation_shield(int pow) -{ - if (you.equip[EQ_SHIELD] != -1 || you.fire_shield) - canned_msg(MSG_SPELL_FIZZLES); - else - { - if (you.duration[DUR_CONDENSATION_SHIELD] > 0) - you.duration[DUR_CONDENSATION_SHIELD] += 5 + roll_dice(2, 3); - else - { - mpr("A crackling disc of dense vapour forms in the air!"); - you.redraw_armour_class = 1; - - you.duration[DUR_CONDENSATION_SHIELD] = 10 + roll_dice(2, pow / 5); - } - - if (you.duration[DUR_CONDENSATION_SHIELD] > 30) - you.duration[DUR_CONDENSATION_SHIELD] = 30; - } - - return; -} // end cast_condensation_shield() - -static int quadrant_blink(int x, int y, int pow, int garbage) -{ - UNUSED( garbage ); - - if (x == you.x_pos && y == you.y_pos) - return (0); - - if (you.level_type == LEVEL_ABYSS) - { - abyss_teleport( false ); - you.pet_target = MHITNOT; - return (1); - } - - if (pow > 100) - pow = 100; - - // setup: Brent's new algorithm - // we are interested in two things: distance of a test point from - // the ideal 'line', and the distance of a test point from two - // actual points, one in the 'correct' direction and one in the - // 'incorrect' direction. - - // scale distance by 10 for more interesting numbers. - int l,m; // for line equation lx + my = 0 - l = (x - you.x_pos); - m = (you.y_pos - y); - - int tx, ty; // test x,y - int rx, ry; // x,y relative to you. - int sx, sy; // test point in the correct direction - int bx = x; // best x - int by = y; // best y - - int best_dist = 10000; - - sx = l; - sy = -m; - - // for each point (a,b), distance from the line is | la + mb | - - for(int tries = pow * pow / 500 + 1; tries > 0; tries--) - { - if (!random_near_space(you.x_pos, you.y_pos, tx, ty)) - return 0; - - rx = tx - you.x_pos; - ry = ty - you.y_pos; - - int dist = l * rx + m * ry; - dist *= 10 * dist; // square and multiply by 10 - - // check distance to test points - int dist1 = distance(rx, ry, sx, sy) * 10; - int dist2 = distance(rx, ry, -sx, -sy) * 10; - - // 'good' points will always be closer to test point 1 - if (dist2 < dist1) - dist += 80; // make the point less attractive - - if (dist < best_dist) - { - best_dist = dist; - bx = tx; - by = ty; - } - } - - you.x_pos = bx; - you.y_pos = by; - - return (1); -} - -void cast_semi_controlled_blink(int pow) -{ - apply_one_neighbouring_square(quadrant_blink, pow); - return; -} - -void cast_stoneskin(int pow) -{ - if (you.is_undead) - { - mpr("This spell does not affect your undead flesh."); - return; - } - - if (you.attribute[ATTR_TRANSFORMATION] != TRAN_NONE - && you.attribute[ATTR_TRANSFORMATION] != TRAN_STATUE - && you.attribute[ATTR_TRANSFORMATION] != TRAN_BLADE_HANDS) - { - mpr("This spell does not affect your current form."); - return; - } - - if (you.duration[DUR_STONEMAIL] || you.duration[DUR_ICY_ARMOUR]) - { - mpr("This spell conflicts with another spell still in effect."); - return; - } - - if (you.duration[DUR_STONESKIN]) - mpr( "Your skin feels harder." ); - else - { - if (you.attribute[ATTR_TRANSFORMATION] == TRAN_STATUE) - mpr( "Your stone body feels more resilient." ); - else - mpr( "Your skin hardens." ); - - you.redraw_armour_class = 1; - } - - you.duration[DUR_STONESKIN] += 10 + random2(pow) + random2(pow); - - if (you.duration[DUR_STONESKIN] > 50) - you.duration[DUR_STONESKIN] = 50; -} |