summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/bloodspatter.cc
diff options
context:
space:
mode:
authorNicholas Feinberg <pleasingfung@gmail.com>2014-07-01 23:20:50 -0700
committerNicholas Feinberg <pleasingfung@gmail.com>2014-07-01 23:24:51 -0700
commitc22efd9fab02d2b1b0ad6a00a8bfc45475efc360 (patch)
tree512d9f5b84e188039de0d689e93f7ecee430b245 /crawl-ref/source/bloodspatter.cc
parent22f25ab0e848bb3f03d7bd4c0331fda178b879c3 (diff)
downloadcrawl-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.cc266
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);
+ }
+}