-
Notifications
You must be signed in to change notification settings - Fork 36
Expand file tree
/
Copy pathlife.dm
More file actions
369 lines (312 loc) · 16.5 KB
/
Copy pathlife.dm
File metadata and controls
369 lines (312 loc) · 16.5 KB
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
/**
* Handles the biological and general over-time processes of the mob.
*
*
* Arguments:
* - seconds_per_tick: The amount of time that has elapsed since this last fired.
* - times_fired: The number of times SSmobs has fired
*/
/mob/living/proc/Life(seconds_per_tick = SSMOBS_DT, times_fired)
set waitfor = FALSE
var/signal_result = SEND_SIGNAL(src, COMSIG_LIVING_LIFE, seconds_per_tick, times_fired)
if(signal_result & COMPONENT_LIVING_CANCEL_LIFE_PROCESSING) // mmm less work
return
if (client)
var/turf/T = get_turf(src)
if(!T)
move_to_error_room()
var/msg = " was found to have no .loc with an attached client, if the cause is unknown it would be wise to ask how this was accomplished."
message_admins(ADMIN_LOOKUPFLW(src) + msg)
send2tgs_adminless_only("Mob", key_name_and_tag(src) + msg, R_ADMIN)
src.log_message("was found to have no .loc with an attached client.", LOG_GAME)
// This is a temporary error tracker to make sure we've caught everything
else if (registered_z != T.z)
#ifdef TESTING
message_admins("[ADMIN_LOOKUPFLW(src)] has somehow ended up in Z-level [T.z] despite being registered in Z-level [registered_z]. If you could ask them how that happened and notify coderbus, it would be appreciated.")
#endif
log_game("Z-TRACKING: [src] has somehow ended up in Z-level [T.z] despite being registered in Z-level [registered_z].")
update_z(T.z)
else if (registered_z)
log_game("Z-TRACKING: [src] of type [src.type] has a Z-registration despite not having a client.")
update_z(null)
if(isnull(loc) || HAS_TRAIT(src, TRAIT_NO_TRANSFORM))
return
if(!HAS_TRAIT(src, TRAIT_STASIS))
if(stat != DEAD)
//Mutations and radiation
handle_mutations(seconds_per_tick, times_fired)
//Breathing, if applicable
handle_breathing(seconds_per_tick, times_fired)
handle_diseases(seconds_per_tick, times_fired)// DEAD check is in the proc itself; we want it to spread even if the mob is dead, but to handle its disease-y properties only if you're not.
if (QDELETED(src)) // diseases can qdel the mob via transformations
return
if(stat != DEAD)
//Random events (vomiting etc)
handle_random_events(seconds_per_tick, times_fired)
//Handle temperature/pressure differences between body and environment
var/datum/gas_mixture/environment = loc.return_air()
if(environment)
handle_environment(environment, seconds_per_tick, times_fired)
body_temperature_damage(environment, seconds_per_tick, times_fired)
if(stat <= SOFT_CRIT && !on_fire)
temperature_homeostasis(seconds_per_tick, times_fired)
handle_gravity(seconds_per_tick, times_fired)
if(stat != DEAD)
body_temperature_alerts()
handle_wounds(seconds_per_tick, times_fired)
if(staminaloss)
adjustStaminaLoss(-1 * (stat == DEAD ? 100 : 2.5) * seconds_per_tick)
if(machine)
machine.check_eye(src)
if(living_flags & QUEUE_NUTRITION_UPDATE)
mob_mood?.update_nutrition_moodlets()
hud_used?.hunger?.update_hunger_bar()
living_flags &= ~QUEUE_NUTRITION_UPDATE
if(stat != DEAD)
return 1
/mob/living/proc/handle_breathing(seconds_per_tick, times_fired)
SEND_SIGNAL(src, COMSIG_LIVING_HANDLE_BREATHING, seconds_per_tick, times_fired)
return
/mob/living/proc/body_temperature_damage(datum/gas_mixture/environment, seconds_per_tick, times_fired)
if(body_temperature > bodytemp_heat_damage_limit \
&& !HAS_TRAIT(src, TRAIT_RESISTHEAT) \
&& !has_reagent(/datum/reagent/medicine/pyroxadone, needs_metabolizing = TRUE))
var/heat_diff = bodytemp_heat_damage_limit - standard_body_temperature
var/heat_threshold_low = bodytemp_heat_damage_limit + heat_diff * 0.75
var/heat_threshold_medium = bodytemp_heat_damage_limit + heat_diff * 1.25
var/heat_threshold_high = bodytemp_heat_damage_limit + heat_diff * 2
var/firemodifier = round(fire_stacks, 1) * 0.01
if (!on_fire) // We are not on fire, reduce the modifier
firemodifier = min(firemodifier, 0) // Note that wetstacks make us take less burn damage
// convering back and forth so we can apply a multiplier from firestacks without sending temp to the moon
var/effective_temp = CELCIUS_TO_KELVIN(KELVIN_TO_CELCIUS(body_temperature) * (1 + firemodifier))
var/burn_damage = HEAT_DAMAGE
if(effective_temp > heat_threshold_high)
burn_damage *= 8
else if(effective_temp > heat_threshold_medium)
burn_damage *= 4
else if(effective_temp > heat_threshold_low)
burn_damage *= 2
if(temperature_burns(burn_damage * seconds_per_tick) > 0 && SPT_PROB(10, seconds_per_tick) && !temperature_insulation && !on_fire)
var/expected_temp = get_temperature(loc?.return_air())
var/insulation = get_insulation(expected_temp)
if(insulation > 0.5 && expected_temp < body_temperature)
to_chat(src, span_danger("Your insulation is keeping you warmer!"))
if(effective_temp > heat_threshold_medium)
apply_status_effect(/datum/status_effect/stacking/heat_exposure, 1, heat_threshold_medium)
// For cold damage, we cap at the threshold if you're dead
if(body_temperature < bodytemp_cold_damage_limit \
&& !HAS_TRAIT(src, TRAIT_RESISTCOLD) \
&& !has_reagent(/datum/reagent/medicine/cryoxadone, needs_metabolizing = TRUE) \
&& (getFireLoss() < maxHealth || stat != DEAD))
var/cold_diff = bodytemp_cold_damage_limit - standard_body_temperature
var/cold_threshold_low = bodytemp_cold_damage_limit + cold_diff * 1.2
var/cold_threshold_medium = bodytemp_cold_damage_limit + cold_diff * 1.75
var/cold_threshold_high = bodytemp_cold_damage_limit + cold_diff * 2
var/cold_damage = COLD_DAMAGE
if(body_temperature < cold_threshold_high)
cold_damage *= 8
else if(body_temperature < cold_threshold_medium)
cold_damage *= 4
else if(body_temperature < cold_threshold_low)
cold_damage *= 2
if(temperature_cold_damage(cold_damage * seconds_per_tick) > 0 && SPT_PROB(10, seconds_per_tick) && !temperature_insulation)
var/expected_temp = get_temperature(loc?.return_air())
var/insulation = get_insulation(expected_temp)
if(insulation > 0.5 && expected_temp > body_temperature)
to_chat(src, span_danger("You insulation is keeping you cooler!"))
/mob/living/carbon/body_temperature_damage(datum/gas_mixture/environment, seconds_per_tick, times_fired)
if(body_temperature > bodytemp_heat_damage_limit && !HAS_TRAIT(src, TRAIT_RESISTHEAT))
apply_status_effect(/datum/status_effect/thermia/hyper)
if(body_temperature < bodytemp_cold_damage_limit && !HAS_TRAIT(src, TRAIT_RESISTCOLD))
apply_status_effect(/datum/status_effect/thermia/hypo)
/// Applies damage to the mob due to being too cold
/mob/living/proc/temperature_cold_damage(damage)
return apply_damage(damage, HAS_TRAIT(src, TRAIT_HULK) ? BRUTE : BURN, spread_damage = TRUE, wound_bonus = CANT_WOUND)
/mob/living/carbon/human/temperature_cold_damage(damage)
damage *= physiology.cold_mod
return ..()
/// Applies damage to the mob due to being too hot
/mob/living/proc/temperature_burns(damage)
return apply_damage(damage, BURN, spread_damage = TRUE, wound_bonus = CANT_WOUND)
/mob/living/carbon/human/temperature_burns(damage)
damage *= physiology.heat_mod
return ..()
/mob/living/proc/body_temperature_alerts()
// give out alerts based on how the skin feels, not how the body is
// this gives us an early warning system - since we tend to trend up/down to skin temperature -
// how we're going to be feeling soon if we don't change our environment
var/feels_like = get_skin_temperature()
var/hot_diff = bodytemp_heat_damage_limit - standard_body_temperature
var/hot_threshold_low = bodytemp_heat_damage_limit - hot_diff * 0.5
var/hot_threshold_medium = bodytemp_heat_damage_limit
var/hot_threshold_high = bodytemp_heat_damage_limit + hot_diff
// Body temperature is too hot, and we do not have resist traits
if(feels_like > hot_threshold_low && !HAS_TRAIT(src, TRAIT_RESISTHEAT))
clear_mood_event("cold")
// Clear cold once we return to warm
remove_movespeed_modifier(/datum/movespeed_modifier/cold)
if(feels_like > hot_threshold_high)
throw_alert(ALERT_TEMPERATURE, /atom/movable/screen/alert/hot, 3)
add_mood_event("hot", /datum/mood_event/overhot)
else if(feels_like > hot_threshold_medium)
throw_alert(ALERT_TEMPERATURE, /atom/movable/screen/alert/hot, 2)
add_mood_event("hot", /datum/mood_event/hot)
else
throw_alert(ALERT_TEMPERATURE, /atom/movable/screen/alert/hot, 1)
add_mood_event("hot", /datum/mood_event/warm)
temp_alerts = TRUE
var/cold_diff = bodytemp_cold_damage_limit - standard_body_temperature
var/cold_threshold_low = bodytemp_cold_damage_limit - cold_diff * 0.5
var/cold_threshold_medium = bodytemp_cold_damage_limit
var/cold_threshold_high = bodytemp_cold_damage_limit + cold_diff
// Body temperature is too cold, and we do not have resist traits
if(feels_like < cold_threshold_low && !HAS_TRAIT(src, TRAIT_RESISTCOLD))
clear_mood_event("hot")
if(feels_like < cold_threshold_high)
throw_alert(ALERT_TEMPERATURE, /atom/movable/screen/alert/cold, 3)
add_mood_event("cold", /datum/mood_event/freezing)
else if(feels_like < cold_threshold_medium)
throw_alert(ALERT_TEMPERATURE, /atom/movable/screen/alert/cold, 2)
add_mood_event("cold", /datum/mood_event/cold)
else
throw_alert(ALERT_TEMPERATURE, /atom/movable/screen/alert/cold, 1)
add_mood_event("cold", /datum/mood_event/chilly)
temp_alerts = TRUE
// Only apply slowdown if the body is cold rather than the skin
if(body_temperature < cold_threshold_medium && !HAS_TRAIT(src, TRAIT_RESISTCOLD))
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/cold, multiplicative_slowdown = ((cold_threshold_medium - body_temperature) / COLD_SLOWDOWN_FACTOR))
else if(LAZYACCESS(movespeed_modification, "[/datum/movespeed_modifier/cold]"))
remove_movespeed_modifier(/datum/movespeed_modifier/cold)
// We are not to hot or cold, remove status and moods
if(temp_alerts && (feels_like < hot_threshold_low || HAS_TRAIT(src, TRAIT_RESISTHEAT)) && (feels_like > cold_threshold_low || HAS_TRAIT(src, TRAIT_RESISTCOLD)))
clear_alert(ALERT_TEMPERATURE)
clear_mood_event("cold")
clear_mood_event("hot")
temp_alerts = FALSE
/mob/living/silicon/body_temperature_alerts()
return // Not yet
/mob/living/proc/handle_mutations(seconds_per_tick, times_fired)
return
/mob/living/proc/handle_diseases(seconds_per_tick, times_fired)
return
/mob/living/proc/handle_wounds(seconds_per_tick, times_fired)
return
/mob/living/proc/handle_random_events(seconds_per_tick, times_fired)
return
/**
* Handle this mob's interactions with the environment
*
* By default handles body temperature normalization to the area's temperature,
* but also handles pressure for many mobs
*
* Arguments:
* * environment: The gas mixture of the area the mob is in, will never be null
* * seconds_per_tick: The amount of time that has elapsed since this last fired.
* * times_fired: The number of times SSmobs has fired
*/
/mob/living/proc/handle_environment(datum/gas_mixture/environment, seconds_per_tick, times_fired)
var/loc_temp = get_temperature(environment)
var/thermal_protection = get_insulation(loc_temp)
// calculate a target temperature based on environment vs insulation
var/equilibrium_temp = loc_temp + (standard_body_temperature - loc_temp) * (thermal_protection * 0.5)
var/temp_delta = equilibrium_temp - body_temperature
if(temp_delta == 0)
return
if(temp_delta < 0 && on_fire) // do not reduce body temp when on fire
return
// Adds a modifier onto how strong insulation is
// If we are above SBT, we are overheating and sweating, which reduces insulation
var/protection_modifier = body_temperature > standard_body_temperature + 2 KELVIN ? 0.7 : 1
var/temp_sign = SIGN(temp_delta)
var/temp_change = temp_sign * (1 - (thermal_protection * protection_modifier)) * ((0.1 * max(1, abs(temp_delta))) ** 1.8) * temperature_normalization_speed
// Cap increase and decrease
temp_change = temp_change < 0 ? max(temp_change, BODYTEMP_ENVIRONMENT_COOLING_MAX) : min(temp_change, BODYTEMP_ENVIRONMENT_HEATING_MAX)
// Boost when returning to equilibrium
if(equilibrium_temp < standard_body_temperature - 2 KELVIN || equilibrium_temp > standard_body_temperature + 2 KELVIN)
temp_change *= 3
adjust_body_temperature(temp_change * seconds_per_tick) // no use_insulation beacuse we account for it manually
/mob/living/silicon/handle_environment(datum/gas_mixture/environment, seconds_per_tick, times_fired)
return // Not yet
/**
* Handles this mob internally managing its body temperature (sweating or generating heat)
*
* Arguments:
* * seconds_per_tick: The amount of time that has elapsed since this last fired.
* * times_fired: The number of times SSmobs has fired
*/
/mob/living/proc/temperature_homeostasis(seconds_per_tick, times_fired)
if(temperature_homeostasis_speed == 0) // cold blooded creature
return
if(!HAS_TRAIT(src, TRAIT_NOHUNGER) && nutrition < (NUTRITION_LEVEL_STARVING / 3))
return
// find exactly what temperature we're aiming for
var/homeostasis_target = standard_body_temperature
if(LAZYLEN(homeostasis_targets))
homeostasis_target = 0
for(var/source in homeostasis_targets)
homeostasis_target += homeostasis_targets[source]
homeostasis_target /= LAZYLEN(homeostasis_targets)
// temperature delta is capped, so you can't attempt to homeostaize from vacuum to standard temp in a second
var/temp_delta = (homeostasis_target - body_temperature)
temp_delta = temp_delta < 0 ? max(temp_delta, BODYTEMP_HOMEOSTASIS_COOLING_MAX) : min(temp_delta, BODYTEMP_HOMEOSTASIS_HEATING_MAX)
// note: Because this scales by metabolism efficiency, being well fed boosts your homeostasis, and being poorly fed reduces it
var/natural_change = temp_delta * metabolism_efficiency * temperature_homeostasis_speed
if(natural_change == 0)
return
var/sigreturn = SEND_SIGNAL(src, COMSIG_LIVING_HOMEOSTASIS, natural_change, seconds_per_tick)
if(sigreturn & HOMEOSTASIS_HANDLED)
return
var/min = natural_change < 0 ? homeostasis_target : 0
var/max = natural_change > 0 ? homeostasis_target : INFINITY
// calculates how much nutrition decay per kelvin of temperature change
// while having this scale may be confusing, it's to make sure that stepping into an extremely cold environment (space)
// doesn't immediately drain nutrition to zero in under a minute
// at 0.25 kelvin, nutrition_per_kelvin is 2.5. at 1, it's ~1.5, and at 4, it's 1.
var/nutrition_per_kelvin = round(2.5 / ((abs(natural_change) / 0.25) ** 0.33), 0.01)
adjust_body_temperature(natural_change * seconds_per_tick, min_temp = min, max_temp = max) // no use_insulation beacuse this is internal
if(!(sigreturn & HOMEOSTASIS_NO_HUNGER))
adjust_nutrition(-1 * HOMEOSTASIS_HUNGER_MULTIPLIER * HUNGER_FACTOR * nutrition_per_kelvin * abs(natural_change) * seconds_per_tick)
/mob/living/silicon/temperature_homeostasis(seconds_per_tick, times_fired)
return // Not yet
/**
* Get the fullness of the mob
*
* Fullness is a representation of how much nutrition the mob has,
* including the nutrition of stuff yet to be digested (reagents in blood / stomach)
*
* * only_consumable - if TRUE, only consumable reagents are counted.
* Otherwise, all reagents contribute to fullness, despite not adding nutrition as they process.
*
* Returns a number representing fullness, scaled similarly to nutrition.
*/
/mob/living/proc/get_fullness(only_consumable)
var/fullness = nutrition
// we add the nutrition value of what we're currently digesting
for(var/datum/reagent/consumable/bits in reagents.reagent_list)
fullness += bits.get_nutriment_factor(src) * bits.volume / bits.metabolization_rate
return fullness
/**
* Check if the mob contains this reagent.
*
* This will validate the the reagent holder for the mob and any sub holders contain the requested reagent.
* Vars:
* * reagent (typepath) takes a PATH to a reagent.
* * amount (int) checks for having a specific amount of that chemical.
* * needs_metabolizing (bool) takes into consideration if the chemical is matabolizing when it's checked.
*/
/mob/living/proc/has_reagent(reagent, amount = -1, needs_metabolizing = FALSE)
return reagents.has_reagent(reagent, amount, needs_metabolizing)
/mob/living/proc/handle_gravity(seconds_per_tick, times_fired)
if(gravity_state > STANDARD_GRAVITY)
handle_high_gravity(gravity_state, seconds_per_tick, times_fired)
/mob/living/proc/gravity_animate()
if(!get_filter("gravity"))
add_filter("gravity",1,list("type"="motion_blur", "x"=0, "y"=0))
animate(get_filter("gravity"), y = 1, time = 10, loop = -1)
animate(y = 0, time = 10)
/mob/living/proc/handle_high_gravity(gravity, seconds_per_tick, times_fired)
if(gravity < GRAVITY_DAMAGE_THRESHOLD) //Aka gravity values of 3 or more
return
var/grav_strength = gravity - GRAVITY_DAMAGE_THRESHOLD
adjustBruteLoss(min(GRAVITY_DAMAGE_SCALING * grav_strength, GRAVITY_DAMAGE_MAXIMUM) * seconds_per_tick)