If on the chest, walk, grasp it, splint, rest or buckle yourself to something to reduce movement effects. \
+ Afterwards, get someone else, ideally a robo/engi to screwdriver/wrench it, and then re-solder it!"
+ homemade_treat_text = "If unable to screw/wrench, bone gel can, over time, secure inner components at risk of corrossion. \
+ Alternatively, crowbar the limb open to expose the internals - this will make it easier to re-secure them, but has a high risk of shocking you, \
+ so use insulated gloves. This will cripple the limb, so use it only as a last resort!"
+
+ wound_flags = (ACCEPTS_GAUZE|MANGLES_EXTERIOR|SPLINT_OVERLAY|CAN_BE_GRASPED)
+ treatable_by = list(/obj/item/stack/medical/bone_gel)
+ status_effect_type = /datum/status_effect/wound/blunt/robotic/severe
+ treatable_tools = list(TOOL_WELDER, TOOL_CROWBAR)
+
+ interaction_efficiency_penalty = 2
+ limp_slowdown = 6
+ limp_chance = 60
+
+ brain_trauma_group = BRAIN_TRAUMA_MILD
+ trauma_cycle_cooldown = 1.5 MINUTES
+
+ threshold_penalty = 40
+
+ base_movement_stagger_score = 40
+
+ chest_attacked_stagger_chance_ratio = 5
+ chest_attacked_stagger_mult = 3
+
+ chest_movement_stagger_chance = 2
+
+ stagger_aftershock_knockdown_ratio = 0.3
+ stagger_aftershock_knockdown_movement_ratio = 0.2
+
+ a_or_from = "from"
+
+ ready_to_secure_internals = TRUE
+ ready_to_resolder = FALSE
+
+ scar_keyword = "bluntsevere"
+
+/datum/wound_pregen_data/blunt_metal/fastenings
+ abstract = FALSE
+
+ wound_path_to_generate = /datum/wound/blunt/robotic/secures_internals/severe
+
+ threshold_minimum = 65
diff --git a/modular_doppler/modular_medical/wounds/synth/blunt/robotic_blunt_T3.dm b/modular_doppler/modular_medical/wounds/synth/blunt/robotic_blunt_T3.dm
new file mode 100644
index 0000000000000..0c06eff483f69
--- /dev/null
+++ b/modular_doppler/modular_medical/wounds/synth/blunt/robotic_blunt_T3.dm
@@ -0,0 +1,398 @@
+/datum/wound/blunt/robotic/secures_internals/critical
+ name = "Collapsed Superstructure"
+ desc = "The superstructure has totally collapsed in one or more locations, causing extreme internal oscillation with every move and massive limb dysfunction"
+ treat_text = "Reforming of superstructure via either RCD or manual molding, followed by typical treatment of loosened internals. \
+ To manually mold, the limb must be aggressively grabbed and welded held to it to make it malleable (though attacking it til thermal overload may be adequate) \
+ followed by firmly grasping and molding the limb with heat-resistant gloves."
+ occur_text = "caves in on itself, damaged solder and shrapnel flying out in a miniature explosion"
+ examine_desc = "has caved in, with internal components visible through gaps in the metal"
+ severity = WOUND_SEVERITY_CRITICAL
+
+ disabling = TRUE
+
+ simple_treat_text = "If on the chest, walk, grasp it, splint, rest or buckle yourself to something to reduce movement effects. \
+ Afterwards, get someone, ideally a robo/engi to firmly grasp the limb and hold a welder to it. Then, have them use their hands to mold the metal - \
+ careful though, it's hot! An RCD can skip all this, but is hard to come by. Afterwards, have them screw/wrench and then re-solder the limb!"
+
+ homemade_treat_text = "The metal can be made malleable by repeated application of a welder, to a severe burn. Afterwards, a plunger can reset the metal, \
+ as can percussive maintenance. After the metal is reset, if unable to screw/wrench, bone gel can, over time, secure inner components at risk of corrossion. \
+ Alternatively, crowbar the limb open to expose the internals - this will make it easier to re-secure them, but has a high risk of shocking you, \
+ so use insulated gloves. This will cripple the limb, so use it only as a last resort!"
+
+ interaction_efficiency_penalty = 2.8
+ limp_slowdown = 8
+ limp_chance = 80
+ threshold_penalty = 60
+
+ brain_trauma_group = BRAIN_TRAUMA_SEVERE
+ trauma_cycle_cooldown = 2.5 MINUTES
+
+ scar_keyword = "bluntcritical"
+
+ status_effect_type = /datum/status_effect/wound/blunt/robotic/critical
+
+ sound_effect = 'sound/effects/wounds/crack2.ogg'
+
+ wound_flags = (ACCEPTS_GAUZE|MANGLES_EXTERIOR|SPLINT_OVERLAY|CAN_BE_GRASPED)
+ treatable_by = list(/obj/item/stack/medical/bone_gel)
+ status_effect_type = /datum/status_effect/wound/blunt/robotic/critical
+ treatable_tools = list(TOOL_WELDER, TOOL_CROWBAR)
+
+ base_movement_stagger_score = 50
+
+ base_aftershock_camera_shake_duration = 1.75 SECONDS
+ base_aftershock_camera_shake_strength = 1
+
+ chest_attacked_stagger_chance_ratio = 6.5
+ chest_attacked_stagger_mult = 4
+
+ chest_movement_stagger_chance = 8
+
+ aftershock_stopped_moving_score_mult = 0.3
+
+ stagger_aftershock_knockdown_ratio = 0.5
+ stagger_aftershock_knockdown_movement_ratio = 0.3
+
+ percussive_maintenance_repair_chance = 3
+ percussive_maintenance_damage_max = 6
+
+ regen_time_needed = 60 SECONDS
+ gel_damage = 20
+
+ ready_to_secure_internals = FALSE
+ ready_to_resolder = FALSE
+
+ a_or_from = "a"
+
+ /// Has the first stage of our treatment been completed? E.g. RCDed, manually molded...
+ var/superstructure_remedied = FALSE
+
+/datum/wound_pregen_data/blunt_metal/superstructure
+ abstract = FALSE
+ wound_path_to_generate = /datum/wound/blunt/robotic/secures_internals/critical
+ threshold_minimum = 125
+
+/datum/wound/blunt/robotic/secures_internals/critical/item_can_treat(obj/item/potential_treater)
+ if(!superstructure_remedied)
+ if(istype(potential_treater, /obj/item/construction/rcd))
+ return TRUE
+ if(limb_malleable() && istype(potential_treater, /obj/item/plunger))
+ return TRUE
+ return ..()
+
+/datum/wound/blunt/robotic/secures_internals/critical/check_grab_treatments(obj/item/potential_treater, mob/user)
+ if(potential_treater.tool_behaviour == TOOL_WELDER && (!superstructure_remedied && !limb_malleable()))
+ return TRUE
+ return ..()
+
+/datum/wound/blunt/robotic/secures_internals/critical/treat(obj/item/item, mob/user)
+ if(!superstructure_remedied)
+ if(istype(item, /obj/item/construction/rcd))
+ return rcd_superstructure(item, user)
+ if(uses_percussive_maintenance() && istype(item, /obj/item/plunger))
+ return plunge(item, user)
+ if(item.tool_behaviour == TOOL_WELDER && !limb_malleable() && isliving(victim.pulledby))
+ var/mob/living/living_puller = victim.pulledby
+ if (living_puller.grab_state >= GRAB_AGGRESSIVE) // only let other people do this
+ return heat_metal(item, user)
+ return ..()
+
+/datum/wound/blunt/robotic/secures_internals/critical/try_handling(mob/living/carbon/human/user)
+ if(user.pulling != victim || user.zone_selected != limb.body_zone)
+ return FALSE
+
+ if(superstructure_remedied || !limb_malleable())
+ return FALSE
+
+ if(user.grab_state < GRAB_AGGRESSIVE)
+ to_chat(user, span_warning("You must have [victim] in an aggressive grab to manipulate [victim.p_their()] [LOWER_TEXT(name)]!"))
+ return TRUE
+
+ user.visible_message(span_danger("[user] begins softly pressing against [victim]'s collapsed [limb.plaintext_zone]..."), \
+ span_notice("You begin softly pressing against [victim]'s collapsed [limb.plaintext_zone]..."), \
+ ignored_mobs = victim)
+ to_chat(victim, span_userdanger("[user] begins pressing against your collapsed [limb.plaintext_zone]!"))
+
+ var/delay_mult = 1
+ if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED))
+ delay_mult *= 0.75
+
+ if(!do_after(user, 4 SECONDS, target = victim, extra_checks = CALLBACK(src, PROC_REF(still_exists))))
+ return
+ mold_metal(user)
+ return TRUE
+
+/// If the user turns combat mode on after they start to mold metal, our limb takes this much brute damage.
+#define MOLD_METAL_SABOTAGE_BRUTE_DAMAGE 30 // really punishing
+/// Our limb takes this much brute damage on a failed mold metal attempt.
+#define MOLD_METAL_FAILURE_BRUTE_DAMAGE 5
+/// If the user's hand is unprotected from heat when they mold metal, we do this much burn damage to it.
+#define MOLD_METAL_HAND_BURNT_BURN_DAMAGE 5
+/// Gloves must be above or at this threshold to cause the user to not be burnt apon trying to mold metal.
+#define MOLD_METAL_HEAT_RESISTANCE_THRESHOLD 1000 // less than the black gloves max resist
+/**
+ * Standard treatment for 1st step of T3, after the limb has been made malleable. Done via aggrograb.
+ * High chance to work, very high with robo/engi wires and diag hud.
+ * Can be sabotaged by switching to combat mode.
+ * Deals brute to the limb on failure.
+ * Burns the hand of the user if it's not insulated.
+ */
+/datum/wound/blunt/robotic/secures_internals/critical/proc/mold_metal(mob/living/carbon/human/user)
+ var/chance = 60
+
+ var/knows_wires = FALSE
+ if (HAS_TRAIT(user, TRAIT_KNOW_ROBO_WIRES))
+ chance *= 2
+ knows_wires = TRUE
+ else if (HAS_TRAIT(user, TRAIT_KNOW_ENGI_WIRES))
+ chance *= 1.25
+ knows_wires = TRUE
+ if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED))
+ chance *= 2
+ if (HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD))
+ if (knows_wires)
+ chance *= 1.25
+ else
+ chance *= 2
+
+ var/their_or_other = (user == victim ? "[user.p_their()]" : "[victim]'s")
+ var/your_or_other = (user == victim ? "your" : "[victim]'s")
+
+ if ((user != victim && user.combat_mode))
+ user.visible_message(span_bolddanger("[user] molds [their_or_other] [limb.plaintext_zone] into a really silly shape! What a goofball!"), \
+ span_danger("You maliciously mold [victim]'s [limb.plaintext_zone] into a weird shape, damaging it in the process!"), ignored_mobs = victim)
+ to_chat(victim, span_userdanger("[user] molds your [limb.plaintext_zone] into a weird shape, damaging it in the process!"))
+
+ limb.receive_damage(brute = MOLD_METAL_SABOTAGE_BRUTE_DAMAGE, wound_bonus = CANT_WOUND, damage_source = user)
+ else if (prob(chance))
+ user.visible_message(span_green("[user] carefully molds [their_or_other] [limb.plaintext_zone] into the proper shape!"), \
+ span_green("You carefully mold [victim]'s [limb.plaintext_zone] into the proper shape!"), ignored_mobs = victim)
+ to_chat(victim, span_green("[user] carefully molds your [limb.plaintext_zone] into the proper shape!"))
+ to_chat(user, span_green("[capitalize(your_or_other)] [limb.plaintext_zone] has been molded into the proper shape! Your next step is to use a screwdriver/wrench to secure your internals."))
+ set_superstructure_status(TRUE)
+ else
+ user.visible_message(span_danger("[user] accidentally molds [their_or_other] [limb.plaintext_zone] into the wrong shape!"), \
+ span_danger("You accidentally mold [your_or_other] [limb.plaintext_zone] into the wrong shape!"), ignored_mobs = victim)
+ to_chat(victim, span_userdanger("[user] accidentally molds your [limb.plaintext_zone] into the wrong shape!"))
+
+ limb.receive_damage(brute = MOLD_METAL_FAILURE_BRUTE_DAMAGE, damage_source = user, wound_bonus = CANT_WOUND)
+
+ var/sufficiently_insulated_gloves = FALSE
+ var/obj/item/clothing/gloves/worn_gloves = user.gloves
+ if ((worn_gloves?.heat_protection & HANDS) && worn_gloves?.max_heat_protection_temperature && worn_gloves.max_heat_protection_temperature >= MOLD_METAL_HEAT_RESISTANCE_THRESHOLD)
+ sufficiently_insulated_gloves = TRUE
+
+ if (sufficiently_insulated_gloves || HAS_TRAIT(user, TRAIT_RESISTHEAT) || HAS_TRAIT(user, TRAIT_RESISTHEATHANDS))
+ return
+
+ to_chat(user, span_danger("You burn your hand on [victim]'s [limb.plaintext_zone]!"))
+ var/obj/item/bodypart/affecting = user.get_bodypart("[(user.active_hand_index % 2 == 0) ? "r" : "l" ]_arm")
+ affecting?.receive_damage(burn = MOLD_METAL_HAND_BURNT_BURN_DAMAGE, damage_source = limb)
+
+#undef MOLD_METAL_SABOTAGE_BRUTE_DAMAGE
+#undef MOLD_METAL_FAILURE_BRUTE_DAMAGE
+#undef MOLD_METAL_HAND_BURNT_BURN_DAMAGE
+#undef MOLD_METAL_HEAT_RESISTANCE_THRESHOLD
+
+/**
+ * A "safe" way to give our victim a T2 burn wound. Requires an aggrograb, and a welder. This is required to mold metal, the 1st step of treatment.
+ * Guaranteed to work. After a delay, causes a T2 burn wound with no damage.
+ * Can be sabotaged by enabling combat mode to cause a T3.
+ */
+/datum/wound/blunt/robotic/secures_internals/critical/proc/heat_metal(obj/item/welder, mob/living/user)
+ if (!welder.tool_use_check())
+ return TRUE
+
+ var/their_or_other = (user == victim ? "[user.p_their()]" : "[victim]'s")
+ var/your_or_other = (user == victim ? "your" : "[victim]'s")
+
+ user?.visible_message(span_danger("[user] carefully holds [welder] to [their_or_other] [limb.plaintext_zone], slowly heating it..."), \
+ span_warning("You carefully hold [welder] to [your_or_other] [limb.plaintext_zone], slowly heating it..."), ignored_mobs = victim)
+
+ var/delay_mult = 1
+ if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED))
+ delay_mult *= 0.75
+
+ if (!welder.use_tool(target = victim, user = user, delay = 3 SECONDS * delay_mult, volume = 50, extra_checks = CALLBACK(src, PROC_REF(still_exists))))
+ return TRUE
+
+ var/wound_path = /datum/wound/burn/robotic/overheat/moderate
+ if (user != victim && user.combat_mode)
+ wound_path = /datum/wound/burn/robotic/overheat/critical // it really isnt that bad, overheat wounds are a bit funky
+ user.visible_message(span_danger("[user] heats [victim]'s [limb.plaintext_zone] aggressively, overheating it far beyond the necessary point!"), \
+ span_danger("You heat [victim]'s [limb.plaintext_zone] aggressively, overheating it far beyond the necessary point!"), ignored_mobs = victim)
+ to_chat(victim, span_userdanger("[user] heats your [limb.plaintext_zone] aggressively, overheating it far beyond the necessary point!"))
+
+ var/datum/wound/burn/robotic/overheat/overheat_wound = new wound_path
+ overheat_wound.apply_wound(limb, wound_source = welder)
+
+ to_chat(user, span_green("[capitalize(your_or_other)] [limb.plaintext_zone] is now heated, allowing it to be molded! Your next step is to have someone physically reset the superstructure with their hands."))
+ return TRUE
+
+/// Cost of an RCD to quickly fix our broken in raw matter
+#define ROBOTIC_T3_BLUNT_WOUND_RCD_COST 25
+/// Cost of an RCD to quickly fix our broken in silo material
+#define ROBOTIC_T3_BLUNT_WOUND_RCD_SILO_COST ROBOTIC_T3_BLUNT_WOUND_RCD_COST / 4
+
+/// The "premium" treatment for 1st step of T3. Requires an RCD. Guaranteed to work, but can cause damage if delay is high.
+/datum/wound/blunt/robotic/secures_internals/critical/proc/rcd_superstructure(obj/item/construction/rcd/treating_rcd, mob/user)
+ if (!treating_rcd.tool_use_check())
+ return TRUE
+
+ var/has_enough_matter = (treating_rcd.get_matter(user) > ROBOTIC_T3_BLUNT_WOUND_RCD_COST)
+ var/silo_has_enough_materials = (treating_rcd.get_silo_iron() > ROBOTIC_T3_BLUNT_WOUND_RCD_SILO_COST)
+
+ if (!silo_has_enough_materials && !has_enough_matter) // neither the silo, nor the rcd, has enough
+ user?.balloon_alert(user, "not enough matter!")
+ return TRUE
+
+ var/their_or_other = (user == victim ? "[user.p_their()]" : "[victim]'s")
+ var/your_or_other = (user == victim ? "your" : "[victim]'s")
+
+ var/base_time = 7 SECONDS
+ var/delay_mult = 1
+ var/knows_wires = FALSE
+ if (victim == user)
+ delay_mult *= 2
+ if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED))
+ delay_mult *= 0.75
+ if (HAS_TRAIT(user, TRAIT_KNOW_ROBO_WIRES))
+ delay_mult *= 0.5
+ knows_wires = TRUE
+ else if (HAS_TRAIT(user, TRAIT_KNOW_ENGI_WIRES))
+ delay_mult *= 0.5 // engis are accustomed to using RCDs
+ knows_wires = TRUE
+ if (HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD))
+ if (knows_wires)
+ delay_mult *= 0.85
+ else
+ delay_mult *= 0.5
+
+ var/final_time = (base_time * delay_mult)
+ var/misused = (final_time > base_time) // if we damage the limb when we're done
+
+ if (user)
+ var/misused_text = (misused ? "unsteadily " : "")
+
+ var/message = "[user]'s RCD whirs to life as it begins [misused_text]replacing the damaged superstructure of [their_or_other] [limb.plaintext_zone]..."
+ var/self_message = "Your RCD whirs to life as it begins [misused_text]replacing the damaged superstructure of [your_or_other] [limb.plaintext_zone]..."
+
+ if (misused) // warning span if misused, notice span otherwise
+ message = span_danger(message)
+ self_message = span_danger(self_message)
+ else
+ message = span_notice(message)
+ self_message = span_notice(self_message)
+
+ user.visible_message(message, self_message)
+
+ if (!treating_rcd.use_tool(target = victim, user = user, delay = final_time, volume = 50, extra_checks = CALLBACK(src, PROC_REF(still_exists))))
+ return TRUE
+ playsound(get_turf(treating_rcd), 'sound/machines/ping.ogg', 75) // celebration! we did it
+ set_superstructure_status(TRUE)
+
+ var/use_amount = (silo_has_enough_materials ? ROBOTIC_T3_BLUNT_WOUND_RCD_SILO_COST : ROBOTIC_T3_BLUNT_WOUND_RCD_COST)
+ if (!treating_rcd.useResource(use_amount, user))
+ return TRUE
+
+ if (user)
+ var/misused_text = (misused ? ", though it replaced a bit more than it should've..." : "!")
+ var/message = "[user]'s RCD lets out a small ping as it finishes replacing the superstructure of [their_or_other] [limb.plaintext_zone][misused_text]"
+ var/self_message = "Your RCD lets out a small ping as it finishes replacing the superstructure of [your_or_other] [limb.plaintext_zone][misused_text]"
+ if (misused)
+ message = span_danger(message)
+ self_message = span_danger(self_message)
+ else
+ message = span_green(message)
+ self_message = span_green(self_message)
+
+ user.visible_message(message, self_message)
+ if (misused)
+ limb.receive_damage(brute = 10, damage_source = treating_rcd, wound_bonus = CANT_WOUND)
+ // the double message is fine here, since the first message also tells you if you fucked up and did some damage
+ to_chat(user, span_green("The superstructure has been reformed! Your next step is to secure the internals via a screwdriver/wrench."))
+ return TRUE
+
+#undef ROBOTIC_T3_BLUNT_WOUND_RCD_COST
+#undef ROBOTIC_T3_BLUNT_WOUND_RCD_SILO_COST
+
+/**
+ * Goofy but practical, this is the superior ghetto self-tend of T3's first step compared to percussive maintenance.
+ * Still requires the limb to be malleable, but has a high chance of success and doesn't burn your hand, but gives worse bonuses for wires/HUD.
+ */
+/datum/wound/blunt/robotic/secures_internals/critical/proc/plunge(obj/item/plunger/treating_plunger, mob/user)
+ if (!treating_plunger.tool_use_check())
+ return TRUE
+
+ var/their_or_other = (user == victim ? "[user.p_their()]" : "[victim]'s")
+ var/your_or_other = (user == victim ? "your" : "[victim]'s")
+ user?.visible_message(span_notice("[user] begins plunging at the dents on [their_or_other] [limb.plaintext_zone] with [treating_plunger]..."), \
+ span_green("You begin plunging at the dents on [your_or_other] [limb.plaintext_zone] with [treating_plunger]..."))
+
+ var/delay_mult = 1
+ if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED))
+ delay_mult *= 0.75
+
+ delay_mult /= treating_plunger.plunge_mod
+
+ if (!treating_plunger.use_tool(target = victim, user = user, delay = 6 SECONDS * delay_mult, volume = 50, extra_checks = CALLBACK(src, PROC_REF(still_exists))))
+ return TRUE
+
+ var/success_chance = 80
+ if (victim == user)
+ success_chance *= 0.6
+
+ if (HAS_TRAIT(user, TRAIT_KNOW_ROBO_WIRES))
+ success_chance *= 1.25
+ else if (HAS_TRAIT(user, TRAIT_KNOW_ENGI_WIRES))
+ success_chance *= 1.1
+ if (HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD))
+ success_chance *= 1.25 // it's kinda alien to do this, so even people with the wires get the full bonus of diag huds
+ if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED))
+ success_chance *= 1.5
+
+ if (prob(success_chance))
+ user?.visible_message(span_green("[victim]'s [limb.plaintext_zone] lets out a sharp POP as [treating_plunger] forces it into its normal position!"), \
+ span_green("[victim]'s [limb.plaintext_zone] lets out a sharp POP as your [treating_plunger] forces it into its normal position!"))
+ to_chat(user, span_green("[capitalize(your_or_other)] [limb.plaintext_zone]'s structure has been reset to its proper position! Your next step is to secure it with a screwdriver/wrench, though bone gel would also work."))
+ set_superstructure_status(TRUE)
+ else
+ user?.visible_message(span_danger("[victim]'s [limb.plaintext_zone] splinters from [treating_plunger]'s plunging!"), \
+ span_danger("[capitalize(your_or_other)] [limb.plaintext_zone] splinters from your [treating_plunger]'s plunging!"))
+ limb.receive_damage(brute = 5, damage_source = treating_plunger)
+
+ return TRUE
+
+/datum/wound/blunt/robotic/secures_internals/critical/handle_percussive_maintenance_success(attacking_item, mob/living/user)
+ var/your_or_other = (user == victim ? "your" : "[victim]'s")
+ victim.visible_message(span_green("[victim]'s [limb.plaintext_zone] gets smashed into a proper shape!"), \
+ span_green("Your [limb.plaintext_zone] gets smashed into a proper shape!"))
+
+ var/user_message = "[capitalize(your_or_other)] [limb.plaintext_zone]'s superstructure has been reset! Your next step is to screwdriver/wrench the internals, \
+ though if you're desperate enough to use percussive maintenance, you might want to either use a crowbar or bone gel..."
+ to_chat(user, span_green(user_message))
+
+ set_superstructure_status(TRUE)
+
+/datum/wound/blunt/robotic/secures_internals/critical/handle_percussive_maintenance_failure(attacking_item, mob/living/user)
+ to_chat(victim, span_danger("Your [limb.plaintext_zone] only deforms more from the impact..."))
+ limb.receive_damage(brute = 1, damage_source = attacking_item, wound_bonus = CANT_WOUND)
+
+/datum/wound/blunt/robotic/secures_internals/critical/uses_percussive_maintenance()
+ return (!superstructure_remedied && limb_malleable())
+
+/// Transitions our steps by setting both superstructure and secure internals readiness.
+/datum/wound/blunt/robotic/secures_internals/critical/proc/set_superstructure_status(remedied)
+ superstructure_remedied = remedied
+ ready_to_secure_internals = remedied
+
+/datum/wound/blunt/robotic/secures_internals/critical/get_wound_step_info()
+ . = ..()
+
+ if (!superstructure_remedied)
+ . = "The superstructure must be reformed."
+ if (!limb_malleable())
+ . += " The limb must be heated to thermal overload, then manually molded with a firm grasp"
+ else
+ . += " The limb has been sufficiently heated, and can be manually molded with a firm grasp/repeated application of a low-force object"
+ . += " - OR an RCD may be used with little risk."
diff --git a/modular_doppler/modular_medical/wounds/synth/blunt/secures_internals.dm b/modular_doppler/modular_medical/wounds/synth/blunt/secures_internals.dm
new file mode 100644
index 0000000000000..335fa074a3e2f
--- /dev/null
+++ b/modular_doppler/modular_medical/wounds/synth/blunt/secures_internals.dm
@@ -0,0 +1,416 @@
+/// A subtype of blunt wounds that has a "secure internals" step
+/datum/wound/blunt/robotic/secures_internals
+ /// Our current counter for gel + gauze regeneration
+ var/regen_time_elapsed = 0 SECONDS
+ /// Time needed for gel to secure internals.
+ var/regen_time_needed = 30 SECONDS
+
+ /// If we have used bone gel to secure internals.
+ var/gelled = FALSE
+ /// Total brute damage taken over the span of [regen_time_needed] deciseconds when we gel our limb.
+ var/gel_damage = 10 // brute in total
+
+ /// If we are ready to begin screwdrivering or gelling our limb.
+ var/ready_to_secure_internals = FALSE
+ /// If our external plating has been torn open and we can access our internals without a tool
+ var/crowbarred_open = FALSE
+ /// If internals are secured, and we are ready to weld our limb closed and end the wound
+ var/ready_to_resolder = TRUE
+
+/datum/wound/blunt/robotic/secures_internals/handle_process(seconds_per_tick, times_fired)
+ . = ..()
+
+ if (!victim || HAS_TRAIT(victim, TRAIT_STASIS))
+ return
+
+ if (gelled)
+ regen_time_elapsed += ((seconds_per_tick SECONDS) / 2)
+ if(victim.body_position == LYING_DOWN)
+ if(SPT_PROB(30, seconds_per_tick))
+ regen_time_elapsed += 1 SECONDS
+ if(victim.IsSleeping() && SPT_PROB(30, seconds_per_tick))
+ regen_time_elapsed += 1 SECONDS
+
+ var/effective_damage = ((gel_damage / (regen_time_needed / 10)) * seconds_per_tick)
+ var/obj/item/stack/gauze = limb.current_gauze
+ if (gauze)
+ effective_damage *= gauze.splint_factor
+ limb.receive_damage(effective_damage, wound_bonus = CANT_WOUND, damage_source = src)
+ if(effective_damage && prob(33))
+ var/gauze_text = (gauze?.splint_factor ? ", although the [gauze] helps to prevent some of the leakage" : "")
+ to_chat(victim, span_danger("Your [limb.plaintext_zone] sizzles as some gel leaks and warps the exterior metal[gauze_text]..."))
+
+ if(regen_time_elapsed > regen_time_needed)
+ if(!victim || !limb)
+ qdel(src)
+ return
+ to_chat(victim, span_green("The gel within your [limb.plaintext_zone] has fully hardened, allowing you to re-solder it!"))
+ gelled = FALSE
+ ready_to_resolder = TRUE
+ ready_to_secure_internals = FALSE
+ set_disabling(FALSE)
+
+/datum/wound/blunt/robotic/secures_internals/modify_desc_before_span(desc)
+ . = ..()
+
+ var/use_exclamation = FALSE
+
+ if (!limb.current_gauze) // gauze covers it up
+ if (crowbarred_open)
+ . += ", [span_notice("and is violently torn open, internals visible to the outside")]"
+ use_exclamation = TRUE
+ if (gelled)
+ . += ", [span_notice("with fizzling blue surgical gel leaking out of the cracks")]"
+ use_exclamation = TRUE
+ if (use_exclamation)
+ . += "!"
+
+/datum/wound/blunt/robotic/secures_internals/get_scanner_description(mob/user)
+ . = ..()
+
+ var/to_add = get_wound_status()
+ if (!isnull(to_add))
+ . += "\nWound status: [to_add]"
+
+/datum/wound/blunt/robotic/secures_internals/get_simple_scanner_description(mob/user)
+ . = ..()
+
+ var/to_add = get_wound_status()
+ if (!isnull(to_add))
+ . += "\nWound status: [to_add]"
+
+/// Returns info specific to the dynamic state of the wound.
+/datum/wound/blunt/robotic/secures_internals/proc/get_wound_status(mob/user)
+ if (crowbarred_open)
+ . += "The limb has been torn open, allowing ease of access to internal components, but also disabling it. "
+ if (gelled)
+ . += "Bone gel has been applied, causing progressive corrosion of the metal, but eventually securing the internals. "
+
+/datum/wound/blunt/robotic/secures_internals/item_can_treat(obj/item/potential_treater, mob/user)
+ if (potential_treater.tool_behaviour == TOOL_WELDER || potential_treater.tool_behaviour == TOOL_CAUTERY)
+ if (ready_to_resolder)
+ return TRUE
+
+ if (ready_to_secure_internals)
+ if (item_can_secure_internals(potential_treater))
+ return TRUE
+
+ return ..()
+
+/datum/wound/blunt/robotic/secures_internals/treat(obj/item/potential_treater, mob/user)
+ if (ready_to_secure_internals)
+ if (istype(potential_treater, /obj/item/stack/medical/bone_gel))
+ return apply_gel(potential_treater, user)
+ else if (!crowbarred_open && potential_treater.tool_behaviour == TOOL_CROWBAR)
+ return crowbar_open(potential_treater, user)
+ else if (item_can_secure_internals(potential_treater))
+ return secure_internals_normally(potential_treater, user)
+ else if (ready_to_resolder && (potential_treater.tool_behaviour == TOOL_WELDER) || (potential_treater.tool_behaviour == TOOL_CAUTERY))
+ return resolder(potential_treater, user)
+
+ return ..()
+
+/// Returns TRUE if the item can be used in our 1st step (2nd if T3) of repairs.
+/datum/wound/blunt/robotic/secures_internals/proc/item_can_secure_internals(obj/item/potential_treater)
+ return (potential_treater.tool_behaviour == TOOL_SCREWDRIVER || potential_treater.tool_behaviour == TOOL_WRENCH || istype(potential_treater, /obj/item/stack/medical/bone_gel))
+
+#define CROWBAR_OPEN_SELF_TEND_DELAY_MULT 2
+#define CROWBAR_OPEN_KNOWS_ROBO_WIRES_DELAY_MULT 0.5
+#define CROWBAR_OPEN_KNOWS_ENGI_WIRES_DELAY_MULT 0.5
+#define CROWBAR_OPEN_HAS_DIAG_HUD_DELAY_MULT 0.5
+#define CROWBAR_OPEN_WOUND_SCANNED_DELAY_MULT 0.5
+/// If our limb is essential, damage dealt to it by tearing it open will be multiplied against this.
+#define CROWBAR_OPEN_ESSENTIAL_LIMB_DAMAGE_MULT 1.5
+
+/// The "power" put into electrocute_act whenever someone gets shocked when they crowbar open our limb
+#define CROWBAR_OPEN_SHOCK_POWER 20
+/// The brute damage done to this limb (doubled on essential limbs) when it is crowbarred open
+#define CROWBAR_OPEN_BRUTE_DAMAGE 20
+
+/**
+ * Available during the "secure internals" step of T2 and T3. Requires a crowbar. Low-quality ghetto option.
+ *
+ * Tears open the limb, exposing internals. This massively increases the chance of secure internals succeeding, and removes the self-tend malice.
+ *
+ * Deals significant damage to the limb, and shocks the user (causing failure) if victim is alive, this limb is wired, and user is not insulated.
+ */
+/datum/wound/blunt/robotic/secures_internals/proc/crowbar_open(obj/item/crowbarring_item, mob/living/user)
+ if (!crowbarring_item.tool_start_check())
+ return TRUE
+
+ var/delay_mult = 1
+ if (user == victim)
+ delay_mult *= CROWBAR_OPEN_SELF_TEND_DELAY_MULT
+
+ var/knows_wires = FALSE
+ if (HAS_TRAIT(user, TRAIT_KNOW_ROBO_WIRES))
+ delay_mult *= CROWBAR_OPEN_KNOWS_ROBO_WIRES_DELAY_MULT
+ knows_wires = TRUE
+ else if (HAS_TRAIT(user, TRAIT_KNOW_ENGI_WIRES))
+ delay_mult *= CROWBAR_OPEN_KNOWS_ENGI_WIRES_DELAY_MULT
+ knows_wires = TRUE
+ if (HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD))
+ if (knows_wires)
+ delay_mult *= (CROWBAR_OPEN_HAS_DIAG_HUD_DELAY_MULT * 1.5)
+ else
+ delay_mult *= CROWBAR_OPEN_HAS_DIAG_HUD_DELAY_MULT
+ if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED))
+ delay_mult *= CROWBAR_OPEN_WOUND_SCANNED_DELAY_MULT
+
+ var/their_or_other = (user == victim ? "[user.p_their()]" : "[victim]'s")
+ var/your_or_other = (user == victim ? "your" : "[victim]'s")
+
+ var/limb_can_shock_pre_sleep = (victim.stat != DEAD && limb.biological_state & BIO_WIRED)
+ var/shock_or_not = (limb_can_shock_pre_sleep ? ", risking electrocution" : "")
+ var/self_message = span_warning("You start prying open [your_or_other] [limb.plaintext_zone] with [crowbarring_item][shock_or_not]...")
+
+ user?.visible_message(span_bolddanger("[user] starts prying open [their_or_other] [limb.plaintext_zone] with [crowbarring_item]!"), self_message, ignored_mobs = list(victim))
+
+ var/victim_message
+ if (user != victim) // this exists so we can do a userdanger
+ victim_message = span_userdanger("[user] starts prying open your [limb.plaintext_zone] with [crowbarring_item]!")
+ else
+ victim_message = self_message
+ to_chat(victim, victim_message)
+
+ playsound(get_turf(crowbarring_item), 'sound/machines/airlock/airlock_alien_prying.ogg', 30, TRUE)
+ if (!crowbarring_item.use_tool(target = victim, user = user, delay = (7 SECONDS * delay_mult), volume = 50, extra_checks = CALLBACK(src, PROC_REF(still_exists))))
+ return TRUE
+
+ var/limb_can_shock = (victim.stat != DEAD && limb.biological_state & BIO_WIRED) // re-define the previous shock variable because we slept
+ var/stunned = FALSE
+
+ var/message
+
+ if (user && limb_can_shock)
+ var/electrocute_flags = (SHOCK_KNOCKDOWN|SHOCK_NO_HUMAN_ANIM|SHOCK_SUPPRESS_MESSAGE)
+ var/stun_chance = 100
+
+ if (HAS_TRAIT(user, TRAIT_SHOCKIMMUNE))
+ stun_chance = 0
+
+ else if (iscarbon(user)) // doesn't matter if we're shock immune, it's set to 0 anyway
+ var/mob/living/carbon/carbon_user = user
+ if (carbon_user.gloves)
+ stun_chance *= carbon_user.gloves.siemens_coefficient
+
+ if (ishuman(user))
+ var/mob/living/carbon/human/human_user = user
+ stun_chance *= human_user.physiology.siemens_coeff
+ stun_chance *= carbon_user.dna.species.siemens_coeff
+
+ if (stun_chance && prob(stun_chance))
+ electrocute_flags &= ~SHOCK_KNOCKDOWN
+ electrocute_flags &= ~SHOCK_NO_HUMAN_ANIM
+ stunned = TRUE
+
+ message = span_boldwarning("[user] is shocked by [their_or_other] [limb.plaintext_zone], [user.p_their()] crowbar slipping as [user.p_they()] briefly convulse!")
+ self_message = span_userdanger("You are shocked by [your_or_other] [limb.plaintext_zone], causing your crowbar to slip out!")
+ if (user != victim)
+ victim_message = span_userdanger("[user] is shocked by your [limb.plaintext_zone] in [user.p_their()] efforts to tear it open!")
+
+ var/shock_damage = CROWBAR_OPEN_SHOCK_POWER
+ if (limb.current_gauze)
+ shock_damage *= limb.current_gauze.splint_factor // always good to let gauze do something
+ user.electrocute_act(shock_damage, limb, flags = electrocute_flags)
+
+ if (!stunned)
+ var/other_shock_text = ""
+ var/self_shock_text = ""
+ if (!limb_can_shock)
+ other_shock_text = ", and is striken by golden bolts of electricity"
+ self_shock_text = ", but are immediately shocked by the electricity contained within"
+ message = span_boldwarning("[user] tears open [their_or_other] [limb.plaintext_zone] with [user.p_their()] crowbar[other_shock_text]!")
+ self_message = span_warning("You tear open [your_or_other] [limb.plaintext_zone] with your crowbar[self_shock_text]!")
+ if (user != victim)
+ victim_message = span_userdanger("Your [limb.plaintext_zone] fragments and splinters as [user] tears it open with [user.p_their()] crowbar!")
+
+ playsound(get_turf(crowbarring_item), 'sound/effects/bang.ogg', 35, TRUE) // we did it!
+ to_chat(user, span_green("You've torn [your_or_other] [limb.plaintext_zone] open, heavily damaging it but making it a lot easier to screwdriver the internals!"))
+ var/damage = CROWBAR_OPEN_BRUTE_DAMAGE
+ if (limb_essential()) // can't be disabled
+ damage *= CROWBAR_OPEN_ESSENTIAL_LIMB_DAMAGE_MULT
+ limb.receive_damage(brute = CROWBAR_OPEN_BRUTE_DAMAGE, wound_bonus = CANT_WOUND, damage_source = crowbarring_item)
+ set_torn_open(TRUE)
+
+ if (user == victim)
+ victim_message = self_message
+
+ user.visible_message(message, self_message, ignored_mobs = list(victim))
+ to_chat(victim, victim_message)
+ return TRUE
+
+#undef CROWBAR_OPEN_SELF_TEND_DELAY_MULT
+#undef CROWBAR_OPEN_KNOWS_ROBO_WIRES_DELAY_MULT
+#undef CROWBAR_OPEN_KNOWS_ENGI_WIRES_DELAY_MULT
+#undef CROWBAR_OPEN_HAS_DIAG_HUD_DELAY_MULT
+#undef CROWBAR_OPEN_WOUND_SCANNED_DELAY_MULT
+#undef CROWBAR_OPEN_ESSENTIAL_LIMB_DAMAGE_MULT
+
+#undef CROWBAR_OPEN_BRUTE_DAMAGE
+#undef CROWBAR_OPEN_SHOCK_POWER
+
+/// Sets [crowbarred_open] to the new value. If we werent originally disabling, or if we arent currently and we're torn open, we set disabling to true.
+/datum/wound/blunt/robotic/secures_internals/proc/set_torn_open(torn_open_state)
+ // if we aren't disabling but we were torn open, OR if we aren't disabling by default
+ var/should_update_disabling = ((!disabling && torn_open_state) || !initial(disabling))
+
+ crowbarred_open = torn_open_state
+ if(should_update_disabling)
+ set_disabling(torn_open_state)
+
+/// If, on a secure internals attempt, we have less than this chance to succeed, we warn the user.
+#define SECURE_INTERNALS_CONFUSED_CHANCE_THRESHOLD 25
+#define SECURE_INTERNALS_FAILURE_BRUTE_DAMAGE 5
+
+/**
+ * The primary way of performing the secure internals step for T2/T3. Uses a screwdriver/wrench. Very hard to do by yourself, or without a diag hud/wire knowledge.
+ * Roboticists/engineers have a very high chance of succeeding.
+ * Deals some brute damage on failure, but moves to the final step of treatment (re-soldering) on success.
+ *
+ * If [crowbarred_open], made far more likely and remove the self-tend malice.
+ */
+/datum/wound/blunt/robotic/secures_internals/proc/secure_internals_normally(obj/item/securing_item, mob/user)
+ if (!securing_item.tool_start_check())
+ return TRUE
+
+ var/chance = 10
+ var/delay_mult = 1
+
+ if (user == victim)
+ if (!crowbarred_open)
+ chance *= 0.2
+ delay_mult *= 2
+
+ var/knows_wires = FALSE
+ if (crowbarred_open)
+ chance *= 4 // even self-tends get a high chance of success if torn open!
+ if (HAS_TRAIT(user, TRAIT_KNOW_ROBO_WIRES))
+ chance *= 8 // almost guaranteed if it's not self surgery - guaranteed with diag hud
+ delay_mult *= 0.75
+ knows_wires = TRUE
+ else if (HAS_TRAIT(user, TRAIT_KNOW_ENGI_WIRES))
+ chance *= 5.5
+ delay_mult *= 0.85
+ knows_wires = TRUE
+ if (HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD))
+ if (knows_wires)
+ chance *= 1.25 // ((10 * 8) * 1.25) = 100%
+ else
+ chance *= 4
+ if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED))
+ chance *= 1.5 // youre not intended to fix this by yourself this way
+ delay_mult *= 0.8
+
+ var/confused = (chance < SECURE_INTERNALS_CONFUSED_CHANCE_THRESHOLD) // generate chance beforehand, so we can use this var
+
+ var/their_or_other = (user == victim ? "[user.p_their()]" : "[victim]'s")
+ var/your_or_other = (user == victim ? "your" : "[victim]'s")
+ user?.visible_message(span_notice("[user] begins the delicate operation of securing the internals of [their_or_other] [limb.plaintext_zone]..."), \
+ span_notice("You begin the delicate operation of securing the internals of [your_or_other] [limb.plaintext_zone]..."))
+ if (confused)
+ to_chat(user, span_warning("You are confused by the layout of [your_or_other] [limb.plaintext_zone]! A diagnostic hud would help, as would knowing robo/engi wires! You could also tear the limb open with a crowbar, or get someone else to help."))
+
+ if (!securing_item.use_tool(target = victim, user = user, delay = (10 SECONDS * delay_mult), volume = 50, extra_checks = CALLBACK(src, PROC_REF(still_exists))))
+ return TRUE
+
+ if (prob(chance))
+ user?.visible_message(span_green("[user] finishes securing the internals of [their_or_other] [limb.plaintext_zone]!"), \
+ span_green("You finish securing the internals of [your_or_other] [limb.plaintext_zone]!"))
+ to_chat(user, span_green("[capitalize(your_or_other)] [limb.plaintext_zone]'s internals are now secure! Your next step is to weld/cauterize it."))
+ ready_to_secure_internals = FALSE
+ ready_to_resolder = TRUE
+ else
+ user?.visible_message(span_danger("[user] screws up and accidentally damages [their_or_other] [limb.plaintext_zone]!"))
+ limb.receive_damage(brute = SECURE_INTERNALS_FAILURE_BRUTE_DAMAGE, damage_source = securing_item, wound_bonus = CANT_WOUND)
+
+ return TRUE
+
+#undef SECURE_INTERNALS_CONFUSED_CHANCE_THRESHOLD
+#undef SECURE_INTERNALS_FAILURE_BRUTE_DAMAGE
+
+/**
+ * "Premium" ghetto option of the secure internals step for T2/T3. Requires bone gel. Guaranteed to work.
+ * Deals damage over time and disables the limb, but finishes the step afterwards.
+ */
+/datum/wound/blunt/robotic/secures_internals/proc/apply_gel(obj/item/stack/medical/bone_gel/gel, mob/user)
+ if (gelled)
+ to_chat(user, span_warning("[user == victim ? "Your" : "[victim]'s"] [limb.plaintext_zone] is already filled with bone gel!"))
+ return TRUE
+
+ var/delay_mult = 1
+ if (victim == user)
+ delay_mult *= 1.5
+
+ if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED))
+ delay_mult *= 0.75
+
+ user.visible_message(span_danger("[user] begins hastily applying [gel] to [victim]'s [limb.plaintext_zone]..."), span_warning("You begin hastily applying [gel] to [user == victim ? "your" : "[victim]'s"] [limb.plaintext_zone], disregarding the acidic effect it seems to have on the metal..."))
+
+ if (!do_after(user, (6 SECONDS * delay_mult), target = victim, extra_checks = CALLBACK(src, PROC_REF(still_exists))))
+ return TRUE
+
+ gel.use(1)
+ if(user != victim)
+ user.visible_message(span_notice("[user] finishes applying [gel] to [victim]'s [limb.plaintext_zone], emitting a fizzing noise!"), span_notice("You finish applying [gel] to [victim]'s [limb.plaintext_zone]!"), ignored_mobs=victim)
+ to_chat(victim, span_userdanger("[user] finishes applying [gel] to your [limb.plaintext_zone], and you can hear the sizzling of the metal..."))
+ else
+ victim.visible_message(span_notice("[victim] finishes applying [gel] to [victim.p_their()] [limb.plaintext_zone], emitting a funny fizzing sound!"), span_notice("You finish applying [gel] to your [limb.plaintext_zone], and you can hear the sizzling of the metal..."))
+
+ gelled = TRUE
+ set_disabling(TRUE)
+ processes = TRUE
+ return TRUE
+
+/**
+ * The final step of T2/T3, requires a welder/cautery. Guaranteed to work. Cautery is slower.
+ * Once complete, removes the wound entirely.
+ */
+/datum/wound/blunt/robotic/secures_internals/proc/resolder(obj/item/welding_item, mob/user)
+ if (!welding_item.tool_start_check())
+ return TRUE
+
+ var/their_or_other = (user == victim ? "[user.p_their()]" : "[victim]'s")
+ var/your_or_other = (user == victim ? "your" : "[victim]'s")
+ victim.visible_message(span_notice("[user] begins re-soldering [their_or_other] [limb.plaintext_zone]..."), \
+ span_notice("You begin re-soldering [your_or_other] [limb.plaintext_zone]..."))
+
+ var/delay_mult = 1
+ if (welding_item.tool_behaviour == TOOL_CAUTERY)
+ delay_mult *= 3 // less efficient
+ if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED))
+ delay_mult *= 0.75
+
+ if (!welding_item.use_tool(target = victim, user = user, delay = 7 SECONDS * delay_mult, volume = 50, extra_checks = CALLBACK(src, PROC_REF(still_exists))))
+ return TRUE
+
+ victim.visible_message(span_green("[user] finishes re-soldering [their_or_other] [limb.plaintext_zone]!"), \
+ span_notice("You finish re-soldering [your_or_other] [limb.plaintext_zone]!"))
+ remove_wound()
+ return TRUE
+
+/// Returns a string with our current treatment step for use in health analyzers.
+/datum/wound/blunt/robotic/secures_internals/proc/get_wound_step_info()
+ var/string
+
+ if (ready_to_resolder)
+ string = "Apply a welder/cautery to the limb to finalize repairs."
+ else if (ready_to_secure_internals)
+ string = "Use a screwdriver/wrench to secure the internals of the limb. This step is best performed by a qualified technician. \
+ In absence of one, bone gel or a crowbar may be used."
+
+ return string
+
+/datum/wound/blunt/robotic/secures_internals/get_scanner_description(mob/user)
+ . = ..()
+
+ var/wound_step = get_wound_step_info()
+ if (wound_step)
+ . += "\n\nCurrent step: [span_notice(wound_step)]"
+
+/datum/wound/blunt/robotic/secures_internals/get_simple_scanner_description(mob/user)
+ . = ..()
+
+ var/wound_step = get_wound_step_info()
+ if (wound_step)
+ . += "\n\nCurrent step: [span_notice(wound_step)]"
diff --git a/modular_doppler/modular_medical/wounds/synth/medicine_reagents.dm b/modular_doppler/modular_medical/wounds/synth/medicine_reagents.dm
new file mode 100644
index 0000000000000..a5399f26802aa
--- /dev/null
+++ b/modular_doppler/modular_medical/wounds/synth/medicine_reagents.dm
@@ -0,0 +1,64 @@
+// a potent coolant that treats synthetic burns at decent efficiency. compared to hercuri it's worse, but without
+// the lethal side effects, opting for a movement speed decrease instead
+/datum/reagent/dinitrogen_plasmide
+ name = "Dinitrogen Plasmide"
+ description = "A compound of nitrogen and stabilized plasma, this substance has the ability to flash-cool overheated metals \
+ while avoiding excessive damage. Being a heavy compound, it has the effect of slowing anything that metabolizes it."
+ ph = 4.8
+ specific_heat = SPECIFIC_HEAT_PLASMA * 1.2
+ color = "#b779cc"
+ taste_description = "dull plasma"
+ chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
+ process_flags = REAGENT_ORGANIC | REAGENT_SYNTHETIC
+ overdose_threshold = 60 // it takes a lot, if youre really messed up you CAN hit this but it's unlikely
+ chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
+
+/datum/reagent/dinitrogen_plasmide/on_mob_metabolize(mob/living/affected_mob)
+ . = ..()
+
+ affected_mob.add_movespeed_modifier(/datum/movespeed_modifier/dinitrogen_plasmide)
+ to_chat(affected_mob, span_warning("Your joints suddenly feel stiff."))
+
+/datum/reagent/dinitrogen_plasmide/on_mob_end_metabolize(mob/living/affected_mob)
+ . = ..()
+
+ affected_mob.remove_movespeed_modifier(/datum/movespeed_modifier/dinitrogen_plasmide)
+ affected_mob.remove_movespeed_modifier(/datum/movespeed_modifier/dinitrogen_plasmide_overdose)
+ to_chat(affected_mob, span_warning("Your joints no longer feel stiff!"))
+
+/datum/reagent/dinitrogen_plasmide/overdose_start(mob/living/affected_mob)
+ . = ..()
+
+ to_chat(affected_mob, span_danger("You feel like your joints are filling with some viscous fluid!"))
+ affected_mob.add_movespeed_modifier(/datum/movespeed_modifier/dinitrogen_plasmide_overdose)
+
+/datum/reagent/dinitrogen_plasmide/overdose_process(mob/living/affected_mob, seconds_per_tick, times_fired)
+ . = ..()
+
+ holder.remove_reagent(type, 1.2 * seconds_per_tick) // decays
+ holder.add_reagent(/datum/reagent/stable_plasma, 0.4 * seconds_per_tick)
+ holder.add_reagent(/datum/reagent/nitrogen, 0.8 * seconds_per_tick)
+
+/datum/movespeed_modifier/dinitrogen_plasmide
+ multiplicative_slowdown = 0.3
+
+/datum/movespeed_modifier/dinitrogen_plasmide_overdose
+ multiplicative_slowdown = 1.3
+
+/datum/chemical_reaction/dinitrogen_plasmide_formation
+ results = list(/datum/reagent/dinitrogen_plasmide = 3)
+ required_reagents = list(/datum/reagent/stable_plasma = 1, /datum/reagent/nitrogen = 2)
+ required_catalysts = list(/datum/reagent/acetone = 0.1)
+ required_temp = 400
+ optimal_temp = 550
+ overheat_temp = 590
+
+ reaction_tags = REACTION_TAG_EASY | REACTION_TAG_UNIQUE | REACTION_TAG_HEALING
+
+/obj/item/reagent_containers/spray/dinitrogen_plasmide
+ name = "coolant spray"
+ desc = "A medical spray bottle. This one contains dinitrogen plasmide, a potent coolant commonly used to treat synthetic burns. \
+ Has the side effect of causing movement slowdown."
+ icon = 'icons/obj/medical/chemical.dmi'
+ icon_state = "sprayer_med_yellow"
+ list_reagents = list(/datum/reagent/dinitrogen_plasmide = 100)
diff --git a/modular_doppler/modular_medical/wounds/synth/robotic_burns.dm b/modular_doppler/modular_medical/wounds/synth/robotic_burns.dm
new file mode 100644
index 0000000000000..b03d615f4edcc
--- /dev/null
+++ b/modular_doppler/modular_medical/wounds/synth/robotic_burns.dm
@@ -0,0 +1,484 @@
+#define OVERHEAT_ON_STASIS_HEAT_MULT 0.25
+
+/datum/wound_pregen_data/burnt_metal
+ abstract = TRUE
+ required_limb_biostate = BIO_METAL
+ required_wounding_types = list(WOUND_BURN)
+ wound_series = WOUND_SERIES_METAL_BURN_OVERHEAT
+
+/datum/wound_pregen_data/burnt_metal/generate_scar_priorities()
+ return list("[BIO_METAL]")
+
+/datum/wound/burn/robotic/overheat
+ treat_text = "Introduction of a cold environment or lowering of body temperature."
+
+ simple_desc = "Metals are overheated, increasing damage taken significantly and raising body temperature!"
+ simple_treat_text = "Ideally cryogenics, but any source of low body temperature can work. Spraying with spray bottles/extinguishers/showers \
+ will quickly cool the limb, but cause damage. Hercuri is especially effective in quick cooling. \
+ Clothing reduces the water/hercuri that makes it to the metal, and gauze binds it and reduces the damage taken."
+ homemade_treat_text = "You can also splash any liquid on it for a rather inefficient and damaging coolant!"
+
+ default_scar_file = METAL_SCAR_FILE
+
+ wound_flags = (ACCEPTS_GAUZE|SPLINT_OVERLAY|CAN_BE_GRASPED) // gauze binds the metal and makes it resistant to thermal shock
+
+ processes = TRUE
+
+ /// The virtual temperature of the chassis. Crucial for many things, like our severity, the temp we transfer, our cooling damage, etc.
+ var/chassis_temperature
+
+ /// The lower bound of the chassis_temperature we can start with.
+ var/starting_temperature_min = (BODYTEMP_NORMAL + 200)
+ /// The upper bound of the chassis_temperature we can start with.
+ var/starting_temperature_max = (BODYTEMP_NORMAL + 250)
+
+ /// If [chassis_temperature] goes below this, we reduce in severity.
+ var/cooling_threshold = (BODYTEMP_NORMAL + 3)
+ /// If [chassis_temperature] goes above this, we increase in severity.
+ var/heating_threshold = (BODYTEMP_NORMAL + 300)
+
+ /// The buffer in kelvin we will subtract from the chassis_temperature of a wound we demote to.
+ var/cooling_demote_buffer = 60
+ /// The buffer in kelvin we will add to the chassis_temperature of a wound we promote to.
+ var/heating_promote_buffer = 60
+
+ /// The coefficient of heat transfer we will use when shifting our temp to the victim's.
+ var/bodytemp_coeff = 0.04
+ /// For every degree below normal bodytemp, we will multiply our incoming temperature by 1 + degrees * this. Allows incentivization of freezing yourself instead of just waiting.
+ var/bodytemp_difference_expose_bonus_ratio = 0.035
+ /// The coefficient of heat transfer we will use when shifting our victim's temp to ours.
+ var/outgoing_bodytemp_coeff = 0
+ /// The mult applied to heat output when we are on a important limb, e.g. head/torso.
+ var/important_outgoing_mult = 1.2
+ /// The coefficient of heat transfer we will use when shifting our temp to a turf.
+ var/turf_coeff = 0.02
+
+ /// The maximum temperature we can cause by heating our victim.
+ var/max_outgoing_temperature = BODYTEMP_HEAT_WOUND_LIMIT - 1
+
+ /// If we are hit with burn damage, the damage will be multiplied against this to determine the effective heat we get.
+ var/incoming_damage_heat_coeff = 3
+
+ /// The coefficient of heat transfer we will use when receiving heat from reagent contact.
+ var/base_reagent_temp_coefficient = 0.02
+
+ /// The ratio of temp shift -> brute damage. Careful with this value, it can make stuff really really nasty.
+ var/heat_shock_delta_to_damage_ratio = 0.12
+ /// The minimum heat difference we must have on reagent contact to cause heat shock damage.
+ var/heat_shock_minimum_delta = 5
+
+ /// If we are sprayed with a extinguisher/shower with obscuring clothing on (think clothing that prevents surgery), the effect is multiplied against this.
+ var/sprayed_with_reagent_clothed_mult = 0.15
+
+ /// The wound we demote to when we go below cooling threshold. If null, removes us.
+ var/datum/wound/burn/robotic/demotes_to
+ /// The wound we promote to when we go above heating threshold.
+ var/datum/wound/burn/robotic/promotes_to
+
+ /// The color of the light we will generate.
+ var/light_color
+ /// The power of the light we will generate.
+ var/light_power
+ /// The range of the light we will generate.
+ var/light_range
+
+ /// The glow we have attached to our victim, to simulate our limb glowing.
+ var/obj/effect/dummy/lighting_obj/moblight/mob_glow
+
+ /// A bad system I'm using to track the worst scar we earned (since we can demote, we want the biggest our wound has been, not what it was when it was cured (probably moderate))
+ var/datum/scar/highest_scar
+
+ /// A assoc list of (reagent typepath -> cooling), where cooling is how much its presence will reduce the effective temperature of a reagent spray for cooling us.
+ var/static/list/reagent_types_to_extra_cooling = list(
+ /datum/reagent/medicine/c2/hercuri = 60,
+ /datum/reagent/dinitrogen_plasmide = 50,
+ )
+
+ /// A assoc list of (reagent typepath -> damage mult), where the mult will be multiplied against the thermal shock damage.
+ var/static/list/reagent_types_to_thermal_shock_mult = list(
+ /datum/reagent/medicine/c2/hercuri = 0.3,
+ /datum/reagent/dinitrogen_plasmide = 0.6,
+ )
+
+
+/datum/wound/burn/robotic/overheat/New(temperature)
+ chassis_temperature = (isnull(temperature) ? get_random_starting_temperature() : temperature)
+
+ return ..()
+
+/datum/wound/burn/robotic/overheat/wound_injury(datum/wound/old_wound, attack_direction)
+ . = ..()
+
+ if (old_wound && old_wound.severity > severity && istype(old_wound, /datum/wound/burn/robotic/overheat))
+ var/datum/wound/burn/robotic/overheat/overheat_wound = old_wound
+ if (overheat_wound.highest_scar)
+ set_highest_scar(overheat_wound.highest_scar)
+ overheat_wound.clear_highest_scar()
+
+ if (!highest_scar && can_scar)
+ var/datum/scar/new_scar = new
+ set_highest_scar(new_scar)
+ new_scar.generate(limb, src, add_to_scars = FALSE)
+
+/datum/wound/burn/robotic/overheat/proc/set_highest_scar(datum/scar/new_scar)
+ if (highest_scar)
+ UnregisterSignal(highest_scar, COMSIG_QDELETING)
+ if (new_scar)
+ RegisterSignal(new_scar, COMSIG_QDELETING, PROC_REF(clear_highest_scar))
+ highest_scar = new_scar
+
+/datum/wound/burn/robotic/overheat/proc/clear_highest_scar(datum/source)
+ SIGNAL_HANDLER
+
+ set_highest_scar(null)
+
+/datum/wound/burn/robotic/overheat/remove_wound(ignore_limb, replaced)
+ if (!replaced && highest_scar)
+ already_scarred = TRUE
+ highest_scar.lazy_attach(limb)
+ return ..()
+
+/datum/wound/burn/robotic/overheat/Destroy()
+ QDEL_NULL(mob_glow)
+
+ highest_scar = null
+ return ..()
+
+/datum/wound/burn/robotic/overheat/set_victim(mob/living/new_victim)
+ if (victim)
+ QDEL_NULL(mob_glow)
+ UnregisterSignal(victim, COMSIG_MOB_AFTER_APPLY_DAMAGE)
+ UnregisterSignal(victim, COMSIG_ATOM_AFTER_EXPOSE_REAGENTS)
+ if (new_victim)
+ mob_glow = new_victim.mob_light(light_range, light_power, light_color)
+ mob_glow.set_light_on(TRUE)
+ RegisterSignal(new_victim, COMSIG_MOB_AFTER_APPLY_DAMAGE, PROC_REF(victim_attacked))
+ RegisterSignal(new_victim, COMSIG_ATOM_AFTER_EXPOSE_REAGENTS, PROC_REF(victim_exposed_to_reagents))
+
+ return ..()
+
+/datum/wound/burn/robotic/overheat/proc/get_random_starting_temperature()
+ return LERP(starting_temperature_min, starting_temperature_max, rand()) // LERP since we deal with decimals
+
+/datum/wound/burn/robotic/get_limb_examine_description()
+ return span_warning("The metal on this limb is glowing radiantly.")
+
+/datum/wound/burn/robotic/overheat/handle_process(seconds_per_tick, times_fired)
+ if (isnull(victim))
+ var/turf/our_turf = get_turf(limb)
+ if (!isnull(our_turf))
+ expose_temperature(our_turf.GetTemperature(), (turf_coeff * seconds_per_tick))
+ return
+ if (outgoing_bodytemp_coeff <= 0)
+ return
+ var/statis_mult = 1
+ if (HAS_TRAIT(victim, TRAIT_STASIS)) // stasis heavily reduces the ingoing and outgoing transfer of heat
+ statis_mult *= OVERHEAT_ON_STASIS_HEAT_MULT
+
+ var/difference_from_average = max((BODYTEMP_NORMAL - victim.bodytemperature), 0)
+ var/difference_mult = 1 + (difference_from_average * bodytemp_difference_expose_bonus_ratio)
+ if (expose_temperature(victim.bodytemperature, (bodytemp_coeff * seconds_per_tick * statis_mult * difference_mult)))
+ return
+ var/mult = outgoing_bodytemp_coeff
+ if (limb_essential())
+ mult *= important_outgoing_mult
+ var/adjustment_allowed = max((max_outgoing_temperature - victim.bodytemperature), 0)
+ var/amount_to_adjust = min((((chassis_temperature - victim.bodytemperature) * mult) * TEMPERATURE_DAMAGE_COEFFICIENT * seconds_per_tick * statis_mult), adjustment_allowed)
+ victim.adjust_bodytemperature(amount_to_adjust)
+
+/// Signal proc for when our victim is externally attacked. Increases chassis temp based on burn damage received.
+/datum/wound/burn/robotic/overheat/proc/victim_attacked(datum/source, damage, damagetype, def_zone, blocked, wound_bonus, bare_wound_bonus, sharpness, attack_direction, attacking_item)
+ SIGNAL_HANDLER
+
+ if (def_zone != limb.body_zone) // use this proc since receive damage can also be called for like, chems and shit
+ return
+
+ if (!victim)
+ return
+
+ if (damagetype != BURN)
+ return
+
+ if (wound_bonus == CANT_WOUND)
+ return
+
+ var/effective_damage = (damage - blocked)
+ if (effective_damage <= 0)
+ return
+
+ expose_temperature((chassis_temperature + effective_damage), incoming_damage_heat_coeff)
+
+/**
+ * Signal proc for when our victim is exposed to reagents, obviously.
+ *
+ * Equalizes temp to the reagent temp, but also causes thermal shock. Basically, does damage based on the temp differential.
+ * Clothes reduce the effects massively. Hercuri reduces the thermal shock and gets a special temp buff.
+ */
+/datum/wound/burn/robotic/overheat/proc/victim_exposed_to_reagents(datum/signal_source, list/reagents, datum/reagents/source, methods, volume_modifier, show_message)
+ SIGNAL_HANDLER
+
+ var/reagent_coeff = base_reagent_temp_coefficient
+ if (!get_location_accessible(victim, limb.body_zone))
+ if (ishuman(victim))
+ // hi! it's niko! small rant
+ // this proc has no goddamn reason to be on human, it could so easily just have used a proc on carbon that would get the required bodyparts to check
+ // but no. it had to hardcode the list in the proc itself so it's impossible to modularly fix this
+ // so instead we just say fuck it and hope to god only human subtypes get this wound
+ // tldr; ryll why
+ var/mob/living/carbon/human/human_victim = victim
+ for (var/obj/item/clothing/iter_clothing as anything in human_victim.get_clothing_on_part(limb))
+ if (iter_clothing.clothing_flags & THICKMATERIAL)
+ return
+
+ reagent_coeff *= sprayed_with_reagent_clothed_mult
+
+ if (istype(source.my_atom, /obj/effect/particle_effect/water/extinguisher)) // this used to be a lot, lot more modular, but sadly reagent temps/volumes and shit are horribly inconsistant
+ expose_temperature(source.chem_temp, (2.55 * reagent_coeff), TRUE)
+ return
+
+ if (istype(source.my_atom, /obj/machinery/shower))
+ expose_temperature(source.chem_temp, (15 * volume_modifier * reagent_coeff), TRUE)
+ return
+
+ var/total_reagent_amount = 0
+ var/chem_temp_increment = 0
+ var/thermal_shock_mult = 1
+ // imperfect, this means you can microdose hercuri/plasmide in a huge tank of water and have the entire effect.
+ // really not a big deal, though, they arent really limited by availability
+ for (var/datum/reagent/iterated_reagent as anything in reagents)
+ total_reagent_amount += reagents[iterated_reagent]
+ chem_temp_increment += reagent_types_to_extra_cooling[iterated_reagent.type]
+ thermal_shock_mult *= reagent_types_to_thermal_shock_mult[iterated_reagent.type]
+
+ var/local_chem_temp = max(source.chem_temp - chem_temp_increment, 0)
+
+ expose_temperature(local_chem_temp, (reagent_coeff * volume_modifier * total_reagent_amount), TRUE, heat_shock_damage_mult = thermal_shock_mult)
+
+/// Adjusts chassis_temperature by the delta between temperature and itself, multiplied by coeff.
+/// If heat_shock is TRUE, limb will receive brute damage based on the delta.
+/datum/wound/burn/robotic/overheat/proc/expose_temperature(temperature, coeff = 0.02, heat_shock = FALSE, heat_shock_damage_mult = 1)
+ var/temp_delta = (temperature - chassis_temperature) * coeff
+
+ var/unclamped_new_temperature = (chassis_temperature + temp_delta)
+ var/clamped_new_temperature
+ var/heat_adjustment_used
+
+ if (temp_delta > 0)
+ clamped_new_temperature = min(min(chassis_temperature + max(temp_delta, 1), temperature), heating_threshold)
+ heat_adjustment_used = (clamped_new_temperature / unclamped_new_temperature)
+ else
+ clamped_new_temperature = max(max(chassis_temperature + min(temp_delta, -1), temperature), cooling_threshold)
+ heat_adjustment_used = (unclamped_new_temperature / clamped_new_temperature)
+
+ if (heat_shock && abs(temp_delta) > heat_shock_minimum_delta)
+ var/gauze_mult = 1
+ var/obj/item/stack/gauze = limb.current_gauze
+ if (gauze)
+ gauze_mult *= (gauze.splint_factor) * 0.4 // very very effective
+
+ if (limb.grasped_by)
+ gauze_mult *= 0.7 // hold it down yourself
+
+ if (victim)
+ var/gauze_or_not = (!isnull(gauze) ? ", but [gauze] helps to keep it together" : "")
+ var/clothing_text = (!get_location_accessible(victim, limb.body_zone) ? ", [victim.p_their()] clothing absorbing some of the liquid" : "")
+ victim.visible_message(span_warning("[victim]'s [limb.plaintext_zone] strains from the thermal shock[clothing_text][gauze_or_not]!"))
+ playsound(victim, 'sound/items/tools/welder.ogg', 25)
+
+ var/damage = (((abs(temp_delta) * heat_shock_delta_to_damage_ratio) * gauze_mult) * heat_shock_damage_mult) * heat_adjustment_used
+ limb.receive_damage(brute = damage, wound_bonus = CANT_WOUND)
+
+ chassis_temperature = clamped_new_temperature // can only be decimal or 1, so it can only reduce the intensity of the adjustment
+
+ return check_temperature()
+
+/// Removes, demotes, or promotes ourselves to a new wound type if our temperature is past a heating/cooling threshold.
+/datum/wound/burn/robotic/overheat/proc/check_temperature()
+ if (chassis_temperature <= cooling_threshold)
+ if (demotes_to)
+ victim.visible_message(span_green("[victim]'s [limb.plaintext_zone] turns a more pleasant thermal color as it cools down a little..."), span_green("Your [limb.plaintext_zone] seems to cool down a little!"))
+ replace_wound(new demotes_to(cooling_threshold - cooling_demote_buffer))
+ return TRUE
+ else
+ victim.visible_message(span_green("[victim]'s [limb.plaintext_zone] simmers gently as it returns to its usual colors!"), span_green("Your [limb.plaintext_zone] simmers gently as it returns to its usual colors!"))
+ remove_wound()
+ return TRUE
+ else if (promotes_to && chassis_temperature >= heating_threshold)
+ victim.visible_message(span_danger("[victim]'s [limb.plaintext_zone] brightens as it overheats further!"), span_userdanger("Your [limb.plaintext_zone] sizzles and brightens as it overheats further!"))
+ replace_wound(new promotes_to(heating_threshold + heating_promote_buffer))
+ return TRUE
+
+/// Returns a string with our temperature and heating/cooling thresholds, for use in health analyzers.
+/datum/wound/burn/robotic/overheat/proc/get_wound_status_info()
+ var/current_temp_celcius = round(chassis_temperature - T0C, 0.1)
+ var/current_temp_fahrenheit = round(chassis_temperature * 1.8-459.67, 0.1)
+
+ var/cool_celcius = round(cooling_threshold - T0C, 0.1)
+ var/cool_fahrenheit = round(cooling_threshold * 1.8-459.67, 0.1)
+
+ var/heat_celcius = round(heating_threshold - T0C, 0.1)
+ var/heat_fahrenheit = round(heating_threshold * 1.8-459.67, 0.1)
+
+ return "Its current temperature is [span_blue("[current_temp_celcius ] °C ([current_temp_fahrenheit] °F)")], \
+ and needs to cool to [span_nicegreen("[cool_celcius] °C ([cool_fahrenheit] °F)")], but \
+ will worsen if heated to [span_purple("[heat_celcius] °C ([heat_fahrenheit] °F)")]."
+
+/datum/wound/burn/robotic/overheat/get_scanner_description(mob/user)
+ . = ..()
+
+ . += "\nWound status: [get_wound_status_info()]"
+
+/datum/wound/burn/robotic/overheat/get_simple_scanner_description(mob/user)
+ . = ..()
+
+ . += "\nWound status: [get_wound_status_info()]"
+
+// this wound is unaffected by cryoxadone and pyroxadone
+/datum/wound/burn/robotic/overheat/on_xadone(power)
+ return
+
+/datum/wound/burn/robotic/overheat/moderate
+ name = "Transient Overheating"
+ desc = "External metals have exceeded lower-bound thermal limits and have lost some structural integrity, increasing damage taken as well as the chance to \
+ sustain additional wounds."
+ occur_text = "lets out a slight groan as it turns a dull shade of thermal red"
+ examine_desc = "is glowing a dull thermal red and giving off heat"
+ treat_text = "Reduction of body temperature to expedite the passive heat dissipation - or, if thermal shock is to be risked, application of a fire extinguisher/shower."
+ severity = WOUND_SEVERITY_MODERATE
+
+ damage_multiplier_penalty = 1.15 //1.15x damage taken
+
+ starting_temperature_min = (BODYTEMP_NORMAL + 350)
+ starting_temperature_max = (BODYTEMP_NORMAL + 400)
+
+ cooling_threshold = (BODYTEMP_NORMAL + 100)
+ heating_threshold = (BODYTEMP_NORMAL + 500)
+
+ cooling_demote_buffer = 60
+ heating_promote_buffer = 100
+
+ a_or_from = "from"
+
+ // easy to get
+ threshold_penalty = 30
+
+ status_effect_type = /datum/status_effect/wound/burn/robotic/moderate
+
+ sound_volume = 20
+
+ outgoing_bodytemp_coeff = 0.0056
+ bodytemp_coeff = 0.006
+
+ base_reagent_temp_coefficient = 0.03
+ heat_shock_delta_to_damage_ratio = 0.2
+
+ promotes_to = /datum/wound/burn/robotic/overheat/severe
+
+ light_color = COLOR_RED
+ light_power = 0.1
+ light_range = 0.5
+
+ can_scar = FALSE
+
+/datum/wound_pregen_data/burnt_metal/transient_overheat
+ abstract = FALSE
+
+ wound_path_to_generate = /datum/wound/burn/robotic/overheat/moderate
+
+ threshold_minimum = 30
+
+/datum/wound/burn/robotic/overheat/severe
+ name = "Thermal Overload"
+ desc = "Exterior plating has surpassed critical thermal levels, causing significant failure in structural integrity and overheating of internal systems."
+ occur_text = "sizzles, the externals turning a dull shade of orange"
+ examine_desc = "appears discolored and polychromatic, parts of it glowing a dull orange"
+ treat_text = "Isolation from physical hazards, and accommodation of passive heat dissipation - active cooling may be used, but temperature differentials significantly \
+ raise the risk of thermal shock."
+ severity = WOUND_SEVERITY_SEVERE
+
+ a_or_from = "from"
+
+ threshold_penalty = 65
+
+ status_effect_type = /datum/status_effect/wound/burn/robotic/severe
+ damage_multiplier_penalty = 1.25 // 1.25x damage taken
+
+ starting_temperature_min = (BODYTEMP_NORMAL + 550)
+ starting_temperature_max = (BODYTEMP_NORMAL + 600)
+
+ heating_promote_buffer = 150
+
+ cooling_threshold = (BODYTEMP_NORMAL + 375)
+ heating_threshold = (BODYTEMP_NORMAL + 800)
+
+ outgoing_bodytemp_coeff = 0.0053
+ bodytemp_coeff = 0.004
+
+ base_reagent_temp_coefficient = 0.03
+ heat_shock_delta_to_damage_ratio = 0.2
+
+ demotes_to = /datum/wound/burn/robotic/overheat/moderate
+ promotes_to = /datum/wound/burn/robotic/overheat/critical
+
+ light_color = COLOR_BRIGHT_ORANGE
+ light_power = 0.8
+ light_range = 0.5
+
+ scar_keyword = "burnsevere"
+
+/datum/wound_pregen_data/burnt_metal/severe
+ abstract = FALSE
+ wound_path_to_generate = /datum/wound/burn/robotic/overheat/severe
+ threshold_minimum = 80
+
+/datum/wound/burn/robotic/overheat/critical
+ name = "Runaway Exothermy"
+ desc = "Carapace is beyond melting point, causing catastrophic structural integrity failure as well as massively heating up the subject."
+ occur_text = "turns a bright shade of radiant white as it sizzles and melts"
+ examine_desc = "is a blinding shade of white, almost melting from the heat"
+ treat_text = "Immediate confinement to cryogenics, as rapid overheating and physical vulnerability may occur. Active cooling is not advised, \
+ since the thermal shock may be lethal with such a temperature differential."
+ severity = WOUND_SEVERITY_CRITICAL
+
+ a_or_from = "from"
+
+ sound_effect = 'sound/effects/wounds/sizzle2.ogg'
+
+ threshold_penalty = 100
+
+ status_effect_type = /datum/status_effect/wound/burn/robotic/critical
+
+ damage_multiplier_penalty = 1.5 //1.5x damage taken
+
+ starting_temperature_min = (BODYTEMP_NORMAL + 1050)
+ starting_temperature_max = (BODYTEMP_NORMAL + 1100)
+
+ cooling_demote_buffer = 100
+
+ cooling_threshold = (BODYTEMP_NORMAL + 775)
+ heating_threshold = INFINITY
+
+ outgoing_bodytemp_coeff = 0.0055 // burn... BURN...
+ bodytemp_coeff = 0.0025
+
+ base_reagent_temp_coefficient = 0.03
+ heat_shock_delta_to_damage_ratio = 0.2
+
+ max_outgoing_temperature = BODYTEMP_HEAT_WOUND_LIMIT // critical CAN cause wounds, but only barely
+
+ demotes_to = /datum/wound/burn/robotic/overheat/severe
+
+ wound_flags = (MANGLES_EXTERIOR|ACCEPTS_GAUZE|SPLINT_OVERLAY|CAN_BE_GRASPED)
+
+ light_color = COLOR_VERY_SOFT_YELLOW
+ light_power = 1.3
+ light_range = 1.5
+
+ scar_keyword = "burncritical"
+
+/datum/wound_pregen_data/burnt_metal/critical
+ abstract = FALSE
+ wound_path_to_generate = /datum/wound/burn/robotic/overheat/critical
+ threshold_minimum = 140
+
+#undef OVERHEAT_ON_STASIS_HEAT_MULT
diff --git a/modular_doppler/modular_medical/wounds/synth/robotic_muscle.dm b/modular_doppler/modular_medical/wounds/synth/robotic_muscle.dm
new file mode 100644
index 0000000000000..4e91c58f7f675
--- /dev/null
+++ b/modular_doppler/modular_medical/wounds/synth/robotic_muscle.dm
@@ -0,0 +1,47 @@
+/datum/wound/muscle/robotic
+ sound_effect = 'sound/effects/wounds/blood1.ogg'
+
+/datum/wound_pregen_data/muscle/robotic
+ required_limb_biostate = (BIO_METAL)
+
+/datum/wound/muscle/robotic/moderate
+ name = "Overworked Servo"
+ desc = "A servo has been overworked, and will operate with reduced efficiency until rested."
+ treat_text = "A tight splint on the affected limb, as well as plenty of rest and sleep."
+ examine_desc = "appears to be moving sluggishly"
+ occur_text = "jitters for a moment before moving sluggishly"
+ severity = WOUND_SEVERITY_MODERATE
+ interaction_efficiency_penalty = 1.5
+ limp_slowdown = 2
+ limp_chance = 30
+ threshold_penalty = 15
+ status_effect_type = /datum/status_effect/wound/muscle/robotic/moderate
+ regen_ticks_needed = 90
+
+/datum/wound_pregen_data/muscle/robotic/servo
+ abstract = FALSE
+ wound_path_to_generate = /datum/wound/muscle/robotic/moderate
+ threshold_minimum = 35
+
+/datum/wound/muscle/robotic/severe
+ name = "Exhausted Piston"
+ sound_effect = 'sound/effects/wounds/blood2.ogg'
+ desc = "An important hydraulic piston has been critically overused, resulting in total dysfunction until it recovers."
+ treat_text = "A tight splint on the affected limb, as well as plenty of rest and sleep."
+ examine_desc = "is stiffly limp, the extremities splayed out widely"
+ occur_text = "goes completely stiff, seeming to lock into position"
+ severity = WOUND_SEVERITY_SEVERE
+ interaction_efficiency_penalty = 2
+ limp_slowdown = 5
+ limp_chance = 40
+ threshold_penalty = 35
+ disabling = TRUE
+ status_effect_type = /datum/status_effect/wound/muscle/robotic/severe
+ regen_ticks_needed = 150
+
+/datum/wound_pregen_data/muscle/robotic/hydraulic
+ abstract = FALSE
+
+ wound_path_to_generate = /datum/wound/muscle/robotic/severe
+ threshold_minimum = 80
+
diff --git a/modular_doppler/modular_medical/wounds/synth/robotic_pierce.dm b/modular_doppler/modular_medical/wounds/synth/robotic_pierce.dm
new file mode 100644
index 0000000000000..97ee48225eb9a
--- /dev/null
+++ b/modular_doppler/modular_medical/wounds/synth/robotic_pierce.dm
@@ -0,0 +1,152 @@
+// Pierce
+// Slow to rise but high damage overall
+// Hard-ish to fix
+/datum/wound/electrical_damage/pierce
+ heat_differential_healing_mult = 0.01
+ simple_desc = "Electrical conduits have been pierced open, resulting in a fault that slowly intensifies, but with extreme maximum voltage!"
+
+/datum/wound_pregen_data/electrical_damage/pierce
+ abstract = TRUE
+ wound_series = WOUND_SERIES_WIRE_PIERCE_ELECTRICAL_DAMAGE
+ required_wounding_types = list(WOUND_PIERCE)
+
+/datum/wound/burn/electrical_damage/pierce/get_limb_examine_description()
+ return span_warning("The metal on this limb is pierced open.")
+
+/datum/wound/electrical_damage/pierce/wound_injury(datum/wound/old_wound = null, attack_direction = null)
+ if(limb.can_bleed() && attack_direction && victim.blood_volume > BLOOD_VOLUME_OKAY)
+ victim.spray_blood(attack_direction, severity)
+
+ return ..()
+
+/datum/wound/electrical_damage/pierce/moderate
+ name = "Punctured Capacitor"
+ desc = "A major capacitor has been broken open, causing slow but noticable electrical damage."
+ occur_text = "shoots out a short stream of sparks"
+ examine_desc = "is shuddering gently, movements a little weak"
+ treat_text = "Replacing of damaged wiring, though repairs via wirecutting instruments or sutures may suffice, albeit at limited efficiency. In case of emergency, \
+ subject may be subjected to high temperatures to allow solder to reset."
+
+ sound_effect = 'modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T1.ogg'
+
+ severity = WOUND_SEVERITY_MODERATE
+
+ sound_volume = 30
+
+ threshold_penalty = 30
+
+ intensity = 10 SECONDS
+ processing_full_shock_threshold = 7 MINUTES
+
+ processing_shock_power_per_second_max = 1.2
+ processing_shock_power_per_second_min = 1.1
+
+ processing_shock_stun_chance = 0.5
+ processing_shock_spark_chance = 35
+
+ process_shock_spark_count_max = 1
+ process_shock_spark_count_min = 1
+
+ wirecut_repair_percent = 0.078
+ wire_repair_percent = 0.018
+
+ initial_sparks_amount = 1
+
+ status_effect_type = /datum/status_effect/wound/electrical_damage/pierce/moderate
+
+ a_or_from = "a"
+
+ scar_keyword = "piercemoderate"
+
+/datum/wound_pregen_data/electrical_damage/pierce/moderate
+ abstract = FALSE
+ wound_path_to_generate = /datum/wound/electrical_damage/pierce/moderate
+ threshold_minimum = 40
+
+/datum/wound/electrical_damage/pierce/severe
+ name = "Penetrated Transformer"
+ desc = "A major transformer has been pierced, causing slow-to-progess but eventually intense electrical damage."
+ occur_text = "sputters and goes limp for a moment as it ejects a stream of sparks"
+ examine_desc = "is shuddering significantly, its servos briefly giving way in a rythmic pattern"
+ treat_text = "Containment of damaged wiring via gauze, then application of fresh wiring/sutures, or resetting of displaced wiring via wirecutter/retractor."
+
+ sound_effect = 'modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T2.ogg'
+
+ severity = WOUND_SEVERITY_SEVERE
+
+ sound_volume = 15
+
+ threshold_penalty = 40
+
+ intensity = 20 SECONDS
+ processing_full_shock_threshold = 6.5 MINUTES
+
+ processing_shock_power_per_second_max = 1.6
+ processing_shock_power_per_second_min = 1.5
+
+ processing_shock_stun_chance = 2.5
+ processing_shock_spark_chance = 60
+
+ process_shock_spark_count_max = 2
+ process_shock_spark_count_min = 1
+
+ wirecut_repair_percent = 0.046
+ wire_repair_percent = 0.01
+
+ initial_sparks_amount = 3
+
+ status_effect_type = /datum/status_effect/wound/electrical_damage/pierce/moderate
+
+ a_or_from = "a"
+
+ scar_keyword = "piercemoderate"
+
+/datum/wound_pregen_data/electrical_damage/pierce/severe
+ abstract = FALSE
+ wound_path_to_generate = /datum/wound/electrical_damage/pierce/severe
+ threshold_minimum = 60
+
+/datum/wound/electrical_damage/pierce/critical
+ name = "Ruptured PSU"
+ desc = "The local PSU of this limb has suffered a core rupture, causing a progressive power failure that will slowly intensify into massive electrical damage."
+ occur_text = "flashes with radiant blue, emitting a noise not unlike a Jacob's Ladder"
+ examine_desc = "'s PSU is visible, with a sizable hole in the center"
+ treat_text = "Immediate securing via gauze, followed by emergency cable replacement and securing via wirecutters or hemostat. \
+ If the fault has become uncontrollable, extreme heat therapy is recommended."
+
+ severity = WOUND_SEVERITY_CRITICAL
+ wound_flags = (ACCEPTS_GAUZE|MANGLES_EXTERIOR|CAN_BE_GRASPED|SPLINT_OVERLAY)
+
+ sound_effect = 'modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T3.ogg'
+
+ sound_volume = 30
+
+ threshold_penalty = 60
+
+ intensity = 30 SECONDS
+ processing_full_shock_threshold = 5.5 MINUTES
+
+ processing_shock_power_per_second_max = 2.2
+ processing_shock_power_per_second_min = 2.1
+
+ processing_shock_stun_chance = 1
+ processing_shock_spark_chance = 90
+
+ process_shock_spark_count_max = 3
+ process_shock_spark_count_min = 2
+
+ wirecut_repair_percent = 0.032
+ wire_repair_percent = 0.008
+
+ initial_sparks_amount = 8
+
+ status_effect_type = /datum/status_effect/wound/electrical_damage/pierce/moderate
+
+ a_or_from = "a"
+
+ scar_keyword = "piercecritical"
+
+/datum/wound_pregen_data/electrical_damage/pierce/critical
+ abstract = FALSE
+ wound_path_to_generate = /datum/wound/electrical_damage/pierce/critical
+ threshold_minimum = 110
diff --git a/modular_doppler/modular_medical/wounds/synth/robotic_slash.dm b/modular_doppler/modular_medical/wounds/synth/robotic_slash.dm
new file mode 100644
index 0000000000000..82f4308229b4b
--- /dev/null
+++ b/modular_doppler/modular_medical/wounds/synth/robotic_slash.dm
@@ -0,0 +1,654 @@
+/// How much damage and progress is reduced when on stasis.
+#define ELECTRICAL_DAMAGE_ON_STASIS_MULT 0.15
+/// How much damage and progress is reduced when limb is grasped.
+#define ELECTRICAL_DAMAGE_GRASPED_MULT 0.7
+/// How much damage and progress is reduced when our victim lies down.
+#define ELECTRICAL_DAMAGE_LYING_DOWN_MULT 0.7
+/// How much progress is reduced when our victim is dead.
+#define ELECTRICAL_DAMAGE_DEAD_PROGRESS_MULT 0.2 // they'll be resting to, so this is more like 0.1
+
+/// Base time for a wirecutter being used.
+#define ELECTRICAL_DAMAGE_WIRECUTTER_BASE_DELAY 8 SECONDS
+/// Base time for a cable coil being used.
+#define ELECTRICAL_DAMAGE_SUTURE_WIRE_BASE_DELAY 0.8 SECONDS
+/// Global damage multiplier for the power a given electrical damage wound will add per tick.
+#define ELECTRICAL_DAMAGE_POWER_PER_TICK_MULT 1
+/// Global damage multiplier for how much repairing wiring will reduce intensity. Higher is more.
+#define ELECTRICAL_DAMAGE_SUTURE_WIRE_HEALING_AMOUNT_MULT 1
+
+/// The minimum shock power we must have available to zap our victim. Must be at least one, since electrocute_act fails if it's lower.
+#define ELECTRICAL_DAMAGE_MINIMUM_SHOCK_POWER_PER_ZAP 1
+/// The maximum burn damage our limb can have before we refuse to let people who havent aggrograbbed the limb repair it with wires. This is so people can opt to just fix the burn damage.
+#define ELECTRICAL_DAMAGE_MAX_BURN_DAMAGE_TO_LET_WIRES_REPAIR 5
+
+/// If progress is positive (not decreasing) after applying ELECTRICAL_DAMAGE_CLOTTING_HEALING_AMOUNT, we multiply it against this.
+#define ELECTRICAL_DAMAGE_CLOTTING_PROGRESS_MULT 0.5
+
+/datum/wound/electrical_damage
+ name = "Electrical (Wires) Wound"
+
+ simple_treat_text = "Replacing of broken wiring, or repairing via a wirecutter. Bandaging binds the wiring and reduces intensity buildup, \
+ as does firmly grasping the limb - both the victim and someone else can do this. Roboticists/Engineers get a bonus to treatment, as do diagnostic HUDs."
+ homemade_treat_text = "Sutures can repair the wiring at reduced efficiency, as can retractors. In a pinch, high temperatures can repair the wiring!"
+
+ wound_flags = (ACCEPTS_GAUZE|CAN_BE_GRASPED|SPLINT_OVERLAY)
+
+ treatable_tools = list(TOOL_WIRECUTTER, TOOL_RETRACTOR)
+ treatable_by = list(/obj/item/stack/medical/suture)
+ treatable_by_grabbed = list(/obj/item/stack/cable_coil)
+
+ default_scar_file = METAL_SCAR_FILE
+
+ processes = TRUE
+
+ /// How many sparks do we spawn when we're gained?
+ var/initial_sparks_amount = 1
+
+ /// How much of our damage is reduced if the target is shock immune. Percent.
+ var/shock_immunity_self_damage_reduction = 75
+
+ /// Mult for our damage if we are unimportant.
+ var/limb_unimportant_damage_mult = 0.8
+ /// Mult for our progress if we are unimportant.
+ var/limb_unimportant_progress_mult = 0.8
+
+ /// The overall "intensity" of this wound. Goes up to [processing_full_shock_threshold], and is used for determining our effect scaling. Measured in deciseconds.
+ var/intensity
+ /// The time, in deciseconds, it takes to reach 100% power.
+ var/processing_full_shock_threshold = 3 MINUTES
+ /// If [intensity] is at or below this, we remove ourselves.
+ var/minimum_intensity = 0
+
+ /// How much shock power we add to [processing_shock_power_this_tick] per tick. Lower bound
+ var/processing_shock_power_per_second_min = 0.1
+ /// How much shock power we add to [processing_shock_power_this_tick] per tick. Upper bound
+ var/processing_shock_power_per_second_max = 0.2
+
+ /// In the case we get below 1 power, we add the power to this buffer and use it next tick.
+ var/processing_shock_power_this_tick = 0
+ /// The chance for each processed shock to stun the user.
+ var/processing_shock_stun_chance = 0
+ /// The chance for each processed shock to spark.
+ var/processing_shock_spark_chance = 30
+ /// The chance for each processed shock to message the user.
+ var/process_shock_message_chance = 80
+
+ /// Simple mult for how much of real time is added to [intensity].
+ var/seconds_per_intensity_mult = 1
+
+ /// How many sparks we spawn if a shock sparks. Lower bound
+ var/process_shock_spark_count_min = 1
+ /// How many sparks we spawn if a shock sparks. Upper bound
+ var/process_shock_spark_count_max = 1
+
+ // Generally should be less fast than wire, but its effectiveness should increase with severity
+ /// The percent, in decimal, a successful wirecut use will reduce intensity by.
+ var/wirecut_repair_percent
+ // Generally should be lower than wirecut
+ /// The percent, in decimal, a successful wire use will reduce intensity by.
+ var/wire_repair_percent
+
+ /// The basic multiplier to all our effects. Damage, progress, etc.
+ var/overall_effect_mult = 1
+
+ /// The bodyheat our victim must be at or above to start getting passive healing.
+ var/heat_thresh_to_heal = (BODYTEMP_HEAT_DAMAGE_LIMIT + 30)
+ /// The mult that heat differences between normal and bodytemp threshold is multiplied against. Controls passive heat healing.
+ var/heat_differential_healing_mult = 0.08
+
+ /// Percent chance for a heat repair to give the victim a message.
+ var/heat_heal_message_chance = 20
+
+ /// If [get_intensity_mult()] is at or above this, the limb gets disabled. If null, it will never occur.
+ var/disable_at_intensity_mult
+
+/datum/wound_pregen_data/electrical_damage
+ abstract = TRUE
+ required_limb_biostate = (BIO_WIRED)
+ required_wounding_types = list(WOUND_SLASH)
+ wound_series = WOUND_SERIES_WIRE_SLASH_ELECTRICAL_DAMAGE
+
+/datum/wound_pregen_data/electrical_damage/generate_scar_priorities()
+ return list("[BIO_METAL]") // wire scars dont exist so we can just use metal
+
+/datum/wound/burn/electrical_damage/slash/get_limb_examine_description()
+ return span_warning("The wiring on this limb is slashed open.")
+
+/datum/wound/electrical_damage/handle_process(seconds_per_tick, times_fired)
+ . = ..()
+
+ var/base_mult = get_base_mult()
+
+ var/seconds_per_tick_for_intensity = seconds_per_tick * get_progress_mult()
+ seconds_per_tick_for_intensity = modify_progress_after_progress_mult(seconds_per_tick_for_intensity, seconds_per_tick)
+
+ adjust_intensity(seconds_per_tick_for_intensity SECONDS)
+
+ if (!victim || victim.stat == DEAD)
+ return
+
+ var/damage_mult = get_damage_mult(victim)
+ var/intensity_mult = get_intensity_mult()
+
+ damage_mult *= seconds_per_tick
+ damage_mult *= intensity_mult
+
+ var/picked_damage = LERP(processing_shock_power_per_second_min, processing_shock_power_per_second_max, rand())
+ processing_shock_power_this_tick += (picked_damage * damage_mult)
+ if (processing_shock_power_this_tick <= ELECTRICAL_DAMAGE_MINIMUM_SHOCK_POWER_PER_ZAP)
+ return
+
+ var/stun_chance = (processing_shock_stun_chance * intensity_mult) * base_mult
+ var/spark_chance = (processing_shock_spark_chance * intensity_mult) * base_mult
+
+ var/should_stun = SPT_PROB(stun_chance, seconds_per_tick)
+ var/should_message = SPT_PROB(process_shock_message_chance, seconds_per_tick)
+
+ zap(victim,
+ processing_shock_power_this_tick,
+ stun = should_stun,
+ spark = SPT_PROB(spark_chance, seconds_per_tick),
+ animation = should_stun, message = FALSE,
+ message = should_stun,
+ tell_victim_if_no_message = should_message,
+ ignore_immunity = TRUE,
+ jitter_time = seconds_per_tick,
+ stutter_time = 0,
+ delay_stun = TRUE,
+ knockdown = TRUE,
+ ignore_gloves = TRUE
+ )
+ processing_shock_power_this_tick = 0
+
+/// If someone is aggrograbbing us and targetting our limb, intensity progress is multiplied against this.
+#define LIMB_AGGROGRABBED_PROGRESS_MULT 0.5
+
+/// Returns the multiplier used by our intensity progress. Intensity increment is multiplied against this.
+/datum/wound/electrical_damage/proc/get_progress_mult()
+ var/progress_mult = get_base_mult() * seconds_per_intensity_mult
+
+ if (!limb_essential())
+ progress_mult *= limb_unimportant_progress_mult
+
+ if (isliving(victim.pulledby))
+ var/mob/living/living_puller = victim.pulledby
+ if (living_puller.grab_state >= GRAB_AGGRESSIVE && living_puller.zone_selected == limb.body_zone)
+ progress_mult *= LIMB_AGGROGRABBED_PROGRESS_MULT // they're holding it down
+
+ if (victim.stat == DEAD)
+ progress_mult *= ELECTRICAL_DAMAGE_DEAD_PROGRESS_MULT // doesnt totally stop it but slows it down a lot
+
+ return progress_mult
+#undef LIMB_AGGROGRABBED_PROGRESS_MULT
+
+/// Returns the multiplier used by the damage we deal.
+/datum/wound/electrical_damage/proc/get_damage_mult(mob/living/target)
+ SHOULD_BE_PURE(TRUE)
+
+ var/damage_mult = get_base_mult()
+
+ if (!limb_essential())
+ damage_mult *= limb_unimportant_damage_mult
+
+ return damage_mult * ELECTRICAL_DAMAGE_POWER_PER_TICK_MULT
+
+/// Returns the global multiplier used by both progress and damage.
+/datum/wound/electrical_damage/proc/get_base_mult()
+ var/base_mult = 1
+
+ if (victim)
+ if (HAS_TRAIT(victim, TRAIT_STASIS))
+ base_mult *= ELECTRICAL_DAMAGE_ON_STASIS_MULT
+ if (victim.body_position == LYING_DOWN)
+ base_mult *= ELECTRICAL_DAMAGE_LYING_DOWN_MULT
+ if (limb.grasped_by)
+ base_mult *= ELECTRICAL_DAMAGE_GRASPED_MULT
+
+ if (victim.has_status_effect(/datum/status_effect/determined))
+ base_mult *= WOUND_DETERMINATION_BLEED_MOD
+
+ if (HAS_TRAIT(victim, TRAIT_SHOCKIMMUNE)) // it'd be a bit cheesy to just become immune to this, so it only makes it a lot lot better
+ base_mult *= shock_immunity_self_damage_reduction
+
+ var/splint_mult = (limb.current_gauze ? limb.current_gauze.splint_factor : 1)
+ base_mult *= splint_mult
+
+ return overall_effect_mult * base_mult
+
+/// Is called after seconds_for_intensity is modified by get_progress_mult().
+/datum/wound/electrical_damage/proc/modify_progress_after_progress_mult(seconds_for_intensity, seconds_per_tick)
+ if (!victim)
+ return seconds_for_intensity
+
+ seconds_for_intensity -= (get_heat_healing() * seconds_per_tick)
+
+ if (seconds_for_intensity > 0 && HAS_TRAIT(victim, TRAIT_COAGULATING))
+ seconds_for_intensity *= ELECTRICAL_DAMAGE_CLOTTING_PROGRESS_MULT
+
+ if (HAS_TRAIT(src, TRAIT_ELECTRICAL_DAMAGE_REPAIRING))
+ seconds_for_intensity = min(seconds_for_intensity, 0) // it cant get any worse
+
+ return seconds_for_intensity
+
+/// Returns how many deciseconds progress should be reduced by, based on the current heat of our victim's body.
+/datum/wound/electrical_damage/proc/get_heat_healing(do_message = prob(heat_heal_message_chance))
+ var/healing_amount = max((victim.bodytemperature - heat_thresh_to_heal), 0) * heat_differential_healing_mult
+ if (do_message && healing_amount)
+ to_chat(victim, span_notice("You feel the solder within your [limb.plaintext_zone] reform and repair your [name]..."))
+
+ return healing_amount
+
+/// Changes intensity by the given amount, and then updates our status, removing ourselves if fixed.
+/datum/wound/electrical_damage/proc/adjust_intensity(to_adjust)
+ intensity = clamp((intensity + to_adjust), 0, processing_full_shock_threshold)
+
+ if (disable_at_intensity_mult)
+ set_disabling(get_intensity_mult() >= disable_at_intensity_mult)
+
+ remove_if_fixed()
+
+/datum/wound/electrical_damage/wound_injury(datum/wound/electrical_damage/old_wound, attack_direction)
+ . = ..()
+
+ if (old_wound)
+ intensity = max(intensity, old_wound.intensity)
+ processing_shock_power_this_tick = old_wound.processing_shock_power_this_tick
+
+ do_sparks(initial_sparks_amount, FALSE, victim)
+ if (limb.can_bleed() && attack_direction && victim.blood_volume > BLOOD_VOLUME_OKAY)
+ victim.spray_blood(attack_direction, severity)
+
+/datum/wound/electrical_damage/modify_desc_before_span(desc, mob/user)
+ . = ..()
+
+ if (limb.current_gauze)
+ return
+
+ var/intensity_mult = get_intensity_mult()
+ if (intensity_mult < 0.2 || (victim.stat == DEAD))
+ return
+
+ . += ", and "
+
+ var/extra
+ switch (intensity_mult)
+ if (0.2 to 0.4)
+ extra += "[span_deadsay("is letting out some sparks")]"
+ if (0.4 to 0.6)
+ extra += "[span_deadsay("is sparking quite a bit")]"
+ if (0.6 to 0.8)
+ extra += "[span_deadsay("is practically hemorrhaging sparks")]"
+ if (0.8 to 1)
+ extra += "[span_deadsay("has golden bolts of electricity constantly striking the surface")]"
+
+ . += extra
+
+/datum/wound/electrical_damage/get_scanner_description(mob/user)
+ . = ..()
+
+ . += "\nWound status: [get_wound_status_info()]"
+
+/datum/wound/electrical_damage/get_simple_scanner_description(mob/user)
+ . = ..()
+
+ . += "\nWound status: [get_wound_status_info()]"
+
+/// Returns a string with our fault intensity and threshold to removal for use in health analyzers.
+/datum/wound/electrical_damage/proc/get_wound_status_info()
+ return "Fault intensity is currently at [span_bold("[get_intensity_mult() * 100]")]%. It must be reduced to [span_blue("[minimum_intensity]")]% to remove the wound."
+
+// this wound is unaffected by cryoxadone and pyroxadone
+/datum/wound/electrical_damage/on_xadone(power)
+ return
+
+/datum/wound/electrical_damage/item_can_treat(obj/item/potential_treater, mob/user)
+ if (istype(potential_treater, /obj/item/stack/cable_coil) && ((user.pulling == victim && user.grab_state >= GRAB_AGGRESSIVE) || (limb.burn_dam <= ELECTRICAL_DAMAGE_MAX_BURN_DAMAGE_TO_LET_WIRES_REPAIR)))
+ return TRUE // if we're aggrograbbed, or relatively undamaged, go ahead. else, we dont want to impede normal treatment
+
+ return ..()
+
+/datum/wound/electrical_damage/treat(obj/item/treating_item, mob/user)
+ if (treating_item.tool_behaviour == TOOL_WIRECUTTER || treating_item.tool_behaviour == TOOL_RETRACTOR)
+ return wirecut(treating_item, user)
+
+ if (istype(treating_item, /obj/item/stack/medical/suture) || istype(treating_item, /obj/item/stack/cable_coil))
+ return suture_wires(treating_item, user)
+
+ return ..()
+
+/**
+ * The "trauma" treatment, done with cables/sutures. Sutures get a debuff.
+ * Low self-tend penalty.
+ * Very fast, but low value. Eats up wires for breakfast.
+ * Has limited wire/HUD bonuses. If you're a robo, use a wirecutter instead.
+ */
+/datum/wound/electrical_damage/proc/suture_wires(obj/item/stack/suturing_item, mob/living/carbon/human/user)
+ if (!suturing_item.tool_start_check())
+ return TRUE
+
+ var/is_suture = (istype(suturing_item, /obj/item/stack/medical/suture))
+
+ var/change = (processing_full_shock_threshold * wire_repair_percent) * ELECTRICAL_DAMAGE_SUTURE_WIRE_HEALING_AMOUNT_MULT
+ var/delay_mult = 1
+ if (user == victim)
+ delay_mult *= 1.4
+ if (is_suture)
+ delay_mult *= 1.5
+ var/obj/item/stack/medical/suture/suture_item = suturing_item
+ var/obj/item/stack/medical/suture/base_suture = /obj/item/stack/medical/suture
+ change = max(change - (suture_item.heal_brute - initial(base_suture.heal_brute)), 0.00001)
+
+ // as this is the trauma treatment, there are less bonuses
+ // if youre doing this, youre probably doing this on-the-spot
+ if (HAS_TRAIT(user, TRAIT_KNOW_ROBO_WIRES))
+ delay_mult *= 0.8
+ else if (HAS_TRAIT(user, TRAIT_KNOW_ENGI_WIRES))
+ delay_mult *= 0.9
+ if (HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD))
+ delay_mult *= 0.8
+ if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED))
+ change *= 1.5
+
+ ADD_TRAIT(src, TRAIT_ELECTRICAL_DAMAGE_REPAIRING, REF(user))
+ do_suture_repairs(suturing_item, user, change, delay_mult)
+ REMOVE_TRAIT(src, TRAIT_ELECTRICAL_DAMAGE_REPAIRING, REF(user))
+ return TRUE
+
+/// Does a while loop that repairs us with cables. A proc for containing runtimes and allowing trait removal at all times.
+/datum/wound/electrical_damage/proc/do_suture_repairs(obj/item/stack/suturing_item, mob/living/carbon/human/user, change, delay_mult)
+ var/is_suture = (istype(suturing_item, /obj/item/stack/medical/suture))
+ var/their_or_other = (user == victim ? "[user.p_their()]" : "[victim]'s")
+ var/your_or_other = (user == victim ? "your" : "[victim]'s")
+ var/replacing_or_suturing = (is_suture ? "repairing some" : "replacing")
+ while (suturing_item.tool_start_check())
+ user?.visible_message(span_danger("[user] begins [replacing_or_suturing] wiring within [their_or_other] [limb.plaintext_zone] with [suturing_item]..."), \
+ span_notice("You begin [replacing_or_suturing] wiring within [your_or_other] [limb.plaintext_zone] with [suturing_item]..."))
+ if (!suturing_item.use_tool(target = victim, user = user, delay = ELECTRICAL_DAMAGE_SUTURE_WIRE_BASE_DELAY * delay_mult, amount = 1, volume = 50, extra_checks = CALLBACK(src, PROC_REF(still_exists))))
+ return
+
+ if (user != victim && user.combat_mode)
+ user?.visible_message(span_danger("[user] mangles some of [their_or_other] [limb.plaintext_zone]'s wiring!"), \
+ span_danger("You mangle some of [your_or_other] [limb.plaintext_zone]'s wiring!"), ignored_mobs = victim)
+ to_chat(victim, span_userdanger("[capitalize(your_or_other)] mangles some of your [limb.plaintext_zone]'s wiring!"))
+ adjust_intensity(change * 2)
+ else
+ var/repairs_or_replaces = (is_suture ? "repairs" : "replaces")
+ var/repair_or_replace = (is_suture ? "repair" : "replace")
+ user?.visible_message(span_notice("[user] [repairs_or_replaces] some of [their_or_other] [limb.plaintext_zone]'s wiring!"), \
+ span_notice("You [repair_or_replace] some of [your_or_other] [limb.plaintext_zone]'s wiring!"))
+ adjust_intensity(-change)
+ victim?.balloon_alert(user, "intensity reduced to [get_intensity_mult() * 100]%")
+
+ if (fixed())
+ return
+
+/**
+ * The "proper" treatment, done with wirecutters/retractors. Retractors get a debuff.
+ * High self-tend penalty.
+ * Slow, but high value.
+ * Has high wire/HUD bonuses. The ideal treatment for a robo.
+ */
+/datum/wound/electrical_damage/proc/wirecut(obj/item/wirecutting_tool, mob/living/carbon/human/user)
+ if (!wirecutting_tool.tool_start_check())
+ return TRUE
+
+ var/is_retractor = (wirecutting_tool.tool_behaviour == TOOL_RETRACTOR)
+
+ var/change = (processing_full_shock_threshold * wirecut_repair_percent)
+ var/delay_mult = 1
+ if (user == victim)
+ delay_mult *= 2
+ if (is_retractor)
+ delay_mult *= 2
+ var/knows_wires = FALSE
+ if (HAS_TRAIT(user, TRAIT_KNOW_ROBO_WIRES))
+ delay_mult *= 0.3
+ knows_wires = TRUE
+ else if (HAS_TRAIT(user, TRAIT_KNOW_ENGI_WIRES))
+ delay_mult *= 0.6
+ knows_wires = TRUE
+ if (HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD))
+ if (knows_wires)
+ delay_mult *= 0.9
+ else
+ delay_mult *= 0.75
+ if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED))
+ change *= 1.5
+
+ ADD_TRAIT(src, TRAIT_ELECTRICAL_DAMAGE_REPAIRING, REF(user))
+ do_wirecutter_repairs(wirecutting_tool, user, change, delay_mult)
+ REMOVE_TRAIT(src, TRAIT_ELECTRICAL_DAMAGE_REPAIRING, REF(user))
+ return TRUE
+
+/// Does a while loop that repairs us with a wirecutter. A proc for containing runtimes and allowing trait removal at all times.
+/datum/wound/electrical_damage/proc/do_wirecutter_repairs(obj/item/wirecutting_tool, mob/living/carbon/human/user, change, delay_mult)
+ var/their_or_other = (user == victim ? "[user.p_their()]" : "[victim]'s")
+ var/your_or_other = (user == victim ? "your" : "[victim]'s")
+ while (wirecutting_tool.tool_start_check())
+ user?.visible_message(span_danger("[user] begins resetting misplaced wiring within [their_or_other] [limb.plaintext_zone]..."), \
+ span_notice("You begin resetting misplaced wiring within [your_or_other] [limb.plaintext_zone]..."))
+ if (!wirecutting_tool.use_tool(target = victim, user = user, delay = ELECTRICAL_DAMAGE_WIRECUTTER_BASE_DELAY * delay_mult, volume = 50, extra_checks = CALLBACK(src, PROC_REF(still_exists))))
+ return
+
+ if (user != victim && user.combat_mode)
+ user?.visible_message(span_danger("[user] mangles some of [their_or_other] [limb.plaintext_zone]'s wiring!"), \
+ span_danger("You mangle some of [your_or_other] [limb.plaintext_zone]'s wiring!"), ignored_mobs = victim)
+ to_chat(victim, span_userdanger("[capitalize(your_or_other)] mangles some of your [limb.plaintext_zone]'s wiring!"))
+ adjust_intensity(change * 2)
+ else
+ user?.visible_message(span_notice("[user] resets some of [their_or_other] [limb.plaintext_zone]'s wiring!"), \
+ span_notice("You reset some of [your_or_other] [limb.plaintext_zone]'s wiring!"))
+ adjust_intensity(-change)
+ victim?.balloon_alert(user, "intensity reduced to [get_intensity_mult() * 100]%")
+
+ if (fixed())
+ return
+
+/// If fixed() is true, we remove ourselves and return TRUE. FALSE otherwise.
+/datum/wound/electrical_damage/proc/remove_if_fixed()
+ if (fixed())
+ to_chat(victim, span_green("Your [limb.plaintext_zone] has recovered from its [name]!"))
+ remove_wound()
+ return TRUE
+ return FALSE
+
+/// Should we remove ourselves?
+/datum/wound/electrical_damage/proc/fixed()
+ return (intensity <= minimum_intensity || isnull(limb))
+
+/// Returns the multiplier we apply to our outgoing damage based off our current intensity. Is always between 0-1.
+/datum/wound/electrical_damage/proc/get_intensity_mult()
+ return (min((intensity / processing_full_shock_threshold), 1))
+
+/// Wrapper for electrocute_act
+/datum/wound/electrical_damage/proc/zap(
+ mob/living/target,
+ damage,
+ coeff = 1,
+ stun,
+ spark = TRUE,
+ animation = TRUE,
+ message = TRUE,
+ ignore_immunity = FALSE,
+ delay_stun = FALSE,
+ knockdown = FALSE,
+ ignore_gloves = FALSE,
+ tell_victim_if_no_message = TRUE,
+ jitter_time = 20 SECONDS,
+ stutter_time = 4 SECONDS,
+)
+
+ var/flags = NONE
+ if (!stun)
+ flags |= SHOCK_NOSTUN
+ if (!animation)
+ flags |= SHOCK_NO_HUMAN_ANIM
+ if (!message)
+ flags |= SHOCK_SUPPRESS_MESSAGE
+ if (tell_victim_if_no_message && target == victim)
+ to_chat(target, span_warning("Your [limb.plaintext_zone] short-circuits and zaps you!"))
+ if (ignore_immunity)
+ flags |= SHOCK_IGNORE_IMMUNITY
+ if (delay_stun)
+ flags |= SHOCK_DELAY_STUN
+ if (knockdown)
+ flags |= SHOCK_KNOCKDOWN
+ if (ignore_gloves)
+ flags |= SHOCK_NOGLOVES
+
+ target.electrocute_act(damage, limb, coeff, flags, jitter_time, stutter_time)
+ if (spark)
+ do_sparks(rand(process_shock_spark_count_min, process_shock_spark_count_max), FALSE, victim)
+
+// Slash
+// Fast to rise, but lower damage overall
+// Also a bit easy to treat
+/datum/wound/electrical_damage/slash
+ simple_desc = "Wiring has been slashed open, resulting in a fault that quickly intensifies!"
+
+/datum/wound/electrical_damage/slash/moderate
+ name = "Frayed Wiring"
+ desc = "Internal wiring has suffered a slight abrasion, causing a slow electrical fault that will intensify over time."
+ occur_text = "lets out a few sparks, as a few frayed wires stick out"
+ examine_desc = "has a few frayed wires sticking out"
+ treat_text = "Replacing of damaged wiring, though repairs via wirecutting instruments or sutures may suffice, albeit at limited efficiency. In case of emergency, \
+ subject may be subjected to high temperatures to allow solder to reset."
+
+ sound_effect = 'modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T1.ogg'
+
+ severity = WOUND_SEVERITY_MODERATE
+
+ sound_volume = 30
+
+ threshold_penalty = 20
+
+ intensity = 10 SECONDS
+ processing_full_shock_threshold = 3 MINUTES
+
+ processing_shock_power_per_second_max = 0.5
+ processing_shock_power_per_second_min = 0.4
+
+ processing_shock_stun_chance = 0
+ processing_shock_spark_chance = 30
+
+ process_shock_spark_count_max = 1
+ process_shock_spark_count_min = 1
+
+ wirecut_repair_percent = 0.1
+ wire_repair_percent = 0.023
+
+ initial_sparks_amount = 1
+
+ status_effect_type = /datum/status_effect/wound/electrical_damage/slash/moderate
+
+ a_or_from = "from"
+
+ scar_keyword = "slashmoderate"
+
+/datum/wound_pregen_data/electrical_damage/slash/moderate
+ abstract = FALSE
+ wound_path_to_generate = /datum/wound/electrical_damage/slash/moderate
+ threshold_minimum = 35
+
+/datum/wound/electrical_damage/slash/severe
+ name = "Severed Conduits"
+ desc = "A number of wires have been completely cut, resulting in electrical faults that will intensify at a worrying rate."
+ occur_text = "sends some electrical fiber in the direction of the blow, beginning to profusely spark"
+ examine_desc = "has multiple severed wires visible to the outside"
+ treat_text = "Containment of damaged wiring via gauze, then application of fresh wiring/sutures, or resetting of displaced wiring via wirecutter/retractor."
+
+ sound_effect = 'modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T2.ogg'
+
+ severity = WOUND_SEVERITY_SEVERE
+
+ sound_volume = 15
+
+ threshold_penalty = 30
+
+ intensity = 10 SECONDS
+ processing_full_shock_threshold = 2 MINUTES
+
+ processing_shock_power_per_second_max = 0.7
+ processing_shock_power_per_second_min = 0.6
+
+ processing_shock_stun_chance = 0
+ processing_shock_spark_chance = 60
+
+ process_shock_spark_count_max = 2
+ process_shock_spark_count_min = 1
+
+ wirecut_repair_percent = 0.09
+ wire_repair_percent = 0.015
+
+ initial_sparks_amount = 3
+
+ status_effect_type = /datum/status_effect/wound/electrical_damage/slash/severe
+
+ a_or_from = "from"
+
+ scar_keyword = "slashsevere"
+
+/datum/wound_pregen_data/electrical_damage/slash/severe
+ abstract = FALSE
+ wound_path_to_generate = /datum/wound/electrical_damage/slash/severe
+ threshold_minimum = 60
+
+/datum/wound/electrical_damage/slash/critical
+ name = "Systemic Fault"
+ desc = "A significant portion of the power distribution network has been cut open, resulting in massive power loss and runaway electrocution."
+ occur_text = "lets out a violent \"zhwarp\" sound as angry electric arcs attack the surrounding air"
+ examine_desc = "has lots of mauled wires sticking out"
+ treat_text = "Immediate securing via gauze, followed by emergency cable replacement and securing via wirecutters or retractor. \
+ If the fault has become uncontrollable, extreme heat therapy is recommended."
+
+ severity = WOUND_SEVERITY_CRITICAL
+ wound_flags = (ACCEPTS_GAUZE|MANGLES_EXTERIOR|CAN_BE_GRASPED|SPLINT_OVERLAY)
+
+ sound_effect = 'modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T3.ogg'
+
+ sound_volume = 30
+
+ threshold_penalty = 50
+
+ intensity = 10 SECONDS
+ processing_full_shock_threshold = 1.25 MINUTES
+
+ processing_shock_power_per_second_max = 1.3
+ processing_shock_power_per_second_min = 1.1
+
+ processing_shock_stun_chance = 5
+ processing_shock_spark_chance = 90
+
+ process_shock_spark_count_max = 3
+ process_shock_spark_count_min = 2
+
+ wirecut_repair_percent = 0.08
+ wire_repair_percent = 0.01
+
+ initial_sparks_amount = 8
+
+ status_effect_type = /datum/status_effect/wound/electrical_damage/slash/critical
+
+ a_or_from = "a"
+
+ scar_keyword = "slashcritical"
+
+/datum/wound_pregen_data/electrical_damage/slash/critical
+ abstract = FALSE
+ wound_path_to_generate = /datum/wound/electrical_damage/slash/critical
+ threshold_minimum = 100
+
+#undef ELECTRICAL_DAMAGE_ON_STASIS_MULT
+#undef ELECTRICAL_DAMAGE_GRASPED_MULT
+#undef ELECTRICAL_DAMAGE_LYING_DOWN_MULT
+#undef ELECTRICAL_DAMAGE_DEAD_PROGRESS_MULT
+
+#undef ELECTRICAL_DAMAGE_WIRECUTTER_BASE_DELAY
+#undef ELECTRICAL_DAMAGE_SUTURE_WIRE_BASE_DELAY
+
+#undef ELECTRICAL_DAMAGE_MINIMUM_SHOCK_POWER_PER_ZAP
+#undef ELECTRICAL_DAMAGE_MAX_BURN_DAMAGE_TO_LET_WIRES_REPAIR
+#undef ELECTRICAL_DAMAGE_POWER_PER_TICK_MULT
+#undef ELECTRICAL_DAMAGE_SUTURE_WIRE_HEALING_AMOUNT_MULT
+
+#undef ELECTRICAL_DAMAGE_CLOTTING_PROGRESS_MULT
diff --git a/modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T1.ogg b/modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T1.ogg
new file mode 100644
index 0000000000000..cb7b3d0a79f49
Binary files /dev/null and b/modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T1.ogg differ
diff --git a/modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T2.ogg b/modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T2.ogg
new file mode 100644
index 0000000000000..4fad258357538
Binary files /dev/null and b/modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T2.ogg differ
diff --git a/modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T3.ogg b/modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T3.ogg
new file mode 100644
index 0000000000000..f0ea8e2217027
Binary files /dev/null and b/modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T3.ogg differ
diff --git a/modular_doppler/modular_medical/wounds/wound_effects.dm b/modular_doppler/modular_medical/wounds/wound_effects.dm
index f5b5c8cb9a138..8f9aef16210d3 100644
--- a/modular_doppler/modular_medical/wounds/wound_effects.dm
+++ b/modular_doppler/modular_medical/wounds/wound_effects.dm
@@ -1,3 +1,12 @@
+/datum/status_effect/wound/blunt/robotic/moderate
+ id = "unsecure_moderate"
+
+/datum/status_effect/wound/blunt/robotic/severe
+ id = "unsecure_severe"
+
+/datum/status_effect/wound/blunt/robotic/critical
+ id = "unsecure_critical"
+
/datum/status_effect/wound/electrical_damage/slash/moderate
id = "electric_slash_moderate"
diff --git a/modular_doppler/modular_quirks/code/good_quirks.dm b/modular_doppler/modular_quirks/code/good_quirks.dm
deleted file mode 100644
index 2b0b26fefd478..0000000000000
--- a/modular_doppler/modular_quirks/code/good_quirks.dm
+++ /dev/null
@@ -1,9 +0,0 @@
-/datum/quirk/linguist
- name = "Linguist"
- desc = "You're a student of numerous languages and come with an additional language point."
- value = 4
- mob_trait = TRAIT_LINGUIST
- gain_text = span_notice("Your brain seems more equipped to handle different modes of conversation.")
- lose_text = span_danger("Your grasp of the finer points of Draconic idioms fades away.")
- medical_record_text = "Patient demonstrates a high brain plasticity in regards to language learning."
- icon = FA_ICON_BOOK_ATLAS
diff --git a/modular_doppler/modular_quirks/code/excitable/quirk.dm b/modular_doppler/modular_quirks/excitable/excitable.dm
similarity index 100%
rename from modular_doppler/modular_quirks/code/excitable/quirk.dm
rename to modular_doppler/modular_quirks/excitable/excitable.dm
diff --git a/modular_doppler/modular_quirks/left_handed/left_handed.dm b/modular_doppler/modular_quirks/left_handed/left_handed.dm
new file mode 100644
index 0000000000000..3f25672e25a73
--- /dev/null
+++ b/modular_doppler/modular_quirks/left_handed/left_handed.dm
@@ -0,0 +1,7 @@
+/datum/quirk/left_handed
+ name = "Left Handed"
+ desc = "You just so happen to be a left-handed, neat!"
+ icon = FA_ICON_HAND_SPARKLES
+ value = 0
+ mob_trait = TRAIT_LEFT_HANDED
+ medical_record_text = "Patient is documented to have a left dominant hand."
diff --git a/modular_doppler/modular_quirks/linguist/linguist.dm b/modular_doppler/modular_quirks/linguist/linguist.dm
new file mode 100644
index 0000000000000..1a92e80509461
--- /dev/null
+++ b/modular_doppler/modular_quirks/linguist/linguist.dm
@@ -0,0 +1,141 @@
+/datum/quirk/linguist
+ name = "Linguist"
+ desc = "You're a student of numerous languages and come with an additional language point."
+ value = 4
+ mob_trait = TRAIT_LINGUIST
+ gain_text = span_notice("Your brain seems more equipped to handle different modes of conversation.")
+ lose_text = span_danger("Your grasp of the finer points of Draconic idioms fades away.")
+ medical_record_text = "Patient demonstrates a high brain plasticity in regards to language learning."
+ icon = FA_ICON_BOOK_ATLAS
+
+/datum/quirk/night_vision
+ name = "Night Vision"
+ desc = "You can see slightly more clearly in full darkness than most people."
+ icon = FA_ICON_MOON
+ value = 4
+ mob_trait = TRAIT_NIGHT_VISION
+ gain_text = span_notice("The shadows seem a little less dark.")
+ lose_text = span_danger("Everything seems a little darker.")
+ medical_record_text = "Patient's eyes show above-average acclimation to darkness."
+ mail_goodies = list(
+ /obj/item/flashlight/flashdark,
+ /obj/item/food/grown/mushroom/glowshroom/shadowshroom,
+ /obj/item/skillchip/light_remover,
+ )
+
+/datum/quirk/night_vision/add(client/client_source)
+ refresh_quirk_holder_eyes()
+
+/datum/quirk/night_vision/remove()
+ refresh_quirk_holder_eyes()
+
+/datum/quirk/night_vision/proc/refresh_quirk_holder_eyes()
+ var/mob/living/carbon/human/human_quirk_holder = quirk_holder
+ var/obj/item/organ/internal/eyes/eyes = human_quirk_holder.get_organ_by_type(/obj/item/organ/internal/eyes)
+ if(!eyes)
+ return
+ // We've either added or removed TRAIT_NIGHT_VISION before calling this proc. Just refresh the eyes.
+ eyes.refresh()
+
+// This NV quirk variant applies color_offsets night vision to a mob based on its chosen eye color.
+// Some eye colors will produce very slightly stronger mechanical night vision effects just by virtue of their RGB values being scaled higher (typically lighter colours).
+
+/datum/quirk/night_vision
+ desc = "You can see a little better in darkness than most ordinary humanoids. If your eyes are naturally more sensitive to light through other means (such as being photophobic or a mothperson), this effect is significantly stronger."
+ medical_record_text = "Patient's visual sensory organs demonstrate non-standard performance in low-light conditions."
+ var/nv_color = null /// Holds the player's selected night vision colour
+ var/list/nv_color_cutoffs = null /// Contains the color_cutoffs applied to the user's eyes w/ our custom hue (once built)
+
+/datum/quirk/night_vision/add_unique(client/client_source)
+ . = ..()
+ nv_color = client_source?.prefs.read_preference(/datum/preference/color/nv_color)
+ if (isnull(nv_color))
+ var/mob/living/carbon/human/human_holder = quirk_holder
+ nv_color = process_chat_color(human_holder.eye_color_left)
+ nv_color_cutoffs = calculate_color_cutoffs(nv_color)
+ refresh_quirk_holder_eyes() // make double triple dog sure we apply the overlay
+
+/// Calculate eye organ color_cutoffs used in tinted night vision with a supplied hexcode colour, clamping and scaling appropriately.
+/datum/quirk/night_vision/proc/calculate_color_cutoffs(color)
+ var/mob/living/carbon/human/target = quirk_holder
+
+ // if we have more sensitive eyes, increase the power
+ var/obj/item/organ/internal/eyes/target_eyes = target.get_organ_slot(ORGAN_SLOT_EYES)
+ if (!istype(target_eyes))
+ return
+ var/infravision_multiplier = max(0, (-(target_eyes.flash_protect) * DOPPLER_NIGHT_VISION_SENSITIVITY_MULT)) + 1
+
+ var/list/new_rgb_cutoffs = new /list(3)
+ for(var/i in 1 to 3)
+ var/base_color = hex2num(copytext(color, (i*2), (i*2)+2)) //convert their supplied hex colour value to RGB
+ var/adjusted_color = max(((base_color / 255) * (DOPPLER_NIGHT_VISION_POWER_MAX * infravision_multiplier)), (DOPPLER_NIGHT_VISION_POWER_MIN * infravision_multiplier)) //linear convert their eye color into a color_cutoff range, ensuring it is clamped
+ new_rgb_cutoffs[i] = adjusted_color
+
+ return new_rgb_cutoffs
+
+/datum/quirk_constant_data/night_vision
+ associated_typepath = /datum/quirk/night_vision
+ customization_options = list(/datum/preference/color/nv_color)
+
+// Client preference for night vision colour
+/datum/preference/color/nv_color
+ savefile_key = "nv_color"
+ savefile_identifier = PREFERENCE_CHARACTER
+ category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
+
+/datum/preference/color/nv_color/is_accessible(datum/preferences/preferences)
+ if (!..(preferences))
+ return FALSE
+
+ return "Night Vision" in preferences.all_quirks
+
+/datum/preference/color/nv_color/apply_to_human(mob/living/carbon/human/target, value)
+ return
+
+// run the Blessed Runechat Proc since it does most of what we want regarding luminance clamping anyway. could it be better? probably. is it more work? yes, it's a LOT of work.
+/datum/preference/color/nv_color/deserialize(input, datum/preferences/preferences)
+ return process_chat_color(sanitize_hexcolor(input))
+
+/datum/preference/color/nv_color/serialize(input)
+ return process_chat_color(sanitize_hexcolor(input))
+
+/datum/preference/color/nv_color/create_default_value()
+ return process_chat_color("#[random_color()]")
+
+/datum/quirk/photophobia
+ desc = "Bright lights are uncomfortable and upsetting to you for whatever reason. Your eyes are also more sensitive to light in general. This shares a unique interaction with Night Vision."
+ /// how much of a flash_protect deficit the quirk inflicts
+ var/severity = 1
+
+/datum/quirk/photophobia/add_unique(client/client_source)
+ var/sensitivity = client_source?.prefs.read_preference(/datum/preference/choiced/photophobia_severity)
+ switch (sensitivity)
+ if ("Hypersensitive")
+ severity = 2
+ if ("Sensitive")
+ severity = 1
+ var/obj/item/organ/internal/eyes/holder_eyes = quirk_holder.get_organ_slot(ORGAN_SLOT_EYES)
+ restore_eyes(holder_eyes) // add_unique() happens after add() so we need to jank reset this to ensure sensitivity is properly applied at roundstart
+ check_eyes(holder_eyes)
+
+/datum/quirk_constant_data/photophobia
+ associated_typepath = /datum/quirk/photophobia
+ customization_options = list(/datum/preference/choiced/photophobia_severity)
+
+/datum/preference/choiced/photophobia_severity
+ category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
+ savefile_key = "photophobia_severity"
+ savefile_identifier = PREFERENCE_CHARACTER
+
+/datum/preference/choiced/photophobia_severity/is_accessible(datum/preferences/preferences)
+ if (!..(preferences))
+ return FALSE
+
+ return "Photophobia" in preferences.all_quirks
+
+/datum/preference/choiced/photophobia_severity/init_possible_values()
+ var/list/values = list("Sensitive", "Hypersensitive")
+ return values
+
+/datum/preference/choiced/photophobia_severity/apply_to_human(mob/living/carbon/human/target, value)
+ return
diff --git a/modular_doppler/modular_quirks/code/overwrites/musician.dm b/modular_doppler/modular_quirks/overwrites/musician.dm
similarity index 100%
rename from modular_doppler/modular_quirks/code/overwrites/musician.dm
rename to modular_doppler/modular_quirks/overwrites/musician.dm
diff --git a/modular_doppler/modular_quirks/permitted_cybernetic/code/preferences.dm b/modular_doppler/modular_quirks/permitted_cybernetic/code/preferences.dm
new file mode 100644
index 0000000000000..056b0caf0939c
--- /dev/null
+++ b/modular_doppler/modular_quirks/permitted_cybernetic/code/preferences.dm
@@ -0,0 +1,20 @@
+/datum/preference/choiced/permitted_cybernetic
+ category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
+ savefile_key = "permitted_cybernetic"
+ savefile_identifier = PREFERENCE_CHARACTER
+ can_randomize = FALSE
+
+/datum/preference/choiced/permitted_cybernetic/init_possible_values()
+ return list("Random") + assoc_to_keys(GLOB.possible_quirk_implants)
+
+/datum/preference/choiced/permitted_cybernetic/create_default_value()
+ return "Random"
+
+/datum/preference/choiced/permitted_cybernetic/is_accessible(datum/preferences/preferences)
+ if (!..())
+ return FALSE
+
+ return "Permitted Cybernetic" in preferences.all_quirks
+
+/datum/preference/choiced/permitted_cybernetic/apply_to_human(mob/living/carbon/human/target, value)
+ return
diff --git a/modular_doppler/modular_quirks/permitted_cybernetic/permitted_cybernetic.dm b/modular_doppler/modular_quirks/permitted_cybernetic/permitted_cybernetic.dm
new file mode 100644
index 0000000000000..e9a15bf98d88c
--- /dev/null
+++ b/modular_doppler/modular_quirks/permitted_cybernetic/permitted_cybernetic.dm
@@ -0,0 +1,73 @@
+GLOBAL_LIST_INIT(possible_quirk_implants, list(
+ "Engineering Toolset" = /obj/item/organ/internal/cyberimp/arm/toolset,
+ "Surgery Toolset" = /obj/item/organ/internal/cyberimp/arm/surgery,
+ "Hydroponics Toolset" = /obj/item/organ/internal/cyberimp/arm/botany,
+ "Sanitation Toolset" = /obj/item/organ/internal/cyberimp/arm/janitor,
+ "Razorclaw Implant" = /obj/item/organ/internal/cyberimp/arm/razor_claws,
+ "Excavator Implant" = /obj/item/organ/internal/cyberimp/arm/mining_drill,
+))
+
+/datum/quirk/permitted_cybernetic
+ name = "Permitted Cybernetic"
+ desc = "You're allowed a cybernetic implant aboard the station, though this is information is available for security."
+ value = 8
+ mob_trait = TRAIT_PERMITTED_CYBERNETIC
+ icon = FA_ICON_WRENCH
+
+/datum/quirk_constant_data/implanted
+ associated_typepath = /datum/quirk/permitted_cybernetic
+ customization_options = list(/datum/preference/choiced/permitted_cybernetic)
+
+/datum/quirk/permitted_cybernetic/add_unique(client/client_source)
+ var/obj/item/organ/desired_implant = GLOB.possible_quirk_implants[client_source?.prefs?.read_preference(/datum/preference/choiced/permitted_cybernetic)]
+ if(isnull(desired_implant)) //Client gone or they chose a random implant
+ desired_implant = GLOB.possible_quirk_implants[pick(GLOB.possible_quirk_implants)]
+
+ var/mob/living/carbon/human/human_holder = quirk_holder
+ if(desired_implant.zone in GLOB.arm_zones)
+ if(HAS_TRAIT(human_holder, TRAIT_LEFT_HANDED)) //Left handed person? Give them a leftie implant
+ desired_implant = text2path("[desired_implant]/l")
+
+ if(human_holder.dna.species.type in GLOB.species_blacklist_no_humanoid)
+ to_chat(human_holder, span_warning("Due to your species type, the [name] quirk has been disabled."))
+ return
+ if(human_holder.mind?.assigned_role.title == JOB_PRISONER)
+ to_chat(human_holder, span_warning("Due to your job, the [name] quirk has been disabled."))
+ return
+
+ var/obj/item/organ/internal/cybernetic = new desired_implant()
+ cybernetic.Insert(human_holder, special = TRUE, movement_flags = DELETE_IF_REPLACED)
+ medical_record_text = "Patient has a company approved [cybernetic.name] installed within their body."
+
+/datum/quirk/permitted_cybernetic/add(client/client_source)
+ . = ..()
+ quirk_holder.update_implanted_hud()
+
+/datum/quirk/permitted_cybernetic/remove()
+ var/mob/living/old_holder = quirk_holder
+ . = ..()
+ old_holder.update_implanted_hud()
+
+/mob/living/prepare_data_huds()
+ . = ..()
+ update_implanted_hud()
+
+/// Adds the HUD element if src has its trait. Removes it otherwise.
+/mob/living/proc/update_implanted_hud()
+ var/image/quirk_holder = hud_list?[SEC_IMPLANT_HUD]
+ if(isnull(quirk_holder))
+ return
+
+ var/icon/temporary_icon = icon(icon, icon_state, dir)
+ quirk_holder.pixel_y = temporary_icon.Height() - world.icon_size
+
+ if(ishuman(src))
+ var/mob/living/carbon/human/target = src
+ if(target.dna.species.type in GLOB.species_blacklist_no_humanoid)
+ return
+ if(HAS_TRAIT(src, TRAIT_PERMITTED_CYBERNETIC))
+ set_hud_image_active(SEC_IMPLANT_HUD)
+ quirk_holder.icon = 'modular_doppler/overwrites/huds/hud.dmi'
+ quirk_holder.icon_state = "hud_imp_quirk"
+ else
+ set_hud_image_inactive(SEC_IMPLANT_HUD)
diff --git a/modular_doppler/modular_sounds/sound/machines/whirr.ogg b/modular_doppler/modular_sounds/sound/machines/whirr.ogg
new file mode 100644
index 0000000000000..bc51d5aa04676
Binary files /dev/null and b/modular_doppler/modular_sounds/sound/machines/whirr.ogg differ
diff --git a/modular_doppler/modular_sounds/sound/mobs/humanoids/android/attributions.txt b/modular_doppler/modular_sounds/sound/mobs/humanoids/android/attributions.txt
new file mode 100644
index 0000000000000..947512733701f
--- /dev/null
+++ b/modular_doppler/modular_sounds/sound/mobs/humanoids/android/attributions.txt
@@ -0,0 +1,3 @@
+{
+monitor_switch.ogg - https://freesound.org/people/bolkmar/sounds/511890/ , License: CC0
+}
diff --git a/modular_doppler/modular_sounds/sound/mobs/humanoids/android/drain.wav b/modular_doppler/modular_sounds/sound/mobs/humanoids/android/drain.wav
new file mode 100644
index 0000000000000..eeaee3a92fc35
Binary files /dev/null and b/modular_doppler/modular_sounds/sound/mobs/humanoids/android/drain.wav differ
diff --git a/modular_doppler/modular_sounds/sound/mobs/humanoids/android/monitor_switch.ogg b/modular_doppler/modular_sounds/sound/mobs/humanoids/android/monitor_switch.ogg
new file mode 100644
index 0000000000000..a2a12fc306539
Binary files /dev/null and b/modular_doppler/modular_sounds/sound/mobs/humanoids/android/monitor_switch.ogg differ
diff --git a/modular_doppler/modular_species/_species.dm b/modular_doppler/modular_species/_species.dm
index 422499d5faacd..00048be82343c 100644
--- a/modular_doppler/modular_species/_species.dm
+++ b/modular_doppler/modular_species/_species.dm
@@ -1,3 +1,45 @@
+/**
+ * # species datum
+ *
+ * Datum that handles different species in the game.
+ *
+ * This datum handles species in the game, such as lizardpeople, mothmen, zombies, skeletons, etc.
+ * It is used in [carbon humans][mob/living/carbon/human] to determine various things about them, like their food preferences, if they have biological genders, their damage resistances, and more.
+ *
+ */
+/datum/species
+ /// Adding a language type to this in the form of /datum/language will allow the language to be displayed in preferences for that species, even if it is a secret language.
+ /// Currently used for Ættmál in hearthkin.
+ var/list/language_prefs_whitelist
+ ///If a species can always be picked in prefs for the purposes of customizing it for ghost roles or events
+ var/always_customizable = FALSE
+ ///How are we treated regarding processing reagents, by default we process them as if we're organic
+ var/reagent_flags = PROCESS_ORGANIC
+ ///This is the outfit which will be used by the species its preview portrait
+ var/datum/outfit/preview_outfit = /datum/outfit/job/assistant/consistent
+
+
+/// Cybernetic limbs logic here!
+// Used for most races
+/datum/species/on_species_gain(mob/living/carbon/human/target, datum/species/old_species, pref_load)
+ var/list/frame_bodyparts = target.dna.features["frame_list"]
+ if(type in GLOB.species_blacklist_no_humanoid)
+ return ..()
+ if(type == /datum/species/android && frame_bodyparts && frame_bodyparts[BODY_ZONE_HEAD])
+ bodypart_overrides[BODY_ZONE_HEAD] = frame_bodyparts[BODY_ZONE_HEAD]
+ if(frame_bodyparts && frame_bodyparts[BODY_ZONE_CHEST])
+ bodypart_overrides[BODY_ZONE_CHEST] = frame_bodyparts[BODY_ZONE_CHEST]
+ if(frame_bodyparts && frame_bodyparts[BODY_ZONE_R_ARM])
+ bodypart_overrides[BODY_ZONE_R_ARM] = frame_bodyparts[BODY_ZONE_R_ARM]
+ if(frame_bodyparts && frame_bodyparts[BODY_ZONE_L_ARM])
+ bodypart_overrides[BODY_ZONE_L_ARM] = frame_bodyparts[BODY_ZONE_L_ARM]
+ if(frame_bodyparts && frame_bodyparts[BODY_ZONE_R_LEG])
+ bodypart_overrides[BODY_ZONE_R_LEG] = frame_bodyparts[BODY_ZONE_R_LEG]
+ if(frame_bodyparts && frame_bodyparts[BODY_ZONE_L_LEG])
+ bodypart_overrides[BODY_ZONE_L_LEG] = frame_bodyparts[BODY_ZONE_L_LEG]
+ return ..()
+
+
/// Animal trait logic goes here!
// Used for the genemod and anthro species
@@ -28,3 +70,13 @@
ADD_TRAIT(target, TRAIT_CATLIKE_GRACE, SPECIES_TRAIT)
ADD_TRAIT(target, TRAIT_HATED_BY_DOGS, SPECIES_TRAIT)
+
+/// spec_revival logic
+/datum/species/proc/spec_revival(mob/living/carbon/human/target)
+ return
+
+/mob/living/carbon/human/revive(full_heal_flags = NONE, excess_healing = 0, force_grab_ghost = FALSE)
+ . = ..()
+ if(.)
+ if(dna && dna.species)
+ dna.species.spec_revival(src)
diff --git a/modular_doppler/modular_species/code/bloodsplatter.dm b/modular_doppler/modular_species/code/bloodsplatter.dm
index 4243a8813a35a..f4b1f4611beda 100644
--- a/modular_doppler/modular_species/code/bloodsplatter.dm
+++ b/modular_doppler/modular_species/code/bloodsplatter.dm
@@ -1,15 +1,3 @@
-/// Green blood reagent
-/datum/reagent/blood/green
- data = list("viruses"=null,"blood_DNA"=null,"blood_type"=null,"resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null,"quirks"=null)
- name = "Green Blood"
- color = "#50c034" // rgb: 0, 200, 0
- metabolization_rate = 12.5 * REAGENTS_METABOLISM //fast rate so it disappears fast.
- taste_description = "iron"
- taste_mult = 1.3
- penetrates_skin = NONE
- ph = 7.4
- default_container = /obj/item/reagent_containers/blood
-
/// In this .dm we make a green bloodsplatter subtype
// Using the xenoblood icons
/obj/effect/decal/cleanable/blood/hitsplatter/green
@@ -18,20 +6,44 @@
random_icon_states = list("xhitsplatter1", "xhitsplatter2", "xhitsplatter3")
blood_state = BLOOD_STATE_XENO
+/obj/effect/decal/cleanable/blood/hitsplatter/blue
+ icon = 'modular_doppler/modular_species/icons/blood.dmi'
+ icon_state = "xhitsplatter1"
+ random_icon_states = list("bhitsplatter1", "bhitsplatter2", "bhitsplatter3")
+ blood_state = BLOOD_STATE_NOT_BLOODY
+
/obj/effect/temp_visual/dir_setting/bloodsplatter/green
splatter_type = "xsplatter"
+/obj/effect/temp_visual/dir_setting/bloodsplatter/blue
+ icon = 'modular_doppler/modular_species/icons/blood.dmi'
+ splatter_type = "bsplatter"
+
/obj/effect/decal/cleanable/blood/green
- name = "green blood"
+ name = "insect blood"
desc = "It's green... And it looks like... blood?"
icon = 'icons/effects/blood.dmi'
icon_state = "xfloor1"
random_icon_states = list("xfloor1", "xfloor2", "xfloor3", "xfloor4", "xfloor5", "xfloor6", "xfloor7")
bloodiness = BLOOD_AMOUNT_PER_DECAL
+ should_dry = FALSE
blood_state = BLOOD_STATE_XENO
beauty = -250
clean_type = CLEAN_TYPE_BLOOD
+/obj/effect/decal/cleanable/blood/blue
+ name = "robot blood"
+ desc = "It's blue... And it looks like... blood?"
+ icon = 'modular_doppler/modular_species/icons/blood.dmi'
+ icon_state = "bfloor1"
+ random_icon_states = list("bfloor1", "bfloor2", "bfloor3", "bfloor4", "bfloor5", "bfloor6", "bfloor7")
+ bloodiness = BLOOD_AMOUNT_PER_DECAL
+ should_dry = FALSE
+ blood_state = BLOOD_STATE_NOT_BLOODY
+ clean_type = CLEAN_TYPE_BLOOD
+
+//// splatter
+// green
/obj/effect/decal/cleanable/blood/green/splatter
random_icon_states = list("xgibbl1", "xgibbl2", "xgibbl3", "xgibbl4", "xgibbl5")
@@ -44,22 +56,49 @@
/obj/effect/decal/cleanable/blood/green/splatter/over_window/NeverShouldHaveComeHere(turf/here_turf)
return isgroundlessturf(here_turf)
+// blue
+/obj/effect/decal/cleanable/blood/blue/splatter
+ random_icon_states = list("bgibbl1", "bgibbl2", "bgibbl3", "bgibbl4", "bgibbl5")
+
+/obj/effect/decal/cleanable/blood/blue/splatter/over_window
+ layer = ABOVE_WINDOW_LAYER
+ plane = GAME_PLANE
+ vis_flags = VIS_INHERIT_PLANE
+ alpha = 180
+
+/obj/effect/decal/cleanable/blood/blue/splatter/over_window/NeverShouldHaveComeHere(turf/here_turf)
+ return isgroundlessturf(here_turf)
+
+//// drips
+// green
/obj/effect/decal/cleanable/blood/drip/green
- name = "drips of blood"
+ name = "drips of insect blood"
desc = "It's green."
icon = 'modular_doppler/modular_species/icons/blood.dmi'
icon_state = "xdrip5"
random_icon_states = list("xdrip1","xdrip2","xdrip3","xdrip4","xdrip5")
- should_dry = FALSE //human only thing
+ should_dry = FALSE
blood_state = BLOOD_STATE_XENO
beauty = -150
+// blue
+/obj/effect/decal/cleanable/blood/drip/blue
+ name = "drips of robot blood"
+ desc = "It's blue."
+ icon = 'modular_doppler/modular_species/icons/blood.dmi'
+ icon_state = "bdrip5"
+ random_icon_states = list("bdrip1","bdrip2","bdrip3","bdrip4","bdrip5")
+ should_dry = FALSE
+ blood_state = BLOOD_STATE_NOT_BLOODY
+ beauty = -100
//getTrail overwrite
/mob/living/carbon/human/getTrail()
- if(!(hasgreenblood(src)))
- return ..()
- if(getBruteLoss() < 300)
- return pick (list("xltrails_1", "xltrails2"))
- else
- return pick (list("xttrails_1", "xttrails2"))
+ if((hasgreenblood(src)))
+ if(getBruteLoss() < 300)
+ return pick (list("xltrails_1", "xltrails2"))
+ else
+ return pick (list("xttrails_1", "xttrails2"))
+ if((hasblueblood(src)))
+ return pick (list("btrails_1", "btrails2"))
+ return ..()
diff --git a/modular_doppler/modular_species/icons/blood.dmi b/modular_doppler/modular_species/icons/blood.dmi
index 530731c98d33a..574b8ab5e9c26 100644
Binary files a/modular_doppler/modular_species/icons/blood.dmi and b/modular_doppler/modular_species/icons/blood.dmi differ
diff --git a/modular_doppler/modular_species/overwrites/code/species.dm b/modular_doppler/modular_species/overwrites/code/species.dm
deleted file mode 100644
index dec467a8fae0e..0000000000000
--- a/modular_doppler/modular_species/overwrites/code/species.dm
+++ /dev/null
@@ -1,20 +0,0 @@
-// Code which allows us to set custom outfits for species portraits
-/datum/species
- ///This is the outfit which will be used by the species its preview portrait
- var/datum/outfit/preview_outfit = /datum/outfit/job/assistant/consistent
-
-/**
- * # species datum
- *
- * Datum that handles different species in the game.
- *
- * This datum handles species in the game, such as lizardpeople, mothmen, zombies, skeletons, etc.
- * It is used in [carbon humans][mob/living/carbon/human] to determine various things about them, like their food preferences, if they have biological genders, their damage resistances, and more.
- *
- */
-/datum/species
- /// Adding a language type to this in the form of /datum/language will allow the language to be displayed in preferences for that species, even if it is a secret language.
- /// Currently used for Ættmál in hearthkin.
- var/list/language_prefs_whitelist
- ///If a species can always be picked in prefs for the purposes of customizing it for ghost roles or events
- var/always_customizable = FALSE
diff --git a/modular_doppler/modular_species/species_types/android/android.dm b/modular_doppler/modular_species/species_types/android/android.dm
new file mode 100644
index 0000000000000..c1837933bf462
--- /dev/null
+++ b/modular_doppler/modular_species/species_types/android/android.dm
@@ -0,0 +1,134 @@
+/// The starter amount for the android's core
+#define ENERGY_START_AMT 10 MEGA JOULES
+/// The amount at which mob energy decreases
+#define ENERGY_DRAIN_AMT 10 KILO JOULES
+
+/datum/species/android
+ name = "Android"
+ id = SPECIES_ANDROID
+ preview_outfit = /datum/outfit/android_preview
+ examine_limb_id = SPECIES_HUMAN
+ inherent_traits = list(
+ TRAIT_MUTANT_COLORS,
+ TRAIT_GENELESS,
+ TRAIT_LIMBATTACHMENT,
+ TRAIT_NOBREATH,
+ TRAIT_NOHUNGER,
+ TRAIT_NOCRITDAMAGE,
+ TRAIT_NO_DNA_COPY,
+ TRAIT_NO_PLASMA_TRANSFORM,
+ TRAIT_RADIMMUNE,
+ TRAIT_RESISTLOWPRESSURE,
+ /*TG traits we remove
+ TRAIT_LIVERLESS_METABOLISM,
+ TRAIT_PIERCEIMMUNE,
+ TRAIT_OVERDOSEIMMUNE,
+ TRAIT_TOXIMMUNE,
+ TRAIT_NOFIRE,
+ TRAIT_NOBLOOD,
+ TRAIT_NO_UNDERWEAR,
+ TRAIT_RESISTHEAT,
+ TRAIT_RESISTCOLD,
+ TRAIT_RESISTHIGHPRESSURE,*/
+ TRAIT_STABLEHEART,
+ TRAIT_STABLELIVER,
+ )
+ reagent_flags = PROCESS_SYNTHETIC
+ body_markings = list(/datum/bodypart_overlay/simple/body_marking/lizard = "None")
+ mutantheart = /obj/item/organ/internal/heart/cybernetic/tier2
+ mutantstomach = /obj/item/organ/internal/stomach/cybernetic/tier2
+ mutantliver = /obj/item/organ/internal/liver/cybernetic/tier2
+ exotic_blood = /datum/reagent/synth_blood
+ exotic_bloodtype = "R*"
+
+ bodytemp_heat_damage_limit = (BODYTEMP_NORMAL + 146) // 456 K / 183 C
+ bodytemp_cold_damage_limit = (BODYTEMP_NORMAL - 80) // 230 K / -43 C
+ /// Ability to recharge!
+ var/datum/action/innate/power_cord/power_cord
+ /// Hud element to display our energy level
+ var/atom/movable/screen/android/energy/energy_tracker
+ /// How much energy we start with
+ var/core_energy = ENERGY_START_AMT
+
+/datum/outfit/android_preview
+ name = "Android (Species Preview)"
+ // nude
+
+/datum/species/android/on_species_gain(mob/living/carbon/target, datum/species/old_species, pref_load)
+ . = ..()
+ if(ishuman(target))
+ power_cord = new
+ power_cord.Grant(target)
+
+/datum/species/android/on_species_loss(mob/living/carbon/target, datum/species/new_species, pref_load)
+ . = ..()
+ if(power_cord)
+ power_cord.Remove(target)
+ if(target.hud_used)
+ var/datum/hud/hud_used = target.hud_used
+ hud_used.infodisplay -= energy_tracker
+ QDEL_NULL(energy_tracker)
+
+/datum/species/android/spec_revival(mob/living/carbon/human/target)
+ playsound(target.loc, 'sound/machines/chime.ogg', 50, TRUE)
+ target.visible_message(span_notice("[target]'s LEDs flicker to life!"), span_notice("All systems nominal. You're back online!"))
+
+/datum/species/android/spec_life(mob/living/carbon/human/target, seconds_per_tick, times_fired)
+ . = ..()
+ handle_hud(target)
+
+ if(target.stat == SOFT_CRIT || target.stat == HARD_CRIT)
+ target.adjustFireLoss(1) //Still deal some damage in case a cold environment would be preventing us from the sweet release to robot heaven
+ target.adjust_bodytemperature(13) //We're overheating!!
+ if(prob(10))
+ to_chat(target, span_warning("Alert: Critical damage taken! Cooling systems failing!"))
+ do_sparks(3, FALSE, target)
+
+ if(HAS_TRAIT(target, TRAIT_CHARGING))
+ return
+ if(core_energy > 0)
+ core_energy -= ENERGY_DRAIN_AMT
+ if(core_energy <= 0)
+ target.death() // You can do a lot in a day.
+
+/datum/species/android/proc/handle_hud(mob/living/carbon/human/target)
+ // update it
+ if(energy_tracker)
+ energy_tracker.update_energy_hud(core_energy)
+ // initialize it
+ else if(target.hud_used)
+ var/datum/hud/hud_used = target.hud_used
+ energy_tracker = new(null, hud_used)
+ hud_used.infodisplay += energy_tracker
+
+ target.hud_used.show_hud(target.hud_used.hud_version)
+
+/datum/species/android/prepare_human_for_preview(mob/living/carbon/human/robot_for_preview)
+ robot_for_preview.dna.ear_type = CYBERNETIC
+ robot_for_preview.dna.features["ears"] = "TV Antennae"
+ robot_for_preview.dna.features["ears_color_1"] = "#333333"
+ robot_for_preview.dna.features["frame_list"] = list(
+ BODY_ZONE_HEAD = /obj/item/bodypart/head/robot/android/sgm,
+ BODY_ZONE_CHEST = /obj/item/bodypart/chest/robot/android/sgm,
+ BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/robot/android/sgm,
+ BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/robot/android/sgm,
+ BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/robot/android/sgm,
+ BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/robot/android/sgm)
+ regenerate_organs(robot_for_preview)
+ robot_for_preview.update_body(is_creating = TRUE)
+
+/datum/species/android/get_physical_attributes()
+ return "Androids are almost, but not quite, identical to fully augmented humans. \
+ Unlike those, though, they're completely immune to toxin damage, don't have blood or organs (besides their head), don't get hungry, and can reattach their limbs! \
+ That said, an EMP will devastate them and they cannot process any chemicals."
+
+/datum/species/android/get_species_description()
+ return "Androids are an entirely synthetic species."
+
+/datum/species/android/get_species_lore()
+ return list(
+ "Androids are a synthetic species created by Nanotrasen as an intermediary between humans and cyborgs."
+ )
+
+#undef ENERGY_START_AMT
+#undef ENERGY_DRAIN_AMT
diff --git a/modular_doppler/modular_species/species_types/android/android_hud.dm b/modular_doppler/modular_species/species_types/android/android_hud.dm
new file mode 100644
index 0000000000000..b5aca6d217f56
--- /dev/null
+++ b/modular_doppler/modular_species/species_types/android/android_hud.dm
@@ -0,0 +1,22 @@
+/// 1 tile down
+#define UI_ENERGY_DISPLAY "WEST:6,CENTER-1:0"
+
+///Maptext define for Hemophage HUDs
+#define FORMAT_ANDROID_HUD_TEXT(valuecolor, value) MAPTEXT("[round((value/1000000), 0.01)]MJ
")
+
+/atom/movable/screen/android
+ icon = 'modular_doppler/modular_species/species_types/android/icons/android_hud.dmi'
+
+/atom/movable/screen/android/energy
+ name = "Energy Tracker"
+ icon_state = "energy_display"
+ screen_loc = UI_ENERGY_DISPLAY
+
+/atom/movable/screen/android/energy/proc/update_energy_hud(core_energy)
+ maptext = FORMAT_ANDROID_HUD_TEXT(hud_text_color(), core_energy)
+
+/atom/movable/screen/android/energy/proc/hud_text_color(core_energy)
+ return core_energy < 2.5 MEGA JOULES ? "#ffffff" : "#FFAAAA"
+
+#undef UI_ENERGY_DISPLAY
+#undef FORMAT_ANDROID_HUD_TEXT
diff --git a/modular_doppler/modular_species/species_types/android/android_parts.dm b/modular_doppler/modular_species/species_types/android/android_parts.dm
new file mode 100644
index 0000000000000..c29a6d0f4daf2
--- /dev/null
+++ b/modular_doppler/modular_species/species_types/android/android_parts.dm
@@ -0,0 +1,908 @@
+// easy define for head_flags of android heads that dont feature eyes (aka monitor heads (aka IPCs))
+#define HEAD_MONITOR_FACE (HEAD_HAIR|HEAD_LIPS|HEAD_DEBRAIN)
+// easy define for the android bodypart .dmi
+#define ANDROID_BODYPARTS_DMI 'modular_doppler/modular_species/species_types/android/icons/android_parts.dmi'
+
+/obj/item/bodypart/proc/change_type(mob/living/user, obj/item/tool)
+ if(brute_dam || burn_dam)
+ user.balloon_alert(user, "limb damaged!")
+ return NONE
+
+ var/list/possible_appearances = list()
+ for(var/types in GLOB.frame_types)
+ if(types == "none")
+ continue
+ LAZYADDASSOC(possible_appearances, types, image(icon = ANDROID_BODYPARTS_DMI, icon_state = "[types]_[body_zone]"))
+ //pick
+ var/new_type = show_radial_menu(user, src, possible_appearances, require_near = TRUE, tooltips = TRUE, radius = 48)
+ if(!new_type)
+ return NONE
+ //weld
+ if(tool.use_tool(src, user, delay = 2 SECONDS, volume = 20))
+ var/type_to_spawn = text2path("[type]/[new_type]")
+ if(!type_to_spawn)
+ type_to_spawn = text2path("[parent_type]/[new_type]")
+ var/obj/item/bodypart/new_bodypart = new type_to_spawn(loc)
+ //inherit detail
+ for(var/obj/item/organ/to_transfer in contents)
+ to_transfer.bodypart_insert(new_bodypart)
+ new_bodypart.name = name
+ new_bodypart.desc = desc
+ qdel(src)
+ return ITEM_INTERACT_SUCCESS
+
+///
+// Overwrites
+///
+// head
+/obj/item/bodypart/head/robot/android
+ biological_state = (BIO_ROBOTIC|BIO_BLOODED)
+
+/obj/item/bodypart/head/robot/android/welder_act_secondary(mob/living/user, obj/item/tool)
+ . = ..()
+ return change_type(user, tool)
+
+/obj/item/bodypart/head/robot/android/examine(mob/user)
+ . = ..()
+ . += span_blue("Right-click with a welding-tool to alter the limb appearance.")
+
+// chest
+/obj/item/bodypart/chest/robot/android
+ biological_state = (BIO_ROBOTIC|BIO_BLOODED)
+
+/obj/item/bodypart/chest/robot/android/welder_act_secondary(mob/living/user, obj/item/tool)
+ . = ..()
+ return change_type(user, tool)
+
+/obj/item/bodypart/chest/robot/android/examine(mob/user)
+ . = ..()
+ . += span_blue("Right-click with a welding-tool to alter the limb appearance.")
+
+/obj/item/bodypart/chest/robot/android/check_limbs()
+ return
+
+// right arm
+/obj/item/bodypart/arm/right/robot/android
+ biological_state = (BIO_ROBOTIC|BIO_BLOODED)
+
+/obj/item/bodypart/arm/right/robot/android/welder_act_secondary(mob/living/user, obj/item/tool)
+ . = ..()
+ return change_type(user, tool)
+
+/obj/item/bodypart/arm/right/robot/android/examine(mob/user)
+ . = ..()
+ . += span_blue("Right-click with a welding-tool to alter the limb appearance.")
+
+// left arm
+/obj/item/bodypart/arm/left/robot/android
+ biological_state = (BIO_ROBOTIC|BIO_BLOODED)
+
+/obj/item/bodypart/arm/left/robot/android/welder_act_secondary(mob/living/user, obj/item/tool)
+ . = ..()
+ return change_type(user, tool)
+
+/obj/item/bodypart/arm/left/robot/android/examine(mob/user)
+ . = ..()
+ . += span_blue("Right-click with a welding-tool to alter the limb appearance.")
+
+// right leg
+/obj/item/bodypart/leg/right/robot/android
+ biological_state = (BIO_ROBOTIC|BIO_BLOODED)
+
+/obj/item/bodypart/leg/right/robot/android/welder_act_secondary(mob/living/user, obj/item/tool)
+ . = ..()
+ return change_type(user, tool)
+
+/obj/item/bodypart/leg/right/robot/android/examine(mob/user)
+ . = ..()
+ . += span_blue("Right-click with a welding-tool to alter the limb appearance.")
+
+// left leg
+/obj/item/bodypart/leg/left/robot/android
+ biological_state = (BIO_ROBOTIC|BIO_BLOODED)
+
+/obj/item/bodypart/leg/left/robot/android/welder_act_secondary(mob/living/user, obj/item/tool)
+ . = ..()
+ return change_type(user, tool)
+
+/obj/item/bodypart/leg/left/robot/android/examine(mob/user)
+ . = ..()
+ . += span_blue("Right-click with a welding-tool to alter the limb appearance.")
+
+///
+// Classic
+///
+/obj/item/bodypart/head/robot/android/classic
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "classic_head"
+ limb_id = "classic"
+
+/obj/item/bodypart/chest/robot/android/classic
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "classic_chest"
+ limb_id = "classic"
+
+/obj/item/bodypart/arm/right/robot/android/classic
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "classic_r_arm"
+ limb_id = "classic"
+
+/obj/item/bodypart/arm/left/robot/android/classic
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "classic_l_arm"
+ limb_id = "classic"
+
+/obj/item/bodypart/leg/right/robot/android/classic
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "classic_r_leg"
+ limb_id = "classic"
+
+/obj/item/bodypart/leg/left/robot/android/classic
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "classic_l_leg"
+ limb_id = "classic"
+
+///
+// Bare
+///
+/obj/item/bodypart/head/robot/android/bare
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "bare_head"
+ limb_id = "bare"
+ head_flags = HEAD_MONITOR_FACE
+
+/obj/item/bodypart/head/robot/android/bare/on_adding(mob/living/carbon/new_owner)
+ . = ..()
+ new_owner.AddComponent(/datum/component/monitor_head)
+
+/obj/item/bodypart/head/robot/android/bare/on_removal(mob/living/carbon/old_owner)
+ . = ..()
+ qdel(old_owner.GetComponent(/datum/component/monitor_head))
+
+/obj/item/bodypart/chest/robot/android/bare
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "bare_chest"
+ limb_id = "bare"
+
+/obj/item/bodypart/arm/right/robot/android/bare
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "bare_r_arm"
+ limb_id = "bare"
+
+/obj/item/bodypart/arm/left/robot/android/bare
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "bare_l_arm"
+ limb_id = "bare"
+
+/obj/item/bodypart/leg/right/robot/android/bare
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "bare_r_leg"
+ limb_id = "bare"
+
+/obj/item/bodypart/leg/left/robot/android/bare
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "bare_r_leg"
+ limb_id = "bare"
+
+///
+// Mariinsky
+///
+/obj/item/bodypart/head/robot/android/mariinsky
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "mariinsky_head"
+ limb_id = "mariinsky"
+
+/obj/item/bodypart/chest/robot/android/mariinsky
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "mariinsky_chest"
+ limb_id = "mariinsky"
+
+/obj/item/bodypart/arm/right/robot/android/mariinsky
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "mariinsky_r_arm"
+ limb_id = "mariinsky"
+
+/obj/item/bodypart/arm/left/robot/android/mariinsky
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "mariinsky_l_arm"
+ limb_id = "mariinsky"
+
+/obj/item/bodypart/leg/right/robot/android/mariinsky
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "mariinsky_r_leg"
+ limb_id = "mariinsky"
+
+/obj/item/bodypart/leg/left/robot/android/mariinsky
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "mariinsky_r_leg"
+ limb_id = "mariinsky"
+
+///
+// E3N
+///
+/obj/item/bodypart/head/robot/android/e_three_n
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "e_three_n_head"
+ limb_id = "e_three_n"
+ head_flags = (HEAD_HAIR|HEAD_DEBRAIN)
+
+/obj/item/bodypart/chest/robot/android/e_three_n
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "e_three_n_chest"
+ limb_id = "e_three_n"
+
+/obj/item/bodypart/arm/right/robot/android/e_three_n
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "e_three_n_r_arm"
+ limb_id = "e_three_n"
+
+/obj/item/bodypart/arm/left/robot/android/e_three_n
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "e_three_n_l_arm"
+ limb_id = "e_three_n"
+
+/obj/item/bodypart/leg/right/robot/android/e_three_n
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "e_three_n_r_leg"
+ limb_id = "e_three_n"
+
+/obj/item/bodypart/leg/left/robot/android/e_three_n
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "e_three_n_r_leg"
+ limb_id = "e_three_n"
+
+///
+// Morpheus
+///
+/obj/item/bodypart/head/robot/android/mc //morb
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "mc_head"
+ limb_id = "mc"
+ head_flags = HEAD_MONITOR_FACE
+
+/obj/item/bodypart/head/robot/android/mc/on_adding(mob/living/carbon/new_owner)
+ . = ..()
+ new_owner.AddComponent(/datum/component/monitor_head)
+
+/obj/item/bodypart/head/robot/android/mc/on_removal(mob/living/carbon/old_owner)
+ . = ..()
+ qdel(old_owner.GetComponent(/datum/component/monitor_head))
+
+/obj/item/bodypart/chest/robot/android/mc
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "mc_chest"
+ limb_id = "mc"
+
+/obj/item/bodypart/arm/right/robot/android/mc
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "mc_r_arm"
+ limb_id = "mc"
+
+/obj/item/bodypart/arm/left/robot/android/mc
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "mc_l_arm"
+ limb_id = "mc"
+
+/obj/item/bodypart/leg/right/robot/android/mc
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "mc_r_leg"
+ limb_id = "mc"
+
+/obj/item/bodypart/leg/left/robot/android/mc
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "mc_r_leg"
+ limb_id = "mc"
+
+///
+// Bishop Cyberkinetics
+///
+/obj/item/bodypart/head/robot/android/bs_one
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "bs_one_head"
+ limb_id = "bs_one"
+ head_flags = HEAD_MONITOR_FACE
+
+/obj/item/bodypart/head/robot/android/bs_one/on_adding(mob/living/carbon/new_owner)
+ . = ..()
+ new_owner.AddComponent(/datum/component/monitor_head)
+
+/obj/item/bodypart/head/robot/android/bs_one/on_removal(mob/living/carbon/old_owner)
+ . = ..()
+ qdel(old_owner.GetComponent(/datum/component/monitor_head))
+
+/obj/item/bodypart/chest/robot/android/bs_one
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "bs_one_chest"
+ limb_id = "bs_one"
+
+/obj/item/bodypart/arm/right/robot/android/bs_one
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "bs_one_r_arm"
+ limb_id = "bs_one"
+
+/obj/item/bodypart/arm/left/robot/android/bs_one
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "bs_one_l_arm"
+ limb_id = "bs_one"
+
+/obj/item/bodypart/leg/right/robot/android/bs_one
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "bs_one_r_leg"
+ limb_id = "bs_one"
+
+/obj/item/bodypart/leg/left/robot/android/bs_one
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "bs_one_r_leg"
+ limb_id = "bs_one"
+
+///
+// Bishop Cyberkinetics 2.0
+///
+/obj/item/bodypart/head/robot/android/bs_two
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "bs_two_head"
+ limb_id = "bs_two"
+ head_flags = HEAD_MONITOR_FACE
+
+/obj/item/bodypart/head/robot/android/bs_two/on_adding(mob/living/carbon/new_owner)
+ . = ..()
+ new_owner.AddComponent(/datum/component/monitor_head)
+
+/obj/item/bodypart/head/robot/android/bs_two/on_removal(mob/living/carbon/old_owner)
+ . = ..()
+ qdel(old_owner.GetComponent(/datum/component/monitor_head))
+
+/obj/item/bodypart/chest/robot/android/bs_two
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "bs_two_chest"
+ limb_id = "bs_two"
+
+/obj/item/bodypart/arm/right/robot/android/bs_two
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "bs_two_r_arm"
+ limb_id = "bs_two"
+
+/obj/item/bodypart/arm/left/robot/android/bs_two
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "bs_two_l_arm"
+ limb_id = "bs_two"
+
+/obj/item/bodypart/leg/right/robot/android/bs_two
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "bs_two_r_leg"
+ limb_id = "bs_two"
+
+/obj/item/bodypart/leg/left/robot/android/bs_two
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "bs_two_r_leg"
+ limb_id = "bs_two"
+
+///
+// Hephaestus Industries
+///
+/obj/item/bodypart/head/robot/android/hi_one
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "hi_one_head"
+ limb_id = "hi_one"
+ head_flags = HEAD_MONITOR_FACE
+
+/obj/item/bodypart/head/robot/android/hi_one/on_adding(mob/living/carbon/new_owner)
+ . = ..()
+ new_owner.AddComponent(/datum/component/monitor_head)
+
+/obj/item/bodypart/head/robot/android/hi_one/on_removal(mob/living/carbon/old_owner)
+ . = ..()
+ qdel(old_owner.GetComponent(/datum/component/monitor_head))
+
+/obj/item/bodypart/chest/robot/android/hi_one
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "hi_one_chest"
+ limb_id = "hi_one"
+
+/obj/item/bodypart/arm/right/robot/android/hi_one
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "hi_one_r_arm"
+ limb_id = "hi_one"
+
+/obj/item/bodypart/arm/left/robot/android/hi_one
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "hi_one_l_arm"
+ limb_id = "hi_one"
+
+/obj/item/bodypart/leg/right/robot/android/hi_one
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "hi_one_r_leg"
+ limb_id = "hi_one"
+
+/obj/item/bodypart/leg/left/robot/android/hi_one
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "hi_one_r_leg"
+ limb_id = "hi_one"
+
+///
+// Hephaestus Industries 2.0
+///
+/obj/item/bodypart/head/robot/android/hi_two
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "hi_two_head"
+ limb_id = "hi_two"
+ head_flags = HEAD_MONITOR_FACE
+
+/obj/item/bodypart/head/robot/android/hi_two/on_adding(mob/living/carbon/new_owner)
+ . = ..()
+ new_owner.AddComponent(/datum/component/monitor_head)
+
+/obj/item/bodypart/head/robot/android/hi_two/on_removal(mob/living/carbon/old_owner)
+ . = ..()
+ qdel(old_owner.GetComponent(/datum/component/monitor_head))
+
+/obj/item/bodypart/chest/robot/android/hi_two
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "hi_two_chest"
+ limb_id = "hi_two"
+
+/obj/item/bodypart/arm/right/robot/android/hi_two
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "hi_two_r_arm"
+ limb_id = "hi_two"
+
+/obj/item/bodypart/arm/left/robot/android/hi_two
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "hi_two_l_arm"
+ limb_id = "hi_two"
+
+/obj/item/bodypart/leg/right/robot/android/hi_two
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "hi_two_r_leg"
+ limb_id = "hi_two"
+
+/obj/item/bodypart/leg/left/robot/android/hi_two
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "hi_two_r_leg"
+ limb_id = "hi_two"
+
+///
+// Shellguard Munitions Standard Series 😎
+///
+/obj/item/bodypart/head/robot/android/sgm
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "sgm_head"
+ limb_id = "sgm"
+ head_flags = HEAD_MONITOR_FACE
+
+/obj/item/bodypart/head/robot/android/sgm/on_adding(mob/living/carbon/new_owner)
+ . = ..()
+ new_owner.AddComponent(/datum/component/monitor_head)
+
+/obj/item/bodypart/head/robot/android/sgm/on_removal(mob/living/carbon/old_owner)
+ . = ..()
+ qdel(old_owner.GetComponent(/datum/component/monitor_head))
+
+/obj/item/bodypart/chest/robot/android/sgm
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "sgm_chest"
+ limb_id = "sgm"
+
+/obj/item/bodypart/arm/right/robot/android/sgm
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "sgm_r_arm"
+ limb_id = "sgm"
+
+/obj/item/bodypart/arm/left/robot/android/sgm
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "sgm_l_arm"
+ limb_id = "sgm"
+
+/obj/item/bodypart/leg/right/robot/android/sgm
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "sgm_r_leg"
+ limb_id = "sgm"
+
+/obj/item/bodypart/leg/left/robot/android/sgm
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "sgm_r_leg"
+ limb_id = "sgm"
+
+///
+// Ward Takahashi Manufacturing
+///
+/obj/item/bodypart/head/robot/android/wtm
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "wtm_head"
+ limb_id = "wtm"
+ head_flags = HEAD_MONITOR_FACE
+
+/obj/item/bodypart/head/robot/android/wtm/on_adding(mob/living/carbon/new_owner)
+ . = ..()
+ new_owner.AddComponent(/datum/component/monitor_head)
+
+/obj/item/bodypart/head/robot/android/wtm/on_removal(mob/living/carbon/old_owner)
+ . = ..()
+ qdel(old_owner.GetComponent(/datum/component/monitor_head))
+
+/obj/item/bodypart/chest/robot/android/wtm
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "wtm_chest"
+ limb_id = "wtm"
+
+/obj/item/bodypart/arm/right/robot/android/wtm
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "wtm_r_arm"
+ limb_id = "wtm"
+
+/obj/item/bodypart/arm/left/robot/android/wtm
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "wtm_l_arm"
+ limb_id = "wtm"
+
+/obj/item/bodypart/leg/right/robot/android/wtm
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "wtm_r_leg"
+ limb_id = "wtm"
+
+/obj/item/bodypart/leg/left/robot/android/wtm
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "wtm_r_leg"
+ limb_id = "wtm"
+
+///
+// Xion Manufacturing Group
+///
+/obj/item/bodypart/head/robot/android/xmg_one
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "xmg_one_head"
+ limb_id = "xmg_one"
+ head_flags = HEAD_MONITOR_FACE
+
+/obj/item/bodypart/head/robot/android/xmg_one/on_adding(mob/living/carbon/new_owner)
+ . = ..()
+ new_owner.AddComponent(/datum/component/monitor_head)
+
+/obj/item/bodypart/head/robot/android/xmg_one/on_removal(mob/living/carbon/old_owner)
+ . = ..()
+ qdel(old_owner.GetComponent(/datum/component/monitor_head))
+
+/obj/item/bodypart/chest/robot/android/xmg_one
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "xmg_one_chest"
+ limb_id = "xmg_one"
+
+/obj/item/bodypart/arm/right/robot/android/xmg_one
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "xmg_one_r_arm"
+ limb_id = "xmg_one"
+
+/obj/item/bodypart/arm/left/robot/android/xmg_one
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "xmg_one_l_arm"
+ limb_id = "xmg_one"
+
+/obj/item/bodypart/leg/right/robot/android/xmg_one
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "xmg_one_r_leg"
+ limb_id = "xmg_one"
+
+/obj/item/bodypart/leg/left/robot/android/xmg_one
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "xmg_one_r_leg"
+ limb_id = "xmg_one"
+
+///
+// Xion Manufacturing Group 2.0
+///
+/obj/item/bodypart/head/robot/android/xmg_two
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "xmg_two_head"
+ limb_id = "xmg_two"
+ head_flags = HEAD_MONITOR_FACE
+
+/obj/item/bodypart/head/robot/android/xmg_two/on_adding(mob/living/carbon/new_owner)
+ . = ..()
+ new_owner.AddComponent(/datum/component/monitor_head)
+
+/obj/item/bodypart/head/robot/android/xmg_two/on_removal(mob/living/carbon/old_owner)
+ . = ..()
+ qdel(old_owner.GetComponent(/datum/component/monitor_head))
+
+/obj/item/bodypart/chest/robot/android/xmg_two
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "xmg_two_chest"
+ limb_id = "xmg_two"
+
+/obj/item/bodypart/arm/right/robot/android/xmg_two
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "xmg_two_r_arm"
+ limb_id = "xmg_two"
+
+/obj/item/bodypart/arm/left/robot/android/xmg_two
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "xmg_two_l_arm"
+ limb_id = "xmg_two"
+
+/obj/item/bodypart/leg/right/robot/android/xmg_two
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "xmg_two_r_leg"
+ limb_id = "xmg_two"
+
+/obj/item/bodypart/leg/left/robot/android/xmg_two
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "xmg_two_r_leg"
+ limb_id = "xmg_two"
+
+
+///
+// Zeng-Hu Pharmaceuticals
+///
+/obj/item/bodypart/head/robot/android/zhp
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "zhp_head"
+ limb_id = "zhp"
+ head_flags = HEAD_MONITOR_FACE
+
+/obj/item/bodypart/head/robot/android/zhp/on_adding(mob/living/carbon/new_owner)
+ . = ..()
+ new_owner.AddComponent(/datum/component/monitor_head)
+
+/obj/item/bodypart/head/robot/android/zhp/on_removal(mob/living/carbon/old_owner)
+ . = ..()
+ qdel(old_owner.GetComponent(/datum/component/monitor_head))
+
+/obj/item/bodypart/chest/robot/android/zhp
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "zhp_chest"
+ limb_id = "zhp"
+
+/obj/item/bodypart/arm/right/robot/android/zhp
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "zhp_r_arm"
+ limb_id = "zhp"
+
+/obj/item/bodypart/arm/left/robot/android/zhp
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "zhp_l_arm"
+ limb_id = "zhp"
+
+/obj/item/bodypart/leg/right/robot/android/zhp
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "zhp_r_leg"
+ limb_id = "zhp"
+
+/obj/item/bodypart/leg/left/robot/android/zhp
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_state = "zhp_r_leg"
+ limb_id = "zhp"
+
+///
+// Synthetic Lizard
+///
+/obj/item/bodypart/head/robot/android/synth_lizard
+ bodyshape = BODYSHAPE_HUMANOID | BODYSHAPE_SNOUTED
+ should_draw_greyscale = TRUE
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_greyscale = ANDROID_BODYPARTS_DMI
+ icon_state = "synth_lizard_head"
+ limb_id = "synth_lizard"
+ head_flags = HEAD_MONITOR_FACE
+
+/obj/item/bodypart/head/robot/android/synth_lizard/on_adding(mob/living/carbon/new_owner)
+ . = ..()
+ new_owner.AddComponent(/datum/component/monitor_head/lizard)
+
+/obj/item/bodypart/head/robot/android/synth_lizard/on_removal(mob/living/carbon/old_owner)
+ . = ..()
+ qdel(old_owner.GetComponent(/datum/component/monitor_head/lizard))
+
+/obj/item/bodypart/chest/robot/android/synth_lizard
+ is_dimorphic = TRUE
+ should_draw_greyscale = TRUE
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_greyscale = ANDROID_BODYPARTS_DMI
+ icon_state = "synth_lizard_chest_f"
+ limb_id = "synth_lizard"
+
+/obj/item/bodypart/arm/right/robot/android/synth_lizard
+ should_draw_greyscale = TRUE
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_greyscale = ANDROID_BODYPARTS_DMI
+ icon_state = "synth_lizard_r_arm"
+ limb_id = "synth_lizard"
+
+/obj/item/bodypart/arm/left/robot/android/synth_lizard
+ should_draw_greyscale = TRUE
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_greyscale = ANDROID_BODYPARTS_DMI
+ icon_state = "synth_lizard_l_arm"
+ limb_id = "synth_lizard"
+
+/obj/item/bodypart/leg/right/robot/android/synth_lizard
+ bodyshape = BODYSHAPE_HUMANOID | BODYSHAPE_DIGITIGRADE
+ should_draw_greyscale = TRUE
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_greyscale = ANDROID_BODYPARTS_DMI
+ icon_state = "synth_lizard_r_leg"
+ limb_id = "synth_lizard"
+
+/obj/item/bodypart/leg/left/robot/android/synth_lizard
+ bodyshape = BODYSHAPE_HUMANOID | BODYSHAPE_DIGITIGRADE
+ should_draw_greyscale = TRUE
+ icon_static = ANDROID_BODYPARTS_DMI
+ icon = ANDROID_BODYPARTS_DMI
+ icon_greyscale = ANDROID_BODYPARTS_DMI
+ icon_state = "synth_lizard_r_leg"
+ limb_id = "synth_lizard"
+
+#undef HEAD_MONITOR_FACE
+#undef ANDROID_BODYPARTS_DMI
+
+/datum/design/android_head
+ name = "Android Head"
+ id = "android_head"
+ build_type = MECHFAB
+ build_path = /obj/item/bodypart/head/robot/android
+ materials = list(
+ /datum/material/iron=SHEET_MATERIAL_AMOUNT*10,
+ /datum/material/silver=SHEET_MATERIAL_AMOUNT*3,
+ )
+ construction_time = 20 SECONDS
+ category = list(
+ RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ADVANCED_LIMBS
+ )
+
+/datum/design/android_chest
+ name = "Android Chest"
+ id = "android_chest"
+ build_type = MECHFAB
+ build_path = /obj/item/bodypart/chest/robot/android
+ materials = list(
+ /datum/material/iron=SHEET_MATERIAL_AMOUNT*10,
+ /datum/material/silver=SHEET_MATERIAL_AMOUNT*3,
+ )
+ construction_time = 20 SECONDS
+ category = list(
+ RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ADVANCED_LIMBS
+ )
+
+/datum/design/android_l_arm
+ name = "Android Left Arm"
+ id = "android_l_arm"
+ build_type = MECHFAB
+ build_path = /obj/item/bodypart/arm/left/robot/android
+ materials = list(
+ /datum/material/iron=SHEET_MATERIAL_AMOUNT*10,
+ /datum/material/silver=SHEET_MATERIAL_AMOUNT*3,
+ )
+ construction_time = 20 SECONDS
+ category = list(
+ RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ADVANCED_LIMBS
+ )
+
+/datum/design/android_r_arm
+ name = "Android Right Arm"
+ id = "android_r_arm"
+ build_type = MECHFAB
+ build_path = /obj/item/bodypart/arm/right/robot/android
+ materials = list(
+ /datum/material/iron=SHEET_MATERIAL_AMOUNT*10,
+ /datum/material/silver=SHEET_MATERIAL_AMOUNT*3,
+ )
+ construction_time = 20 SECONDS
+ category = list(
+ RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ADVANCED_LIMBS
+ )
+
+/datum/design/android_l_leg
+ name = "Android Left Leg"
+ id = "android_l_leg"
+ build_type = MECHFAB
+ build_path = /obj/item/bodypart/leg/left/robot/android
+ materials = list(
+ /datum/material/iron=SHEET_MATERIAL_AMOUNT*10,
+ /datum/material/silver=SHEET_MATERIAL_AMOUNT*3,
+ )
+ construction_time = 20 SECONDS
+ category = list(
+ RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ADVANCED_LIMBS
+ )
+
+/datum/design/android_r_leg
+ name = "Android Right Leg"
+ id = "android_r_leg"
+ build_type = MECHFAB
+ build_path = /obj/item/bodypart/leg/right/robot/android
+ materials = list(
+ /datum/material/iron=SHEET_MATERIAL_AMOUNT*10,
+ /datum/material/silver=SHEET_MATERIAL_AMOUNT*3,
+ )
+ construction_time = 20 SECONDS
+ category = list(
+ RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ADVANCED_LIMBS
+ )
diff --git a/modular_doppler/modular_species/species_types/android/icons/android_hud.dmi b/modular_doppler/modular_species/species_types/android/icons/android_hud.dmi
new file mode 100644
index 0000000000000..b524cf6328041
Binary files /dev/null and b/modular_doppler/modular_species/species_types/android/icons/android_hud.dmi differ
diff --git a/modular_doppler/modular_species/species_types/android/icons/android_parts.dmi b/modular_doppler/modular_species/species_types/android/icons/android_parts.dmi
new file mode 100644
index 0000000000000..ec042e3409120
Binary files /dev/null and b/modular_doppler/modular_species/species_types/android/icons/android_parts.dmi differ
diff --git a/modular_doppler/modular_species/species_types/android/monitor_component.dm b/modular_doppler/modular_species/species_types/android/monitor_component.dm
new file mode 100644
index 0000000000000..2bdc43cd0cd32
--- /dev/null
+++ b/modular_doppler/modular_species/species_types/android/monitor_component.dm
@@ -0,0 +1,200 @@
+#define MONITOR_HEAD (1<<0)
+#define MONITOR_HEAD_LIZARD (1<<1)
+
+GLOBAL_LIST_INIT(monitor_displays, list(
+ "Disabled" = "none",
+ "Blank" = "blank",
+ "Blank (White)" = "blankwhite",
+ "Blue" = "blue",
+ "Blue Screen of Death" = "bsod",
+ "Breakout" = "breakout",
+ "Cigarette" = "smoking",
+ "Console" = "console",
+ "Cubic Wave" = "squarewave",
+ "Cyclops" = "eye",
+ "Database" = "database",
+ "ECG Wave" = "ecgwave",
+ "Eight" = "eight",
+ "Eyes" = "eyes",
+ "Goggles" = "goggles",
+ "Gol Glider" = "golglider",
+ "Green" = "green",
+ "Heart" = "heart",
+ "Heartrate Monitor" = "heartrate",
+ "Luminous Eyes" = "lumi_eyes",
+ "Mono Eye" = "mono_eye",
+ "Music" = "music",
+ "Nature" = "nature",
+ "Pink" = "pink",
+ "Purple" = "purple",
+ "Rainbow" = "rainbow",
+ "Red Text" = "redtext",
+ "Red" = "red",
+ "RGB" = "rgb",
+ "Scroll" = "scroll",
+ "Shower" = "shower",
+ "Sine Wave" = "sinewave",
+ "Smiley" = "yellow",
+ "Stars" = "stars",
+ "Sunburst" = "sunburst",
+ "Test Screen" = "test",
+ "Text Drop" = "textdrop",
+ "TV Static" = "static",
+ "TV Static (Color)" = "static3",
+ "TV Static (Slow)" = "static2",
+ "Waiting..." = "waiting",
+ ))
+
+GLOBAL_LIST_INIT(monitor_lizard_displays, list(
+ "Disabled" = "none",
+ "Eyes" = "liz_eyes",
+ "Question" = "liz_question",
+ "Exclaim" = "liz_exclaim",
+ ))
+
+// the overlay
+/datum/bodypart_overlay/simple/monitor_head
+ icon = 'modular_doppler/modular_customization/accessories/icons/cybernetic/synth_screens.dmi'
+ icon_state = "none"
+ layers = EXTERNAL_ADJACENT
+
+// the component
+/datum/component/monitor_head
+ dupe_mode = COMPONENT_DUPE_UNIQUE
+ var/datum/action/innate/monitor_head/display_action
+
+/datum/component/monitor_head/lizard
+
+/datum/component/monitor_head/Initialize(...)
+ . = ..()
+ if(!ishuman(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ if(istype(src, /datum/component/monitor_head/lizard))
+ display_action = new /datum/action/innate/monitor_head/lizard
+ else
+ display_action = new
+
+ display_action.Grant(parent)
+
+/datum/component/monitor_head/Destroy(force)
+ if(display_action)
+ display_action.Remove(parent)
+ return ..()
+
+// the action
+/datum/action/innate/monitor_head
+ name = "Change Display"
+ check_flags = AB_CHECK_CONSCIOUS
+ button_icon = 'icons/mob/actions/actions_silicon.dmi'
+ button_icon_state = "drone_vision"
+ background_icon_state = "bg_default"
+ /// the var that should be changed when a different screen list should be used
+ var/head_type = MONITOR_HEAD
+ /// the overlay we use
+ var/datum/bodypart_overlay/simple/monitor_head/display_overlay
+
+/datum/action/innate/monitor_head/Grant(mob/grant_to)
+ . = ..()
+ RegisterSignal(grant_to, COMSIG_MOB_EMOTE, PROC_REF(check_emote))
+
+/datum/action/innate/monitor_head/Remove(mob/remove_from)
+ . = ..()
+ UnregisterSignal(remove_from, COMSIG_MOB_EMOTE)
+
+/datum/action/innate/monitor_head/Activate()
+ var/mob/living/carbon/human/wearer = owner
+ var/new_display = tgui_input_list(usr, "Choose your character's screen:", "Monitor Display", head_type & MONITOR_HEAD ? GLOB.monitor_displays : GLOB.monitor_lizard_displays)
+ if(!new_display)
+ return
+
+ if(!display_overlay)
+ create_screen(wearer)
+
+ change_screen(wearer, "[head_type & MONITOR_HEAD ? GLOB.monitor_displays[new_display] : GLOB.monitor_lizard_displays[new_display]]")
+
+/datum/action/innate/monitor_head/proc/check_emote(mob/living/carbon/wearer, datum/emote/emote)
+ SIGNAL_HANDLER
+ /// a list of the 'key' variable of emotes that have a screen update effect
+ var/static/list/screen_emotes = list(
+ "tunesing",
+ "exclaim",
+ "question",
+ )
+ // early return
+ if(!(emote.key in screen_emotes))
+ return
+
+ if(!display_overlay)
+ create_screen(wearer)
+
+ var/old_screen = display_overlay.icon_state
+
+ if(head_type & MONITOR_HEAD)
+ switch(emote.key)
+ if("tunesing")
+ change_screen(wearer, "music")
+
+ if(head_type & MONITOR_HEAD_LIZARD)
+ switch(emote.key)
+ if("exclaim")
+ change_screen(wearer, "liz_exclaim")
+ if("question")
+ change_screen(wearer, "liz_question")
+ // this timer is 5 seconds just like the emote overlays, so they are synchronized
+ addtimer(CALLBACK(src, PROC_REF(change_screen), wearer, old_screen), 5 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE)
+
+/datum/action/innate/monitor_head/update_status_on_signal(mob/living/carbon/wearer, new_stat, old_stat)
+ . = ..()
+
+ if(!display_overlay)
+ create_screen(wearer)
+
+ if(head_type & MONITOR_HEAD)
+ switch(new_stat)
+ if(SOFT_CRIT)
+ change_screen(wearer, "bsod")
+ if(HARD_CRIT)
+ change_screen(wearer, "static3")
+ if(UNCONSCIOUS)
+ change_screen(wearer, "none")
+ if(DEAD)
+ change_screen(wearer, "none")
+
+ if(head_type & MONITOR_HEAD_LIZARD)
+ switch(new_stat)
+ if(UNCONSCIOUS)
+ change_screen(wearer, "none")
+ if(DEAD)
+ change_screen(wearer, "none")
+
+/datum/action/innate/monitor_head/proc/create_screen(mob/living/carbon/wearer)
+ var/obj/item/bodypart/head/monitor_head = wearer.get_bodypart(BODY_ZONE_HEAD)
+ display_overlay = new /datum/bodypart_overlay/simple/monitor_head
+
+ if(head_type & MONITOR_HEAD_LIZARD)
+ var/mob/living/carbon/human/human_wearer = wearer
+ display_overlay.draw_color = human_wearer.eye_color_left
+
+ monitor_head.add_bodypart_overlay(display_overlay)
+
+/datum/action/innate/monitor_head/proc/change_screen(mob/living/carbon/wearer, screen)
+ playsound(wearer, 'modular_doppler/modular_sounds/sound/mobs/humanoids/android/monitor_switch.ogg', 100, TRUE)
+ display_overlay.icon_state = screen
+
+ wearer.update_body_parts()
+
+/datum/action/innate/monitor_head/Remove(mob/remove_from)
+ if(remove_from && display_overlay)
+ var/mob/living/carbon/human/wearer = remove_from
+ var/obj/item/bodypart/head/monitor_head = wearer.get_bodypart(BODY_ZONE_HEAD)
+ monitor_head.remove_bodypart_overlay(display_overlay)
+ wearer.update_body_parts()
+ return ..()
+
+/datum/action/innate/monitor_head/lizard
+ head_type = MONITOR_HEAD_LIZARD
+
+
+#undef MONITOR_HEAD
+#undef MONITOR_HEAD_LIZARD
diff --git a/modular_doppler/modular_species/species_types/android/power_cord.dm b/modular_doppler/modular_species/species_types/android/power_cord.dm
new file mode 100644
index 0000000000000..a92579b68d533
--- /dev/null
+++ b/modular_doppler/modular_species/species_types/android/power_cord.dm
@@ -0,0 +1,139 @@
+// Charge level defines
+#define POWER_CORD_CHARGE_MAX 10 MEGA JOULES
+#define POWER_CORD_CHARGE_RATE (STANDARD_CELL_RATE * 1.5)
+#define POWER_CORD_CHARGE_DELAY 0.55 SECONDS
+#define POWER_CORD_APC_MINIMUM_PERCENT 5
+
+/datum/action/innate/power_cord
+ name = "Power Cord"
+ check_flags = AB_CHECK_INCAPACITATED|AB_CHECK_HANDS_BLOCKED|AB_CHECK_CONSCIOUS
+ button_icon_state = "toolkit_generic"
+ button_icon = 'icons/obj/medical/organs/organs.dmi'
+ background_icon_state = "bg_default"
+ // What will be given in-hand
+ var/obj/item/hand_item/power_cord/power_cord
+
+/datum/action/innate/power_cord/Activate()
+ for(var/obj/item/hand_item/item in owner.held_items)
+ if(item)
+ owner.balloon_alert(owner, "hand occupied!")
+ return
+ power_cord = new
+ owner.put_in_active_hand(power_cord)
+ playsound(owner, 'sound/vehicles/mecha/mechmove03.ogg', 20, TRUE)
+
+/obj/item/hand_item/power_cord
+ name = "power cord"
+ desc = "An internal power cord. Useful if you run on electricity. Not so much otherwise."
+ icon = 'icons/obj/stack_objects.dmi'
+ icon_state = "wire"
+ /// What can be drained
+ var/static/list/cord_whitelist = typecacheof(list(
+ /obj/item/stock_parts/power_store,
+ /obj/machinery/power/apc,
+ ))
+
+// Attempt to charge from an object by using them on the power cord.
+/obj/item/hand_item/power_cord/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(!can_power_draw(tool, user))
+ return NONE
+ try_power_draw(tool, user)
+ return ITEM_INTERACT_SUCCESS
+
+// Attempt to charge from an object by using the power cord on them.
+/obj/item/hand_item/power_cord/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
+ if(!can_power_draw(interacting_with, user))
+ return NONE
+ try_power_draw(interacting_with, user)
+ return ITEM_INTERACT_SUCCESS
+
+/// Returns TRUE or FALSE depending on if the target object can be used as a power source.
+/obj/item/hand_item/power_cord/proc/can_power_draw(obj/target, mob/user)
+ return ishuman(user) && is_type_in_typecache(target, cord_whitelist)
+
+/// Attempts to start using an object as a power source.
+/obj/item/hand_item/power_cord/proc/try_power_draw(obj/target, mob/living/carbon/human/user)
+ user.changeNext_move(CLICK_CD_MELEE)
+
+ var/datum/species/android/energy_holder = user.dna.species
+ if(energy_holder.core_energy >= POWER_CORD_CHARGE_MAX)
+ user.balloon_alert(user, "fully charged!")
+ return
+
+ user.visible_message(span_notice("[user] inserts a power connector into [target]."), span_notice("You begin to draw power from [target]."))
+ do_power_draw(target, user)
+
+ if(QDELETED(target))
+ return
+
+ if(HAS_TRAIT(user, TRAIT_CHARGING))
+ REMOVE_TRAIT(user, TRAIT_CHARGING, SPECIES_TRAIT)
+ user.visible_message(span_notice("[user] unplugs from [target]."), span_notice("You unplug from [target]."))
+
+/**
+ * Runs a loop to charge an android from a cell or APC.
+ * Displays chat messages to the user and nearby observers.
+ *
+ * Stops when:
+ * - The user's is full.
+ * - The cell has less than the minimum charge.
+ * - The user moves, or anything else that can happen to interrupt a do_after.
+ *
+ * Arguments:
+ * * target - The power cell or APC to drain.
+ * * user - The human mob draining the power cell.
+ */
+/obj/item/hand_item/power_cord/proc/do_power_draw(obj/target, mob/living/carbon/human/user)
+ // Draw power from an APC if one was given.
+ var/obj/machinery/power/apc/target_apc
+ if(istype(target, /obj/machinery/power/apc))
+ target_apc = target
+
+ var/obj/item/stock_parts/power_store/target_cell = target_apc ? target_apc.cell : target
+ var/minimum_cell_charge = target_apc ? POWER_CORD_APC_MINIMUM_PERCENT : 0
+
+ if(!target_cell || target_cell.percent() < minimum_cell_charge)
+ user.balloon_alert(user, "APC charge low!")
+ return
+ var/energy_needed
+ while(TRUE)
+ ADD_TRAIT(user, TRAIT_CHARGING, SPECIES_TRAIT)
+ // Check if the charge level of the cell is below the minimum.
+ // Prevents from overloading the cell.
+ if(target_cell.percent() < minimum_cell_charge)
+ user.balloon_alert(user, "APC charge low!")
+ break
+
+ // Attempt to drain charge from the cell.
+ if(!do_after(user, POWER_CORD_CHARGE_DELAY, target)) // slurp slurp slurp slurp
+ break
+
+ // Check if the user is nearly fully charged.
+ // Ensures minimum draw is always lower than this margin.
+ var/datum/species/android/energy_holder = user.dna.species
+ energy_needed = POWER_CORD_CHARGE_MAX - energy_holder.core_energy
+
+ // Calculate how much to draw from the cell this cycle.
+ var/current_draw = min(energy_needed, POWER_CORD_CHARGE_RATE * POWER_CORD_CHARGE_DELAY)
+
+ var/energy_delivered = target_cell.use(current_draw, force = TRUE)
+ target_cell.update_appearance()
+ if(!energy_delivered)
+ // The cell could be sabotaged, which causes it to explode and qdelete.
+ if(QDELETED(target_cell))
+ return
+ user.balloon_alert(user, "[target_apc ? "APC" : "Cell"] empty!")
+ break
+
+ energy_holder.core_energy += energy_delivered
+
+ playsound(user, 'modular_doppler/modular_sounds/sound/mobs/humanoids/android/drain.wav', 25, FALSE)
+ if(prob(8))
+ do_sparks(3, FALSE, target_cell.loc)
+ if(energy_holder.core_energy >= POWER_CORD_CHARGE_MAX)
+ user.balloon_alert(user, "fully charged")
+ break
+
+#undef POWER_CORD_CHARGE_MAX
+#undef POWER_CORD_CHARGE_RATE
+#undef POWER_CORD_APC_MINIMUM_PERCENT
diff --git a/modular_doppler/modular_species/species_types/flypeople/flypeople.dm b/modular_doppler/modular_species/species_types/flypeople/flypeople.dm
new file mode 100644
index 0000000000000..ec837892e94e8
--- /dev/null
+++ b/modular_doppler/modular_species/species_types/flypeople/flypeople.dm
@@ -0,0 +1,3 @@
+/datum/species/fly
+ exotic_blood = /datum/reagent/bug_blood
+ exotic_bloodtype = "I*"
diff --git a/modular_doppler/modular_species/species_types/genemod/genemod.dm b/modular_doppler/modular_species/species_types/genemod/genemod.dm
index 9cfb03bb94139..d8ddf2b6e9e0e 100644
--- a/modular_doppler/modular_species/species_types/genemod/genemod.dm
+++ b/modular_doppler/modular_species/species_types/genemod/genemod.dm
@@ -10,12 +10,13 @@
TRAIT_ANIMALISTIC,
TRAIT_MUTANT_COLORS,
)
+ body_markings = list(/datum/bodypart_overlay/simple/body_marking/lizard = "None")
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_PRIDE | MIRROR_MAGIC | RACE_SWAP | ERT_SPAWN | SLIME_EXTRACT
digitigrade_customization = DIGITIGRADE_OPTIONAL
digi_leg_overrides = list(
- BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/digitigrade/anthromorph,
- BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/digitigrade/anthromorph,
+ BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/digitigrade/genemod,
+ BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/digitigrade/genemod,
)
/datum/outfit/genemod_preview
diff --git a/modular_doppler/modular_species/species_types/genemod/genemod_bodyparts.dm b/modular_doppler/modular_species/species_types/genemod/genemod_bodyparts.dm
new file mode 100644
index 0000000000000..8ec917271d0f7
--- /dev/null
+++ b/modular_doppler/modular_species/species_types/genemod/genemod_bodyparts.dm
@@ -0,0 +1,15 @@
+/obj/item/bodypart/leg/left/digitigrade/genemod
+ icon_greyscale = 'modular_doppler/modular_species/species_types/genemod/icons/bodyparts.dmi'
+
+/obj/item/bodypart/leg/left/digitigrade/genemod/update_limb(dropping_limb = FALSE, is_creating = FALSE)
+ . = ..()
+ if(limb_id == SPECIES_LIZARD)
+ limb_id = SPECIES_HUMAN
+
+/obj/item/bodypart/leg/right/digitigrade/genemod
+ icon_greyscale = 'modular_doppler/modular_species/species_types/genemod/icons/bodyparts.dmi'
+
+/obj/item/bodypart/leg/right/digitigrade/genemod/update_limb(dropping_limb = FALSE, is_creating = FALSE)
+ . = ..()
+ if(limb_id == SPECIES_LIZARD)
+ limb_id = SPECIES_HUMAN
diff --git a/modular_doppler/modular_species/species_types/genemod/icons/bodyparts.dmi b/modular_doppler/modular_species/species_types/genemod/icons/bodyparts.dmi
new file mode 100644
index 0000000000000..1fd24c1afcc3c
Binary files /dev/null and b/modular_doppler/modular_species/species_types/genemod/icons/bodyparts.dmi differ
diff --git a/modular_doppler/modular_species/species_types/insectoid/insectoid.dm b/modular_doppler/modular_species/species_types/insectoid/insectoid.dm
index a7b36b3d2b590..03407ac158c0b 100644
--- a/modular_doppler/modular_species/species_types/insectoid/insectoid.dm
+++ b/modular_doppler/modular_species/species_types/insectoid/insectoid.dm
@@ -13,7 +13,7 @@
inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID
payday_modifier = 1.0
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_PRIDE | MIRROR_MAGIC | RACE_SWAP | ERT_SPAWN | SLIME_EXTRACT
- exotic_blood = /datum/reagent/blood/green
+ exotic_blood = /datum/reagent/bug_blood
exotic_bloodtype = "I*"
digitigrade_customization = DIGITIGRADE_OPTIONAL
diff --git a/modular_doppler/modular_species/species_types/ramatae/ramatan.dm b/modular_doppler/modular_species/species_types/ramatae/ramatan.dm
index 0a909d4caaa3d..9f37aa915dd29 100644
--- a/modular_doppler/modular_species/species_types/ramatae/ramatan.dm
+++ b/modular_doppler/modular_species/species_types/ramatae/ramatan.dm
@@ -13,6 +13,7 @@
/mob/living/carbon/human/species/ramatan
race = /datum/species/ramatan
+
/datum/species/ramatan
// The Ramatae of Aadia III - The Origin
name = "\improper Ramatan"
diff --git a/modular_doppler/modular_species/species_types/snails/modular_snail.dm b/modular_doppler/modular_species/species_types/snails/modular_snail.dm
index e18aa0f758227..31b7048f25a92 100644
--- a/modular_doppler/modular_species/species_types/snails/modular_snail.dm
+++ b/modular_doppler/modular_species/species_types/snails/modular_snail.dm
@@ -1,9 +1,10 @@
#define SHELL_TRANSPARENCY_ALPHA 90
/datum/species/snail
+ preview_outfit = /datum/outfit/snail_preview
mutantliver = /obj/item/organ/internal/liver/snail //This is just a better liver to deal with toxins, it's a thematic thing.
mutantheart = /obj/item/organ/internal/heart/snail //This gives them the shell buff where they take less damage from behind, and their heart's more durable.
- exotic_blood = /datum/reagent/blood/green
+ exotic_blood = /datum/reagent/bug_blood
exotic_bloodtype = "I*"
digitigrade_customization = DIGITIGRADE_OPTIONAL
@@ -12,6 +13,11 @@
BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/digitigrade/insectoid,
)
+/datum/outfit/snail_preview
+ name = "Snail (Species Preview)"
+ uniform = /obj/item/clothing/under/rank/medical/chemist/pharmacologist/skirt
+ mask = /obj/item/clothing/mask/surgical
+
/datum/species/snail/on_species_gain(mob/living/carbon/new_snailperson, datum/species/old_species, pref_load)
. = ..()
new_snailperson.update_icons()
@@ -141,7 +147,12 @@
wearer.update_worn_back()
/datum/species/snail/prepare_human_for_preview(mob/living/carbon/human/snail)
- snail.dna.features["mcolor"] = "#adaba7"
+ snail.dna.features["mcolor"] = "#797289"
+ snail.hairstyle = "Phoenix Half-Shaven"
+ snail.hair_color = "#4C3C7E"
+ snail.eye_color_left = "#615188"
+ snail.eye_color_right = "#615188"
+ regenerate_organs(snail, src, visual_only = TRUE)
snail.update_body(TRUE)
/datum/species/snail/create_pref_unique_perks()
diff --git a/modular_doppler/modular_vending/code/doppler_vendors/de_forest/vendor.dm b/modular_doppler/modular_vending/code/doppler_vendors/de_forest/vendor.dm
index 265bf226425eb..271e53f5bb968 100644
--- a/modular_doppler/modular_vending/code/doppler_vendors/de_forest/vendor.dm
+++ b/modular_doppler/modular_vending/code/doppler_vendors/de_forest/vendor.dm
@@ -22,8 +22,11 @@
/obj/item/stack/medical/mesh = 4,
/obj/item/stack/medical/mesh/bloody = 2,
/obj/item/stack/medical/bandage = 4,
+ /obj/item/reagent_containers/pill/robotic_patch/synth_repair = 4,
/obj/item/stack/medical/wound_recovery = 2,
/obj/item/stack/medical/wound_recovery/rapid_coagulant = 2,
+ /obj/item/stack/medical/wound_recovery/robofoam = 2,
+ /obj/item/stack/medical/wound_recovery/robofoam_super = 1,
/obj/item/storage/pill_bottle/painkiller = 4,
/obj/item/storage/medkit/civil_defense/stocked = 2,
),
@@ -42,6 +45,8 @@
/obj/item/reagent_containers/hypospray/medipen/deforest/lepoturi = 3,
/obj/item/reagent_containers/hypospray/medipen/deforest/psifinil = 3,
/obj/item/reagent_containers/hypospray/medipen/deforest/halobinin = 3,
+ /obj/item/reagent_containers/hypospray/medipen/deforest/robot_system_cleaner = 3,
+ /obj/item/reagent_containers/hypospray/medipen/deforest/robot_liquid_solder = 3,
),
),
)
diff --git a/modular_doppler/modular_weapons/code/jousting.dm b/modular_doppler/modular_weapons/code/jousting.dm
new file mode 100644
index 0000000000000..f4e6beffe190d
--- /dev/null
+++ b/modular_doppler/modular_weapons/code/jousting.dm
@@ -0,0 +1,15 @@
+/datum/component/jousting/Initialize(damage_boost_per_tile, knockdown_chance_per_tile, knockdown_time, max_tile_charge, min_tile_charge, datum/callback/successful_joust_callback)
+ . = ..()
+
+ RegisterSignal(parent, COMSIG_PRE_BATON_FINALIZE_ATTACK, PROC_REF(on_successful_baton_attack))
+
+/datum/component/jousting/proc/on_successful_baton_attack(datum/source, mob/living/target, mob/user)
+ SIGNAL_HANDLER
+
+ if (!istype(parent, /obj/item/melee/baton/security))
+ return
+
+ var/obj/item/melee/baton/security/baton = parent
+ var/usable_charge = on_successful_attack(source, target, user)
+ if(usable_charge)
+ baton.on_successful_joust(target, user, usable_charge)
diff --git a/modular_doppler/modular_weapons/code/melee.dm b/modular_doppler/modular_weapons/code/melee.dm
new file mode 100644
index 0000000000000..5557424c3a022
--- /dev/null
+++ b/modular_doppler/modular_weapons/code/melee.dm
@@ -0,0 +1,16 @@
+/obj/item/melee/baton
+ /// For use with jousting. For each usable jousting tile, increase the stamina damage of the jousting hit by this much.
+ var/stamina_damage_per_jousting_tile = 2
+
+/obj/item/melee/baton/Initialize(mapload)
+ . = ..()
+
+ add_jousting_component()
+
+/// Component adder proc for custom behavior, without needing to add more vars.
+/obj/item/melee/baton/proc/add_jousting_component()
+ AddComponent(/datum/component/jousting, damage_boost_per_tile = 0, knockdown_chance_per_tile = 6)
+
+/// For jousting. Called when a joust is considered successfully done.
+/obj/item/melee/baton/proc/on_successful_joust(mob/living/target, mob/user, usable_charge)
+ target.apply_damage(stamina_damage_per_jousting_tile * usable_charge, STAMINA)
diff --git a/modular_doppler/overwrites/huds/hud.dmi b/modular_doppler/overwrites/huds/hud.dmi
index 4084f10c8acc8..c0fa61ebe4112 100644
Binary files a/modular_doppler/overwrites/huds/hud.dmi and b/modular_doppler/overwrites/huds/hud.dmi differ
diff --git a/strings/wounds/metal_scar_desc.json b/strings/wounds/metal_scar_desc.json
new file mode 100644
index 0000000000000..4cbe5eb058da4
--- /dev/null
+++ b/strings/wounds/metal_scar_desc.json
@@ -0,0 +1,67 @@
+{
+ "generic": ["general disfigurement"],
+
+ "dislocate": [
+ "some slight crookedness"
+ ],
+
+ "bluntsevere": [
+ "an area of slightly crumpled metal",
+ "some misaligned plates"
+ ],
+
+ "bluntcritical": [
+ "a malformed superstructure",
+ "some heavily beaten plating"
+ ],
+
+ "slashmoderate": [
+ "a very thin line of solder",
+ "a few welded cuts"
+ ],
+
+ "slashsevere": [
+ "a pair of soldered cracks",
+ "an line of freshly replaced metal",
+ "some deep welds on the metal"
+ ],
+
+ "slashcritical": [
+ "a matrix of desperately soldered cracks",
+ "some gruesome welding lines",
+ "a series of deep and wide welded gashes"
+ ],
+
+ "piercemoderate": [
+ "a dot of hardened solder",
+ "a dot of fresh metal"
+ ],
+
+ "piercesevere": [
+ "a large wad of hardened solder",
+ "a few cracks originating from a small hole"
+ ],
+
+ "piercecritical": [
+ "a large splot of hardened solder",
+ "a spiderweb of cracks crawling from a sealed hole"
+ ],
+
+ "burnsevere": [
+ "some vaguely discolored metal",
+ "some burnt ridges",
+ "a few heat cracks"
+ ],
+
+ "burncritical": [
+ "streaks of polychromatic metal",
+ "gruesomely heat-warped plating",
+ "huge melting marks"
+ ],
+
+ "dismember": [
+ "has fresh metal around various joints",
+ "has some solder marks securing various joints",
+ "has clear re-seating marks"
+ ]
+}
diff --git a/tgstation.dme b/tgstation.dme
index db6f599134955..8e2b4184dede4 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -406,7 +406,6 @@
#include "code\__DEFINES\~doppler_defines\colony_fabricator_misc.dm"
#include "code\__DEFINES\~doppler_defines\colors.dm"
#include "code\__DEFINES\~doppler_defines\construction.dm"
-#include "code\__DEFINES\~doppler_defines\declarations.dm"
#include "code\__DEFINES\~doppler_defines\DNA.dm"
#include "code\__DEFINES\~doppler_defines\enterprise_resource_planning.dm"
#include "code\__DEFINES\~doppler_defines\examinemore.dm"
@@ -420,10 +419,12 @@
#include "code\__DEFINES\~doppler_defines\manufacturer_strings.dm"
#include "code\__DEFINES\~doppler_defines\mobfactions.dm"
#include "code\__DEFINES\~doppler_defines\mobs.dm"
+#include "code\__DEFINES\~doppler_defines\mutant_blacklists.dm"
#include "code\__DEFINES\~doppler_defines\mutant_variations.dm"
#include "code\__DEFINES\~doppler_defines\obj_flags_doppler.dm"
#include "code\__DEFINES\~doppler_defines\organ_slots.dm"
#include "code\__DEFINES\~doppler_defines\preferences.dm"
+#include "code\__DEFINES\~doppler_defines\quirks.dm"
#include "code\__DEFINES\~doppler_defines\reagent_forging_tools.dm"
#include "code\__DEFINES\~doppler_defines\reagents.dm"
#include "code\__DEFINES\~doppler_defines\reskin_defines.dm"
@@ -6532,6 +6533,8 @@
#include "modular_doppler\deforest_medical_items\code\injectors.dm"
#include "modular_doppler\deforest_medical_items\code\medstation.dm"
#include "modular_doppler\deforest_medical_items\code\storage_items.dm"
+#include "modular_doppler\deforest_medical_items\code\storage_items_robotics.dm"
+#include "modular_doppler\deforest_medical_items\code\synth_healing.dm"
#include "modular_doppler\deforest_medical_items\code\treatment_zone_projector.dm"
#include "modular_doppler\deforest_medical_items\code\vulnerable_status_effect.dm"
#include "modular_doppler\deforest_medical_items\code\chemicals\demoneye.dm"
@@ -6705,10 +6708,15 @@
#include "modular_doppler\modular_cosmetics\GAGS\greyscale_configs_under.dm"
#include "modular_doppler\modular_crafting\code\crafting_extended.dm"
#include "modular_doppler\modular_crafting\code\sheet_types.dm"
+#include "modular_doppler\modular_customization\accessories\code\hair.dm"
#include "modular_doppler\modular_customization\accessories\code\aquatic_accessories\aquatic_body_markings.dm"
#include "modular_doppler\modular_customization\accessories\code\aquatic_accessories\aquatic_ears.dm"
#include "modular_doppler\modular_customization\accessories\code\aquatic_accessories\aquatic_snout.dm"
#include "modular_doppler\modular_customization\accessories\code\aquatic_accessories\aquatic_tail.dm"
+#include "modular_doppler\modular_customization\accessories\code\cybernetic_accessories\synth_body_markings.dm"
+#include "modular_doppler\modular_customization\accessories\code\cybernetic_accessories\synth_ears.dm"
+#include "modular_doppler\modular_customization\accessories\code\cybernetic_accessories\synth_horns.dm"
+#include "modular_doppler\modular_customization\accessories\code\cybernetic_accessories\synth_tail.dm"
#include "modular_doppler\modular_customization\accessories\code\human_accessories\human_ears.dm"
#include "modular_doppler\modular_customization\accessories\code\human_accessories\human_horns.dm"
#include "modular_doppler\modular_customization\accessories\code\human_accessories\human_tail.dm"
@@ -6753,8 +6761,6 @@
#include "modular_doppler\modular_customization\accessories\code\ramatan_accessories\ramatan_frills.dm"
#include "modular_doppler\modular_customization\accessories\code\ramatan_accessories\ramatan_snout.dm"
#include "modular_doppler\modular_customization\accessories\code\ramatan_accessories\ramatan_tail.dm"
-#include "modular_doppler\modular_customization\accessories\code\synthetic_accessories\synth_ears.dm"
-#include "modular_doppler\modular_customization\accessories\code\synthetic_accessories\synth_tail.dm"
#include "modular_doppler\modular_customization\accessories\code\underwear_accessories\bras.dm"
#include "modular_doppler\modular_customization\accessories\code\underwear_accessories\socks.dm"
#include "modular_doppler\modular_customization\accessories\code\underwear_accessories\undershirts.dm"
@@ -6772,6 +6778,7 @@
#include "modular_doppler\modular_customization\preferences\body_marking_lizard.dm"
#include "modular_doppler\modular_customization\preferences\body_marking_moth.dm"
#include "modular_doppler\modular_customization\preferences\chat_color.dm"
+#include "modular_doppler\modular_customization\preferences\cyber_limbs.dm"
#include "modular_doppler\modular_customization\preferences\ears.dm"
#include "modular_doppler\modular_customization\preferences\fluff.dm"
#include "modular_doppler\modular_customization\preferences\frills.dm"
@@ -6822,39 +6829,66 @@
#include "modular_doppler\modular_mapping\code\areas\station.dm"
#include "modular_doppler\modular_mapping\code\ruins\icemoon.dm"
#include "modular_doppler\modular_medical\code\carbon_update_icons.dm"
+#include "modular_doppler\modular_medical\code\medkit.dm"
#include "modular_doppler\modular_medical\medical_designs\medical_designs.dm"
+#include "modular_doppler\modular_medical\reagents\blood_pack.dm"
#include "modular_doppler\modular_medical\reagents\bug_blood.dm"
+#include "modular_doppler\modular_medical\reagents\crates.dm"
#include "modular_doppler\modular_medical\reagents\medicine.dm"
+#include "modular_doppler\modular_medical\reagents\pill.dm"
+#include "modular_doppler\modular_medical\reagents\pill_bottles.dm"
#include "modular_doppler\modular_medical\reagents\reagents.dm"
+#include "modular_doppler\modular_medical\reagents\sprays.dm"
#include "modular_doppler\modular_medical\reagents\synth_blood.dm"
+#include "modular_doppler\modular_medical\surgery\revival.dm"
+#include "modular_doppler\modular_medical\surgery\organs\internal\cyberimp.dm"
#include "modular_doppler\modular_medical\wounds\_wounds.dm"
#include "modular_doppler\modular_medical\wounds\bleed.dm"
#include "modular_doppler\modular_medical\wounds\medical.dm"
#include "modular_doppler\modular_medical\wounds\muscle.dm"
#include "modular_doppler\modular_medical\wounds\wound_effects.dm"
+#include "modular_doppler\modular_medical\wounds\synth\medicine_reagents.dm"
+#include "modular_doppler\modular_medical\wounds\synth\robotic_burns.dm"
+#include "modular_doppler\modular_medical\wounds\synth\robotic_muscle.dm"
+#include "modular_doppler\modular_medical\wounds\synth\robotic_pierce.dm"
+#include "modular_doppler\modular_medical\wounds\synth\robotic_slash.dm"
+#include "modular_doppler\modular_medical\wounds\synth\blunt\robotic_blunt.dm"
+#include "modular_doppler\modular_medical\wounds\synth\blunt\robotic_blunt_T1.dm"
+#include "modular_doppler\modular_medical\wounds\synth\blunt\robotic_blunt_T2.dm"
+#include "modular_doppler\modular_medical\wounds\synth\blunt\robotic_blunt_T3.dm"
+#include "modular_doppler\modular_medical\wounds\synth\blunt\secures_internals.dm"
#include "modular_doppler\modular_mob_spawn\code\mob_spawn.dm"
#include "modular_doppler\modular_mood\code\mood_events\brushed.dm"
#include "modular_doppler\modular_mood\code\mood_events\dog_wag.dm"
#include "modular_doppler\modular_mood\code\mood_events\hotspring.dm"
#include "modular_doppler\modular_mood\code\mood_events\race_drink.dm"
-#include "modular_doppler\modular_quirks\code\good_quirks.dm"
-#include "modular_doppler\modular_quirks\code\excitable\quirk.dm"
-#include "modular_doppler\modular_quirks\code\overwrites\musician.dm"
+#include "modular_doppler\modular_quirks\excitable\excitable.dm"
+#include "modular_doppler\modular_quirks\left_handed\left_handed.dm"
+#include "modular_doppler\modular_quirks\linguist\linguist.dm"
+#include "modular_doppler\modular_quirks\overwrites\musician.dm"
#include "modular_doppler\modular_quirks\paycheck_rations\code\quirk.dm"
#include "modular_doppler\modular_quirks\paycheck_rations\code\rationpacks.dm"
#include "modular_doppler\modular_quirks\paycheck_rations\code\reagents.dm"
#include "modular_doppler\modular_quirks\paycheck_rations\code\ticket_book.dm"
#include "modular_doppler\modular_quirks\paycheck_rations\code\tickets.dm"
+#include "modular_doppler\modular_quirks\permitted_cybernetic\permitted_cybernetic.dm"
+#include "modular_doppler\modular_quirks\permitted_cybernetic\code\preferences.dm"
#include "modular_doppler\modular_sounds\code\sounds.dm"
#include "modular_doppler\modular_species\_species.dm"
#include "modular_doppler\modular_species\code\bloodsplatter.dm"
-#include "modular_doppler\modular_species\overwrites\code\species.dm"
+#include "modular_doppler\modular_species\species_types\android\android.dm"
+#include "modular_doppler\modular_species\species_types\android\android_hud.dm"
+#include "modular_doppler\modular_species\species_types\android\android_parts.dm"
+#include "modular_doppler\modular_species\species_types\android\monitor_component.dm"
+#include "modular_doppler\modular_species\species_types\android\power_cord.dm"
#include "modular_doppler\modular_species\species_types\anthromorph\anthromorph.dm"
#include "modular_doppler\modular_species\species_types\anthromorph\anthromorph_bodyparts.dm"
#include "modular_doppler\modular_species\species_types\aquatic\aquatic.dm"
#include "modular_doppler\modular_species\species_types\aquatic\aquatic_bodyparts.dm"
#include "modular_doppler\modular_species\species_types\ethereal\ethereal.dm"
+#include "modular_doppler\modular_species\species_types\flypeople\flypeople.dm"
#include "modular_doppler\modular_species\species_types\genemod\genemod.dm"
+#include "modular_doppler\modular_species\species_types\genemod\genemod_bodyparts.dm"
#include "modular_doppler\modular_species\species_types\golem\golem.dm"
#include "modular_doppler\modular_species\species_types\hemophage\_hemophage_defines.dm"
#include "modular_doppler\modular_species\species_types\hemophage\_organ_corruption.dm"
@@ -6899,6 +6933,8 @@
#include "modular_doppler\modular_vending\code\tg_vendors\megaseed.dm"
#include "modular_doppler\modular_vending\code\tg_vendors\wardrobes.dm"
#include "modular_doppler\modular_weapons\code\gunsets.dm"
+#include "modular_doppler\modular_weapons\code\jousting.dm"
+#include "modular_doppler\modular_weapons\code\melee.dm"
#include "modular_doppler\modular_weapons\company_and_or_faction_based\carwo_defense_systems\gunsets.dm"
#include "modular_doppler\modular_weapons\manufacturer_examine\code\gun_company_additions.dm"
#include "modular_doppler\modular_weapons\manufacturer_examine\code\manufacturer_element.dm"
@@ -6933,7 +6969,6 @@
#include "modular_doppler\religion\code\mind.dm"
#include "modular_doppler\religion\code\religious_sects.dm"
#include "modular_doppler\research\designs\limbgrower_designs.dm"
-#include "modular_doppler\sprite_accessories\code\hair.dm"
#include "modular_doppler\stone\code\ore_veins.dm"
#include "modular_doppler\stone\code\stone.dm"
#include "modular_doppler\tableflip\tableflip.dm"
diff --git a/tgui/packages/tgui/interfaces/CrewConsole.tsx b/tgui/packages/tgui/interfaces/CrewConsole.tsx
index 9c15a551bb0ea..fc08d6d4493ab 100644
--- a/tgui/packages/tgui/interfaces/CrewConsole.tsx
+++ b/tgui/packages/tgui/interfaces/CrewConsole.tsx
@@ -126,6 +126,8 @@ type CrewSensor = {
name: string;
assignment: string | undefined;
ijob: number;
+ charge: string | undefined; // DOPPLER ADDITION
+ is_robot: any; // DOPPLER ADDITION
life_status: number;
oxydam: number;
toxdam: number;
@@ -229,6 +231,8 @@ const CrewTableEntry = (props: CrewTableEntryProps) => {
name,
assignment,
ijob,
+ charge, // DOPPLER ADDITION
+ is_robot, // DOPPLER ADDITION
life_status,
oxydam,
toxdam,
@@ -245,6 +249,12 @@ const CrewTableEntry = (props: CrewTableEntryProps) => {
{assignment !== undefined ? ` (${assignment})` : ''}
+ {/* DOPPLER ADDITION START */}
+ {is_robot ? : ''}
+ {charge !== undefined ? ` ${charge}` : ''}
+
+
+ {/* DOPPLER ADDITION END */}
{oxydam !== undefined ? (
= {
+ name: 'Night vision tint',
+ component: FeatureColorInput,
+};
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/doppler/photophobia.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/doppler/photophobia.tsx
new file mode 100644
index 0000000000000..6cf0afd15714e
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/doppler/photophobia.tsx
@@ -0,0 +1,8 @@
+// THIS IS A NOVA SECTOR UI FILE
+import { FeatureChoiced } from '../../base';
+import { FeatureDropdownInput } from '../../dropdowns';
+
+export const photophobia_severity: FeatureChoiced = {
+ name: 'Severity',
+ component: FeatureDropdownInput,
+};
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/implanted.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/implanted.tsx
new file mode 100644
index 0000000000000..68788cbd83887
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/implanted.tsx
@@ -0,0 +1,7 @@
+import { FeatureChoiced } from '../base';
+import { FeatureDropdownInput } from '../dropdowns';
+
+export const permitted_cybernetic: FeatureChoiced = {
+ name: 'Permitted Cybernetic',
+ component: FeatureDropdownInput,
+};
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/dopplershift_preferences/mutant_limbs.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/dopplershift_preferences/mutant_limbs.tsx
new file mode 100644
index 0000000000000..a99d032fe2de4
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/dopplershift_preferences/mutant_limbs.tsx
@@ -0,0 +1,78 @@
+import {
+ FeatureChoiced,
+ FeatureChoicedServerData,
+ FeatureValueProps,
+} from '../base';
+import { FeatureDropdownInput } from '../dropdowns';
+
+export const head_type: FeatureChoiced = {
+ name: 'Add Limb: Head',
+ description: `
+ Add a cybernetic head to your character, this option is exclusive to this species.
+`,
+ component: (
+ props: FeatureValueProps,
+ ) => {
+ return ;
+ },
+};
+
+export const chest_type: FeatureChoiced = {
+ name: 'Add Limb: Chest',
+ description: `
+ Add a cybernetic chassis to your character.
+`,
+ component: (
+ props: FeatureValueProps,
+ ) => {
+ return ;
+ },
+};
+
+export const arm_r_type: FeatureChoiced = {
+ name: 'Add Limb: R-Arm',
+ description: `
+ Add a cybernetic arm to your character.
+`,
+ component: (
+ props: FeatureValueProps,
+ ) => {
+ return ;
+ },
+};
+
+export const arm_l_type: FeatureChoiced = {
+ name: 'Add Limb: L-Arm',
+ description: `
+ Add a cybernetic arm to your character.
+`,
+ component: (
+ props: FeatureValueProps,
+ ) => {
+ return ;
+ },
+};
+
+export const leg_r_type: FeatureChoiced = {
+ name: 'Add Limb: R-Leg',
+ description: `
+ Add a cybernetic leg to your character.
+`,
+ component: (
+ props: FeatureValueProps,
+ ) => {
+ return ;
+ },
+};
+
+export const leg_l_type: FeatureChoiced = {
+ name: 'Add Limb: L-Leg',
+ description: `
+ Add a cybernetic leg to your character.
+`,
+ component: (
+ props: FeatureValueProps,
+ ) => {
+ return ;
+ },
+};
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/dopplershift_preferences/mutant_tricolors.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/dopplershift_preferences/mutant_tricolors.tsx
index 1083dfcef89d1..76f0a9f931ead 100644
--- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/dopplershift_preferences/mutant_tricolors.tsx
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/dopplershift_preferences/mutant_tricolors.tsx
@@ -115,10 +115,10 @@ export const caps_color: Feature = {
};
export const has_markings: FeatureToggle = {
- name: 'Add Part: Basic Markings',
+ name: 'Add Part: Markings',
category: 'GAMEPLAY',
description: `
- When toggled, adds basic bodymarkings to your character.
+ When toggled, adds bodymarkings to your character.
`,
component: CheckboxInput,
};
@@ -166,10 +166,10 @@ export const has_moth_wings: FeatureToggle = {
};
export const has_antennae: FeatureToggle = {
- name: 'Add Part: Antennae',
+ name: 'Add Part: Moth Antennae',
category: 'GAMEPLAY',
description: `
- When toggled, adds antennae to your character.
+ When toggled, adds moff antennae to your character.
`,
component: CheckboxInput,
};