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