diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index 6e3d1e3e52d..48d4f22934f 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -71,6 +71,9 @@
var/health_threshold_crit = 0
var/health_threshold_dead = -100
+ var/organ_health_multiplier = 1
+ var/organ_regeneration_multiplier = 1
+
var/revival_pod_plants = 1
var/revival_cloning = 1
var/revival_brain_life = -1
@@ -365,6 +368,10 @@
config.metroid_delay = value
if("animal_delay")
config.animal_delay = value
+ if("organ_health_multiplier")
+ config.organ_health_multiplier = value / 100
+ if("organ_regeneration_multiplier")
+ config.organ_regeneration_multiplier = value / 100
else
diary << "Unknown setting in configuration: '[name]'"
diff --git a/code/datums/organs/organ.dm b/code/datums/organs/organ.dm
index 81b45f51287..ba7b1bf014c 100644
--- a/code/datums/organs/organ.dm
+++ b/code/datums/organs/organ.dm
@@ -1,8 +1,6 @@
-//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:33
-
/datum/organ
var/name = "organ"
- var/mob/owner = null
+ var/mob/living/carbon/human/owner = null
///datum/organ/proc/process()
// return 0
@@ -10,5 +8,8 @@
///datum/organ/proc/receive_chem(chemical as obj)
// return 0
+ proc/process()
+ return 0
-
+ proc/receive_chem(chemical as obj)
+ return 0
\ No newline at end of file
diff --git a/code/datums/organs/organ_external.dm b/code/datums/organs/organ_external.dm
index 85df6bf56fe..9d8dc5c45ce 100644
--- a/code/datums/organs/organ_external.dm
+++ b/code/datums/organs/organ_external.dm
@@ -1,142 +1,640 @@
+/****************************************************
+ EXTERNAL ORGANS
+****************************************************/
/datum/organ/external
name = "external"
var/icon_name = null
var/body_part = null
- var/brutestate = 0
- var/burnstate = 0
+
+ var/damage_state = "00"
var/brute_dam = 0
var/burn_dam = 0
var/max_damage = 0
-// var/bandaged = 0
-// var/wound_size = 0
-// var/max_size = 0
+ var/max_size = 0
+ var/tmp/list/obj/item/weapon/implant/implant
+
+ var/display_name
+ var/list/wounds = list()
+ var/number_wounds = 0 // cache the number of wounds, which is NOT wounds.len!
+
+ var/tmp/perma_injury = 0
+ var/tmp/perma_dmg = 0
+ var/tmp/destspawn = 0 //Has it spawned the broken limb?
+ var/tmp/amputated = 0 // Whether this has been cleanly amputated, thus causing no pain
+ var/min_broken_damage = 30
+
+ var/datum/organ/external/parent
+ var/list/datum/organ/external/children
+
+ var/damage_msg = "\red You feel an intense pain"
+
+ var/status = 0
+ var/broken_description
+ var/open = 0
+ var/stage = 0
+
+ proc/take_damage(brute, burn, sharp, used_weapon = null, list/forbidden_limbs = list())
+ // TODO: this proc needs to be rewritten to not update damages directly
+ if((brute <= 0) && (burn <= 0))
+ return 0
+ if(status & ORGAN_DESTROYED)
+ return 0
+ if(status & ORGAN_ROBOT)
+ brute *= 0.66 //~2/3 damage for ROBOLIMBS
+ burn *= 0.66 //~2/3 damage for ROBOLIMBS
+
+ //if(owner && !(status & ORGAN_ROBOT))
+ // owner.pain(display_name, (brute+burn)*3, 1, burn > brute)
+
+ if(sharp)
+ var/nux = brute * rand(10,15)
+ if(brute_dam >= max_damage * config.organ_health_multiplier)
+ if(prob(5 * brute))
+ status |= ORGAN_DESTROYED
+ droplimb()
+ return
+
+ else if(prob(nux))
+ createwound( CUT, brute )
+ if(!(status & ORGAN_ROBOT))
+ owner << "You feel something wet on your [display_name]"
+
+ if((brute_dam + burn_dam + brute + burn) < max_damage)
+ if(brute)
+ brute_dam += brute
+ if( (prob(brute*2) && !sharp) || sharp )
+ createwound( CUT, brute )
+ else if(!sharp)
+ createwound( BRUISE, brute )
+ if(burn)
+ burn_dam += burn
+ createwound( BURN, burn )
+ else
+ var/can_inflict = max_damage - (brute_dam + burn_dam) //How much damage can we actually cause?
+ if(can_inflict)
+ if (brute > 0 && burn > 0)
+ brute = can_inflict/2
+ burn = can_inflict/2
+ var/ratio = brute / (brute + burn)
+ brute_dam += ratio * can_inflict
+ burn_dam += (1 - ratio) * can_inflict
+ else
+ if (brute > 0)
+ brute = can_inflict
+ brute_dam += brute
+ createwound(BRUISE, brute)
+ else
+ burn = can_inflict
+ burn_dam += burn
+ createwound(BRUISE, brute)
+ else if(!(status & ORGAN_ROBOT))
+ var/passed_dam = (brute + burn) - can_inflict //Getting how much overdamage we have.
+ var/list/datum/organ/external/possible_points = list()
+ if(parent)
+ possible_points += parent
+ if(children)
+ possible_points += children
+ if(forbidden_limbs.len)
+ possible_points -= forbidden_limbs
+ if(!possible_points.len)
+ message_admins("Oh god WHAT! [owner]'s [src] was unable to find an organ to pass overdamage too!")
+ else
+ var/datum/organ/external/target = pick(possible_points)
+ if(brute)
+ target.take_damage(passed_dam, 0, sharp, used_weapon, forbidden_limbs + src)
+ else
+ target.take_damage(0, passed_dam, sharp, used_weapon, forbidden_limbs + src)
+ else
+ droplimb(1) //Robot limbs just kinda fail at full damage.
+
+
+ if(status & ORGAN_BROKEN)
+ owner.emote("scream")
+
+ owner.updatehealth()
+
+ // sync the organ's damage with its wounds
+ src.update_damages()
+
+ var/result = update_icon()
+ return result
+
+
+
+ proc/heal_damage(brute, burn, internal = 0, robo_repair = 0)
+ if(status & ORGAN_ROBOT && !robo_repair)
+ return
+
+ // heal damage on the individual wounds
+ for(var/datum/wound/W in wounds)
+ if(brute == 0 && burn == 0)
+ break
+
+ // heal brute damage
+ if(W.damage_type == CUT || W.damage_type == BRUISE)
+ brute = W.heal_damage(brute)
+ else if(W.damage_type == BURN)
+ burn = W.heal_damage(burn)
+
+ // sync organ damage with wound damages
+ update_damages()
+
+ if(internal)
+ status &= ~ORGAN_BROKEN
+ perma_injury = 0
+
+ // sync the organ's damage with its wounds
+ src.update_damages()
+
+ owner.updatehealth()
+ var/result = update_icon()
+ return result
+
+ proc/update_damages()
+ number_wounds = 0
+ brute_dam = 0
+ burn_dam = 0
+ status &= ~ORGAN_BLEEDING
+ for(var/datum/wound/W in wounds)
+ if(W.damage_type == CUT || W.damage_type == BRUISE)
+ brute_dam += W.damage
+ else if(W.damage_type == BURN)
+ burn_dam += W.damage
+
+ if(!W.bandaged && W.damage > 4)
+ status |= ORGAN_BLEEDING
+
+ number_wounds += W.amount
+
+ proc/update_wounds()
+ for(var/datum/wound/W in wounds)
+ // wounds can disappear after 10 minutes at the earliest
+ if(W.damage == 0 && W.created + 10 * 10 * 60 <= world.time)
+ wounds -= W
+ // let the GC handle the deletion of the wound
+ if(W.is_treated())
+ // slow healing
+ var/amount = 0.2
+ if(W.bandaged) amount++
+ if(W.salved) amount++
+ if(W.disinfected) amount++
+ // amount of healing is spread over all the wounds
+ W.heal_damage((amount * W.amount * config.organ_regeneration_multiplier) / (5*owner.number_wounds+1))
+
+ // sync the organ's damage with its wounds
+ src.update_damages()
+
+
+ proc/get_damage() //returns total damage
+ return max(brute_dam + burn_dam - perma_injury, perma_injury) //could use health?
+
+ proc/get_damage_brute()
+ return max(brute_dam+perma_injury, perma_injury)
+
+ proc/get_damage_fire()
+ return burn_dam
+
+ process()
+ // process wounds, doing healing etc., only do this every 4 ticks to save processing power
+ if(owner.life_tick % 4 == 0)
+ update_wounds()
+ if(status & ORGAN_DESTROYED)
+ if(!destspawn)
+ droplimb()
+ return
+ if(!(status & ORGAN_BROKEN))
+ perma_dmg = 0
+ if(parent)
+ if(parent.status & ORGAN_DESTROYED)
+ status |= ORGAN_DESTROYED
+ owner:update_body()
+ return
+ if(brute_dam > min_broken_damage * config.organ_health_multiplier && !(status & ORGAN_ROBOT))
+ if(!(status & ORGAN_BROKEN))
+ owner.visible_message("\red You hear a loud cracking sound coming from \the [owner].","\red Something feels like it shattered in your [display_name]!","You hear a sickening crack.")
+ owner.emote("scream")
+ status |= ORGAN_BROKEN
+ broken_description = pick("broken","fracture","hairline fracture")
+ perma_injury = brute_dam
+ return
+
+// new damage icon system
+// returns just the brute/burn damage code
+ proc/damage_state_text()
+ var/tburn = 0
+ var/tbrute = 0
+
+ if(burn_dam ==0)
+ tburn =0
+ else if (burn_dam < (max_damage * 0.25 / 2))
+ tburn = 1
+ else if (burn_dam < (max_damage * 0.75 / 2))
+ tburn = 2
+ else
+ tburn = 3
+
+ if (brute_dam == 0)
+ tbrute = 0
+ else if (brute_dam < (max_damage * 0.25 / 2))
+ tbrute = 1
+ else if (brute_dam < (max_damage * 0.75 / 2))
+ tbrute = 2
+ else
+ tbrute = 3
+ return "[tbrute][tburn]"
+
+
+// new damage icon system
+// adjusted to set damage_state to brute/burn code only (without r_name0 as before)
+ proc/update_icon()
+ var/n_is = damage_state_text()
+ if (n_is != damage_state)
+ damage_state = n_is
+ owner.update_body()
+ return 1
+ return 0
+
+ proc/droplimb(var/override = 0,var/no_explode = 0)
+ if(override)
+ status |= ORGAN_DESTROYED
+ if(status & ORGAN_DESTROYED)
+ if(status & ORGAN_SPLINTED)
+ status &= ~ORGAN_SPLINTED
+ if(implant)
+ for(var/implants in implant)
+ del(implants)
+ //owner.unlock_medal("Lost something?", 0, "Lose a limb.", "easy")
+
+ for(var/datum/organ/external/I in children)
+ if(I && !(I.status & ORGAN_DESTROYED))
+ I.droplimb(1,1)
+ var/obj/item/weapon/organ/H
+ switch(body_part)
+ if(UPPER_TORSO)
+ owner.gib()
+ if(LOWER_TORSO)
+ owner << "\red You are now sterile."
+ if(HEAD)
+ H = new /obj/item/weapon/organ/head(owner.loc, owner)
+ if(ishuman(owner))
+ if(owner.gender == FEMALE)
+ H.icon_state = "head_f_l"
+ H.overlays += owner.generate_head_icon()
+ H:transfer_identity(owner)
+ H.pixel_x = -10
+ H.pixel_y = 6
+ H.name = "[owner.real_name]'s head"
+
+ // TODO: re-add this when dismemberment has been ported properly
+ //if(ishuman(owner))
+ // owner.update_face()
+ owner.update_body()
+ owner.death()
+ if(ARM_RIGHT)
+ H = new /obj/item/weapon/organ/r_arm(owner.loc, owner)
+ if(ismonkey(owner))
+ H.icon_state = "r_arm_l"
+ if(ARM_LEFT)
+ H = new /obj/item/weapon/organ/l_arm(owner.loc, owner)
+ if(ismonkey(owner))
+ H.icon_state = "l_arm_l"
+ if(LEG_RIGHT)
+ H = new /obj/item/weapon/organ/r_leg(owner.loc, owner)
+ if(ismonkey(owner))
+ H.icon_state = "r_leg_l"
+ if(LEG_LEFT)
+ H = new /obj/item/weapon/organ/l_leg(owner.loc, owner)
+ if(ismonkey(owner))
+ H.icon_state = "l_leg_l"
+ if(HAND_RIGHT)
+ H = new /obj/item/weapon/organ/r_hand(owner.loc, owner)
+ if(ismonkey(owner))
+ H.icon_state = "r_hand_l"
+ if(HAND_LEFT)
+ H = new /obj/item/weapon/organ/l_hand(owner.loc, owner)
+ if(ismonkey(owner))
+ H.icon_state = "l_hand_l"
+ if(FOOT_RIGHT)
+ H = new /obj/item/weapon/organ/r_foot/(owner.loc, owner)
+ if(ismonkey(owner))
+ H.icon_state = "r_foot_l"
+ if(FOOT_LEFT)
+ H = new /obj/item/weapon/organ/l_foot(owner.loc, owner)
+ if(ismonkey(owner))
+ H.icon_state = "l_foot_l"
+ if(ismonkey(owner))
+ H.icon = 'monkey.dmi'
+ var/lol = pick(cardinal)
+ step(H,lol)
+ destspawn = 1
+ if(status & ORGAN_ROBOT)
+ owner.visible_message("\red \The [owner]'s [display_name] explodes violently!",\
+ "\red Your [display_name] explodes!",\
+ "You hear an explosion followed by a scream!")
+ if(!no_explode)
+ explosion(get_turf(owner),-1,-1,2,3)
+ var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread()
+ spark_system.set_up(5, 0, src)
+ spark_system.attach(src)
+ spark_system.start()
+ spawn(10)
+ del(spark_system)
+ else
+ owner.visible_message("\red [owner.name]'s [display_name] flies off in an arc.",\
+ "Your [display_name] goes flying off!",\
+ "You hear a terrible sound of ripping tendons and flesh.")
+ owner.update_body()
+ // TODO: also add stuff to update the appropriate clothing
+
+ proc/createwound(var/type = CUT, var/damage)
+ if(hasorgans(owner))
+ var/wound_type
+ var/size = min( max( 1, damage/10 ) , 6)
+
+ // first check whether we can widen an existing wound
+ if(wounds.len > 0 && prob(25))
+ if((type == CUT || type == BRUISE) && damage >= 5)
+ var/datum/wound/W = pick(wounds)
+ if(W.amount == 1 && W.started_healing())
+ W.open_wound()
+ owner.visible_message("\red The wound on [owner.name]'s [display_name] widens with a nasty ripping voice.",\
+ "\red The wound on your [display_name] widens with a nasty ripping voice.",\
+ "You hear a nasty ripping noise, as if flesh is being torn apart.")
+
+ return
+
+ if(damage == 0) return
+
+ // the wound we will create
+ var/datum/wound/W
+
+ switch(type)
+ if(CUT)
+ src.status |= ORGAN_BLEEDING
+ var/list/size_names = list(/datum/wound/cut, /datum/wound/deep_cut, /datum/wound/flesh_wound, /datum/wound/gaping_wound, /datum/wound/big_gaping_wound, /datum/wound/massive_wound)
+ wound_type = size_names[size]
+
+ W = new wound_type(damage)
+
+ if(BRUISE)
+ var/list/size_names = list(/datum/wound/bruise/tiny_bruise, /datum/wound/bruise/small_bruise, /datum/wound/bruise/moderate_bruise, /datum/wound/bruise/large_bruise, /datum/wound/bruise/huge_bruise, /datum/wound/bruise/monumental_bruise)
+ wound_type = size_names[size]
+
+ W = new wound_type(damage)
+ if(BURN)
+ var/list/size_names = list(/datum/wound/moderate_burn, /datum/wound/large_burn, /datum/wound/severe_burn, /datum/wound/deep_burn, /datum/wound/carbonised_area)
+ wound_type = size_names[size]
+
+ W = new wound_type(damage)
+
+
+
+ // check whether we can add the wound to an existing wound
+ for(var/datum/wound/other in wounds)
+ if(other.desc == W.desc)
+ // okay, add it!
+ other.damage += W.damage
+ other.amount += 1
+ W = null // to signify that the wound was added
+ break
+
+ // if we couldn't add the wound to another wound, ignore
+ if(W)
+ wounds += W
+
+ proc/emp_act(severity)
+ if(!(status & ORGAN_ROBOT))
+ return
+ if(prob(30*severity))
+ take_damage(4(4-severity), 0, 1, used_weapon = "EMP")
+ else
+ droplimb(1)
+
+ proc/getDisplayName()
+ switch(name)
+ if("l_leg")
+ return "left leg"
+ if("r_leg")
+ return "right leg"
+ if("l_arm")
+ return "left arm"
+ if("r_arm")
+ return "right arm"
+ if("l_foot")
+ return "left foot"
+ if("r_foot")
+ return "right foot"
+ if("l_hand")
+ return "left hand"
+ if("r_hand")
+ return "right hand"
+ else
+ return name
/datum/organ/external/chest
name = "chest"
icon_name = "chest"
+ display_name = "chest"
max_damage = 150
+ min_broken_damage = 75
body_part = UPPER_TORSO
+/datum/organ/external/groin
+ name = "groin"
+ icon_name = "diaper"
+ display_name = "groin"
+ max_damage = 115
+ min_broken_damage = 70
+ body_part = LOWER_TORSO
+
/datum/organ/external/head
name = "head"
icon_name = "head"
- max_damage = 125
+ display_name = "head"
+ max_damage = 75
+ min_broken_damage = 40
body_part = HEAD
+ var/disfigured = 0
/datum/organ/external/l_arm
name = "l_arm"
+ display_name = "left arm"
icon_name = "l_arm"
max_damage = 75
+ min_broken_damage = 30
body_part = ARM_LEFT
/datum/organ/external/l_leg
name = "l_leg"
+ display_name = "left leg"
icon_name = "l_leg"
max_damage = 75
+ min_broken_damage = 30
body_part = LEG_LEFT
/datum/organ/external/r_arm
name = "r_arm"
+ display_name = "right arm"
icon_name = "r_arm"
max_damage = 75
+ min_broken_damage = 30
body_part = ARM_RIGHT
/datum/organ/external/r_leg
name = "r_leg"
+ display_name = "right leg"
icon_name = "r_leg"
max_damage = 75
+ min_broken_damage = 30
body_part = LEG_RIGHT
-/*Leaving these here in case we want to use them later
/datum/organ/external/l_foot
- name = "l foot"
+ name = "l_foot"
+ display_name = "left foot"
icon_name = "l_foot"
+ max_damage = 40
+ min_broken_damage = 15
body_part = FOOT_LEFT
/datum/organ/external/r_foot
- name = "r foot"
+ name = "r_foot"
+ display_name = "right foot"
icon_name = "r_foot"
+ max_damage = 40
+ min_broken_damage = 15
body_part = FOOT_RIGHT
/datum/organ/external/r_hand
- name = "r hand"
+ name = "r_hand"
+ display_name = "right hand"
icon_name = "r_hand"
+ max_damage = 40
+ min_broken_damage = 15
body_part = HAND_RIGHT
/datum/organ/external/l_hand
- name = "l hand"
+ name = "l_hand"
+ display_name = "left hand"
icon_name = "l_hand"
+ max_damage = 40
+ min_broken_damage = 15
body_part = HAND_LEFT
-/datum/organ/external/groin
- name = "groin"
- icon_name = "groin"
- body_part = LOWER_TORSO
-*/
+/****************************************************
+ EXTERNAL ORGAN ITEMS
+****************************************************/
-//Applies brute and burn damage to the organ. Returns 1 if the damage-icon states changed at all.
-//Damage will not exceed max_damage using this proc
-//Cannot apply negative damage
-/datum/organ/external/proc/take_damage(brute, burn)
- if(owner && owner.nodamage) return 0 //godmode
- brute = max(brute,0)
- burn = max(burn,0)
+obj/item/weapon/organ
+ icon = 'human.dmi'
- var/can_inflict = max_damage - (brute_dam + burn_dam)
- if(!can_inflict) return 0
+obj/item/weapon/organ/New(loc, mob/living/carbon/human/H)
+ ..(loc)
+ if(!istype(H))
+ return
+ if(H.dna)
+ if(!blood_DNA)
+ blood_DNA = list()
+ blood_DNA[H.dna.unique_enzymes] = H.dna.b_type
- if((brute + burn) < can_inflict)
- brute_dam += brute
- burn_dam += burn
+ var/icon/I = new /icon(icon, icon_state)
+
+ if (H.s_tone >= 0)
+ I.Blend(rgb(H.s_tone, H.s_tone, H.s_tone), ICON_ADD)
else
- if(brute > 0)
- if(burn > 0)
- brute = round( (brute/(brute+burn)) * can_inflict, 1 )
- burn = can_inflict - brute //gets whatever damage is left over
- brute_dam += brute
- burn_dam += burn
+ I.Blend(rgb(-H.s_tone, -H.s_tone, -H.s_tone), ICON_SUBTRACT)
+ icon = I
+
+obj/item/weapon/organ/head
+ name = "head"
+ icon_state = "head_m_l"
+ var/mob/living/carbon/brain/brainmob
+ var/brain_op_stage = 0
+
+obj/item/weapon/organ/head/New()
+ ..()
+ spawn(5)
+ if(brainmob && brainmob.client)
+ brainmob.client.screen.len = null //clear the hud
+
+obj/item/weapon/organ/head/proc/transfer_identity(var/mob/living/carbon/human/H)//Same deal as the regular brain proc. Used for human-->head
+ brainmob = new(src)
+ brainmob.name = H.real_name
+ brainmob.real_name = H.real_name
+ brainmob.dna = H.dna
+ if(H.mind)
+ H.mind.transfer_to(brainmob)
+ brainmob.container = src
+
+obj/item/weapon/organ/head/attackby(obj/item/weapon/W as obj, mob/user as mob)
+ if(istype(W,/obj/item/weapon/scalpel))
+ switch(brain_op_stage)
+ if(0)
+ for(var/mob/O in (oviewers(brainmob) - user))
+ O.show_message("\red [brainmob] is beginning to have \his head cut open with [src] by [user].", 1)
+ brainmob << "\red [user] begins to cut open your head with [src]!"
+ user << "\red You cut [brainmob]'s head open with [src]!"
+
+ brain_op_stage = 1
+
+ if(2)
+ for(var/mob/O in (oviewers(brainmob) - user))
+ O.show_message("\red [brainmob] is having \his connections to the brain delicately severed with [src] by [user].", 1)
+ brainmob << "\red [user] begins to cut open your head with [src]!"
+ user << "\red You cut [brainmob]'s head open with [src]!"
+
+ brain_op_stage = 3.0
else
- brute_dam += can_inflict
- else
- if(burn > 0)
- burn_dam += can_inflict
+ ..()
+ else if(istype(W,/obj/item/weapon/circular_saw))
+ switch(brain_op_stage)
+ if(1)
+ for(var/mob/O in (oviewers(brainmob) - user))
+ O.show_message("\red [brainmob] has \his skull sawed open with [src] by [user].", 1)
+ brainmob << "\red [user] begins to saw open your head with [src]!"
+ user << "\red You saw [brainmob]'s head open with [src]!"
+
+ brain_op_stage = 2
+ if(3)
+ for(var/mob/O in (oviewers(brainmob) - user))
+ O.show_message("\red [brainmob] has \his spine's connection to the brain severed with [src] by [user].", 1)
+ brainmob << "\red [user] severs your brain's connection to the spine with [src]!"
+ user << "\red You sever [brainmob]'s brain's connection to the spine with [src]!"
+
+ user.attack_log += "\[[time_stamp()]\] Debrained [brainmob.name] ([brainmob.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)])"
+ brainmob.attack_log += "\[[time_stamp()]\] Debrained by [user.name] ([user.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)])"
+ log_admin("ATTACK: [brainmob] ([brainmob.ckey]) debrained [user] ([user.ckey]).")
+ message_admins("ATTACK: [brainmob] ([brainmob.ckey]) debrained [user] ([user.ckey]).")
+
+ var/obj/item/brain/B = new(loc)
+ B.transfer_identity(brainmob)
+
+ brain_op_stage = 4.0
else
- return 0
- return update_icon()
+ ..()
+ else
+ ..()
-
-//Heals brute and burn damage for the organ. Returns 1 if the damage-icon states changed at all.
-//Damage cannot go below zero.
-//Cannot remove negative damage (i.e. apply damage)
-/datum/organ/external/proc/heal_damage(brute, burn)
- brute = max(brute, 0)
- burn = max(burn, 0)
- brute_dam = max(brute_dam - brute, 0)
- burn_dam = max(burn_dam - burn, 0)
- return update_icon()
-
-
-//Returns total damage...kinda pointless really
-/datum/organ/external/proc/get_damage() //returns total damage
- return brute_dam + burn_dam
-
-
-//Updates an organ's brute/burn states for use by updateDamageIcon()
-//Returns 1 if we need to update overlays. 0 otherwise.
-/datum/organ/external/proc/update_icon()
- var/tbrute = round( (brute_dam/max_damage)*3, 1 )
- var/tburn = round( (burn_dam/max_damage)*3, 1 )
- if((tbrute != brutestate) || (tburn != burnstate))
- brutestate = tbrute
- burnstate = tburn
- return 1
- return 0
-
-//Returns a display name for the organ
-/datum/organ/external/proc/getDisplayName()
- switch(name)
- if("l_leg") return "left leg"
- if("r_leg") return "right leg"
- if("l_arm") return "left arm"
- if("r_arm") return "right arm"
- else return name
+obj/item/weapon/organ/l_arm
+ name = "left arm"
+ icon_state = "l_arm_l"
+obj/item/weapon/organ/l_foot
+ name = "left foot"
+ icon_state = "l_foot_l"
+obj/item/weapon/organ/l_hand
+ name = "left hand"
+ icon_state = "l_hand_l"
+obj/item/weapon/organ/l_leg
+ name = "left leg"
+ icon_state = "l_leg_l"
+obj/item/weapon/organ/r_arm
+ name = "right arm"
+ icon_state = "r_arm_l"
+obj/item/weapon/organ/r_foot
+ name = "right foot"
+ icon_state = "r_foot_l"
+obj/item/weapon/organ/r_hand
+ name = "right hand"
+ icon_state = "r_hand_l"
+obj/item/weapon/organ/r_leg
+ name = "right leg"
+ icon_state = "r_leg_l"
\ No newline at end of file
diff --git a/code/datums/organs/organ_internal.dm b/code/datums/organs/organ_internal.dm
index 66496141bbc..2f1738bea2d 100644
--- a/code/datums/organs/organ_internal.dm
+++ b/code/datums/organs/organ_internal.dm
@@ -1,3 +1,6 @@
+/****************************************************
+ INTERNAL ORGANS
+****************************************************/
/datum/organ/internal
name = "internal"
diff --git a/code/datums/organs/wound.dm b/code/datums/organs/wound.dm
new file mode 100644
index 00000000000..1caf45b7587
--- /dev/null
+++ b/code/datums/organs/wound.dm
@@ -0,0 +1,196 @@
+
+/****************************************************
+ WOUNDS
+****************************************************/
+/datum/wound
+ // stages such as "cut", "deep cut", etc.
+ var/list/stages
+ // number representing the current stage
+ var/current_stage = 0
+
+ // description of the wound
+ var/desc = ""
+
+ // amount of damage this wound causes
+ var/damage = 0
+
+ // amount of damage the current wound type requires(less means we need to apply the next healing stage)
+ var/min_damage = 0
+
+ // one of CUT, BRUISE, BURN
+ var/damage_type = CUT
+
+ // whether this wound needs a bandage/salve to heal at all
+ var/needs_treatment = 0
+
+ // is the wound bandaged?
+ var/tmp/bandaged = 0
+ // is the wound salved?
+ var/tmp/salved = 0
+ // is the wound disinfected?
+ var/tmp/disinfected = 0
+ var/tmp/created = 0
+
+ // number of wounds of this type
+ var/tmp/amount = 1
+
+ // helper lists
+ var/tmp/list/desc_list = list()
+ var/tmp/list/damage_list = list()
+ New(var/damage)
+
+ created = world.time
+
+ // reading from a list("stage" = damage) is pretty difficult, so build two separate
+ // lists from them instead
+ for(var/V in stages)
+ desc_list += V
+ damage_list += stages[V]
+
+ src.damage = damage
+
+ // initialize with the first stage
+ next_stage()
+
+ // this will ensure the size of the wound matches the damage
+ src.heal_damage(0)
+
+ // returns 1 if there's a next stage, 0 otherwise
+ proc/next_stage()
+ if(current_stage + 1 > src.desc_list.len)
+ return 0
+
+ current_stage++
+
+ src.min_damage = damage_list[current_stage]
+ src.desc = desc_list[current_stage]
+ return 1
+
+ // returns 1 if the wound has started healing
+ proc/started_healing()
+ return (current_stage > 1)
+
+ // checks whether the wound has been appropriately treated
+ // always returns 1 for wounds that don't need to be treated
+ proc/is_treated()
+ if(!needs_treatment) return 1
+
+ if(damage_type == BRUISE || damage_type == CUT)
+ return bandaged
+ else if(damage_type == BURN)
+ return salved
+
+ // heal the given amount of damage, and if the given amount of damage was more
+ // than what needed to be healed, return how much heal was left
+ proc/heal_damage(amount)
+ var/healed_damage = min(src.damage, amount)
+ amount -= healed_damage
+ src.damage -= healed_damage
+
+ while(src.damage / src.amount < damage_list[current_stage] && current_stage < src.desc_list.len)
+ current_stage++
+ desc = desc_list[current_stage]
+
+ // return amount of healing still leftover, can be used for other wounds
+ return amount
+
+ // opens the wound again
+ proc/open_wound()
+ if(current_stage > 1)
+ // e.g. current_stage is 2, then reset it to 0 and do next_stage(), bringing it to 1
+ src.current_stage -= 2
+ next_stage()
+ src.damage = src.min_damage + 5
+
+/** CUTS **/
+/datum/wound/cut
+ // link wound descriptions to amounts of damage
+ stages = list("cut" = 5, "healing cut" = 2, "small scab" = 0)
+
+/datum/wound/deep_cut
+ stages = list("deep cut" = 15, "clotted cut" = 8, "scab" = 2, "fresh skin" = 0)
+
+/datum/wound/flesh_wound
+ stages = list("flesh wound" = 25, "blood soaked clot" = 15, "large scab" = 5, "fresh skin" = 0)
+
+/datum/wound/gaping_wound
+ stages = list("gaping wound" = 50, "large blood soaked clot" = 25, "large clot" = 15, "small angry scar" = 5, \
+ "small straight scar" = 0)
+
+/datum/wound/big_gaping_wound
+ stages = list("big gaping wound" = 60, "gauze wrapped wound" = 50, "blood soaked bandage" = 25,\
+ "large angry scar" = 10, "large straight scar" = 0)
+
+ needs_treatment = 1 // this only heals when bandaged
+
+/datum/wound/massive_wound
+ stages = list("massive wound" = 70, "massive blood soaked bandage" = 40, "huge bloody mess" = 20,\
+ "massive angry scar" = 10, "massive jagged scar" = 0)
+
+ needs_treatment = 1 // this only heals when bandaged
+
+/** BRUISES **/
+/datum/wound/bruise
+ stages = list("monumental bruise" = 80, "huge bruise" = 50, "large bruise" = 30,\
+ "moderate bruise" = 20, "small bruise" = 10, "tiny bruise" = 5)
+
+ needs_treatment = 1 // this only heals when bandaged
+ damage_type = BRUISE
+
+/datum/wound/bruise/monumental_bruise
+
+// implement sub-paths by starting at a later stage
+/datum/wound/bruise/huge_bruise
+ current_stage = 1
+
+/datum/wound/bruise/large_bruise
+ current_stage = 2
+
+/datum/wound/bruise/moderate_bruise
+ current_stage = 3
+ needs_treatment = 0
+
+/datum/wound/bruise/small_bruise
+ current_stage = 4
+ needs_treatment = 0
+
+/datum/wound/bruise/tiny_bruise
+ current_stage = 5
+ needs_treatment = 0
+
+/** BURNS **/
+/datum/wound/moderate_burn
+ stages = list("moderate burn" = 5, "moderate salved burn" = 2, "fresh skin" = 0)
+
+ needs_treatment = 1 // this only heals when bandaged
+
+ damage_type = BURN
+
+/datum/wound/large_burn
+ stages = list("large burn" = 15, "large salved burn" = 5, "fresh skin" = 0)
+
+ needs_treatment = 1 // this only heals when bandaged
+
+ damage_type = BURN
+
+/datum/wound/severe_burn
+ stages = list("severe burn" = 30, "severe salved burn" = 10, "burn scar" = 0)
+
+ needs_treatment = 1 // this only heals when bandaged
+
+ damage_type = BURN
+
+/datum/wound/deep_burn
+ stages = list("deep burn" = 40, "deep salved burn" = 15, "large burn scar" = 0)
+
+ needs_treatment = 1 // this only heals when bandaged
+
+ damage_type = BURN
+
+
+/datum/wound/carbonised_area
+ stages = list("carbonised area" = 50, "treated carbonised area" = 20, "massive burn scar" = 0)
+
+ needs_treatment = 1 // this only heals when bandaged
+
+ damage_type = BURN
\ No newline at end of file
diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm
index 81a0c4a1737..851445c6200 100644
--- a/code/defines/obj/weapon.dm
+++ b/code/defines/obj/weapon.dm
@@ -1355,6 +1355,7 @@
m_amt = 12000
origin_tech = "materials=1"
attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
+ sharp = 1
/obj/item/weapon/kitchenknife/ritual
name = "ritual knife"
@@ -1436,6 +1437,7 @@
icon_state = "knife"
force = 10.0
throwforce = 10.0
+ sharp = 1
/obj/item/weapon/kitchen/utensil/spoon
name = "spoon"
@@ -1458,6 +1460,7 @@
g_amt = 5000
origin_tech = "materials=1;biotech=1"
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
+ sharp = 1
/obj/item/weapon/retractor
name = "retractor"
@@ -1524,6 +1527,7 @@
g_amt = 10000
origin_tech = "materials=1;biotech=1"
attack_verb = list("attacked", "slashed", "sawed", "cut")
+ sharp = 1
/obj/item/weapon/syntiflesh
name = "syntiflesh"
diff --git a/code/game/dna.dm b/code/game/dna.dm
index 0ea61c118e2..eec31a2565c 100644
--- a/code/game/dna.dm
+++ b/code/game/dna.dm
@@ -505,8 +505,6 @@
for(var/obj/T in (M.contents-implants))
del(T)
- //for(var/R in M.organs)
- // del(M.organs[text("[]", R)])
O.loc = M.loc
diff --git a/code/game/gamemodes/events/ninja_equipment.dm b/code/game/gamemodes/events/ninja_equipment.dm
index dd77de486bc..58bfa17ff48 100644
--- a/code/game/gamemodes/events/ninja_equipment.dm
+++ b/code/game/gamemodes/events/ninja_equipment.dm
@@ -844,15 +844,16 @@ ________________________________________________________________________________
U << "\red Procedure interrupted. Protocol terminated."
return
else if(istype(I, /obj/item/weapon/disk/tech_disk))//If it's a data disk, we want to copy the research on to the suit.
- if(I:stored)//If it has something on it.
+ var/obj/item/weapon/disk/tech_disk/tdisk = I
+ if(tdisk.stored)//If it has something on it.
U << "Research information detected, processing..."
if(do_after(U,s_delay))
for(var/datum/tech/current_data in stored_research)
- if(current_data.id==I:stored.id)
- if(current_data.levelERROR: \black Procedure interrupted. Process terminated."
diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm
index 8363837874f..80dc869698c 100644
--- a/code/game/objects/items/devices/PDA/PDA.dm
+++ b/code/game/objects/items/devices/PDA/PDA.dm
@@ -837,8 +837,8 @@ var/global/list/obj/item/device/pda/PDAs = list()
C.loc = src
user << "You insert [C] into [src]."
cartridge = C
- if(C:radio)
- C:radio.hostpda = src
+ if(cartridge.radio)
+ cartridge.radio.hostpda = src
else if(istype(C, /obj/item/weapon/card/id))
var/obj/item/weapon/card/id/idcard = C
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 01f0f06a5d1..9d782959bdd 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -11,6 +11,7 @@
animate_movement = 2
var/throwforce = 1
var/list/attack_verb = list() //Used in attackby() to say how something was attacked "[x] has been [z.attack_verb] by [y] with [z]"
+ var/sharp = 0 // whether this object cuts
/obj/proc/process()
processing_objects.Remove(src)
diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm
index 5bc421dee4d..4277e3b301f 100644
--- a/code/modules/mob/inventory.dm
+++ b/code/modules/mob/inventory.dm
@@ -203,3 +203,16 @@
//if(hasvar(src,"r_hand")) if(src:r_hand) items += src:r_hand
return items
+
+/** BS12's proc to get the item in the active hand. Couldn't find the /tg/ equivalent. **/
+/mob/proc/equipped()
+ if(issilicon(src))
+ if(isrobot(src))
+ if(src:module_active)
+ return src:module_active
+ else
+ if (hand)
+ return l_hand
+ else
+ return r_hand
+ return
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm
index 22142887241..0f1fedb8de4 100644
--- a/code/modules/mob/living/carbon/carbon_defines.dm
+++ b/code/modules/mob/living/carbon/carbon_defines.dm
@@ -10,3 +10,8 @@
var/silent = null //Can't talk. Value goes down every life proc.
var/last_eating = 0 //Not sure what this does... I found it hidden in food.dm
+
+ var/life_tick = 0 // The amount of life ticks that have processed on this mob.
+
+ // total amount of wounds on mob, used to spread out healing and the like over all wounds
+ var/number_wounds = 0
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index c1a12fd29d0..12160fcc265 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -201,40 +201,6 @@
msg += "...\n"
else
-
- msg += ""
-
- var/temp = getBruteLoss() //no need to calculate each of these twice
- if(temp)
- if(temp < 30)
- msg += "[t_He] [t_has] minor bruising.\n"
- else
- msg += "[t_He] [t_has] severe bruising!\n"
-
- temp = getFireLoss()
- if(temp)
- if(temp < 30)
- msg += "[t_He] [t_has] minor burns.\n"
- else
- msg += "[t_He] [t_has] severe burns!\n"
-
- temp = getCloneLoss()
- if(temp)
- if(temp < 30)
- msg += "[t_He] [t_has] minor genetic deformities.\n"
- else
- msg += "[t_He] [t_has] severe genetic deformities.\n"
-
- if(nutrition < 100)
- msg += "[t_He] [t_is] severely malnourished.\n"
- else if(nutrition >= 500)
- if(usr.nutrition < 100)
- msg += "[t_He] [t_is] plump and delicious looking - Like a fat little piggy. A tasty piggy.\n"
- else
- msg += "[t_He] [t_is] quite chubby.\n"
-
- msg += ""
-
if(stat == UNCONSCIOUS)
msg += "[t_He] [t_is]n't responding to anything around [t_him] and seems to be asleep.\n"
else if(getBrainLoss() >= 60)
@@ -245,6 +211,148 @@
else if(!client)
msg += "[t_He] [t_has] a vacant, braindead stare...\n"
+ msg += ""
+
+ if(nutrition < 100)
+ msg += "[t_He] [t_is] severely malnourished.\n"
+ else if(nutrition >= 500)
+ if(usr.nutrition < 100)
+ msg += "[t_He] [t_is] plump and delicious looking - Like a fat little piggy. A tasty piggy.\n"
+ else
+ msg += "[t_He] [t_is] quite chubby.\n"
+ msg += ""
+
+ var/list/wound_flavor_text = list()
+ var/list/is_destroyed = list()
+ var/list/is_bleeding = list()
+ for(var/datum/organ/external/temp in organs)
+ if(temp)
+ if(temp.status & ORGAN_DESTROYED)
+ is_destroyed["[temp.display_name]"] = 1
+ wound_flavor_text["[temp.display_name]"] = "[t_He] is missing [t_his] [temp.display_name].\n"
+ continue
+ if(temp.status & ORGAN_ROBOT)
+ if(!(temp.brute_dam + temp.burn_dam))
+ wound_flavor_text["[temp.display_name]"] = "[t_He] has a robot [temp.display_name]!\n"
+ continue
+ else
+ wound_flavor_text["[temp.display_name]"] = "[t_He] has a robot [temp.display_name], it has"
+ if(temp.brute_dam) switch(temp.brute_dam)
+ if(0 to 20)
+ wound_flavor_text["[temp.display_name]"] += " some dents"
+ if(21 to INFINITY)
+ wound_flavor_text["[temp.display_name]"] += pick(" a lot of dents"," severe denting")
+ if(temp.brute_dam && temp.burn_dam)
+ wound_flavor_text["[temp.display_name]"] += " and"
+ if(temp.burn_dam) switch(temp.burn_dam)
+ if(0 to 20)
+ wound_flavor_text["[temp.display_name]"] += " some burns"
+ if(21 to INFINITY)
+ wound_flavor_text["[temp.display_name]"] += pick(" a lot of burns"," severe melting")
+ wound_flavor_text["[temp.display_name]"] += "!\n"
+ else if(temp.wounds.len > 0)
+ var/list/wound_descriptors = list()
+ for(var/datum/wound/W in temp.wounds)
+ if(W.desc in wound_descriptors)
+ wound_descriptors[W.desc] += W.amount
+ continue
+ wound_descriptors[W.desc] = W.amount
+ var/list/flavor_text = list()
+ var/list/no_exclude = list("gaping wound", "big gaping wound", "massive wound", "large bruise",\
+ "huge bruise", "massive bruise", "severe burn", "large burn", "deep burn", "carbonised area")
+ for(var/wound in wound_descriptors)
+ switch(wound_descriptors[wound])
+ if(1)
+ if(!flavor_text.len)
+ flavor_text += "[t_He] has[prob(10) && !(wound in no_exclude) ? " what might be" : ""] a [wound]"
+ else
+ flavor_text += "[prob(10) && !(wound in no_exclude) ? " what might be" : ""] a [wound]"
+ if(2)
+ if(!flavor_text.len)
+ flavor_text += "[t_He] has[prob(10) && !(wound in no_exclude) ? " what might be" : ""] a pair of [wound]s"
+ else
+ flavor_text += "[prob(10) && !(wound in no_exclude) ? " what might be" : ""] a pair of [wound]s"
+ if(3 to 5)
+ if(!flavor_text.len)
+ flavor_text += "[t_He] has several [wound]s"
+ else
+ flavor_text += " several [wound]s"
+ if(6 to INFINITY)
+ if(!flavor_text.len)
+ flavor_text += "[t_He] has a bunch of [wound]s"
+ else
+ flavor_text += " a ton of [wound]\s"
+ var/flavor_text_string = ""
+ for(var/text = 1, text <= flavor_text.len, text++)
+ if(text == flavor_text.len && flavor_text.len > 1)
+ flavor_text_string += ", and"
+ else if(flavor_text.len > 1 && text > 1)
+ flavor_text_string += ","
+ flavor_text_string += flavor_text[text]
+ flavor_text_string += " on [t_his] [temp.display_name].
"
+ wound_flavor_text["[temp.name]"] = flavor_text_string
+ if(temp.status & ORGAN_BLEEDING)
+ is_bleeding["[temp.name]"] = 1
+ else
+ wound_flavor_text["[temp.display_name]"] = ""
+
+ //Handles the text strings being added to the actual description.
+ //If they have something that covers the limb, and it is not missing, put flavortext. If it is covered but bleeding, add other flavortext.
+ var/display_chest = 0
+ var/display_shoes = 0
+ var/display_gloves = 0
+ if(wound_flavor_text["head"] && (is_destroyed["head"] || (!skipmask && !(wear_mask && istype(wear_mask, /obj/item/clothing/mask/gas)))))
+ msg += wound_flavor_text["head"]
+ else if(is_bleeding["head"])
+ msg += "[src] has blood running down [t_his] face!\n"
+ if(wound_flavor_text["chest"] && !w_uniform && !skipjumpsuit) //No need. A missing chest gibs you.
+ msg += wound_flavor_text["chest"]
+ else if(is_bleeding["chest"])
+ display_chest = 1
+ if(wound_flavor_text["left arm"] && (is_destroyed["left arm"] || (!w_uniform && !skipjumpsuit)))
+ msg += wound_flavor_text["left arm"]
+ else if(is_bleeding["left arm"])
+ display_chest = 1
+ if(wound_flavor_text["left hand"] && (is_destroyed["left hand"] || (!gloves && !skipgloves)))
+ msg += wound_flavor_text["left hand"]
+ else if(is_bleeding["left hand"])
+ display_gloves = 1
+ if(wound_flavor_text["right arm"] && (is_destroyed["right arm"] || (!w_uniform && !skipjumpsuit)))
+ msg += wound_flavor_text["right arm"]
+ else if(is_bleeding["right arm"])
+ display_chest = 1
+ if(wound_flavor_text["right hand"] && (is_destroyed["right hand"] || (!gloves && !skipgloves)))
+ msg += wound_flavor_text["right hand"]
+ else if(is_bleeding["right hand"])
+ display_gloves = 1
+ if(wound_flavor_text["groin"] && (is_destroyed["groin"] || (!w_uniform && !skipjumpsuit)))
+ msg += wound_flavor_text["groin"]
+ else if(is_bleeding["groin"])
+ display_chest = 1
+ if(wound_flavor_text["left leg"] && (is_destroyed["left leg"] || (!w_uniform && !skipjumpsuit)))
+ msg += wound_flavor_text["left leg"]
+ else if(is_bleeding["left leg"])
+ display_chest = 1
+ if(wound_flavor_text["left foot"]&& (is_destroyed["left foot"] || (!shoes && !skipshoes)))
+ msg += wound_flavor_text["left foot"]
+ else if(is_bleeding["left foot"])
+ display_shoes = 1
+ if(wound_flavor_text["right leg"] && (is_destroyed["right leg"] || (!w_uniform && !skipjumpsuit)))
+ msg += wound_flavor_text["right leg"]
+ else if(is_bleeding["right leg"])
+ display_chest = 1
+ if(wound_flavor_text["right foot"]&& (is_destroyed["right foot"] || (!shoes && !skipshoes)))
+ msg += wound_flavor_text["right foot"]
+ else if(is_bleeding["right foot"])
+ display_shoes = 1
+ if(display_chest)
+ msg += "[src] has blood soaking through from under [t_his] clothing!\n"
+ if(display_shoes)
+ msg += "[src] has blood running from [t_his] shoes!\n"
+ if(display_gloves)
+ msg += "[src] has blood running from under [t_his] gloves!\n"
+
+
if(digitalcamo)
msg += "[t_He] [t_is] repulsively uncanny!\n"
diff --git a/code/modules/mob/living/carbon/human/hud.dm b/code/modules/mob/living/carbon/human/hud.dm
index 4d017d14d59..e0e3361924e 100644
--- a/code/modules/mob/living/carbon/human/hud.dm
+++ b/code/modules/mob/living/carbon/human/hud.dm
@@ -587,6 +587,8 @@
mymob.flash.screen_loc = "1,1 to 15,15"
mymob.flash.layer = 17
+ mymob.pain = new /obj/screen( null )
+
/*
mymob.hands = new /obj/screen( null )
mymob.hands.icon = ui_style
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index f9c572ae67b..b2d5cf58624 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -21,8 +21,31 @@
dna = new /datum/dna(null)
//initialise organs
- organs = newlist(/datum/organ/external/chest, /datum/organ/external/head, /datum/organ/external/l_arm, \
- /datum/organ/external/r_arm, /datum/organ/external/r_leg, /datum/organ/external/l_leg)
+ organs = list()
+ organs_by_name["chest"] = new/datum/organ/external/chest()
+ organs_by_name["head"] = new/datum/organ/external/head()
+ organs_by_name["l_arm"] = new/datum/organ/external/l_arm()
+ organs_by_name["r_arm"] = new/datum/organ/external/r_arm()
+ organs_by_name["r_leg"] = new/datum/organ/external/r_leg()
+ organs_by_name["l_leg"] = new/datum/organ/external/l_leg()
+ organs_by_name["l_hand"] = new/datum/organ/external/l_hand()
+ organs_by_name["r_hand"] = new/datum/organ/external/r_hand()
+ organs_by_name["l_foot"] = new/datum/organ/external/l_foot()
+ organs_by_name["r_foot"] = new/datum/organ/external/r_foot()
+
+ // connect feet to legs and hands to arms
+ var/datum/organ/external/organ = organs_by_name["l_hand"]
+ organ.parent = organs_by_name["l_arm"]
+ organ = organs_by_name["r_hand"]
+ organ.parent = organs_by_name["r_arm"]
+ organ = organs_by_name["l_foot"]
+ organ.parent = organs_by_name["l_leg"]
+ organ = organs_by_name["r_foot"]
+ organ.parent = organs_by_name["r_leg"]
+
+ for(var/name in organs_by_name)
+ organs += organs_by_name[name]
+
for(var/datum/organ/external/O in organs)
O.owner = src
@@ -518,8 +541,8 @@
//Returns "Unknown" if facially disfigured and real_name if not. Useful for setting name when polyacided or when updating a human's name variable
/mob/living/carbon/human/proc/get_face_name()
- var/datum/organ/external/O = get_organ("head")
- if( (status_flags&DISFIGURED) || (O.brutestate+O.burnstate)>2 || !real_name ) //disfigured. use id-name if possible
+ var/datum/organ/external/head/head = get_organ("head")
+ if( !head || head.disfigured || !real_name ) //disfigured. use id-name if possible
return "Unknown"
return real_name
diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm
index 13672c0baf3..e2110f90fa5 100644
--- a/code/modules/mob/living/carbon/human/human_damage.dm
+++ b/code/modules/mob/living/carbon/human/human_damage.dm
@@ -86,11 +86,11 @@
//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)
+/mob/living/carbon/human/take_organ_damage(var/brute, var/burn, var/sharp = 0)
var/list/datum/organ/external/parts = get_damageable_organs()
if(!parts.len) return
var/datum/organ/external/picked = pick(parts)
- if(picked.take_damage(brute,burn))
+ if(picked.take_damage(brute,burn,sharp))
UpdateDamageIcon()
updatehealth()
@@ -116,7 +116,7 @@
if(update) UpdateDamageIcon()
// damage MANY external organs, in random order
-/mob/living/carbon/human/take_overall_damage(var/brute, var/burn)
+/mob/living/carbon/human/take_overall_damage(var/brute, var/burn, var/sharp = 0)
if(nodamage) return //godmode
var/list/datum/organ/external/parts = get_damageable_organs()
var/update = 0
@@ -126,7 +126,7 @@
var/brute_was = picked.brute_dam
var/burn_was = picked.burn_dam
- update |= picked.take_damage(brute,burn)
+ update |= picked.take_damage(brute,burn,sharp)
brute -= (picked.brute_dam - brute_was)
burn -= (picked.burn_dam - burn_was)
@@ -150,19 +150,17 @@
/mob/living/carbon/human/proc/get_organ(var/zone)
if(!zone) zone = "chest"
- for(var/datum/organ/external/O in organs)
- if(O.name == zone)
- return O
- return null
+ return organs_by_name[zone]
-
-/mob/living/carbon/human/apply_damage(var/damage = 0,var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0)
+/mob/living/carbon/human/apply_damage(var/damage = 0,var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/obj/item/weapon)
if((damagetype != BRUTE) && (damagetype != BURN))
..(damage, damagetype, def_zone, blocked)
return 1
if(blocked >= 2) return 0
+ var/sharp = weapon.sharp
+
var/datum/organ/external/organ = null
if(isorgan(def_zone))
organ = def_zone
@@ -178,10 +176,10 @@
switch(damagetype)
if(BRUTE)
- if(organ.take_damage(damage, 0))
+ if(organ.take_damage(damage, 0, sharp))
UpdateDamageIcon()
if(BURN)
- if(organ.take_damage(0, damage))
+ if(organ.take_damage(0, damage, sharp))
UpdateDamageIcon()
updatehealth()
return 1
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index c593b32b1dc..ad2d4dc294e 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -44,6 +44,7 @@
var/icon/lying_icon = null
var/list/organs = list()
+ var/list/organs_by_name = list() // map organ names to organs
var/miming = null //Toggle for the mime's abilities.
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index 75bacdc02b4..e62aca49398 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -53,6 +53,8 @@
fire_alert = 0 //Reset this here, because both breathe() and handle_environment() have a chance to set it.
//TODO: seperate this out
+ // update the current life tick, can be used to e.g. only do something every 4 ticks
+ life_tick++
var/datum/gas_mixture/environment = loc.return_air()
//No need to update all of these procs if the guy is dead.
@@ -167,6 +169,71 @@
if(3)
emote("drool")
+ proc/handle_organs()
+ // take care of organ related updates, such as broken and missing limbs
+
+ // recalculate number of wounds
+ number_wounds = 0
+ for(var/datum/organ/external/E in organs)
+ if(!E)
+ world << name
+ continue
+ number_wounds += E.number_wounds
+
+ var/leg_tally = 2
+ for(var/datum/organ/external/E in organs)
+ E.process()
+ if(E.status & ORGAN_ROBOT && prob(E.brute_dam + E.burn_dam))
+ if(E.name == "l_hand" || E.name == "l_arm")
+ if(hand && equipped())
+ drop_item()
+ emote("custom v drops what they were holding, their [E.display_name?"[E.display_name]":"[E]"] malfunctioning!")
+ var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread()
+ spark_system.set_up(5, 0, src)
+ spark_system.attach(src)
+ spark_system.start()
+ spawn(10)
+ del(spark_system)
+ else if(E.name == "r_hand" || E.name == "r_arm")
+ if(!hand && equipped())
+ drop_item()
+ emote("custom v drops what they were holding, their [E.display_name?"[E.display_name]":"[E]"] malfunctioning!")
+ var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread()
+ spark_system.set_up(5, 0, src)
+ spark_system.attach(src)
+ spark_system.start()
+ spawn(10)
+ del(spark_system)
+ else if(E.name == "l_leg" || E.name == "l_foot" \
+ || E.name == "r_leg" || E.name == "r_foot" && !lying)
+ leg_tally-- // let it fail even if just foot&leg
+ if(E.status & ORGAN_BROKEN || E.status & ORGAN_DESTROYED)
+ if(E.name == "l_hand" || E.name == "l_arm")
+ if(hand && equipped())
+ if(E.status & ORGAN_SPLINTED && prob(10))
+ drop_item()
+ emote("scream")
+ else
+ drop_item()
+ emote("scream")
+ else if(E.name == "r_hand" || E.name == "r_arm")
+ if(!hand && equipped())
+ if(E.status & ORGAN_SPLINTED && prob(10))
+ drop_item()
+ emote("scream")
+ else
+ drop_item()
+ emote("scream")
+ else if(E.name == "l_leg" || E.name == "l_foot" \
+ || E.name == "r_leg" || E.name == "r_foot" && !lying)
+ if(!(E.status & ORGAN_SPLINTED))
+ leg_tally-- // let it fail even if just foot&leg
+ // can't stand
+ if(leg_tally == 0 && !paralysis && !(lying || resting))
+ emote("scream")
+ emote("collapse")
+ paralysis = 10
+
proc/handle_mutations_and_radiation()
if(getFireLoss())
@@ -817,6 +884,7 @@
silent = 0
else //ALIVE. LIGHTS ARE ON
updatehealth() //TODO
+ handle_organs()
if(health <= config.health_threshold_dead || brain_op_stage == 4.0)
death()
blinded = 1
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index 8f9135de521..eeaf1e23aa5 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -122,6 +122,7 @@ Please contact me on #coderbus IRC. ~Carn x
/mob/living/carbon/human
var/list/overlays_lying[TOTAL_LAYERS]
var/list/overlays_standing[TOTAL_LAYERS]
+ var/previous_damage_appearance // store what the body last looked like, so we only have to update it if something changed
//UPDATES OVERLAYS FROM OVERLAYS_LYING/OVERLAYS_STANDING
@@ -164,39 +165,108 @@ Please contact me on #coderbus IRC. ~Carn x
//DAMAGE OVERLAYS
//constructs damage icon for each organ from mask * damage field and saves it in our overlays_ lists
/mob/living/carbon/human/UpdateDamageIcon(var/update_icons=1)
- var/image/standing = image("icon" = 'icons/mob/dam_human.dmi', "icon_state" = "blank")
- var/image/lying = image("icon" = 'icons/mob/dam_human.dmi', "icon_state" = "blank2")
- for(var/datum/organ/external/O in organs)
- if(O.brutestate)
- standing.overlays += "[O.icon_name]_[O.brutestate]0" //we're adding icon_states of the base image as overlays
- lying.overlays += "[O.icon_name]2_[O.brutestate]0"
- if(O.burnstate)
- standing.overlays += "[O.icon_name]_0[O.burnstate]"
- lying.overlays += "[O.icon_name]2_0[O.burnstate]"
+ // first check whether something actually changed about damage appearance
+ var/damage_appearance = ""
+
+ for(var/datum/organ/external/O in organs)
+ if(O.status & ORGAN_DESTROYED) damage_appearance += "d"
+ else
+ damage_appearance += O.damage_state
+
+ if(damage_appearance == previous_damage_appearance)
+ // nothing to do here
+ return
+
+ previous_damage_appearance = damage_appearance
+
+ var/icon/standing = new /icon('dam_human.dmi', "00")
+ var/icon/lying = new /icon('dam_human.dmi', "00-2")
+
+ // blend the individual damage states with our icons
+ for(var/datum/organ/external/O in organs)
+ if(!(O.status & ORGAN_DESTROYED))
+ O.update_icon()
+ var/icon/DI = new /icon('icons/mob/dam_human.dmi', O.damage_state) // the damage icon for whole human
+ DI.Blend(new /icon('dam_mask.dmi', O.icon_name), ICON_MULTIPLY) // mask with this organ's pixels
+
+ standing.Blend(DI,ICON_OVERLAY)
+ DI = new /icon('icons/mob/dam_human.dmi', "[O.damage_state]-2") // repeat for lying icons
+ DI.Blend(new /icon('dam_mask.dmi', "[O.icon_name]2"), ICON_MULTIPLY)
+
+ lying.Blend(DI,ICON_OVERLAY)
+
+ var/image/standing_image = new /image("icon" = standing)
+ var/image/lying_image = new /image("icon" = lying)
+
+ overlays_standing[DAMAGE_LAYER] = standing_image
+ overlays_lying[DAMAGE_LAYER] = lying_image
- overlays_standing[DAMAGE_LAYER] = standing
- overlays_lying[DAMAGE_LAYER] = lying
if(update_icons) update_icons()
//BASE MOB SPRITE
/mob/living/carbon/human/proc/update_body(var/update_icons=1)
+
if(stand_icon) del(stand_icon)
if(lying_icon) del(lying_icon)
if(dna && dna.mutantrace) return
- var/husk = (HUSK in src.mutations)
- var/fat = (FAT in src.mutations)
+
var/g = "m"
if(gender == FEMALE) g = "f"
+ var/husk = (HUSK in src.mutations)
+ var/obese = (FAT in src.mutations)
+
+ // whether to draw the individual limbs
+ var/individual_limbs = 1
+
//Base mob icon
- if(husk)
- stand_icon = new /icon('icons/mob/human.dmi', "husk_s")
- lying_icon = new /icon('icons/mob/human.dmi', "husk_l")
- else if(fat)
+ if(obese)
+ // Sorry, no dismemberment for fat people.
stand_icon = new /icon('icons/mob/human.dmi', "fatbody_s")
lying_icon = new /icon('icons/mob/human.dmi', "fatbody_l")
+ individual_limbs = 0
else
- stand_icon = new /icon('icons/mob/human.dmi', "body_[g]_s")
- lying_icon = new /icon('icons/mob/human.dmi', "body_[g]_l")
+ stand_icon = new /icon('icons/mob/human.dmi', "torso_[g]_s")
+ lying_icon = new /icon('icons/mob/human.dmi', "torso_[g]_l")
+
+ // Draw each individual limb
+ if(individual_limbs)
+ stand_icon.Blend(new /icon('icons/mob/human.dmi', "chest_[g]_s"), ICON_OVERLAY)
+ lying_icon.Blend(new /icon('icons/mob/human.dmi', "chest_[g]_l"), ICON_OVERLAY)
+
+ var/datum/organ/external/head = get_organ("head")
+ if(head && !(head.status & ORGAN_DESTROYED))
+ stand_icon.Blend(new /icon('icons/mob/human.dmi', "head_[g]_s"), ICON_OVERLAY)
+ lying_icon.Blend(new /icon('icons/mob/human.dmi', "head_[g]_l"), ICON_OVERLAY)
+
+ for(var/datum/organ/external/part in organs)
+ if(!istype(part, /datum/organ/external/groin) \
+ && !istype(part, /datum/organ/external/chest) \
+ && !istype(part, /datum/organ/external/head) \
+ && !(part.status & ORGAN_DESTROYED))
+ var/icon/temp = new /icon('human.dmi', "[part.icon_name]_s")
+ if(part.status & ORGAN_ROBOT) temp.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0))
+ stand_icon.Blend(temp, ICON_OVERLAY)
+ temp = new /icon('human.dmi', "[part.icon_name]_l")
+ if(part.status & ORGAN_ROBOT) temp.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0))
+ lying_icon.Blend(temp , ICON_OVERLAY)
+
+ stand_icon.Blend(new /icon('human.dmi', "groin_[g]_s"), ICON_OVERLAY)
+ lying_icon.Blend(new /icon('human.dmi', "groin_[g]_l"), ICON_OVERLAY)
+
+ if (husk)
+ var/icon/husk_s = new /icon('human.dmi', "husk_s")
+ var/icon/husk_l = new /icon('human.dmi', "husk_l")
+
+ for(var/datum/organ/external/part in organs)
+ if(!istype(part, /datum/organ/external/groin) \
+ && !istype(part, /datum/organ/external/chest) \
+ && !istype(part, /datum/organ/external/head) \
+ && (part.status & ORGAN_DESTROYED))
+ husk_s.Blend(new /icon('dam_mask.dmi', "[part.icon_name]"), ICON_SUBTRACT)
+ husk_l.Blend(new /icon('dam_mask.dmi', "[part.icon_name]2"), ICON_SUBTRACT)
+
+ stand_icon.Blend(husk_s, ICON_OVERLAY)
+ lying_icon.Blend(husk_l, ICON_OVERLAY)
//Skin tone
if(s_tone >= 0)
@@ -207,6 +277,7 @@ Please contact me on #coderbus IRC. ~Carn x
lying_icon.Blend(rgb(-s_tone, -s_tone, -s_tone), ICON_SUBTRACT)
//Eyes
+ // Note: These used to be in update_face(), and the fact they're here will make it difficult to create a disembodied head
var/icon/eyes_s = new/icon('icons/mob/human_face.dmi', "eyes_s")
var/icon/eyes_l = new/icon('icons/mob/human_face.dmi', "eyes_l")
eyes_s.Blend(rgb(r_eyes, g_eyes, b_eyes), ICON_ADD)
@@ -219,11 +290,11 @@ Please contact me on #coderbus IRC. ~Carn x
lying_icon.Blend(new/icon('icons/mob/human_face.dmi', "mouth_[g]_l"), ICON_OVERLAY)
//Underwear
- if(underwear < 12 && underwear > 0)
- if(!fat)
- stand_icon.Blend(new /icon('icons/mob/human.dmi', "underwear[underwear]_[g]_s"), ICON_OVERLAY)
- lying_icon.Blend(new /icon('icons/mob/human.dmi', "underwear[underwear]_[g]_l"), ICON_OVERLAY)
- if(update_icons) update_icons()
+ if(underwear < 6 && underwear > 0)
+ if(!obese)
+ stand_icon.Blend(new /icon('human.dmi', "underwear[underwear]_[g]_s"), ICON_OVERLAY)
+ lying_icon.Blend(new /icon('human.dmi', "underwear[underwear]_[g]_l"), ICON_OVERLAY)
+
//HAIR OVERLAY
@@ -649,6 +720,42 @@ Please contact me on #coderbus IRC. ~Carn x
overlays_standing[L_HAND_LAYER] = null
if(update_icons) update_icons()
+// Used mostly for creating head items
+/mob/living/carbon/human/proc/generate_head_icon()
+ var/g = "m"
+ if (gender == FEMALE) g = "f"
+
+ //base icons
+ var/icon/face_lying = new /icon('icons/mob/human_face.dmi',"bald_l")
+
+ if(f_style)
+ var/datum/sprite_accessory/facial_hair_style = facial_hair_styles_list[f_style]
+ if(facial_hair_style)
+ var/icon/facial_l = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_l")
+ facial_l.Blend(rgb(r_facial, g_facial, b_facial), ICON_ADD)
+ face_lying.Blend(facial_l, ICON_OVERLAY)
+
+ if(h_style)
+ var/datum/sprite_accessory/hair_style = hair_styles_list[h_style]
+ if(hair_style)
+ var/icon/hair_l = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_l")
+ hair_l.Blend(rgb(r_hair, g_hair, b_hair), ICON_ADD)
+ face_lying.Blend(hair_l, ICON_OVERLAY)
+
+ //Eyes
+ // Note: These used to be in update_face(), and the fact they're here will make it difficult to create a disembodied head
+ var/icon/eyes_s = new/icon('icons/mob/human_face.dmi', "eyes_s")
+ var/icon/eyes_l = new/icon('icons/mob/human_face.dmi', "eyes_l")
+ eyes_s.Blend(rgb(r_eyes, g_eyes, b_eyes), ICON_ADD)
+ eyes_l.Blend(rgb(r_eyes, g_eyes, b_eyes), ICON_ADD)
+ face_lying.Blend(eyes_l, ICON_OVERLAY)
+
+ //Mouth
+ face_lying.Blend(new/icon('icons/mob/human_face.dmi', "mouth_[g]_l"), ICON_OVERLAY)
+
+ var/image/face_lying_image = new /image(icon = face_lying)
+ return face_lying_image
+
//Human Overlays Indexes/////////
#undef MUTANTRACE_LAYER
#undef MUTATIONS_LAYER
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index 4a442e48e62..c46484f1e63 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -30,6 +30,7 @@
var/obj/screen/nutrition_icon = null
var/obj/screen/pressure = null
var/obj/screen/damageoverlay = null
+ var/obj/screen/pain = null
/*A bunch of this stuff really needs to go under their own defines instead of being globally attached to mob.
A variable should only be globally attached to turfs/objects/whatever, when it is in fact needed as such.
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index c7921f001b4..6702beab34d 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -115,6 +115,9 @@ proc/isorgan(A)
return 1
return 0
+proc/hasorgans(A)
+ return (ishuman(A) || ismonkey(A))
+
/proc/hsl2rgb(h, s, l)
return
diff --git a/code/setup.dm b/code/setup.dm
index e77c20295f4..ad54c4008ab 100644
--- a/code/setup.dm
+++ b/code/setup.dm
@@ -500,3 +500,36 @@ var/list/liftable_structures = list(\
//some arbitrary defines to be used by self-pruning global lists. (see master_controller)
#define PROCESS_KILL 26 //Used to trigger removal from a processing list
+
+//Damage things
+#define CUT "cut"
+#define BRUISE "bruise"
+#define BRUTE "brute"
+#define BURN "fire"
+#define TOX "tox"
+#define OXY "oxy"
+#define CLONE "clone"
+#define HALLOSS "halloss"
+
+#define STUN "stun"
+#define WEAKEN "weaken"
+#define PARALYZE "paralize"
+#define IRRADIATE "irradiate"
+#define STUTTER "stutter"
+#define SLUR "slur"
+#define EYE_BLUR "eye_blur"
+#define DROWSY "drowsy"
+
+/////////////////
+//ORGAN DEFINES//
+/////////////////
+#define ORGAN_CUT_AWAY 1
+#define ORGAN_GAUZED 2
+#define ORGAN_ATTACHABLE 4
+#define ORGAN_BLEEDING 8
+#define ORGAN_BANDAGED 16
+#define ORGAN_BROKEN 32
+#define ORGAN_DESTROYED 64
+#define ORGAN_ROBOT 128
+#define ORGAN_SPLINTED 256
+#define SALVED 512
\ No newline at end of file
diff --git a/config/game_options.txt b/config/game_options.txt
index 9ec32a29484..9eef19a5c8e 100644
--- a/config/game_options.txt
+++ b/config/game_options.txt
@@ -6,6 +6,14 @@ HEALTH_THRESHOLD_CRIT 0
## level of health at which a mob becomes dead
HEALTH_THRESHOLD_DEAD -100
+## multiplier which enables organs to take more damage before bones breaking or limbs being destroyed
+## 100 means normal, 50 means half
+ORGAN_HEALTH_MULTIPLIER 100
+
+## multiplier which influences how fast organs regenerate naturally
+## 100 means normal, 50 means half
+ORGAN_REGENERATION_MULTIPLIER 100
+
### REVIVAL ###
## whether pod plants work or not
diff --git a/icons/mob/dam_human.dmi b/icons/mob/dam_human.dmi
index 379f7dc5eb8..c68299554c8 100644
Binary files a/icons/mob/dam_human.dmi and b/icons/mob/dam_human.dmi differ
diff --git a/icons/mob/dam_mask.dmi b/icons/mob/dam_mask.dmi
new file mode 100644
index 00000000000..5cf1e09f19b
Binary files /dev/null and b/icons/mob/dam_mask.dmi differ
diff --git a/icons/mob/human.dmi b/icons/mob/human.dmi
index 1526448c6fd..1dff87d69e0 100644
Binary files a/icons/mob/human.dmi and b/icons/mob/human.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 800185439ab..7caa8572108 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -223,7 +223,6 @@
// END_FILE_DIR
// BEGIN_PREFERENCES
-#define DEBUG
// END_PREFERENCES
// BEGIN_INCLUDE
@@ -307,6 +306,7 @@
#include "code\datums\helper_datums\topic_input.dm"
#include "code\datums\organs\organ.dm"
#include "code\datums\organs\organ_external.dm"
+#include "code\datums\organs\wound.dm"
#include "code\datums\spells\area_teleport.dm"
#include "code\datums\spells\conjure.dm"
#include "code\datums\spells\emplosion.dm"