/
human_damage.dm
513 lines (427 loc) · 16.4 KB
/
human_damage.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
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
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
//Updates the mob's health from organs and mob damage variables
/mob/living/carbon/human/updatehealth()
if(status_flags & GODMODE)
health = maxHealth
stat = CONSCIOUS
return
var/total_burn = 0
var/total_brute = 0
for(var/datum/organ/external/O in organs) //hardcoded to streamline things a bit
if(O.is_organic() && O.is_existing())
total_brute += O.brute_dam
total_burn += O.burn_dam
var/prevhealth = health
health = maxHealth - getOxyLoss() - getToxLoss() - getCloneLoss() - total_burn - total_brute
critlog(health,prevhealth)
if((maxHealth - total_burn) < config.health_threshold_dead)
death(FALSE)
ChangeToHusk()
return
/mob/living/carbon/human/getBrainLoss()
var/res = brainloss
if(species && species.has_organ["brain"])
var/datum/organ/internal/brain/sponge = internal_organs_by_name["brain"]
if(!sponge)
res += 200
else
if (sponge.is_bruised())
res += 20
if (sponge.is_broken())
res += 50
res = min(res,maxHealth*2)
return res
return 0
//These procs fetch a cumulative total damage from all organs
/mob/living/carbon/human/getBruteLoss(var/ignore_inorganic = FALSE)
var/amount = 0
for(var/datum/organ/external/O in organs)
if(ignore_inorganic && !O.is_organic())
continue
amount += O.brute_dam
return amount
/mob/living/carbon/human/getFireLoss(var/ignore_inorganic = FALSE)
var/amount = 0
for(var/datum/organ/external/O in organs)
if(ignore_inorganic && !O.is_organic())
continue
amount += O.burn_dam
return amount
/mob/living/carbon/human/adjustBruteLoss(var/amount)
amount = amount * brute_damage_modifier
if(INVOKE_EVENT(src, /event/damaged, "kind" = BRUTE, "amount" = amount))
return 0
if(amount > 0)
take_overall_damage(amount, 0)
else
heal_overall_damage(-amount, 0)
hud_updateflag |= 1 << HEALTH_HUD
/mob/living/carbon/human/adjustFireLoss(var/amount)
amount = amount * burn_damage_modifier
if(INVOKE_EVENT(src, /event/damaged, "kind" = BURN, "amount" = amount))
return 0
if(amount > 0)
take_overall_damage(0, amount)
if(config.burn_damage_ash && amount >= config.burn_damage_ash)
dust(TRUE)
return
else
heal_overall_damage(0, -amount)
hud_updateflag |= 1 << HEALTH_HUD
/mob/living/carbon/human/proc/adjustBruteLossByPart(var/amount, var/organ_name, var/obj/damage_source = null)
amount = amount * brute_damage_modifier
if(INVOKE_EVENT(src, /event/damaged, "kind" = BRUTE, "amount" = amount))
return 0
if (organ_name in organs_by_name)
var/datum/organ/external/O = get_organ(organ_name)
if(amount > 0)
O.take_damage(amount, 0, sharp=damage_source.is_sharp(), edge=damage_source.sharpness_flags & SHARP_BLADE, used_weapon=damage_source)
else
//if you don't want to heal robot organs, they you will have to check that yourself before using this proc.
O.heal_damage(-amount, 0, internal=0, robo_repair=(O.status & ORGAN_ROBOT))
hud_updateflag |= 1 << HEALTH_HUD
/mob/living/carbon/human/proc/adjustFireLossByPart(var/amount, var/organ_name, var/obj/damage_source = null)
amount = amount * burn_damage_modifier
if(INVOKE_EVENT(src, /event/damaged, "kind" = BURN, "amount" = amount))
return 0
if (organ_name in organs_by_name)
var/datum/organ/external/O = get_organ(organ_name)
if(amount > 0)
O.take_damage(0, amount, sharp=damage_source.is_sharp(), edge=damage_source.sharpness_flags & SHARP_BLADE, used_weapon=damage_source)
else
//if you don't want to heal robot organs, they you will have to check that yourself before using this proc.
O.heal_damage(0, -amount, internal=0, robo_repair=(O.status & ORGAN_ROBOT))
hud_updateflag |= 1 << HEALTH_HUD
/mob/living/carbon/human/Stun(amount)
if(M_HULK in mutations)
return
..()
/mob/living/carbon/human/Knockdown(amount)
if(M_HULK in mutations)
return
handle_spaghetti(100)
..()
/mob/living/carbon/human/Paralyse(amount)
if(M_HULK in mutations)
return
..()
/mob/living/carbon/human/adjustCloneLoss(var/amount)
..()
amount = amount * clone_damage_modifier
if(isslimeperson(src))
amount = 0
if(INVOKE_EVENT(src, /event/damaged, "kind" = CLONE, "amount" = amount))
return 0
var/heal_prob = max(0, 80 - getCloneLoss())
var/mut_prob = min(80, getCloneLoss()+10)
if (amount > 0)
if (prob(mut_prob))
var/list/datum/organ/external/candidates = list()
for (var/datum/organ/external/O in organs)
if(O.is_organic() && O.is_usable())
candidates |= O
if (candidates.len)
var/datum/organ/external/O = pick(candidates)
O.mutate()
to_chat(src, "<span class = 'notice'>Something is not right with your [O.display_name]...</span>")
return
else
if (prob(heal_prob))
for (var/datum/organ/external/O in organs)
if (O.is_existing() && O.status & ORGAN_MUTATED)
O.unmutate()
to_chat(src, "<span class = 'notice'>Your [O.display_name] is shaped normally again.</span>")
return
if (getCloneLoss() < 1)
for (var/datum/organ/external/O in organs)
if (O.is_existing() && O.status & ORGAN_MUTATED)
O.unmutate()
to_chat(src, "<span class = 'notice'>Your [O.display_name] is shaped normally again.</span>")
hud_updateflag |= 1 << HEALTH_HUD
////////////////////////////////////////////
//Returns a list of damaged organs
/mob/living/carbon/human/proc/get_damaged_organs(var/brute, var/burn, var/ignore_inorganic = FALSE)
var/list/datum/organ/external/parts = list()
for(var/datum/organ/external/O in organs)
if(((brute && O.brute_dam) || (burn && O.burn_dam)) && !(ignore_inorganic && !O.is_organic()))
parts += O
return parts
//Returns a list of damageable organs
/mob/living/carbon/human/proc/get_damageable_organs(var/ignore_inorganics = FALSE)
var/list/datum/organ/external/parts = list()
for(var/datum/organ/external/O in organs)
if(!O.is_existing())
continue
if(ignore_inorganics && !O.is_organic())
continue
if(O.brute_dam + O.burn_dam < O.max_damage)
parts += O
return parts
//Heals ONE external organ, organ gets randomly selected from damaged ones.
//It automatically updates damage overlays if necesary
//It automatically updates health status
/mob/living/carbon/human/heal_organ_damage(var/brute, var/burn)
var/list/datum/organ/external/parts = get_damaged_organs(brute,burn,TRUE)
if(!parts.len)
return
var/datum/organ/external/picked = pick(parts)
if(picked.heal_damage(brute,burn))
UpdateDamageIcon()
hud_updateflag |= 1 << HEALTH_HUD
updatehealth()
/*
In most cases it makes more sense to use apply_damage() instead! And make sure to check armour if applicable.
*/
//Damages ONE external organ, organ gets randomly selected from damagable ones.
//It automatically updates damage overlays if necesary
//It automatically updates health status
/mob/living/carbon/human/take_organ_damage(var/brute, var/burn, var/sharp = 0, var/edge = 0, var/ignore_inorganics = FALSE)
var/list/datum/organ/external/parts = get_damageable_organs(ignore_inorganics)
if(!parts.len)
return
var/datum/organ/external/picked = pick(parts)
if(picked.take_damage(brute,burn,sharp,edge))
UpdateDamageIcon()
hud_updateflag |= 1 << HEALTH_HUD
updatehealth()
//speech_problem_flag = 1
//Heal MANY external organs, in random order
/mob/living/carbon/human/heal_overall_damage(var/brute, var/burn)
var/list/datum/organ/external/parts = get_damaged_organs(brute,burn,TRUE)
var/datum/organ/internal/heart/hivelord/H = get_heart()
if(istype(H)) // hivelord hearts just heal better
brute *= 2
burn *= 2
var/update = 0
while(parts.len && (brute>0 || burn>0) )
var/datum/organ/external/picked = pick(parts)
var/brute_was = picked.brute_dam
var/burn_was = picked.burn_dam
update |= picked.heal_damage(brute,burn)
brute -= (brute_was-picked.brute_dam)
burn -= (burn_was-picked.burn_dam)
parts -= picked
updatehealth()
hud_updateflag |= 1 << HEALTH_HUD
//speech_problem_flag = 1
if(update)
UpdateDamageIcon()
// damage MANY external organs, in random order
/mob/living/carbon/human/take_overall_damage(var/brute, var/burn, var/sharp = 0, var/edge = 0, var/used_weapon = null)
if(species && species.burn_mod)
burn = burn*species.burn_mod
if(species && species.brute_mod)
brute = brute*species.brute_mod
if(status_flags & GODMODE)
return 0 //godmode
. = brute + burn
var/list/datum/organ/external/parts = get_damageable_organs()
var/update = 0
while(parts.len && (brute>0 || burn>0) )
var/datum/organ/external/picked = pick(parts)
var/brute_was = picked.brute_dam
var/burn_was = picked.burn_dam
update |= picked.take_damage(brute,burn,sharp,edge,used_weapon)
brute -= (picked.brute_dam - brute_was)
burn -= (picked.burn_dam - burn_was)
parts -= picked
updatehealth()
hud_updateflag |= 1 << HEALTH_HUD
if(update)
UpdateDamageIcon()
////////////////////////////////////////////
/*
This function restores the subjects blood to max.
*/
/mob/living/carbon/human/proc/restore_blood()
if(!(species.anatomy_flags & NO_BLOOD))
var/blood_volume = vessel.get_reagent_amount(BLOOD)
vessel.add_reagent(BLOOD,560.0-blood_volume)
/*
This function restores all organs.
*/
/mob/living/carbon/human/restore_all_organs()
for(var/datum/organ/external/current_organ in organs)
current_organ.rejuvenate()
/mob/living/carbon/human/proc/HealDamage(zone, brute, burn)
var/datum/organ/external/E = get_organ(zone)
if(istype(E, /datum/organ/external))
if (E.heal_damage(brute, burn))
UpdateDamageIcon()
hud_updateflag |= 1 << HEALTH_HUD
else
return 0
return
/mob/living/carbon/human/get_organ(var/zone, cosmetic = FALSE)
RETURN_TYPE(/datum/organ/external)
if(!zone)
zone = LIMB_CHEST
if (zone in list( "eyes", "mouth" ))
zone = LIMB_HEAD
var/list/organ_list = organs_by_name.Copy()
if(cosmetic)
organ_list |= cosmetic_organs_by_name
return organ_list[zone]
/mob/living/carbon/human/proc/get_cosmetic_organ(zone)
RETURN_TYPE(/datum/organ/external)
return cosmetic_organs_by_name[zone]
//Picks a random usable organ from the organs passed to the arguments
//You can feed organ references, or organ strings into this obj
//So this is valid: pick_usable_organ(LIMB_LEFT_LEG, new /datum/organ/external/r_leg)
/mob/living/carbon/human/proc/pick_usable_organ()
var/list/organs = args.Copy()
ASSERT(organs.len) //this proc should always be called with arguments
//Convert list of strings to list of organ objects
for(var/organ_ in organs)
if(istext(organ_))
organs.Add(get_organ(organ_))
organs.Remove(organ_)
else if(!istype(organ_, /datum/organ/external))
organs.Remove(organ_)
var/datum/organ/external/result
while(!result && organs.len)
result = pick_n_take(organs)
if(!result.is_usable())
result = null
return result
//Proc that returns a list of organs converted from string IDs
//get_organs("l_leg", "r_leg") will return a list with left and right leg datums
//It will also accept lists with string IDs
/mob/living/carbon/human/get_organs()
var/list/organ_list = list()
for(var/O in args)
if(islist(O))
for(var/organ_id in O)
organ_list.Add(src.get_organ(organ_id))
else if(istext(O))
organ_list.Add(src.get_organ(O))
return organ_list
/mob/living/carbon/human/apply_damage(var/damage = 0,var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/sharp = 0, var/edge = 0, var/obj/used_weapon = null, ignore_events = 0)
//visible_message("Hit debug. [damage] | [damagetype] | [def_zone] | [blocked] | [sharp] | [used_weapon]")
if((damagetype != BRUTE) && (damagetype != BURN))
return ..(damage, damagetype, def_zone, blocked, ignore_events = ignore_events)
if(blocked >= 100)
return 0
var/datum/organ/external/organ = null
if(isorgan(def_zone))
organ = def_zone
else
if(!def_zone)
def_zone = ran_zone(def_zone)
organ = get_organ(check_zone(def_zone))
if(!organ)
return 0
if(blocked)
damage = (damage/100)*(100-blocked)
if(!ignore_events && INVOKE_EVENT(src, /event/damaged, "kind" = damagetype, "amount" = damage))
return 0 //This event code is also in the mob/living parent which this proc mostly overrides.
switch(damagetype)
if(BRUTE)
damage = damage * brute_damage_modifier
if (damage > 0)
damageoverlaytemp = 20
if(organ.take_damage(damage, 0, sharp, edge, used_weapon))
UpdateDamageIcon(1)
if(BURN)
damage = damage * burn_damage_modifier
if (damage > 0)
damageoverlaytemp = 20
if(organ.take_damage(0, damage, sharp, edge, used_weapon))
UpdateDamageIcon(1)
// Will set our damageoverlay icon to the next level, which will then be set back to the normal level the next mob.Life().
updatehealth()
hud_updateflag |= 1 << HEALTH_HUD
//Embedded projectile code.
if(!organ)
return damage
/*/vg/ EDIT
if(istype(used_weapon,/obj/item/weapon))
var/obj/item/weapon/W = used_weapon //Sharp objects will always embed if they do enough damage.
if( (damage > (10*W.w_class)) && ( (sharp && !ismob(W.loc)) || prob(damage/W.w_class) ) )
if(!istype(W, /obj/item/weapon/kitchen/utensil/knife/large/butch/meatcleaver))
organ.implants += W
visible_message("<span class='danger'>\The [W] sticks in the wound!</span>")
W.add_blood(src)
if(ismob(W.loc))
var/mob/living/H = W.loc
H.drop_item(W, src)
W.forceMove(src)
*/
if(istype(used_weapon,/obj/item/projectile/bullet)) //We don't want to use the actual projectile item, so we spawn some shrapnel.
var/obj/item/projectile/bullet/P = used_weapon
if(prob(75) && damagetype == BRUTE && P.embed)
var/obj/item/weapon/shard/shrapnel/S = new()
S.name = "[P.name] shrapnel"
S.desc = "[S.desc] It looks like it was fired from [P.shot_from]."
S.forceMove(src)
organ.implants += S
if(P.embed_message)
visible_message("<span class='danger'>The projectile sticks in the wound!</span>")
S.add_blood(src)
if(istype(used_weapon,/obj/item/projectile/flare)) //We want them to carry the flare, not a projectile
var/obj/item/projectile/flare/F = used_weapon
if(damagetype == BURN && F.embed && (istype(F.shot_from, /obj/item/weapon/gun/projectile/flare/syndicate) || istype(F.shot_from, /obj/item/weapon/gun/lawgiver)) && prob(75)) //only syndicate guns are dangerous, except for the lawgiver, which is intended to fire incendiary rounds
var/obj/item/device/flashlight/flare/FS = new
FS.name = "shot [FS.name]"
FS.desc = "[FS.desc]. It looks like it was fired from [F.shot_from]."
FS.forceMove(src)
organ.implants += FS
visible_message("<span class='danger'>The flare sticks in the wound!</span>")
FS.add_blood(src)
FS.luminosity = 4 //not so bright, because it's inside them
FS.Light(src) //Now they glow, because the flare is lit
if(prob(80)) //tends to happen, which is good
visible_message("<span class='danger'><b>[name]</b> bursts into flames!</span>", "<span class='danger'>You burst into flames!</span>")
on_fire = 1
adjust_fire_stacks(0.5) //as seen in ignite code
update_icon = 1
qdel(F)
return damage
//Adds cancer, including stage of cancer and limb
//Right now cancer is adminbus only. You can inflict it via the full (old) Player Panel and all "prayer types" (includes Centcomm message)
//Of course, should it ever come back for realsies, that's the right way to do it. But let's not be silly now
//IMPORTANT NOTE: Currently only works on external organs, because the person who wrote organ code has brain cancer, hopefully I will sweep back to fix this in a later PR
//Since I'd have to change hundreds of procs going through organs, that's not something I'll do now
/mob/living/carbon/human/proc/add_cancer(var/stage = 1, var/target)
var/datum/organ/picked_organ
if(target)
picked_organ = organs_by_name["[target]"]
else
picked_organ = pick(organs)
if(picked_organ)
picked_organ.cancer_stage += stage //This can pick a limb which already has cancer, in which case it will add to it
/mob/living/carbon/human/proc/limitedrevive()
resurrect()
timeofdeath = 0
tod = null
toxloss = 0
oxyloss = 0
bruteloss = 0
fireloss = 0
for(var/datum/organ/external/O in organs)
if(O.destspawn || O.is_robotic())
continue
O.rejuvenate()
O.number_wounds = 0
O.wounds = list()
heal_overall_damage(1000, 1000)
if(reagents)
reagents.clear_reagents()
restore_blood()
bodytemperature = 310
stat = status_flags & BUDDHAMODE ? CONSCIOUS : UNCONSCIOUS
regenerate_icons()
flash_eyes(visual = 1)
apply_effect(10, EYE_BLUR)
apply_effect(10, WEAKEN)
update_canmove()
/mob/living/carbon/human/apply_radiation(var/rads, var/application = RAD_EXTERNAL)
if(species.flags & RAD_IMMUNE)
return
if(application == RAD_EXTERNAL)
INVOKE_EVENT(src, /event/irradiate, "user" = src, "rads" = rads)
if(reagents)
if(reagents.has_reagent(LITHOTORCRAZINE))
rads /= 2
if(species.rad_mod)
rads *= species.rad_mod
return ..()