diff options
author | Nicholas Feinberg <pleasingfung@gmail.com> | 2014-07-01 23:20:50 -0700 |
---|---|---|
committer | Nicholas Feinberg <pleasingfung@gmail.com> | 2014-07-01 23:24:51 -0700 |
commit | c22efd9fab02d2b1b0ad6a00a8bfc45475efc360 (patch) | |
tree | 512d9f5b84e188039de0d689e93f7ecee430b245 /crawl-ref/source/bloodspatter.cc | |
parent | 22f25ab0e848bb3f03d7bd4c0331fda178b879c3 (diff) | |
download | crawl-ref-c22efd9fab02d2b1b0ad6a00a8bfc45475efc360.tar.gz crawl-ref-c22efd9fab02d2b1b0ad6a00a8bfc45475efc360.zip |
Let BLOOD finally have its place in the sun
...by moving bloodspatter functions into their own file.
Death to misc.cc! Long live the new file hierarchy!
Diffstat (limited to 'crawl-ref/source/bloodspatter.cc')
-rw-r--r-- | crawl-ref/source/bloodspatter.cc | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/crawl-ref/source/bloodspatter.cc b/crawl-ref/source/bloodspatter.cc new file mode 100644 index 0000000000..3897eeba44 --- /dev/null +++ b/crawl-ref/source/bloodspatter.cc @@ -0,0 +1,266 @@ +/** + * @file + * @brief Functions for spattering blood all over the place. + **/ + +#include "AppHdr.h" + +#include "bloodspatter.h" + +#include "cloud.h" +#include "coordit.h" +#include "env.h" +#include "fprop.h" +#include "losglobal.h" +#include "shout.h" +#include "terrain.h" +#include "religion.h" + +static bool allow_bleeding_on_square(const coord_def& where) +{ + // No bleeding onto sanctuary ground, please. + // Also not necessary if already covered in blood. + if (is_bloodcovered(where) || is_sanctuary(where)) + return false; + + // No spattering into lava or water. + if (grd(where) >= DNGN_LAVA && grd(where) < DNGN_FLOOR) + return false; + + // No spattering into fountains (other than blood). + if (grd(where) == DNGN_FOUNTAIN_BLUE + || grd(where) == DNGN_FOUNTAIN_SPARKLING) + { + return false; + } + + // The good gods like to keep their altars pristine. + if (is_good_god(feat_altar_god(grd(where)))) + return false; + + return true; +} + +bool maybe_bloodify_square(const coord_def& where) +{ + if (!allow_bleeding_on_square(where)) + return false; + + env.pgrid(where) |= FPROP_BLOODY; + return true; +} + +/** + * Rotate the wall blood splat tile, so that it is facing the source. + * + * Wall blood splat tiles are drawned with the blood dripping down. We need + * the tile to be facing an orthogonal empty space for the effect to look + * good. We choose the empty space closest to the source of the blood. + * + * @param where Coordinates of the wall where there is a blood splat. + * @param from Coordinates of the source of the blood. + * @param old_blood blood splats created at level generation are old and can + * have some blood inscriptions. Only for south facing splats, so you don't + * have to turn your head to read the inscriptions. + */ +static void _orient_wall_blood(const coord_def& where, coord_def from, + bool old_blood) +{ + if (!feat_is_wall(env.grid(where))) + return; + + if (from == INVALID_COORD) + from = where; + + coord_def closer = INVALID_COORD; + int dist = INT_MAX; + for (orth_adjacent_iterator ai(where); ai; ++ai) + { + if (in_bounds(*ai) && !cell_is_solid(*ai) + && cell_see_cell(from, *ai, LOS_SOLID) + && (distance2(*ai, from) < dist + || distance2(*ai, from) == dist && coinflip())) + { + closer = *ai; + dist = distance2(*ai, from); + } + } + + // If we didn't find anything, the wall is in a corner. + // We don't want blood tile there. + if (closer == INVALID_COORD) + { + env.pgrid(where) &= ~FPROP_BLOODY; + return; + } + + const coord_def diff = where - closer; + if (diff == coord_def(1, 0)) + env.pgrid(where) |= FPROP_BLOOD_WEST; + else if (diff == coord_def(0, 1)) + env.pgrid(where) |= FPROP_BLOOD_NORTH; + else if (diff == coord_def(-1, 0)) + env.pgrid(where) |= FPROP_BLOOD_EAST; + else if (old_blood && one_chance_in(10)) + env.pgrid(where) |= FPROP_OLD_BLOOD; +} + +static void _maybe_bloodify_square(const coord_def& where, int amount, + bool spatter = false, + bool smell_alert = true, + const coord_def& from = INVALID_COORD, + const bool old_blood = false) +{ + if (amount < 1) + return; + + bool may_bleed = allow_bleeding_on_square(where); + + bool ignite_blood = player_mutation_level(MUT_IGNITE_BLOOD) + && you.see_cell(where); + + if (ignite_blood) + amount *= 2; + + if (x_chance_in_y(amount, 20)) + { + dprf("might bleed now; square: (%d, %d); amount = %d", + where.x, where.y, amount); + if (may_bleed) + { + env.pgrid(where) |= FPROP_BLOODY; + _orient_wall_blood(where, from, old_blood); + + if (ignite_blood + && !cell_is_solid(where) + && env.cgrid(where) == EMPTY_CLOUD) + { + place_cloud(CLOUD_FIRE, where, 5 + random2(6), &you); + } + } + + // The blood spilled can be detected via blood scent even if the + // spot it would fall is prevented from becoming bloodied (for + // example, because it is water) + if (smell_alert && in_bounds(where)) + blood_smell(12, where); + + if (spatter) + { + // Smaller chance of spattering surrounding squares. + for (adjacent_iterator ai(where); ai; ++ai) + { + _maybe_bloodify_square(*ai, amount/15, false, true, from, + old_blood); + } + } + } +} + +// 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(const coord_def& where, monster_type montype, + int damage, bool spatter, bool smell_alert, + const coord_def& from, const bool old_blood) +{ + ASSERT_IN_BOUNDS(where); + + if (montype == MONS_PLAYER && !you.can_bleed()) + return; + + if (montype != NUM_MONSTERS && montype != MONS_PLAYER) + { + monster m; + m.type = montype; + if (!m.can_bleed()) + return; + } + + _maybe_bloodify_square(where, damage, spatter, smell_alert, from, + old_blood); +} + +void blood_spray(const coord_def& origin, monster_type montype, int level) +{ + 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) && cell_see_cell(origin, bloody, LOS_SOLID)) + { + bleed_onto_floor(bloody, montype, 99, false, true, origin); + break; + } + } + } +} + +static void _spatter_neighbours(const coord_def& where, int chance, + const coord_def& from = INVALID_COORD) +{ + for (adjacent_iterator ai(where, false); ai; ++ai) + { + if (!allow_bleeding_on_square(*ai)) + continue; + + if (one_chance_in(chance)) + { + env.pgrid(*ai) |= FPROP_BLOODY; + _orient_wall_blood(where, from, true); + _spatter_neighbours(*ai, chance+1, from); + } + } +} + +void generate_random_blood_spatter_on_level(const map_bitmask *susceptible_area) +{ + const int max_cluster = 7 + random2(9); + + // Lower chances for large bloodshed areas if we have many clusters, + // but increase chances if we have few. + // Chances for startprob are [1..3] for 7-9 clusters, + // ... [1..4] for 10-12 clusters, and + // ... [2..5] for 13-15 clusters. + + int min_prob = 1; + int max_prob = 4; + + if (max_cluster < 10) + max_prob--; + else if (max_cluster > 12) + min_prob++; + + for (int i = 0; i < max_cluster; ++i) + { + const coord_def c = random_in_bounds(); + + if (susceptible_area && !(*susceptible_area)(c)) + continue; + + // startprob is used to initialise the chance for neighbours + // being spattered, which will be decreased by 1 per recursion + // round. We then use one_chance_in(chance) to determine + // whether to spatter a given grid or not. Thus, startprob = 1 + // means that initially all surrounding grids will be + // spattered (3x3), and the _higher_ startprob the _lower_ the + // overall chance for spattering and the _smaller_ the + // bloodshed area. + const int startprob = min_prob + random2(max_prob); + + maybe_bloodify_square(c); + + _spatter_neighbours(c, startprob); + } +} |