From 447bc8ac9d8557be01da02c40349e4301f42c089 Mon Sep 17 00:00:00 2001 From: j-p-e-g Date: Wed, 9 Jan 2008 14:15:43 +0000 Subject: Add gore and splatter to Crawl - in the form of Jarmo's (strongly modified, I admit) blood patch. Whenever the player or a monster capable of bleeding suffers damage (currently only in melee) or when you dissect a corpse there's a chance (depending on damage/corpse weight) that the square will turn bloody and be coloured red, with a much lower chance of additionally spattering some of the surrounding squares. These chances are probably still a bit too high for later values of possible damage dealt. Also, ranged and some of the magic attacks (earth-based, mostly) should also be able to send blood flying. :p For now, this is flavour only, but it shouldn't be too difficult to introduce relations to Necromancy or evil gods. Also consider dragons to be warm-blooded, like dinosaurs. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@3230 c06c8d41-db1a-0410-9941-cceddc491573 --- crawl-ref/source/beam.cc | 8 ++++ crawl-ref/source/direct.cc | 12 ++++- crawl-ref/source/effects.cc | 14 ++++++ crawl-ref/source/enum.h | 3 +- crawl-ref/source/fight.cc | 29 +++++++++++- crawl-ref/source/food.cc | 4 ++ crawl-ref/source/misc.cc | 102 +++++++++++++++++++++++++++++++++++++++++-- crawl-ref/source/misc.h | 1 + crawl-ref/source/mon-data.h | 6 +-- crawl-ref/source/mstuff2.cc | 3 ++ crawl-ref/source/religion.cc | 13 +++++- crawl-ref/source/traps.cc | 6 ++- crawl-ref/source/view.cc | 28 ++++++++++-- crawl-ref/source/view.h | 1 + 14 files changed, 213 insertions(+), 17 deletions(-) diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc index a09fdbb141..15d3f68a85 100644 --- a/crawl-ref/source/beam.cc +++ b/crawl-ref/source/beam.cc @@ -2669,6 +2669,10 @@ static int affect_wall(bolt &beam, int x, int y) if (grd[x][y] == DNGN_ROCK_WALL || grd[x][y] == DNGN_CLEAR_ROCK_WALL) { grd[x][y] = DNGN_FLOOR; + + // blood does not transfer onto floor + if (is_bloodcovered(x,y)) + env.map[x][y].property = FPROP_NONE; if (!beam.msg_generated) { @@ -2742,6 +2746,10 @@ static int affect_wall(bolt &beam, int x, int y) { grd[x][y] = DNGN_FLOOR; + // blood does not transfer onto floor + if (is_bloodcovered(x,y)) + env.map[x][y].property = FPROP_NONE; + if (!silenced(you.x_pos, you.y_pos)) { if (!see_grid( x, y )) diff --git a/crawl-ref/source/direct.cc b/crawl-ref/source/direct.cc index 36925ca8df..8430d326fb 100644 --- a/crawl-ref/source/direct.cc +++ b/crawl-ref/source/direct.cc @@ -2148,6 +2148,9 @@ static void describe_cell(int mx, int my) if (Options.tutorial_left && tutorial_feat_interesting(grd[mx][my])) { feature_desc += " (Press v for more information.)"; + if (is_bloodcovered(mx, my)) + feature_desc += EOL "It is spattered with blood."; + print_formatted_paragraph(feature_desc, get_number_of_cols()); } else @@ -2157,9 +2160,16 @@ static void describe_cell(int mx, int my) if (interesting_feature(feat)) feature_desc += " (Press 'v' for more information.)"; + bool bloody = false; + if (is_bloodcovered(mx, my)) + { + feature_desc += EOL "It is spattered with blood."; + bloody = true; + } + // Suppress "Floor." if there's something on that square that we've // already described. - if ((feat == DNGN_FLOOR || feat == DNGN_FLOOR_SPECIAL) + if ((feat == DNGN_FLOOR || feat == DNGN_FLOOR_SPECIAL) && !bloody && (monster_described || item_described || cloud_described)) return; diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc index fba2ff8e6f..4658ddb393 100644 --- a/crawl-ref/source/effects.cc +++ b/crawl-ref/source/effects.cc @@ -2681,6 +2681,20 @@ void update_corpses(double elapsedTime) grd[cx][cy] = static_cast( grd[cx][cy] - 1); + + // clean bloody floor + if (is_bloodcovered(cx,cy)) + env.map[cx][cy].property = FPROP_NONE; + // chance of cleaning adjacent squares + for (int k=-1; k<=1; k++) + { + for (int l=-1; l<=1; l++) + if (is_bloodcovered(cx+k,cy+l) + && one_chance_in(5)) + { + env.map[cx+k][cy+l].property = FPROP_NONE; + } + } } } } diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h index e72f69a792..28369c77ac 100644 --- a/crawl-ref/source/enum.h +++ b/crawl-ref/source/enum.h @@ -1016,7 +1016,8 @@ enum floor_property_type { FPROP_NONE, // 0 FPROP_SANCTUARY_1, - FPROP_SANCTUARY_2 + FPROP_SANCTUARY_2, + FPROP_BLOODY // bloody floor and sanctuary are exclusive, so that's okay }; enum duration_type diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc index 48dd950bff..09fa35a48f 100644 --- a/crawl-ref/source/fight.cc +++ b/crawl-ref/source/fight.cc @@ -605,6 +605,15 @@ bool melee_attack::player_attack() // always upset monster regardless of damage behaviour_event(def, ME_WHACK, MHITYOU); + if (damage_done > 0) + { + int blood = damage_done; + if (blood > defender->stat_hp()) + blood = defender->stat_hp(); + + bleed_onto_floor(where.x, where.y, defender->id(), blood, true); + } + player_hurt_monster(); if (damage_done > 0 || !defender_visible) @@ -2139,11 +2148,17 @@ bool melee_attack::chop_hydra_head( int dam, if (def->number < 1) { if (defender_visible) + { mprf( "%s %s %s's last head off!", atk_name(DESC_CAP_THE).c_str(), attacker->conj_verb(verb).c_str(), def_name(DESC_NOCAP_THE).c_str() ); + } + coord_def pos = defender->pos(); + bleed_onto_floor(pos.x, pos.y, defender->id(), + def->hit_points, true); + defender->hurt(attacker, def->hit_points); } else @@ -3520,10 +3535,22 @@ void melee_attack::mons_perform_attack_rounds() mons_announce_hit(attk); check_defender_train_armour(); + int type = -1; // player + if (defender->atype() == ACT_MONSTER) + type = defender->id(); + + int damage = damage_done; + if (damage > defender->stat_hp()) + damage = defender->stat_hp(); + + bleed_onto_floor(pos.x, pos.y, type, damage, true); + if (decapitate_hydra(damage_done, attacker->damage_type(attack_number))) + { continue; - + } + special_damage = 0; special_damage_message.clear(); diff --git a/crawl-ref/source/food.cc b/crawl-ref/source/food.cc index ff0aef44a9..8507ea7d1a 100644 --- a/crawl-ref/source/food.cc +++ b/crawl-ref/source/food.cc @@ -353,6 +353,10 @@ bool butchery() god_likes_butchery(you.religion)) { offer_corpse(corpse_id); + // ritual sacrifice can also bloodify the ground + const int mons_class = mitm[corpse_id].plus; + const int max_chunks = mons_weight( mons_class ) / 150; + bleed_onto_floor(you.x_pos, you.y_pos, mons_class, max_chunks, true); destroy_item(corpse_id); } else diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc index eef3ecc26e..7111bc8881 100644 --- a/crawl-ref/source/misc.cc +++ b/crawl-ref/source/misc.cc @@ -84,11 +84,15 @@ // unsigned char chy, unsigned char ch_col) void turn_corpse_into_chunks( item_def &item ) { - const int mons_class = item.plus; - const int max_chunks = mons_weight( mons_class ) / 150; - ASSERT( item.base_type == OBJ_CORPSES ); + const int mons_class = item.plus; + const int max_chunks = mons_weight( mons_class ) / 150; + + // only fresh corpses bleed enough to colour the ground + if (item.special >= 100) + bleed_onto_floor(you.x_pos, you.y_pos, mons_class, max_chunks, true); + item.base_type = OBJ_FOOD; item.sub_type = FOOD_CHUNK; item.quantity = 1 + random2( max_chunks ); @@ -155,6 +159,98 @@ void turn_corpse_into_chunks( item_def &item ) } } // end place_chunks() +// checks whether the player or a monster is capable of bleeding +static bool victim_can_bleed(int montype) +{ + if (montype == -1) // player + { + if (you.is_undead && (you.species != SP_VAMPIRE + || you.hunger_state >= HS_FULL)) + { + return (false); + } + + int tran = you.attribute[ATTR_TRANSFORMATION]; + if (tran == TRAN_STATUE || tran == TRAN_ICE_BEAST + || tran == TRAN_AIR || tran == TRAN_LICH + || tran == TRAN_SPIDER) // monster spiders don't bleed either + { + return (false); + } + return (true); + } + + // now check monsters + return (mons_class_flag(montype, M_COLD_BLOOD) + || mons_class_flag(montype, M_WARM_BLOOD)); +} + +static bool allow_bleeding_on_square(int x, int y) +{ + // no bleeding onto sanctuary ground, please + // also not necessary if already covered in blood + if (env.map[x][y].property != FPROP_NONE) + return (false); + + // no spattering into lava or water + if (grd[x][y] >= DNGN_LAVA && grd[x][y] < DNGN_FLOOR) + return (false); + + // the good gods like to keep their altars pristine + if (is_good_god(grid_altar_god(grd[x][y]))) + return (false); + + return (true); +} + +static void maybe_bloodify_square(int x, int y, int amount, bool spatter = false) +{ + if (amount < 1) + return; + + if (!spatter && !allow_bleeding_on_square(x,y)) + return; + + if (amount > random2(20)) + { +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, + "might bleed now; square: (%d, %d); amount = %d", + x, y, amount); +#endif + if (allow_bleeding_on_square(x,y)) + env.map[x][y].property = FPROP_BLOODY; + + if (spatter) + { + // smaller chance of spattering surrounding squares + for (int i=-1;i<=1;i++) + for (int j=-1;j<=1;j++) + { + if (i == 0 && j == 0) // current square + continue; + + // spattering onto walls etc. less likely + if (grd[x+i][y+j] < DNGN_MINMOVE && one_chance_in(3)) + continue; + + maybe_bloodify_square(x+i, y+j, amount/10); + } + } + } +} + +// currently flavour only: colour ground (and possibly adjacent squares) red +// "damage" depends on damage taken (or hitpoints, if damage higher), +// or, for sacrifices, on the number of chunks possible to get out of a corpse +void bleed_onto_floor(int x, int y, int montype, int damage, bool spatter) +{ + if (!victim_can_bleed(montype)) + return; + + maybe_bloodify_square(x, y, damage, spatter); +} + void search_around( bool only_adjacent ) { int i; diff --git a/crawl-ref/source/misc.h b/crawl-ref/source/misc.h index 460c76e403..092ab2eb61 100644 --- a/crawl-ref/source/misc.h +++ b/crawl-ref/source/misc.h @@ -71,6 +71,7 @@ void trackers_init_new_level(bool transit); * *********************************************************************** */ void turn_corpse_into_chunks( item_def &item ); +void bleed_onto_floor(int x, int y, int mon, int damage, bool spatter = false); // last updated 12may2000 {dlb} /* *********************************************************************** diff --git a/crawl-ref/source/mon-data.h b/crawl-ref/source/mon-data.h index fc4093a236..100d7edd66 100644 --- a/crawl-ref/source/mon-data.h +++ b/crawl-ref/source/mon-data.h @@ -479,7 +479,7 @@ { MONS_DRAGON, 'D', GREEN, "dragon", - M_FLIES | M_SPECIAL_ABILITY, //jmf: warm blood? + M_FLIES | M_SPECIAL_ABILITY | M_WARM_BLOOD, MR_RES_POISON | MR_RES_FIRE | MR_VUL_COLD, 2200, 12, MONS_DRAGON, MONS_DRAGON, MH_NATURAL, -4, { {AT_BITE, AF_PLAIN, 20}, {AT_CLAW, AF_PLAIN, 13}, {AT_CLAW, AF_PLAIN, 13}, AT_NO_ATK }, @@ -796,7 +796,7 @@ { MONS_WYVERN, 'D', LIGHTRED, "wyvern", - M_NO_FLAGS, //jmf: warm blood? + M_WARM_BLOOD, MR_NO_FLAGS, 2000, 10, MONS_WYVERN, MONS_WYVERN, MH_NATURAL, -3, { {AT_BITE, AF_PLAIN, 20}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, @@ -1544,7 +1544,7 @@ { MONS_STORM_DRAGON, 'D', LIGHTBLUE, "storm dragon", - M_SPELLCASTER | M_FLIES, + M_SPELLCASTER | M_FLIES | M_WARM_BLOOD, MR_RES_ELEC | MR_RES_COLD, 2800, 12, MONS_DRAGON, MONS_STORM_DRAGON, MH_NATURAL, -5, { {AT_BITE, AF_PLAIN, 25}, {AT_CLAW, AF_PLAIN, 15}, {AT_CLAW, AF_PLAIN, 15}, AT_NO_ATK }, diff --git a/crawl-ref/source/mstuff2.cc b/crawl-ref/source/mstuff2.cc index 32ac2be14f..b1ff4b3e38 100644 --- a/crawl-ref/source/mstuff2.cc +++ b/crawl-ref/source/mstuff2.cc @@ -219,6 +219,9 @@ void mons_trap(struct monsters *monster) if (damage_taken < 0) damage_taken = 0; + + bleed_onto_floor(monster->x, monster->y, monster->type, + damage_taken, true); } revealTrap = true; diff --git a/crawl-ref/source/religion.cc b/crawl-ref/source/religion.cc index e6e087d786..f82c9b7c99 100644 --- a/crawl-ref/source/religion.cc +++ b/crawl-ref/source/religion.cc @@ -3091,9 +3091,18 @@ static bool bless_weapon( int god, int brand, int colour ) mprf( MSGCH_GOD, "Your weapon shines brightly!" ); simple_god_message( " booms: Use this gift wisely!" ); - if ( god != GOD_LUGONU ) + if ( god == GOD_SHINING_ONE ) + { holy_word( 100, true ); - + // un-bloodify surrounding squares + for (int i=-3;i<=3;i++) + for (int j=-3;j<=3;j++) + { + if (is_bloodcovered(you.x_pos+i, you.y_pos+j)) + env.map[you.x_pos+i][you.y_pos+j].property = FPROP_NONE; + } + } + delay(1000); return (true); diff --git a/crawl-ref/source/traps.cc b/crawl-ref/source/traps.cc index 00d5793963..35b7880c8e 100644 --- a/crawl-ref/source/traps.cc +++ b/crawl-ref/source/traps.cc @@ -384,8 +384,10 @@ void handle_traps(trap_type trt, int i, bool trap_known) else { mpr("A huge blade swings out and slices into you!"); - ouch( (you.your_level * 2) + random2avg(29, 2) - - random2(1 + player_AC()), 0, KILLED_BY_TRAP, " blade" ); + int damage = (you.your_level * 2) + random2avg(29, 2) + - random2(1 + player_AC()); + ouch( damage, 0, KILLED_BY_TRAP, " blade" ); + bleed_onto_floor(you.x_pos, you.y_pos, -1, damage, true); } break; diff --git a/crawl-ref/source/view.cc b/crawl-ref/source/view.cc index c7c886ca1e..cf22365b0a 100644 --- a/crawl-ref/source/view.cc +++ b/crawl-ref/source/view.cc @@ -208,9 +208,15 @@ void set_envmap_prop( int x, int y, int prop ) env.map[x][y].property = prop; } -bool is_sanctuary( int x, int y) +bool is_sanctuary(int x, int y) { - return (env.map[x][y].property != FPROP_NONE); + return (env.map[x][y].property == FPROP_SANCTUARY_1 + || env.map[x][y].property == FPROP_SANCTUARY_2); +} + +bool is_bloodcovered(int x, int y) +{ + return (env.map[x][y].property == FPROP_BLOODY); } bool is_envmap_item(int x, int y) @@ -368,6 +374,17 @@ static int view_emphasised_colour(int x, int y, dungeon_feature_type feat, return (oldcolour); } +static bool show_bloodcovered(int x, int y) +{ + if (!is_bloodcovered(x,y)) + return (false); + + dungeon_feature_type grid = grd[x][y]; + + return (grid_altar_god(grid) == GOD_NO_GOD && !grid_is_trap(grid) + && !grid_is_portal(grid) && grid != DNGN_ENTER_SHOP); +} + static void get_symbol( int x, int y, int object, unsigned *ch, unsigned short *colour, @@ -401,8 +418,11 @@ static void get_symbol( int x, int y, *colour = LIGHTGRAY | colmask; // 1/12 } } - else - if (object < NUM_REAL_FEATURES && env.grid_colours[x][y]) + else if (object < NUM_REAL_FEATURES && show_bloodcovered(x,y)) + { + *colour = RED | colmask; + } + else if (object < NUM_REAL_FEATURES && env.grid_colours[x][y]) { *colour = env.grid_colours[x][y] | colmask; } diff --git a/crawl-ref/source/view.h b/crawl-ref/source/view.h index fec4d5805f..cf3490fc9b 100644 --- a/crawl-ref/source/view.h +++ b/crawl-ref/source/view.h @@ -166,6 +166,7 @@ void set_envmap_col( int x, int y, int colour, int flags ); void set_envmap_col( int x, int y, int colour ); void set_envmap_prop( int x, int y, int prop ); bool is_sanctuary( int x, int y ); +bool is_bloodcovered( int x, int y ); bool is_envmap_detected_item(int x, int y); bool is_envmap_detected_mons(int x, int y); -- cgit v1.2.3-54-g00ecf