1 /**
2  * @file
3  * @brief Functions for corpse handling.
4  **/
5 
6 #include "AppHdr.h"
7 
8 #include "corpse.h"
9 
10 #include "bloodspatter.h"
11 #include "defines.h"
12 #include "env.h"
13 #include "item-name.h"
14 #include "item-prop.h"
15 #include "item-status-flag-type.h"
16 #include "items.h"
17 #include "makeitem.h"
18 #include "mon-util.h"
19 
20 /**
21  * Check whether an item decays over time. (Corpses, chunks, and blood.)
22  *
23  * @param item      The item to check.
24  * @return          Whether the item changes (rots) over time.
25  */
_item_needs_rot_check(const item_def & item)26 static bool _item_needs_rot_check(const item_def &item)
27 {
28     if (!item.defined())
29         return false;
30 
31     if (item.props.exists(CORPSE_NEVER_DECAYS))
32         return false;
33 
34     return item.base_type == OBJ_CORPSES
35            && item.sub_type <= CORPSE_SKELETON; // XXX: is this needed?
36 }
37 
38 /**
39  * Rot a corpse or skeleton lying on the floor.
40  *
41  * @param it            The corpse or skeleton to rot.
42  * @param mitm_index    The slot of the corpse in the floor item array.
43  * @param rot_time      The amount of time to rot the corpse for.
44  */
_rot_corpse(item_def & it,int mitm_index,int rot_time)45 static void _rot_corpse(item_def &it, int mitm_index, int rot_time)
46 {
47     ASSERT(it.base_type == OBJ_CORPSES);
48     ASSERT(!it.props.exists(CORPSE_NEVER_DECAYS));
49 
50     it.freshness -= rot_time;
51     if (it.freshness > 0)
52         return;
53 
54     if (it.sub_type == CORPSE_SKELETON || !mons_skeleton(it.mon_type))
55     {
56         item_was_destroyed(it);
57         destroy_item(mitm_index);
58     }
59     else
60         turn_corpse_into_skeleton(it);
61 }
62 
63 /**
64  * Decay corpses
65  *
66  * @param elapsedTime   The amount of time to rot the corpses for.
67  */
rot_corpses(int elapsedTime)68 void rot_corpses(int elapsedTime)
69 {
70     if (elapsedTime <= 0)
71         return;
72 
73     const int rot_time = elapsedTime / ROT_TIME_FACTOR;
74 
75     for (int mitm_index = 0; mitm_index < MAX_ITEMS; ++mitm_index)
76     {
77         item_def &it = env.item[mitm_index];
78 
79         if (is_shop_item(it) || !_item_needs_rot_check(it))
80             continue;
81 
82         if (it.base_type == OBJ_CORPSES)
83             _rot_corpse(it, mitm_index, rot_time);
84     }
85 }
86 
87 /** Skeletonise this corpse.
88  *
89  *  @param item the corpse to be turned into a skeleton.
90  *  @returns whether a valid skeleton could be made.
91  */
turn_corpse_into_skeleton(item_def & item)92 bool turn_corpse_into_skeleton(item_def &item)
93 {
94     ASSERT(item.base_type == OBJ_CORPSES);
95     ASSERT(item.sub_type == CORPSE_BODY);
96 
97     // Some monsters' corpses lack the structure to leave skeletons
98     // behind.
99     if (!mons_skeleton(item.mon_type))
100         return false;
101 
102     item.sub_type = CORPSE_SKELETON;
103     item.freshness = FRESHEST_CORPSE; // reset rotting counter
104     item.rnd = 1 + random2(255); // not sure this is necessary, but...
105     item.props.erase(FORCED_ITEM_COLOUR_KEY);
106     return true;
107 }
108 
_bleed_monster_corpse(const item_def & corpse)109 static void _bleed_monster_corpse(const item_def &corpse)
110 {
111     const coord_def pos = item_pos(corpse);
112     if (!pos.origin())
113     {
114         const int max_chunks = max_corpse_chunks(corpse.mon_type);
115         bleed_onto_floor(pos, corpse.mon_type, max_chunks, true);
116     }
117 }
118 
butcher_corpse(item_def & item,bool skeleton)119 void butcher_corpse(item_def &item, bool skeleton)
120 {
121     item_was_destroyed(item);
122     if (!mons_skeleton(item.mon_type))
123         skeleton = false;
124     if (skeleton)
125     {
126         _bleed_monster_corpse(item);
127         turn_corpse_into_skeleton(item);
128     }
129     else
130     {
131         _bleed_monster_corpse(item);
132         destroy_item(item.index());
133     }
134 }
135