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