From 30387f6a8372b62160a6c2a4441a1b5909538055 Mon Sep 17 00:00:00 2001 From: Stefan O'Rear Date: Wed, 4 Nov 2009 02:24:58 -0800 Subject: Disintegration can be very messy. --- crawl-ref/source/beam.cc | 9 ++++- crawl-ref/source/enum.h | 3 +- crawl-ref/source/misc.cc | 33 ++++++++++++++++ crawl-ref/source/misc.h | 1 + crawl-ref/source/monster.cc | 9 +++++ crawl-ref/source/monstuff.cc | 94 ++++++++++++++++++++++++++++++++++++++++++-- crawl-ref/source/monstuff.h | 2 + crawl-ref/source/player.cc | 6 +++ crawl-ref/source/spells4.cc | 55 ++++++++++++++++++++------ 9 files changed, 195 insertions(+), 17 deletions(-) (limited to 'crawl-ref') diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc index 96b139da86..77d9c19741 100644 --- a/crawl-ref/source/beam.cc +++ b/crawl-ref/source/beam.cc @@ -3837,7 +3837,14 @@ void bolt::affect_player_enchantment() if (aux_source.empty()) aux_source = "a disintegration bolt"; - internal_ouch(damage.roll()); + { + int amt = damage.roll(); + internal_ouch(amt); + + if (you.can_bleed()) + blood_spray(you.pos(), MONS_PLAYER, amt / 5); + } + obvious_effect = true; break; diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h index a0b94c81c5..02ad706fb2 100644 --- a/crawl-ref/source/enum.h +++ b/crawl-ref/source/enum.h @@ -2188,7 +2188,8 @@ enum monster_flag_type // name. MF_NAME_MASK = 0x30000, MF_GOD_GIFT = 0x40000, // Is a god gift. - MF_FLEEING_FROM_SANCTUARY = 0x80000 // Is running away from player sanctuary + MF_FLEEING_FROM_SANCTUARY = 0x80000, // Is running away from player sanctuary + MF_EXPLODE_KILL = 0x100000 // Is being killed with disintegration }; // Adding slots breaks saves. YHBW. diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc index 94566c96e4..94603039d3 100644 --- a/crawl-ref/source/misc.cc +++ b/crawl-ref/source/misc.cc @@ -1147,6 +1147,39 @@ void bleed_onto_floor(const coord_def& where, monster_type montype, _maybe_bloodify_square(where, damage, spatter, smell_alert); } +void blood_spray(const coord_def& origin, monster_type montype, int level) +{ + los_def ld(origin, opc_solid); + + ld.update(); + + int tries = 0; + for (int i = 0; i < level; ++i) + { + // Blood drops are small and light and suffer a lot of wind + // resistance. + int range = random2(8) + 1; + + while (tries < 5000) + { + ++tries; + + coord_def bloody = origin; + bloody.x += random_range(-range, range); + bloody.y += random_range(-range, range); + + if (in_bounds(bloody) && ld.see_cell(bloody)) + { + if (feat_is_solid(grd(bloody)) && coinflip()) + continue; + + bleed_onto_floor(bloody, montype, 99); + break; + } + } + } +} + static void _spatter_neighbours(const coord_def& where, int chance) { for (adjacent_iterator ai(where, false); ai; ++ai) diff --git a/crawl-ref/source/misc.h b/crawl-ref/source/misc.h index c9c0e2eaee..83d6791d62 100644 --- a/crawl-ref/source/misc.h +++ b/crawl-ref/source/misc.h @@ -39,6 +39,7 @@ void turn_corpse_into_blood_potions (item_def &item); void turn_corpse_into_skeleton_and_blood_potions(item_def &item); void split_potions_into_decay(int obj, int amount, bool need_msg = true); +void blood_spray(const coord_def& where, monster_type mon, int level); void bleed_onto_floor(const coord_def& where, monster_type mon, int damage, bool spatter = false, bool smell_alert = true); void generate_random_blood_spatter_on_level(); diff --git a/crawl-ref/source/monster.cc b/crawl-ref/source/monster.cc index 319e256d5a..1ad06936d4 100644 --- a/crawl-ref/source/monster.cc +++ b/crawl-ref/source/monster.cc @@ -3267,6 +3267,15 @@ int monsters::hurt(const actor *agent, int amount, beam_type flavour, hit_points = max_hit_points; } + if (flavour == BEAM_NUKE || flavour == BEAM_DISINTEGRATION) + { + if (can_bleed()) + blood_spray(pos(), id(), amount / 5); + + if (!alive()) + flags |= MF_EXPLODE_KILL; + } + // Allow the victim to exhibit passive damage behaviour (royal // jelly). kill_category whose = (agent == NULL) ? KC_OTHER : diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc index e7f5ff2c9e..7eaf3133bb 100644 --- a/crawl-ref/source/monstuff.cc +++ b/crawl-ref/source/monstuff.cc @@ -23,6 +23,7 @@ #include "dgnevent.h" #include "directn.h" #include "files.h" +#include "food.h" #include "godabil.h" #include "items.h" #include "kills.h" @@ -336,6 +337,78 @@ monster_type fill_out_corpse(const monsters* monster, item_def& corpse, return (corpse_class); } +bool explode_corpse(item_def& corpse, const coord_def& where) +{ + los_def ld(where, opc_solid); + + if (monster_descriptor(corpse.plus, MDSC_LEAVES_HIDE) + && mons_genus(corpse.plus) == MONS_DRAGON) + { + // Uh... dragon hide is tough stuff and it keeps the monster in + // one piece? More importantly, it prevents a flavor feature + // from becoming a trap for the unwary. + + return (false); + } + + ld.update(); + + int nchunks = 1 + random2(mons_weight(corpse.plus) / 150); + nchunks = stepdown_value(nchunks, 4, 4, 12, 12); + + int ntries = 0; + + corpse.base_type = OBJ_FOOD; + corpse.sub_type = FOOD_CHUNK; + + blood_spray(where, static_cast(corpse.plus), nchunks * 3); + + while (nchunks > 0 && ntries < 10000) + { + ++ntries; + + coord_def cp = where; + cp.x += random_range(-LOS_RADIUS, LOS_RADIUS); + cp.y += random_range(-LOS_RADIUS, LOS_RADIUS); + +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, "Trying to scatter chunk to %d, %d...", + cp.x, cp.y); +#endif + + if (! in_bounds(cp)) + continue; + + if (! ld.see_cell(cp)) + continue; + +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, "Cell is visible..."); +#endif + + if (feat_is_solid(grd(cp))) + continue; + + --nchunks; + + if (feat_destroys_items(grd(cp))) + { + if (!silenced(cp)) + mprf(MSGCH_SOUND, feat_item_destruction_message(grd(cp))); + + continue; + } + +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, "Success"); +#endif + + copy_item_to_grid(corpse, cp); + } + + return (true); +} + // Returns the item slot of a generated corpse, or -1 if no corpse. int place_monster_corpse(const monsters *monster, bool silent, bool force) @@ -363,19 +436,32 @@ int place_monster_corpse(const monsters *monster, bool silent, if (corpse_class == MONS_NO_MONSTER || (!force && coinflip())) return (-1); - if (feat_destroys_items(grd(monster->pos()))) + int o = get_item_slot(); + if (o == NON_ITEM) { item_was_destroyed(corpse); return (-1); } - int o = get_item_slot(); - if (o == NON_ITEM) - return (-1); mitm[o] = corpse; origin_set_monster(mitm[o], monster); + if ((monster->flags & MF_EXPLODE_KILL) + && explode_corpse(corpse, monster->pos())) + { + // We already have a spray of chunks + destroy_item(o); + return (-1); + } + + if (feat_destroys_items(grd(monster->pos()))) + { + item_was_destroyed(corpse); + destroy_item(o); + return (-1); + } + // Don't care if 'o' is changed, and it shouldn't be (corpses don't // stack). move_item_to_grid(&o, monster->pos()); diff --git a/crawl-ref/source/monstuff.h b/crawl-ref/source/monstuff.h index c8df0edde0..6b1de15f19 100644 --- a/crawl-ref/source/monstuff.h +++ b/crawl-ref/source/monstuff.h @@ -80,6 +80,8 @@ int monster_die(monsters *monster, killer_type killer, monster_type fill_out_corpse(const monsters* monster, item_def& corpse, bool allow_weightless = false); +bool explode_corpse(item_def& corpse, const coord_def& where); + int place_monster_corpse(const monsters *monster, bool silent, bool force = false); diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc index 0ec66b2678..d3dd1d6829 100644 --- a/crawl-ref/source/player.cc +++ b/crawl-ref/source/player.cc @@ -6957,6 +6957,12 @@ int player::hurt(const actor *agent, int amount, beam_type flavour, ASSERT(false); ouch(amount, NON_MONSTER, KILLED_BY_SOMETHING); } + + if ((flavour == BEAM_NUKE || flavour == BEAM_DISINTEGRATION) && can_bleed()) + { + blood_spray(pos(), id(), amount / 5); + } + return (amount); } diff --git a/crawl-ref/source/spells4.cc b/crawl-ref/source/spells4.cc index 08620200b8..39f1be7c52 100644 --- a/crawl-ref/source/spells4.cc +++ b/crawl-ref/source/spells4.cc @@ -64,11 +64,12 @@ enum DEBRIS // jmf: add for shatter, dig, and Giants to throw // Just to avoid typing this over and over. // Returns true if monster died. -- bwr -static bool _player_hurt_monster(monsters& m, int damage) +static bool _player_hurt_monster(monsters& m, int damage, + beam_type flavour = BEAM_MISSILE) { if (damage > 0) { - m.hurt(&you, damage, BEAM_MISSILE, false); + m.hurt(&you, damage, flavour, false); if (m.alive()) { @@ -1422,7 +1423,8 @@ bool cast_fragmentation(int pow, const dist& spd) // fizzle (since we don't actually explode wood golems). -- bwr explode = false; beam.damage.num = 2; - _player_hurt_monster(*mon, beam.damage.roll()); + _player_hurt_monster(*mon, beam.damage.roll(), + BEAM_DISINTEGRATION); break; case MONS_IRON_GOLEM: @@ -1431,7 +1433,8 @@ bool cast_fragmentation(int pow, const dist& spd) beam.name = "blast of metal fragments"; beam.colour = CYAN; beam.damage.num = 4; - if (_player_hurt_monster(*mon, beam.damage.roll())) + if (_player_hurt_monster(*mon, beam.damage.roll(), + BEAM_DISINTEGRATION)) beam.damage.num += 2; break; @@ -1444,7 +1447,8 @@ bool cast_fragmentation(int pow, const dist& spd) beam.name = "blast of rock fragments"; beam.colour = BROWN; beam.damage.num = 3; - if (_player_hurt_monster(*mon, beam.damage.roll())) + if (_player_hurt_monster(*mon, beam.damage.roll(), + BEAM_DISINTEGRATION)) beam.damage.num++; break; @@ -1470,7 +1474,8 @@ bool cast_fragmentation(int pow, const dist& spd) if (pow >= 50 && one_chance_in(10)) statue_damage = mon->hit_points; - if (_player_hurt_monster(*mon, statue_damage)) + if (_player_hurt_monster(*mon, statue_damage, + BEAM_DISINTEGRATION)) beam.damage.num += 2; } break; @@ -1481,7 +1486,8 @@ bool cast_fragmentation(int pow, const dist& spd) beam.name = "blast of crystal shards"; beam.colour = WHITE; beam.damage.num = 4; - if (_player_hurt_monster(*mon, beam.damage.roll())) + if (_player_hurt_monster(*mon, beam.damage.roll(), + BEAM_DISINTEGRATION)) beam.damage.num += 2; break; @@ -1493,7 +1499,8 @@ bool cast_fragmentation(int pow, const dist& spd) beam.colour = WHITE; beam.damage.num = 2; beam.flavour = BEAM_ICE; - if (_player_hurt_monster(*mon, beam.damage.roll())) + if (_player_hurt_monster(*mon, beam.damage.roll()), + BEAM_DISINTEGRATION); beam.damage.num++; break; } @@ -1515,7 +1522,8 @@ bool cast_fragmentation(int pow, const dist& spd) else { beam.damage.num = 2; - if (_player_hurt_monster(*mon, beam.damage.roll())) + if (_player_hurt_monster(*mon, beam.damage.roll(), + BEAM_DISINTEGRATION)) beam.damage.num += 2; } goto all_done; // i.e., no "Foo Explodes!" @@ -1533,7 +1541,8 @@ bool cast_fragmentation(int pow, const dist& spd) beam.name = "blast of petrified fragments"; beam.colour = mons_class_colour(mon->type); beam.damage.num = petrifying ? 2 : 3; - if (_player_hurt_monster(*mon, beam.damage.roll())) + if (_player_hurt_monster(*mon, beam.damage.roll(), + BEAM_DISINTEGRATION)) beam.damage.num++; break; } @@ -1544,7 +1553,8 @@ bool cast_fragmentation(int pow, const dist& spd) // Yes, this spell does lousy damage if the monster // isn't susceptible. -- bwr - _player_hurt_monster(*mon, roll_dice(1, 5 + pow / 25)); + _player_hurt_monster(*mon, roll_dice(1, 5 + pow / 25), + BEAM_DISINTEGRATION); goto do_terrain; } @@ -1552,6 +1562,29 @@ bool cast_fragmentation(int pow, const dist& spd) goto all_done; } + for (stack_iterator si(spd.target); si; ++si) + { + if (si->base_type == OBJ_CORPSES) + { + std::string nm = si->name(DESC_CAP_THE); + if (si->sub_type == CORPSE_BODY) + { + if (!explode_corpse(*si, spd.target)) + { + mprf("%s seems to be exceptionally well connected.", + nm.c_str()); + + goto all_done; + } + } + + mprf("%s explodes!", nm.c_str()); + destroy_item(si.link()); + // si invalid now! + goto all_done; + } + } + if (env.markers.property_at(spd.target, MAT_ANY, "veto_fragmentation") == "veto") { -- cgit v1.2.3-54-g00ecf