-
-
Notifications
You must be signed in to change notification settings - Fork 452
/
organ_internal.dm
281 lines (241 loc) · 9.47 KB
/
organ_internal.dm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
/obj/item/organ
name = "organ"
icon = 'icons/obj/surgery.dmi'
var/mob/living/carbon/owner = null
var/status = ORGAN_ORGANIC
w_class = WEIGHT_CLASS_SMALL
throwforce = 0
var/zone = BODY_ZONE_CHEST
var/slot
// What biotypes is this organ compatible with? Assume it's a flesh-compatible organ by default.
var/compatible_biotypes = MOB_ORGANIC|MOB_INORGANIC|MOB_UNDEAD
// DO NOT add slots with matching names to different zones - it will break internal_organs_slot list!
var/organ_flags = 0
var/maxHealth = STANDARD_ORGAN_THRESHOLD
///total damage this organ has sustained
var/damage = 0
///how functional this organ is, higher numbers = stronger lower = garbage, scales multiplicitively with health (50% health = *50% efficiency)
var/organ_efficiency = 1
///Healing factor and decay factor function on % of maxhealth, and do not work by applying a static number per tick
var/healing_factor = 0 //fraction of maxhealth healed per on_life(), set to 0 for generic organs
var/decay_factor = 0 //same as above but when without a living owner, set to 0 for generic organs
var/life_tick = 0
var/high_threshold = STANDARD_ORGAN_THRESHOLD * 0.45 //when severe organ damage occurs
var/low_threshold = STANDARD_ORGAN_THRESHOLD * 0.1 //when minor organ damage occurs
///Organ variables for determining what we alert the owner with when they pass/clear the damage thresholds
var/prev_damage = 0
var/low_threshold_passed
var/high_threshold_passed
var/now_failing
var/now_fixed
var/high_threshold_cleared
var/low_threshold_cleared
///Do we effect the appearance of our mob. Used to save time in preference code
var/visual = TRUE
/obj/item/organ/proc/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE,special_zone = null)
if(!iscarbon(M) || owner == M)
return
if(special_zone)
zone = special_zone
var/obj/item/organ/replaced = M.getorganslot(slot)
if(replaced && !special_zone)
replaced.Remove(M, special = 1)
if(drop_if_replaced)
replaced.forceMove(get_turf(M))
else
qdel(replaced)
owner = M
M.internal_organs |= src
M.internal_organs_slot[slot] = src
moveToNullspace()
for(var/X in actions)
var/datum/action/A = X
A.Grant(M)
SEND_SIGNAL(M, COMSIG_CARBON_GAIN_ORGAN, src, special)
//Special is for instant replacement like autosurgeons
/obj/item/organ/proc/Remove(mob/living/carbon/M, special = FALSE)
owner = null
if(M)
M.internal_organs -= src
if(M.internal_organs_slot[slot] == src)
M.internal_organs_slot.Remove(slot)
if((organ_flags & ORGAN_VITAL) && !special && !(M.status_flags & GODMODE))
M.death()
for(var/X in actions)
var/datum/action/A = X
A.Remove(M)
SEND_SIGNAL(M, COMSIG_CARBON_LOSE_ORGAN, src, special)
/obj/item/organ/proc/on_find(mob/living/finder)
return
/obj/item/organ/proc/can_extract(mob/living/finder)
return TRUE
/obj/item/organ/process() //runs decay when outside of a person
if((organ_flags & (ORGAN_SYNTHETIC | ORGAN_FROZEN)) || istype(loc, /obj/item/mmi))
return
if(damage >= maxHealth)
organ_flags |= ORGAN_FAILING
damage = maxHealth
return
else if(!owner)
damage = min(maxHealth, damage + (maxHealth * decay_factor))
else
var/mob/living/carbon/C = owner
if(!C)
return
life_tick++
if((C.stat == DEAD || !(compatible_biotypes & owner.mob_biotypes)) && !HAS_TRAIT(C, TRAIT_PRESERVED_ORGANS)) // organic organs decompose inside incompatible bodies
if(damage >= maxHealth)
organ_flags |= ORGAN_FAILING
damage = maxHealth
return
damage = min(maxHealth, damage + (maxHealth * decay_factor))
/obj/item/organ/proc/on_life() //repair organ damage if the organ is not failing
var/mob/living/carbon/C = owner
if(!C)
return
if(damage >= maxHealth)
organ_flags |= ORGAN_FAILING
damage = maxHealth
check_damage_thresholds(C)
prev_damage = damage
return
if((!(organ_flags & ORGAN_FAILING)) && (C.stat !=DEAD))
///Damage decrements by a percent of its maxhealth
damage = max(0, damage - (maxHealth * healing_factor))
if(C.satiety > 0)
///Damage decrements again by a percent of its maxhealth, up to a total of 4 extra times depending on the owner's health
damage = max(0, damage - ((maxHealth * healing_factor) * (C.satiety / MAX_SATIETY) * 4))
check_damage_thresholds(C)
prev_damage = damage
/** check_damage_thresholds
* input: M (a mob, the owner of the organ we call the proc on)
* output:
* description: By checking our current damage against our previous damage, we can decide whether we've passed an organ threshold.
* If we have, send the corresponding threshold message to the owner, if such a message exists.
*/
/obj/item/organ/proc/check_damage_thresholds(M)
if(damage == prev_damage)
return
var/delta = damage - prev_damage
if(delta > 0)
if(damage == maxHealth)
if(now_failing)
to_chat(M, now_failing)
else if(damage > high_threshold && prev_damage <= high_threshold)
if(high_threshold_passed)
to_chat(M, high_threshold_passed)
else if(damage > low_threshold && prev_damage <= low_threshold)
if(low_threshold_passed)
to_chat(M, low_threshold_passed)
else if(delta < 0)
if(prev_damage > low_threshold && damage <= low_threshold)
if(low_threshold_cleared)
to_chat(M, low_threshold_cleared)
else if(prev_damage > high_threshold && damage <= high_threshold)
if(high_threshold_cleared)
to_chat(M, high_threshold_cleared)
else if(prev_damage == maxHealth)
if(now_fixed)
to_chat(M, now_fixed)
/obj/item/organ/examine(mob/user)
. = ..()
if(status == ORGAN_ROBOTIC && (organ_flags & ORGAN_FAILING))
. += span_warning("[src] seems to be broken!")
else if(organ_flags & ORGAN_FAILING)
. += span_warning("[src] has decayed for too long, and has turned a sickly color! It doesn't look like it will work anymore!")
else if(damage > high_threshold)
. += span_warning("[src] is starting to look discolored.")
/obj/item/organ/proc/prepare_eat()
var/obj/item/reagent_containers/food/snacks/organ/S = new
S.name = name
S.desc = desc
S.icon = icon
S.icon_state = icon_state
S.w_class = w_class
return S
/obj/item/reagent_containers/food/snacks/organ
name = "appendix"
icon_state = "appendix"
icon = 'icons/obj/surgery.dmi'
list_reagents = list(/datum/reagent/consumable/nutriment = 5)
foodtype = RAW | MEAT | GROSS
/obj/item/organ/Initialize(mapload)
START_PROCESSING(SSobj, src)
return ..()
///Used as callbacks by object pooling
/obj/item/organ/proc/exit_wardrobe()
START_PROCESSING(SSobj, src)
//See above
/obj/item/organ/proc/enter_wardrobe()
STOP_PROCESSING(SSobj, src)
/obj/item/organ/Destroy()
STOP_PROCESSING(SSobj, src)
if(owner)
// The special flag is important, because otherwise mobs can die
// while undergoing transformation into different mobs.
Remove(owner, special=TRUE)
return ..()
/obj/item/organ/attack(mob/living/carbon/M, mob/user)
if(M == user && ishuman(user))
var/mob/living/carbon/human/H = user
if(status == ORGAN_ORGANIC)
var/obj/item/reagent_containers/food/snacks/S = prepare_eat(H)
if(S)
qdel(src)
if(H.put_in_active_hand(S))
S.attack(H, H)
else
..()
/obj/item/organ/item_action_slot_check(slot,mob/user)
return //so we don't grant the organ's action to mobs who pick up the organ.
///returns an organ's efficiency, a percent value (rounded to the 10s) based on damage that is multiplied by organ_efficiency
/obj/item/organ/proc/get_organ_efficiency()
return damage < low_threshold ? organ_efficiency : round(organ_efficiency * 1-(damage/maxHealth), 0.1)
///Adjusts an organ's damage by the amount "d", up to a maximum amount, which is by default max damage
/obj/item/organ/proc/applyOrganDamage(d, maximum = maxHealth) //use for damaging effects
if(maximum < d + damage)
d = max(0, maximum - damage)
damage = max(0, damage + d)
///SETS an organ's damage to the amount "d", and in doing so clears or sets the failing flag, good for when you have an effect that should fix an organ if broken
/obj/item/organ/proc/setOrganDamage(d) //use mostly for admin heals
damage = clamp(d, 0 ,maxHealth)
if(d >= maxHealth)
organ_flags |= ORGAN_FAILING
else
organ_flags &= ~ORGAN_FAILING
/** get_availability
* returns whether the species should innately have this organ.
*
* regenerate organs works with generic organs, so we need to get whether it can accept certain organs just by what this returns.
* This is set to return true or false, depending on if a species has a trait that would nulify the purpose of the organ.
* For example, lungs won't be given if you have NO_BREATH, stomachs check for NO_HUNGER, and livers check for NO_METABOLISM.
* If you want a carbon to have a trait that normally blocks an organ but still want the organ. Attach the trait to the organ using the organ_traits var
* Arguments:
* owner_species - species, needed to return whether the species has an organ specific trait
*/
/obj/item/organ/proc/get_availability(datum/species/owner_species)
return TRUE
//Looking for brains?
//Try code/modules/mob/living/carbon/brain/brain_item.dm
/mob/living/proc/regenerate_organs()
return 0
/mob/living/carbon/regenerate_organs()
if(dna?.species)
dna.species.regenerate_organs(src)
return
else
if(!getorganslot(ORGAN_SLOT_LUNGS))
var/obj/item/organ/lungs/L = new()
L.Insert(src)
if(!getorganslot(ORGAN_SLOT_HEART))
var/obj/item/organ/heart/H = new()
H.Insert(src)
if(!getorganslot(ORGAN_SLOT_TONGUE))
var/obj/item/organ/tongue/T = new()
T.Insert(src)
if(!getorganslot(ORGAN_SLOT_EYES))
var/obj/item/organ/eyes/E = new()
E.Insert(src)
if(!getorganslot(ORGAN_SLOT_EARS))
var/obj/item/organ/ears/ears = new()
ears.Insert(src)