diff --git a/code/__DEFINES/~yogs_defines/is_helpers.dm b/code/__DEFINES/~yogs_defines/is_helpers.dm
index bda2fe14f2e7..934572c7b3f8 100644
--- a/code/__DEFINES/~yogs_defines/is_helpers.dm
+++ b/code/__DEFINES/~yogs_defines/is_helpers.dm
@@ -3,3 +3,5 @@
#define is_shadow_or_thrall(M) (is_thrall(M) || is_shadow(M))
#define isspacepod(A) (istype(A, /obj/spacepod))
+
+#define ispreternis(A) (is_species(A, /datum/species/preternis))
\ No newline at end of file
diff --git a/code/__DEFINES/~yogs_defines/mobs.dm b/code/__DEFINES/~yogs_defines/mobs.dm
new file mode 100644
index 000000000000..0ef3d1f09169
--- /dev/null
+++ b/code/__DEFINES/~yogs_defines/mobs.dm
@@ -0,0 +1,13 @@
+#define PRETERNIS_LEVEL_FULL 550
+#define PRETERNIS_LEVEL_WELL_FED 450
+#define PRETERNIS_LEVEL_FED 350
+#define PRETERNIS_LEVEL_HUNGRY 250
+#define PRETERNIS_LEVEL_STARVING 150
+#define PRETERNIS_LEVEL_NONE 0
+
+#define ELECTRICITY_TO_NUTRIMENT_FACTOR 0.44 //1 power unit to 44 preternis charge they can uncharge an apc to 50% at most
+
+#define PRETERNIS_NV_OFF 2 //numbers of tile they can see
+#define PRETERNIS_NV_ON 8
+
+#define BODYPART_ANY -1 //use this when healing with something that needs a specefied bodypart type for all
diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm
index f8752e798591..30e4a5ba5b41 100644
--- a/code/modules/mob/living/carbon/damage_procs.dm
+++ b/code/modules/mob/living/carbon/damage_procs.dm
@@ -117,7 +117,7 @@
var/list/obj/item/bodypart/parts = list()
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
- if(status && (BP.status != status))
+ if(!(status == BODYPART_ANY) && (status && (BP.status != status))) //yogs - allows robot limb healing override
continue
if((brute && BP.brute_dam) || (burn && BP.burn_dam) || (stamina && BP.stamina_dam))
parts += BP
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index a896df17ba64..0905b432d807 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -338,7 +338,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
var/dynamic_fhair_suffix = ""
//for augmented heads
- if(HD.status == BODYPART_ROBOTIC)
+ if(HD.status == BODYPART_ROBOTIC && !yogs_draw_robot_hair) //yogs - allow for robot head hair
return
//we check if our hat or helmet hides our facial hair.
diff --git a/code/modules/surgery/bodyparts/bodyparts.dm b/code/modules/surgery/bodyparts/bodyparts.dm
index a858cd1a966e..0133856dcb71 100644
--- a/code/modules/surgery/bodyparts/bodyparts.dm
+++ b/code/modules/surgery/bodyparts/bodyparts.dm
@@ -148,7 +148,7 @@
if(owner && (owner.status_flags & GODMODE))
return FALSE //godmode
- if(required_status && (status != required_status))
+ if(!(required_status == -1) && (required_status && (status != required_status))) //yogs -- adds in BODYPART_ANY
return FALSE
var/dmg_mlt = CONFIG_GET(number/damage_multiplier)
@@ -197,8 +197,8 @@
//Damage cannot go below zero.
//Cannot remove negative damage (i.e. apply damage)
/obj/item/bodypart/proc/heal_damage(brute, burn, stamina, required_status, updating_health = TRUE)
-
- if(required_status && (status != required_status)) //So we can only heal certain kinds of limbs, ie robotic vs organic.
+ // yogs -- line below updated to allow for robotic body part healing override
+ if(!(required_status == BODYPART_ANY) && (required_status && (status != required_status)) ) //So we can only heal certain kinds of limbs, ie robotic vs organic.
return
brute_dam = round(max(brute_dam - brute, 0), DAMAGE_PRECISION)
diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm
index 23f54fefae97..022405011ace 100644
--- a/code/modules/surgery/bodyparts/head.dm
+++ b/code/modules/surgery/bodyparts/head.dm
@@ -195,7 +195,7 @@
. = ..()
if(dropped) //certain overlays only appear when the limb is being detached from its owner.
- if(status != BODYPART_ROBOTIC) //having a robotic head hides certain features.
+ if(status != BODYPART_ROBOTIC || yogs_draw_robot_hair) //having a robotic head hides certain features. //yogs -- preternis hair
//facial hair
if(facial_hair_style)
var/datum/sprite_accessory/S = GLOB.facial_hair_styles_list[facial_hair_style]
diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm
index c37337936d84..02dd25878aa4 100644
--- a/code/modules/surgery/organs/tongue.dm
+++ b/code/modules/surgery/organs/tongue.dm
@@ -18,7 +18,8 @@
/datum/language/ratvar,
/datum/language/aphasia,
/datum/language/piratespeak,
- /datum/language/japanese //yogs
+ /datum/language/japanese,
+ /datum/language/machine //yogs
))
/obj/item/organ/tongue/Initialize(mapload)
diff --git a/config/game_options.txt b/config/game_options.txt
index b2c681e772e1..f0fad9ef9cda 100644
--- a/config/game_options.txt
+++ b/config/game_options.txt
@@ -445,6 +445,7 @@ ROUNDSTART_RACES lizard
#ROUNDSTART_RACES moth
ROUNDSTART_RACES plasmaman
#ROUNDSTART_RACES shadow
+ROUNDSTART_RACES preternis
## Races that are better than humans in some ways, but worse in others
ROUNDSTART_RACES ethereal
diff --git a/yogstation.dme b/yogstation.dme
index 26eedf4896bc..3ed3f0aeb7e2 100644
--- a/yogstation.dme
+++ b/yogstation.dme
@@ -117,6 +117,7 @@
#include "code\__DEFINES\~yogs_defines\keybindings.dm"
#include "code\__DEFINES\~yogs_defines\logging.dm"
#include "code\__DEFINES\~yogs_defines\mentor.dm"
+#include "code\__DEFINES\~yogs_defines\mobs.dm"
#include "code\__DEFINES\~yogs_defines\preferences.dm"
#include "code\__DEFINES\~yogs_defines\shuttles.dm"
#include "code\__DEFINES\~yogs_defines\spacepods.dm"
@@ -2813,7 +2814,9 @@
#include "yogstation\code\datums\components\uplink.dm"
#include "yogstation\code\datums\components\walks.dm"
#include "yogstation\code\datums\components\storage\storage.dm"
+#include "yogstation\code\datums\diseases\_MobProcs.dm"
#include "yogstation\code\datums\diseases\cluwnification.dm"
+#include "yogstation\code\datums\diseases\advance\advance.dm"
#include "yogstation\code\datums\diseases\advance\symptoms\confusion.dm"
#include "yogstation\code\datums\diseases\advance\symptoms\heal.dm"
#include "yogstation\code\datums\mood_events\generic_positive_events.dm"
@@ -3071,6 +3074,10 @@
#include "yogstation\code\modules\mob\living\carbon\human\species_types\jellypeople.dm"
#include "yogstation\code\modules\mob\living\carbon\human\species_types\lizard.dm"
#include "yogstation\code\modules\mob\living\carbon\human\species_types\plantpeople.dm"
+#include "yogstation\code\modules\mob\living\carbon\human\species_types\preternis\organs.dm"
+#include "yogstation\code\modules\mob\living\carbon\human\species_types\preternis\power_suck.dm"
+#include "yogstation\code\modules\mob\living\carbon\human\species_types\preternis\preternis.dm"
+#include "yogstation\code\modules\mob\living\carbon\human\species_types\preternis\screen_alerts.dm"
#include "yogstation\code\modules\mob\living\silicon\silicon.dm"
#include "yogstation\code\modules\mob\living\silicon\ai\ai.dm"
#include "yogstation\code\modules\mob\living\silicon\ai\vox_sounds.dm"
@@ -3159,6 +3166,7 @@
#include "yogstation\code\modules\stock_market\logs.dm"
#include "yogstation\code\modules\stock_market\stockmarket.dm"
#include "yogstation\code\modules\stock_market\stocks.dm"
+#include "yogstation\code\modules\surgery\bodypart.dm"
#include "yogstation\code\modules\surgery\tools.dm"
#include "yogstation\code\modules\surgery\organs\shadowling_organs.dm"
#include "yogstation\code\modules\uplink\uplink_item.dm"
diff --git a/yogstation/code/datums/diseases/_MobProcs.dm b/yogstation/code/datums/diseases/_MobProcs.dm
new file mode 100644
index 000000000000..074271f0afc0
--- /dev/null
+++ b/yogstation/code/datums/diseases/_MobProcs.dm
@@ -0,0 +1,5 @@
+/mob/living/carbon/human/CanContractDisease(datum/disease/D)
+ var/infectchance = dna.species ? dna.species.yogs_virus_infect_chance : 100 //will this compile? who knows
+ if(prob(infectchance))
+ return ..()
+ return FALSE
\ No newline at end of file
diff --git a/yogstation/code/datums/diseases/advance/advance.dm b/yogstation/code/datums/diseases/advance/advance.dm
new file mode 100644
index 000000000000..44c416ee694c
--- /dev/null
+++ b/yogstation/code/datums/diseases/advance/advance.dm
@@ -0,0 +1,9 @@
+/datum/disease/advance/Refresh(new_name)
+ ..()
+ if(affected_mob?.dna)
+ var/datum/species/S = affected_mob.dna.species
+ properties["resistance"] += S.virus_resistance_boost
+ properties["stealth"] += S.virus_stealth_boost
+ properties["stage_rate"] += S.virus_stage_rate_boost
+ properties["transmittable"] += S.virus_transmittable_boost
+ AssignProperties() //this is a bit inefficent because its called twice but modularization amiright?
\ No newline at end of file
diff --git a/yogstation/code/modules/mob/living/carbon/human/species.dm b/yogstation/code/modules/mob/living/carbon/human/species.dm
index 30147d61e0b3..612355a461d1 100644
--- a/yogstation/code/modules/mob/living/carbon/human/species.dm
+++ b/yogstation/code/modules/mob/living/carbon/human/species.dm
@@ -3,8 +3,30 @@
////////////////////
/obj/item/bodypart
var/should_draw_yogs = FALSE
-
+
/mob/living/carbon/proc/draw_yogs_parts(do_it)
for(var/O in bodyparts)
var/obj/item/bodypart/B = O
- B.should_draw_yogs = do_it
\ No newline at end of file
+ B.should_draw_yogs = do_it
+
+/datum/species
+ var/yogs_draw_robot_hair = FALSE //DAMN ROBOTS STEALING OUR HAIR AND AIR
+ var/yogs_virus_infect_chance = 100
+ var/virus_resistance_boost = 0
+ var/virus_stealth_boost = 0
+ var/virus_stage_rate_boost = 0
+ var/virus_transmittable_boost = 0
+
+/datum/species/on_species_gain(mob/living/carbon/C, datum/species/old_species, pref_load)
+ . = ..()
+ if(yogs_draw_robot_hair)
+ for(var/obj/item/bodypart/BP in C.bodyparts)
+ BP.yogs_draw_robot_hair = TRUE
+
+/datum/species/on_species_loss(mob/living/carbon/human/C, datum/species/new_species, pref_load)
+ . = ..()
+ for(var/obj/item/bodypart/BP in C.bodyparts)
+ BP.yogs_draw_robot_hair = initial(BP.yogs_draw_robot_hair)
+
+/datum/species/proc/spec_AltClickOn(atom/A,mob/living/carbon/human/H)
+ return FALSE
diff --git a/yogstation/code/modules/mob/living/carbon/human/species_types/preternis/organs.dm b/yogstation/code/modules/mob/living/carbon/human/species_types/preternis/organs.dm
new file mode 100644
index 000000000000..2206003e834d
--- /dev/null
+++ b/yogstation/code/modules/mob/living/carbon/human/species_types/preternis/organs.dm
@@ -0,0 +1,62 @@
+/obj/item/organ/eyes/preternis
+ name = "preternis eyes"
+ desc = "An experimental upgraded version of eyes that can see in the dark.They are designed to fit preternis"
+ see_in_dark = PRETERNIS_NV_ON
+ lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
+ actions_types = list(/datum/action/item_action/organ_action/use)
+ var/night_vision = TRUE
+
+/obj/item/organ/eyes/preternis/ui_action_click()
+ var/datum/species/preternis/S = owner.dna.species
+ if(S.charge < PRETERNIS_LEVEL_FED)
+ return
+ sight_flags = initial(sight_flags)
+ switch(lighting_alpha)
+ if (LIGHTING_PLANE_ALPHA_VISIBLE)
+ lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
+ if (LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE)
+ lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
+ if (LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE)
+ lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE
+ else
+ lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE
+ sight_flags &= ~SEE_BLACKNESS
+ owner.update_sight()
+
+/obj/item/organ/eyes/preternis/on_life()
+ . = ..()
+ if(!ispreternis(owner))
+ qdel(src) //these eyes depend on being inside a preternis
+ return
+ var/datum/species/preternis/S = owner.dna.species
+ if(S.charge >= PRETERNIS_LEVEL_FED)
+ if(see_in_dark == PRETERNIS_NV_OFF)
+ see_in_dark = PRETERNIS_NV_ON
+ owner.update_sight()
+ else
+ if(see_in_dark == PRETERNIS_NV_ON)
+ see_in_dark = PRETERNIS_NV_OFF
+ owner.update_sight()
+ if(lighting_alpha < LIGHTING_PLANE_ALPHA_VISIBLE)
+ lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE
+ sight_flags &= ~SEE_BLACKNESS
+ owner.update_sight()
+
+/obj/item/organ/lungs/preternis
+ name = "preternis lungs"
+ desc = "An experimental set of lungs.Due to the cybernetic nature of these lungs,they are less resistant to heat and cold but are more efficent at filtering oxygen."
+ icon_state = "lungs-c"
+ safe_oxygen_min = 12
+ safe_toxins_max = 10
+ gas_stimulation_min = 0.1 //fucking filters removing my stimulants
+
+ cold_level_1_threshold = 280
+ cold_level_1_damage = 1.5
+ cold_level_2_threshold = 260
+ cold_level_2_damage = 3
+ cold_level_3_threshold = 200
+ cold_level_3_damage = 4.5
+
+ heat_level_1_threshold = 320
+ heat_level_2_threshold = 400
+ heat_level_3_threshold = 600 //HALP MY LUNGS ARE ON FIRE
diff --git a/yogstation/code/modules/mob/living/carbon/human/species_types/preternis/power_suck.dm b/yogstation/code/modules/mob/living/carbon/human/species_types/preternis/power_suck.dm
new file mode 100644
index 000000000000..7eeb693c948f
--- /dev/null
+++ b/yogstation/code/modules/mob/living/carbon/human/species_types/preternis/power_suck.dm
@@ -0,0 +1,186 @@
+/mob/living/carbon/AltClickOn(atom/A)
+ dna?.species.spec_AltClickOn(A,src)
+ return ..()
+
+/datum/species/preternis/spec_AltClickOn(atom/A,H)
+ return drain_power_from(H, A)
+
+/datum/species/preternis/proc/drain_power_from(mob/living/carbon/human/H, atom/A)
+ if(!istype(H) || !A)
+ return FALSE
+
+ if(draining)
+ to_chat(H,"CONSUME protocols can only be used on one object at any single time.")
+ return FALSE
+ if(!A.can_consume_power_from())
+ return FALSE //if it returns text, we want it to continue so we can get the error message later.
+
+ draining = TRUE
+
+ var/siemens_coefficient = 1
+
+ if(H.reagents.has_reagent("teslium"))
+ siemens_coefficient *= 1.5
+
+ if (charge >= PRETERNIS_LEVEL_FULL - 25) //just to prevent spam a bit
+ to_chat(H,"CONSUME protocol reports no need for additional power at this time.")
+ draining = FALSE
+ return TRUE
+
+ if(H.gloves)
+ if(!H.gloves.siemens_coefficient)
+ to_chat(H,"NOTICE: [H.gloves] prevent electrical contact - CONSUME protocol aborted.")
+ draining = FALSE
+ return TRUE
+ else
+ if(H.gloves.siemens_coefficient < 1)
+ to_chat(H,"NOTICE: [H.gloves] are interfering with electrical contact - advise removal before activating CONSUME protocol.")
+ siemens_coefficient *= H.gloves.siemens_coefficient
+
+ H.face_atom(A)
+ H.visible_message("[H] starts placing their hands on [A]...", "You start placing your hands on [A]...")
+ if(!do_after(H, 20, target = A))
+ to_chat(H,"CONSUME protocol aborted.")
+ draining = FALSE
+ return TRUE
+
+ to_chat(H,"Extracutaneous implants detect viable power source. Initiating CONSUME protocol.")
+
+ var/done = FALSE
+ var/drain = 150 * siemens_coefficient
+
+ var/cycle = 0
+ var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread()
+ spark_system.attach(A)
+ spark_system.set_up(5, 0, A)
+
+
+
+ while(!done)
+ cycle++
+ var/nutritionIncrease = drain * ELECTRICITY_TO_NUTRIMENT_FACTOR
+
+ if(charge + nutritionIncrease > PRETERNIS_LEVEL_FULL)
+ nutritionIncrease = CLAMP(PRETERNIS_LEVEL_FULL - charge, PRETERNIS_LEVEL_NONE,PRETERNIS_LEVEL_FULL) //if their nutrition goes up from some other source, this could be negative, which would cause bad things to happen.
+ drain = nutritionIncrease/ELECTRICITY_TO_NUTRIMENT_FACTOR
+
+ if (do_after(H,5, target = A))
+ var/can_drain = A.can_consume_power_from()
+ if(!can_drain || istext(can_drain))
+ if(istext(can_drain))
+ to_chat(H,can_drain)
+ done = TRUE
+ else
+ playsound(A.loc, "sparks", 50, 1)
+ if(prob(75))
+ spark_system.start()
+ var/drained = A.consume_power_from(drain)
+ if(drained < drain)
+ to_chat(H,"[A]'s power has been depleted, CONSUME protocol halted.")
+ done = TRUE
+ charge = CLAMP(charge + (drained * ELECTRICITY_TO_NUTRIMENT_FACTOR),PRETERNIS_LEVEL_NONE,PRETERNIS_LEVEL_FULL)
+
+ if(!done)
+ if(charge > (PRETERNIS_LEVEL_FULL - 25))
+ to_chat(H,"CONSUME protocol complete. Physical nourishment refreshed.")
+ done = TRUE
+ else if(!(cycle % 4))
+ var/nutperc = round((charge / PRETERNIS_LEVEL_FULL) * 100)
+ to_chat(H,"CONSUME protocol continues. Current satiety level: [nutperc]%.")
+ else
+ done = TRUE
+ qdel(spark_system)
+ draining = FALSE
+ return TRUE
+
+/atom/proc/can_consume_power_from()
+ return FALSE //if a string is returned, it will evaluate as false and be output to the person draining.
+
+/atom/proc/consume_power_from(amount)
+ return FALSE //return the amount that was drained.
+
+#define MIN_DRAINABLE_POWER 10
+
+//CELL//
+/obj/item/stock_parts/cell/can_consume_power_from()
+ if(charge < MIN_DRAINABLE_POWER)
+ return "Power cell depleted, cannot consume power."
+ return TRUE
+
+/obj/item/stock_parts/cell/consume_power_from(amount)
+ if((charge - amount) < MIN_DRAINABLE_POWER)
+ amount = max(charge - MIN_DRAINABLE_POWER, 0)
+ use(amount)
+ return amount
+
+//APC//
+/obj/machinery/power/apc/can_consume_power_from()
+ if(!cell)
+ return "APC cell absent, cannot consume power."
+ if(stat & BROKEN)
+ return "APC is damaged, cannot consume power."
+ if(!operating || shorted)
+ return "APC main breaker is off, cannot consume power."
+ if(cell.charge < MIN_DRAINABLE_POWER)
+ return "APC cell depleted, cannot consume power."
+ return TRUE
+
+/obj/machinery/power/apc/consume_power_from(amount)
+ if((cell.charge - amount) < MIN_DRAINABLE_POWER)
+ amount = max(cell.charge - MIN_DRAINABLE_POWER, 0)
+ cell.use(amount)
+ if(charging == 2)
+ charging = 0 //if we do not do this here, the APC can get stuck thinking it is fully charged.
+ update()
+ return amount
+
+//SMES//
+/obj/machinery/power/smes/can_consume_power_from()
+ if(stat & BROKEN)
+ return "SMES is damaged, cannot consume power."
+ if(!output_attempt)
+ return "SMES is not outputting power, cannot consume power."
+ if(charge < MIN_DRAINABLE_POWER)
+ return "SMES cells depleted, cannot consume power."
+ return TRUE
+
+/obj/machinery/power/smes/consume_power_from(amount)
+ if((charge - amount) < MIN_DRAINABLE_POWER)
+ amount = max(charge - MIN_DRAINABLE_POWER, 0)
+ charge -= amount
+ return amount
+
+//MECH//
+/obj/mecha/can_consume_power_from()
+ if(!cell)
+ return "Mech power cell absent, cannot consume power."
+ if(cell.charge < MIN_DRAINABLE_POWER)
+ return "Mech power cell depleted, cannot consume power."
+ return TRUE
+
+/obj/mecha/consume_power_from(amount)
+ occupant_message("Warning: Unauthorized access through sub-route 4, block H, detected.")
+ if((cell.charge - amount) < MIN_DRAINABLE_POWER)
+ amount = max(cell.charge - MIN_DRAINABLE_POWER, 0)
+ cell.use(amount)
+ return amount
+
+//BORG//
+/mob/living/silicon/robot/can_consume_power_from()
+ if(!cell)
+ return "Cyborg power cell absent, cannot consume power."
+ if(cell.charge < MIN_DRAINABLE_POWER)
+ return "Cyborg power cell depleted, cannot consume power."
+ return TRUE
+
+/mob/living/silicon/robot/consume_power_from(amount)
+ src << "Warning: Unauthorized access through sub-route 12, block C, detected."
+ if((cell.charge - amount) < MIN_DRAINABLE_POWER)
+ amount = max(cell.charge - MIN_DRAINABLE_POWER, 0)
+ cell.use(amount)
+ return amount
+
+#undef MIN_DRAINABLE_POWER
+
+
+
diff --git a/yogstation/code/modules/mob/living/carbon/human/species_types/preternis/preternis.dm b/yogstation/code/modules/mob/living/carbon/human/species_types/preternis/preternis.dm
new file mode 100644
index 000000000000..804cf556940f
--- /dev/null
+++ b/yogstation/code/modules/mob/living/carbon/human/species_types/preternis/preternis.dm
@@ -0,0 +1,161 @@
+/*
+procs:
+
+handle_charge - called in spec_life(),handles the alert indicators,the power loss death and decreasing the charge level
+adjust_charge - take a positive or negative value to adjust the charge level
+*/
+
+/datum/species/preternis
+ name = "Preternis"
+ id = "preternis"
+ default_color = "FFFFFF"
+ changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_PRIDE | MIRROR_MAGIC | RACE_SWAP | ERT_SPAWN | SLIME_EXTRACT
+ inherent_traits = list(TRAIT_NOHUNGER,TRAIT_RADIMMUNE)
+ species_traits = list(EYECOLOR,HAIR,LIPS)
+ say_mod = "intones"
+ attack_verb = "assault"
+ meat = null
+ toxic_food = NONE
+ brutemod = 1.25
+ burnmod = 1.5
+ yogs_draw_robot_hair = TRUE
+ mutanteyes = /obj/item/organ/eyes/preternis
+ mutantlungs = /obj/item/organ/lungs/preternis
+ yogs_virus_infect_chance = 20
+ virus_resistance_boost = 10 //YEOUTCH,good luck getting it out
+
+ var/charge = PRETERNIS_LEVEL_FULL
+ var/eating_msg_cooldown = FALSE
+ var/emag_lvl = 0
+ var/power_drain = 0.5 //probably going to have to tweak this shit
+ var/tesliumtrip = FALSE
+ var/draining = FALSE
+
+
+/datum/species/preternis/on_species_gain(mob/living/carbon/C, datum/species/old_species, pref_load)
+ . = ..()
+ for (var/V in C.bodyparts)
+ var/obj/item/bodypart/BP = V
+ BP.change_bodypart_status(ORGAN_ROBOTIC,FALSE,TRUE)
+ BP.burn_reduction = 0
+ BP.brute_reduction = 0
+ if(istype(BP,/obj/item/bodypart/chest) || istype(BP,/obj/item/bodypart/head))
+ continue
+ BP.max_damage = 35
+ C.grant_language(/datum/language/machine) //learn it once,learn it forever i guess,this isnt removed on species loss to prevent curators from forgetting machine language
+
+/datum/species/preternis/on_species_loss(mob/living/carbon/human/C, datum/species/new_species, pref_load)
+ . = ..()
+ for (var/V in C.bodyparts)
+ var/obj/item/bodypart/BP = V
+ BP.change_bodypart_status(ORGAN_ORGANIC,FALSE,TRUE)
+ BP.burn_reduction = initial(BP.burn_reduction)
+ BP.brute_reduction = initial(BP.brute_reduction)
+ C.clear_alert("preternis_emag") //this means a changeling can transform from and back to a preternis to clear the emag status but w/e i cant find a solution to not do that
+ C.clear_fullscreen("preternis_emag")
+ C.remove_movespeed_modifier("preternis_teslium")
+
+/datum/species/preternis/spec_emp_act(mob/living/carbon/human/H, severity)
+ . = ..()
+ switch(severity)
+ if(EMP_HEAVY)
+ H.adjustBruteLoss(20)
+ H.adjustFireLoss(20)
+ H.Paralyze(50)
+ charge *= 0.4
+ H.visible_message("Electricity ripples over [H]'s subdermal implants, smoking profusely.", \
+ "A surge of searing pain erupts throughout your very being! As the pain subsides, a terrible sensation of emptiness is left in its wake.")
+ if(EMP_LIGHT)
+ H.adjustBruteLoss(10)
+ H.adjustFireLoss(10)
+ H.Paralyze(20)
+ charge *= 0.6
+ H.visible_message("A faint fizzling emanates from [H].", \
+ "A fit of twitching overtakes you as your subdermal implants convulse violently from the electromagnetic disruption. Your sustenance reserves have been partially depleted from the blast.")
+
+/datum/species/preternis/spec_emag_act(mob/living/carbon/human/H, mob/user)
+ . = ..()
+ if(emag_lvl == 2)
+ return
+ emag_lvl = min(emag_lvl + 1,2)
+ playsound(H.loc, 'sound/machines/warning-buzzer.ogg', 50, 1, 1)
+ H.Paralyze(60)
+ switch(emag_lvl)
+ if(1)
+ H.adjustBrainLoss(50) //HALP AM DUMB
+ to_chat(H,"ALERT! MEMORY UNIT [rand(1,5)] FAILURE.NERVEOUS SYSTEM DAMAGE.")
+ if(2)
+ H.overlay_fullscreen("preternis_emag", /obj/screen/fullscreen/high)
+ H.throw_alert("preternis_emag", /obj/screen/alert/high/preternis)
+ to_chat(H,"ALERT! OPTIC SENSORS FAILURE.VISION PROCESSOR COMPROMISED.")
+
+/datum/species/preternis/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H)
+ . = ..()
+
+ if(H.reagents.has_reagent("oil"))
+ H.adjustFireLoss(-2*REAGENTS_EFFECT_MULTIPLIER,FALSE,FALSE, BODYPART_ANY)
+
+ if(H.reagents.has_reagent("welding_fuel"))
+ H.adjustFireLoss(-1*REAGENTS_EFFECT_MULTIPLIER,FALSE,FALSE, BODYPART_ANY)
+
+ if(H.reagents.has_reagent("teslium",10)) //10 u otherwise it wont update and they will remain quikk
+ H.add_movespeed_modifier("preternis_teslium", update=TRUE, priority=101, multiplicative_slowdown=-2, blacklisted_movetypes=(FLYING|FLOATING))
+ if(H.health < 50 && H.health > 0)
+ H.adjustOxyLoss(-1*REAGENTS_EFFECT_MULTIPLIER)
+ H.adjustBruteLoss(-1*REAGENTS_EFFECT_MULTIPLIER,FALSE,FALSE, BODYPART_ANY)
+ H.adjustFireLoss(-1*REAGENTS_EFFECT_MULTIPLIER,FALSE,FALSE, BODYPART_ANY)
+ H.AdjustParalyzed(-3)
+ H.AdjustStun(-3)
+ H.AdjustKnockdown(-3)
+ H.adjustStaminaLoss(-5*REAGENTS_EFFECT_MULTIPLIER)
+ charge = CLAMP(charge - 10 * REAGENTS_METABOLISM,PRETERNIS_LEVEL_NONE,PRETERNIS_LEVEL_FULL)
+ burnmod = 200
+ tesliumtrip = TRUE
+ else if(tesliumtrip)
+ burnmod = initial(burnmod)
+ tesliumtrip = FALSE
+ H.remove_movespeed_modifier("preternis_teslium")
+
+ if (istype(chem,/datum/reagent/consumable))
+ var/datum/reagent/consumable/food = chem
+ if (food.nutriment_factor)
+ var/nutrition = food.nutriment_factor * 0.2
+ charge = CLAMP(charge + nutrition,PRETERNIS_LEVEL_NONE,PRETERNIS_LEVEL_FULL)
+ if (!eating_msg_cooldown)
+ eating_msg_cooldown = TRUE
+ addtimer(VARSET_CALLBACK(src, eating_msg_cooldown, FALSE), 2 MINUTES)
+ to_chat(H,"NOTICE: Digestive subroutines are inefficient. Seek sustenance via power-cell C.O.N.S.U.M.E. technology induction.")
+
+ if(chem.current_cycle >= 20)
+ H.reagents.del_reagent(chem.id)
+
+
+ return FALSE
+
+/datum/species/preternis/spec_fully_heal(mob/living/carbon/human/H)
+ . = ..()
+ charge = PRETERNIS_LEVEL_FULL
+ emag_lvl = 0
+ H.clear_alert("preternis_emag")
+ H.clear_fullscreen("preternis_emag")
+ burnmod = initial(burnmod)
+ tesliumtrip = FALSE
+ H.remove_movespeed_modifier("preternis_teslium") //full heal removes chems so it wont update the teslium speed up until they eat something
+
+/datum/species/preternis/spec_life(mob/living/carbon/human/H)
+ . = ..()
+ handle_charge(H)
+
+/datum/species/preternis/proc/handle_charge(mob/living/carbon/human/H)
+ charge = CLAMP(charge - power_drain,PRETERNIS_LEVEL_NONE,PRETERNIS_LEVEL_FULL)
+ if(charge == PRETERNIS_LEVEL_NONE)
+ to_chat(H,"Warning! System power criti-$#@$")
+ H.death()
+ else if(charge < PRETERNIS_LEVEL_STARVING)
+ H.throw_alert("preternis_charge", /obj/screen/alert/preternis_charge, 3)
+ else if(charge < PRETERNIS_LEVEL_HUNGRY)
+ H.throw_alert("preternis_charge", /obj/screen/alert/preternis_charge, 2)
+ else if(charge < PRETERNIS_LEVEL_FED)
+ H.throw_alert("preternis_charge", /obj/screen/alert/preternis_charge, 1)
+ else
+ H.clear_alert("preternis_charge")
diff --git a/yogstation/code/modules/mob/living/carbon/human/species_types/preternis/screen_alerts.dm b/yogstation/code/modules/mob/living/carbon/human/species_types/preternis/screen_alerts.dm
new file mode 100644
index 000000000000..9a8045f43cd5
--- /dev/null
+++ b/yogstation/code/modules/mob/living/carbon/human/species_types/preternis/screen_alerts.dm
@@ -0,0 +1,8 @@
+/obj/screen/alert/high/preternis
+ name = "Optic sensor failure"
+ desc = "Main unit optic sensors damage detected.Vision processor compromised"
+
+/obj/screen/alert/preternis_charge
+ name = "Low power"
+ desc = "Find a power cell to recharge from or you may lose power."
+ icon_state = "lowcell"
\ No newline at end of file
diff --git a/yogstation/code/modules/surgery/bodypart.dm b/yogstation/code/modules/surgery/bodypart.dm
new file mode 100644
index 000000000000..14ac6280c2fc
--- /dev/null
+++ b/yogstation/code/modules/surgery/bodypart.dm
@@ -0,0 +1,2 @@
+/obj/item/bodypart
+ var/yogs_draw_robot_hair = FALSE
\ No newline at end of file