1 /**
2 * @file
3 * @brief Functions for spattering blood all over the place.
4 **/
5
6 #include "AppHdr.h"
7
8 #include "bloodspatter.h"
9
10 #include "cloud.h"
11 #include "coordit.h"
12 #include "env.h"
13 #include "fprop.h"
14 #include "losglobal.h"
15 #include "mpr.h"
16 #include "religion.h"
17 #include "shout.h"
18 #include "terrain.h"
19
20 /**
21 * Is it okay to bleed onto the given square?
22 *
23 * @param where The square in question.
24 * @param to_ignite Whether the blood will be ignited right
25 * after.
26 * @return Whether blood is permitted.
27 */
_allow_bleeding_on_square(const coord_def & where,bool to_ignite=false)28 static bool _allow_bleeding_on_square(const coord_def& where,
29 bool to_ignite = false)
30 {
31 // No bleeding onto sanctuary ground, please.
32 if (is_sanctuary(where))
33 return false;
34
35 // Also not necessary if already covered in blood
36 // Unless we're going to set it on fire right after.
37 if (is_bloodcovered(where) && !to_ignite)
38 return false;
39
40 // No spattering into lava or water.
41 if (feat_is_lava(env.grid(where)) || feat_is_water(env.grid(where)))
42 return false;
43
44 // No spattering into fountains (other than blood).
45 if (env.grid(where) == DNGN_FOUNTAIN_BLUE
46 || env.grid(where) == DNGN_FOUNTAIN_SPARKLING)
47 {
48 return false;
49 }
50
51 // The good gods like to keep their altars pristine.
52 if (is_good_god(feat_altar_god(env.grid(where))))
53 return false;
54
55 return true;
56 }
57
maybe_bloodify_square(const coord_def & where)58 bool maybe_bloodify_square(const coord_def& where)
59 {
60 if (!_allow_bleeding_on_square(where))
61 return false;
62
63 env.pgrid(where) |= FPROP_BLOODY;
64 return true;
65 }
66
67 /**
68 * Rotate the wall blood splat tile, so that it is facing the source.
69 *
70 * Wall blood splat tiles are drawned with the blood dripping down. We need
71 * the tile to be facing an orthogonal empty space for the effect to look
72 * good. We choose the empty space closest to the source of the blood.
73 *
74 * @param where Coordinates of the wall where there is a blood splat.
75 * @param from Coordinates of the source of the blood.
76 * @param old_blood blood splats created at level generation are old and can
77 * have some blood inscriptions. Only for south facing splats, so you don't
78 * have to turn your head to read the inscriptions.
79 */
_orient_wall_blood(const coord_def & where,coord_def from,bool old_blood)80 static void _orient_wall_blood(const coord_def& where, coord_def from,
81 bool old_blood)
82 {
83 if (!feat_is_wall(env.grid(where)))
84 return;
85
86 if (from == INVALID_COORD)
87 from = where;
88
89 coord_def closer = INVALID_COORD;
90 int dist = INT_MAX;
91 for (orth_adjacent_iterator ai(where); ai; ++ai)
92 {
93 if (in_bounds(*ai) && !cell_is_solid(*ai)
94 && cell_see_cell(from, *ai, LOS_SOLID)
95 && (distance2(*ai, from) < dist
96 || distance2(*ai, from) == dist && coinflip()))
97 {
98 closer = *ai;
99 dist = distance2(*ai, from);
100 }
101 }
102
103 // If we didn't find anything, the wall is in a corner.
104 // We don't want blood tile there.
105 if (closer == INVALID_COORD)
106 {
107 env.pgrid(where) &= ~FPROP_BLOODY;
108 return;
109 }
110
111 const coord_def diff = where - closer;
112 if (diff == coord_def(1, 0))
113 env.pgrid(where) |= FPROP_BLOOD_WEST;
114 else if (diff == coord_def(0, 1))
115 env.pgrid(where) |= FPROP_BLOOD_NORTH;
116 else if (diff == coord_def(-1, 0))
117 env.pgrid(where) |= FPROP_BLOOD_EAST;
118 else if (old_blood && one_chance_in(10))
119 env.pgrid(where) |= FPROP_OLD_BLOOD;
120 }
121
_maybe_bloodify_square(const coord_def & where,int amount,bool spatter=false,const coord_def & from=INVALID_COORD,const bool old_blood=false)122 static void _maybe_bloodify_square(const coord_def& where, int amount,
123 bool spatter = false,
124 const coord_def& from = INVALID_COORD,
125 const bool old_blood = false)
126 {
127 if (amount < 1)
128 return;
129
130 int ignite_blood = you.get_mutation_level(MUT_IGNITE_BLOOD);
131
132 bool may_bleed = _allow_bleeding_on_square(where,
133 ignite_blood > 0 && you.see_cell(where));
134
135 if (ignite_blood && you.see_cell(where))
136 amount *= 2;
137
138 if (x_chance_in_y(amount, 20))
139 {
140 dprf("might bleed now; square: (%d, %d); amount = %d",
141 where.x, where.y, amount);
142 if (may_bleed)
143 {
144 env.pgrid(where) |= FPROP_BLOODY;
145 _orient_wall_blood(where, from, old_blood);
146
147 // Don't apply penance for involuntary cloud placement.
148 if (x_chance_in_y(ignite_blood, 3)
149 && you.see_cell(where)
150 && !cell_is_solid(where)
151 && !cloud_at(where))
152 {
153 int dur = 2 + ignite_blood + random2(2 * ignite_blood);
154 place_cloud(CLOUD_FIRE, where, dur, &you, -1, -1,
155 false);
156 }
157 }
158
159 if (spatter)
160 {
161 // Smaller chance of spattering surrounding squares.
162 for (adjacent_iterator ai(where); ai; ++ai)
163 _maybe_bloodify_square(*ai, amount/15, false, from, old_blood);
164 }
165 }
166 }
167
168 // Colour ground (and possibly adjacent squares) red. "damage" depends on damage
169 // taken (or hitpoints, if damage higher), or, for butchering, on the number of
170 // chunks possible to get out of a corpse.
bleed_onto_floor(const coord_def & where,monster_type montype,int damage,bool spatter,const coord_def & from,const bool old_blood)171 void bleed_onto_floor(const coord_def& where, monster_type montype,
172 int damage, bool spatter, const coord_def& from,
173 const bool old_blood)
174 {
175 ASSERT_IN_BOUNDS(where);
176
177 if (montype == MONS_PLAYER && !you.can_bleed())
178 return;
179
180 if (montype != NUM_MONSTERS && montype != MONS_PLAYER)
181 {
182 monster m;
183 m.type = montype;
184 if (!m.can_bleed())
185 return;
186 }
187
188 _maybe_bloodify_square(where, damage, spatter, from, old_blood);
189 }
190
blood_spray(const coord_def & origin,monster_type montype,int level)191 void blood_spray(const coord_def& origin, monster_type montype, int level)
192 {
193 int tries = 0;
194 for (int i = 0; i < level; ++i)
195 {
196 // Blood drops are small and light and suffer a lot of wind
197 // resistance.
198 int range = random2(8) + 1;
199
200 while (tries < 5000)
201 {
202 ++tries;
203
204 coord_def bloody = origin;
205 bloody.x += random_range(-range, range);
206 bloody.y += random_range(-range, range);
207
208 if (in_bounds(bloody) && cell_see_cell(origin, bloody, LOS_SOLID))
209 {
210 bleed_onto_floor(bloody, montype, 99, false, origin);
211 break;
212 }
213 }
214 }
215 }
216
_spatter_neighbours(const coord_def & where,int chance,const coord_def & from=INVALID_COORD)217 static void _spatter_neighbours(const coord_def& where, int chance,
218 const coord_def& from = INVALID_COORD)
219 {
220 for (adjacent_iterator ai(where, false); ai; ++ai)
221 {
222 if (!_allow_bleeding_on_square(*ai))
223 continue;
224
225 if (one_chance_in(chance))
226 {
227 env.pgrid(*ai) |= FPROP_BLOODY;
228 _orient_wall_blood(where, from, true);
229 _spatter_neighbours(*ai, chance+1, from);
230 }
231 }
232 }
233
generate_random_blood_spatter_on_level(const map_bitmask * susceptible_area)234 void generate_random_blood_spatter_on_level(const map_bitmask *susceptible_area)
235 {
236 const int max_cluster = 7 + random2(9);
237
238 // Lower chances for large bloodshed areas if we have many clusters,
239 // but increase chances if we have few.
240 // Chances for startprob are [1..3] for 7-9 clusters,
241 // ... [1..4] for 10-12 clusters, and
242 // ... [2..5] for 13-15 clusters.
243
244 int min_prob = 1;
245 int max_prob = 4;
246
247 if (max_cluster < 10)
248 max_prob--;
249 else if (max_cluster > 12)
250 min_prob++;
251
252 for (int i = 0; i < max_cluster; ++i)
253 {
254 const coord_def c = random_in_bounds();
255
256 if (susceptible_area && !(*susceptible_area)(c))
257 continue;
258
259 // startprob is used to initialise the chance for neighbours
260 // being spattered, which will be decreased by 1 per recursion
261 // round. We then use one_chance_in(chance) to determine
262 // whether to spatter a given grid or not. Thus, startprob = 1
263 // means that initially all surrounding grids will be
264 // spattered (3x3), and the _higher_ startprob the _lower_ the
265 // overall chance for spattering and the _smaller_ the
266 // bloodshed area.
267 const int startprob = min_prob + random2(max_prob);
268
269 maybe_bloodify_square(c);
270
271 _spatter_neighbours(c, startprob);
272 }
273 }
274