You feel attracted to people smaller than you."
+ lose_text = "You feel less attracted to people smaller than you."
diff --git a/hyperstation/code/datums/weather/oxygen_rain.dm b/hyperstation/code/datums/weather/oxygen_rain.dm
new file mode 100644
index 0000000000..1e6fcdc3bc
--- /dev/null
+++ b/hyperstation/code/datums/weather/oxygen_rain.dm
@@ -0,0 +1,93 @@
+//GS13 Edit
+/*
+/datum/weather/oxygen_rain
+ name = "oxygen rain"
+ desc = "The weather of Layenia can be quite unpredictable. Given the natural low temperature of Layenia, the formation of natural croxygenic liquid gases is possible."
+
+ telegraph_duration = 300
+ telegraph_message = "Oxygen clouds condense above and around the station..."
+ telegraph_overlay = "rain_med"
+
+ weather_message = "Liquid oxygen pours down around you! It's freezing!"
+ weather_overlay = "rain_high"
+ weather_duration_lower = 1200
+ weather_duration_upper = 4000
+
+ end_duration = 300
+ end_message = "The downpour gradually slows to a light shower before fading away..."
+ end_overlay = "rain_low"
+
+ area_type = /area/layenia
+ target_trait = ZTRAIT_STATION
+
+ immunity_type = "storm" // temp
+ probability = 2 //The chances of this happening are very low after all. We'll rarely see it, but it's worth it.
+ barometer_predictable = TRUE
+
+ var/datum/looping_sound/weak_outside_oxygenrain/sound_wo = new(list(), FALSE, TRUE)
+ var/datum/looping_sound/weak_inside_oxygenrain/sound_wi = new(list(), FALSE, TRUE)
+
+/datum/weather/oxygen_rain/telegraph()
+ . = ..()
+ priority_announce("[station_name()]: A large quantity of condensed low temperature oxygen clouds has been detected around and above the station. A liquid oxygen downpour is expected.",
+ sound = 'sound/misc/notice2.ogg',
+ sender_override = "GATO Meteorology Division")
+ for(var/V in GLOB.player_list)
+ var/mob/M = V
+ if((M.client?.prefs?.toggles & SOUND_MIDI) && is_station_level(M.z))
+ M.playsound_local(M, 'hyperstation/sound/ambience/embrace.ogg', 40, FALSE, pressure_affected = FALSE)
+ /*
+ "Sappheiros - Embrace" is under a Creative Commons license (CC BY 3.0)
+ https://www.youtube.com/channel/UCxLKyBhC6igFhLEb0gxvQNg
+ Music promoted by BreakingCopyright: https://youtu.be/DzYp5uqixz0
+ */
+ var/list/inside_areas = list()
+ var/list/outside_areas = list()
+ var/list/eligible_areas = list()
+ for (var/z in impacted_z_levels)
+ eligible_areas += SSmapping.areas_in_z["[z]"]
+ for(var/i in 1 to eligible_areas.len)
+ var/area/place = eligible_areas[i]
+ if(place.outdoors)
+ outside_areas += place
+ else
+ inside_areas += place
+ CHECK_TICK
+
+ sound_wo.output_atoms = outside_areas
+ sound_wi.output_atoms = inside_areas
+
+ sound_wo.start()
+ sound_wi.start()
+
+/datum/weather/oxygen_rain/end()
+ . = ..()
+ sound_wo.stop()
+ sound_wi.stop()
+
+/datum/weather/oxygen_rain/weather_act(mob/living/L)
+ //This is liquid oxygen after all. (-180C give or take)
+ L.adjust_bodytemperature(-rand(5,10))
+
+/datum/looping_sound/weak_outside_oxygenrain
+ mid_sounds = list(
+ 'sound/weather/oxygenrain/outside/weak_mid1.ogg'=1,
+ 'sound/weather/oxygenrain/outside/weak_mid2.ogg'=1
+ )
+ mid_length = 80
+ start_sound = 'sound/weather/oxygenrain/outside/weak_start.ogg'
+ start_length = 130
+ end_sound = 'sound/weather/oxygenrain/outside/weak_end.ogg'
+ volume = 50
+
+/datum/looping_sound/weak_inside_oxygenrain
+ mid_sounds = list(
+ 'sound/weather/oxygenrain/inside/weak_mid1.ogg'=1,
+ 'sound/weather/oxygenrain/inside/weak_mid2.ogg'=1
+ )
+ mid_length = 80
+ start_sound = 'sound/weather/oxygenrain/inside/weak_start.ogg'
+ start_length = 130
+ end_sound = 'sound/weather/oxygenrain/inside/weak_end.ogg'
+ volume = 30
+*/
diff --git a/hyperstation/code/effects/lighting.dm b/hyperstation/code/effects/lighting.dm
new file mode 100644
index 0000000000..efa424dd96
--- /dev/null
+++ b/hyperstation/code/effects/lighting.dm
@@ -0,0 +1,18 @@
+/obj/effect/light/
+ name = "bloom"
+ icon = 'hyperstation/icons/effects/lighting.dmi'
+ icon_state = "light"
+ plane = ABOVE_LIGHTING_PLANE
+ blend_mode = BLEND_ADD
+ mouse_opacity = 0
+ anchored = TRUE //no more flying around.
+
+/obj/effect/light/medium
+ icon = 'hyperstation/icons/effects/lightingmed.dmi'
+ pixel_x = -16
+ pixel_y = -16
+
+/obj/effect/light/large
+ icon = 'hyperstation/icons/effects/lightinglarge.dmi'
+ pixel_x = -48
+ pixel_y = -48
diff --git a/hyperstation/code/game/machinery/lore_terminal.dm b/hyperstation/code/game/machinery/lore_terminal.dm
new file mode 100644
index 0000000000..e0f8d58435
--- /dev/null
+++ b/hyperstation/code/game/machinery/lore_terminal.dm
@@ -0,0 +1,296 @@
+GLOBAL_DATUM_INIT(lore_terminal_controller, /datum/lore_controller, new)
+
+/obj/machinery/computer/lore_terminal
+ name = "Staff info-link terminal"
+ desc = "A small CRT display with an inbuilt microcomputer which is loaded with an extensive database. These terminals contain eveyrthing from information about historical events to instruction manuals for common ship appliances."
+ icon = 'nsv13/icons/obj/computers.dmi'
+ icon_state = "terminal"
+ pixel_y = 26 //So they snap to walls correctly
+ density = FALSE
+ anchored = TRUE
+ idle_power_usage = 15
+ var/access_tag = "kncommon" //Every subtype of this type will be readable by this console. Use this for away terms as seen here \/
+ var/list/entries = list() //Every entry that we've got.
+ var/in_use = FALSE //Stops sound spam
+ var/datum/looping_sound/computer_click/soundloop
+
+/obj/machinery/computer/lore_terminal/command //Put sensitive information on this one
+ name = "Command info-link terminal"
+ access_tag = "kncommand"
+ req_access = list(ACCESS_HEADS)
+
+/obj/machinery/computer/lore_terminal/security
+ name = "Security info-link terminal"
+ access_tag = "knsecurity"
+ req_access = list(ACCESS_SECURITY)
+
+/obj/machinery/computer/lore_terminal/awaymission //Example for having a terminal preloaded with only a set list of files.
+ access_tag = "awaymission_default"
+
+/obj/machinery/computer/lore_terminal/Initialize(mapload)
+ . = ..()
+ get_entries()
+ soundloop = new(list(src), FALSE)
+
+/datum/looping_sound/computer_click
+ mid_sounds = list('nsv13/sound/effects/computer/scroll1.ogg','nsv13/sound/effects/computer/scroll2.ogg','nsv13/sound/effects/computer/scroll3.ogg','nsv13/sound/effects/computer/scroll5.ogg')
+ mid_length = 0.8 SECONDS
+ volume = 30
+
+/obj/machinery/computer/lore_terminal/proc/get_entries()
+ for(var/X in GLOB.lore_terminal_controller.entries)
+ var/datum/lore_entry/instance = X
+ if(instance.access_tag == access_tag || instance.access_tag == "all")
+ entries += instance
+
+/obj/machinery/computer/lore_terminal/attack_hand(mob/user)
+ . = ..()
+ if(!allowed(user))
+ var/sound = pick('nsv13/sound/effects/computer/error.ogg','nsv13/sound/effects/computer/error2.ogg','nsv13/sound/effects/computer/error3.ogg')
+ playsound(src, sound, 100, 1)
+ to_chat(user, "Access denied")
+ return
+ playsound(src, 'nsv13/sound/effects/computer/scroll_start.ogg', 100, 1)
+ user.set_machine(src)
+ var/dat
+ if(!entries.len)
+ get_entries()
+ for(var/X in entries) //Allows you to remove things individually
+ var/datum/lore_entry/content = X
+ dat += "[content.name]
"
+ var/datum/browser/popup = new(user, "cd C:/entries/local", name, 300, 500)
+ popup.set_content(dat)
+ popup.open()
+
+
+/obj/machinery/computer/lore_terminal/Topic(href, href_list)
+ if(!in_range(src, usr))
+ return
+ if(in_use)
+ var/sound = 'nsv13/sound/effects/computer/buzz2.ogg'
+ playsound(src, sound, 100, 1)
+ to_chat(usr, "ERROR: I/O function busy. A file is still loading...")
+ return
+ var/datum/lore_entry/content = locate(href_list["selectitem"])
+ if(!content || !content?.content)
+ return
+ var/clicks = length(content.content) //Split the content into characters. 1 character = 1 click
+ var/dat = "\
+ \
+ \
+ \
+ \
+ \
+ ACCESS FILE: C:/entries/local/[content.name]
\
+ Classification: [content.classified]
\
+ - � Seegson systems inc, 2257
\
+
\
+ [content.title]
\
+ \
+ \
+ \
+ \
+ \
+ \
+ \
+ \
+ "
+ usr << browse(dat, "window=lore_console[content.name];size=600x600")
+ playsound(src, pick('nsv13/sound/effects/computer/buzz.ogg','nsv13/sound/effects/computer/buzz2.ogg'), 100, TRUE)
+ in_use = TRUE //Stops you from crashing the server with infinite sounds
+ icon_state = "terminal_scroll"
+ clicks = clicks/3
+ var/loops = clicks/3 //Each click sound has 4 clicks in it, so we only need to click 1/4th of the time per character yeet.
+ addtimer(CALLBACK(src,PROC_REF(stop_clicking)), loops)
+ soundloop?.start()
+
+
+/obj/machinery/computer/lore_terminal/proc/stop_clicking()
+ soundloop?.stop()
+ icon_state = "terminal"
+ in_use = FALSE
+
+/datum/lore_controller
+ var/name = "Lore archive controller"
+ var/list/entries = list() //All the lore entries we have.
+
+/datum/lore_controller/New()
+ . = ..()
+ instantiate_lore_entries()
+
+/datum/lore_controller/proc/instantiate_lore_entries()
+ for(var/instance in subtypesof(/datum/lore_entry))
+ var/datum/lore_entry/S = new instance
+ entries += S
+
+/datum/lore_entry
+ var/name = "Loredoc.txt" //"File display name" that the term shows (C://blah/yourfile.bmp)
+ var/title = null //What it's all about
+ var/classified = "Declassified" //Fluff, is this a restricted file or not?
+ var/content = null //You may choose to set this here, or via a .txt. file if it's long. Newlines / Enters will break it!
+ var/path = null //The location at which we're stored. If you don't have this, you don't get content
+ var/access_tag = "placeholder" //Set this to match the terminals that you want to be able to access it. EG "ntcommon" for declassified shit.
+
+/datum/lore_entry/New()
+ . = ..()
+ if(path)
+ content = file2text("[path]")
+
+/*
+
+TO GET THE COOL TYPEWRITER EFFECT, I HAD TO STRIP OUT THE HTML FORMATTING STUFF.
+SPECIAL KEYS RESPOND AS FOLLOWS:
+
+` = newline (br) (AKA when you press enter)
+~ = horizontal line (hr)
+� = bullet point //Bullet points are not working? - archie
+
+*/
+
+/datum/lore_entry/station
+ name = "new_employees_memo.mail"
+ title = "Intercepted message"
+ path = "lore_entries/welcome.txt"
+ access_tag = "kncommon"
+
+/datum/lore_entry/all
+ name = "nuclear_authdisk_instructions.mail"
+ title = "Handling the Nuclear Authentication Disk and You!"
+ content = "SYSADMIN -> allcrew@seegnet.kin. RE: The Nuclear Authentication Disk. ` Greetings staff members! We're aware of the questions you have in regards to the Nuclear Authentication disk. The disk itself contains the codes needed to unlock nuclear devices used in a Nuclear Emergency and trigger a station self-destruction sequence. ` It is the duty of every member of this crew to take responsibility for the disk in the off chance that station command is currently absent. ` Even though Layenia itself is a mobile facility and prevents Disk Triangulation in most circumstances, it is still important to hold onto the disk even when not needed- it is a good formality and keeps things secure! ` If the disk location is unknown, look for a red pinpointer, the Head of Security and the Captain should always have one in their offices or lockers. These will help you locate the disk. ` If the disk is in enemy hands, call all hands on deck immediately and order a red alert. ` ` Protect the disk with your life, for the lives of the station's crew depend on it. Stay safe through vigilance."
+ access_tag = "all"
+
+/datum/lore_entry/all/meltdown_proceedures
+ name = "meltdown_proceedures.mail"
+ title = "Emergency proceedures regarding nuclear meltdowns:"
+ path = "lore_entries/meltdowns.txt"
+
+/datum/lore_entry/command
+ name = "command_memo.mail"
+ title = "Intercepted Message"
+ access_tag = "kncommand"
+ classified = "Restricted"
+ content = "SYSADMIN -> command@seegnet.kin. RE: Orientation. ` Greetings station command staff, congratulations on your placement! It is now company policy to attend all briefings as issued by centcom staff. Please speak to your centcom officer for clarification on the new procedures."
+
+/datum/lore_entry/command/xeno
+ name = "outpost_27.mail"
+ title = "Investigation Closed"
+ classified = "Classified"
+ content = "SYSADMIN -> command@seegnet.kin. RE: Outpost 27 Investigation. ` Until further notice, all communications, visits and trade with Outpost 27 must be denied. It is recommended that the subject is avoided. Stay safe through vigilance."
+
+/datum/lore_entry/away_example
+ title = "Intercepted log file"
+ access_tag = "awayexample"
+
+/datum/lore_entry/away_example/pilot_log
+ name = "pilot_log.txt"
+ content = "They're coming in hot! Prepare for flip and bur']###�$55%%% -=File Access Terminated=-"
+
+/datum/lore_entry/away_example/weapons_log
+ name = "weapon_systems_dump2259/11/25.txt"
+ content = "Life support systems terminated. Railgun system status: A6E3. Torpedo system status: ~@##6#6#^^6 -=File Access Terminated=-"
+
+/datum/lore_entry/security
+ name = "usage_and_terms.memo"
+ title = "Usage and Terms"
+ access_tag = "knsecurity"
+ path = "lore_entries/security/usageandterms.txt"
+
+/datum/lore_entry/security/introduction
+ name = "introduction.memo"
+ title = "Security Introduction"
+ path = "lore_entries/security/introduction.txt"
+
+/datum/lore_entry/security/basicgearandyou
+ name = "gearbasics.memo"
+ title = "Basic Gear and You"
+ path = "lore_entries/security/basicgearandyou.txt"
+
+/datum/lore_entry/security/advancedgearandyou
+ name = "gearadvanced.memo"
+ title = "Advanced Gear and You"
+ path = "lore_entries/security/advancedgearandyou.txt"
+
+/datum/lore_entry/security/defensivegearandyou
+ name = "geardefensive.memo"
+ title = "Defensive Gear and You"
+ path = "lore_entries/security/defensivegearandyou.txt"
+
+/datum/lore_entry/security/sop
+ name = "standard_operating_procedure.txt"
+ title = "Standard Operating Procedure"
+ path = "lore_entries/security/wip.txt"
+
+/datum/lore_entry/security/lowcrime
+ name = "low_infractions.txt"
+ title = "Minor Infractions"
+ path = "lore_entries/security/wip.txt"
+
+/datum/lore_entry/security/mediumcrime
+ name = "medium_infractions.txt"
+ title = "Medium-risk Infractions"
+ path = "lore_entries/security/wip.txt"
+
+/datum/lore_entry/security/highcrime
+ name = "high_infractions.txt"
+ title = "Dangerous Infractions"
+ path = "lore_entries/security/wip.txt"
+
+/datum/lore_entry/security/deltacrime
+ name = "delta_infractions.txt"
+ title = "Zealot-class Notice and Warning"
+ path = "lore_entries/security/wip.txt"
diff --git a/hyperstation/code/game/objects/items/cosmetics.dm b/hyperstation/code/game/objects/items/cosmetics.dm
new file mode 100644
index 0000000000..a0edfc0433
--- /dev/null
+++ b/hyperstation/code/game/objects/items/cosmetics.dm
@@ -0,0 +1,77 @@
+//hyperstation 13 nail polish
+
+/obj/item/nailpolish
+ name = "nail polish"
+ desc = "Paint with a fine brush to do your nails, or someone elses."
+ icon = 'hyperstation/icons/obj/cosmetic.dmi'
+ icon_state = "nailcap"
+ item_state = "nailpolish"
+ w_class = WEIGHT_CLASS_SMALL
+ var/paint = "black"
+ price = 5
+ var/mutable_appearance/bottle //show the colour on the bottle.
+
+/obj/item/nailpolish/red
+ name = "red nail polish"
+ paint = "red"
+
+/obj/item/nailpolish/blue
+ name = "blue nail polish"
+ paint = "blue"
+
+/obj/item/nailpolish/aqua
+ name = "cyan nail polish"
+ paint = "aqua"
+
+/obj/item/nailpolish/black
+ name = "black nail polish"
+ paint = "black"
+
+/obj/item/nailpolish/white
+ name = "white nail polish"
+ paint = "white"
+
+/obj/item/nailpolish/navy
+ name = "navy nail polish"
+ paint = "navy"
+
+/obj/item/nailpolish/yellow
+ name = "yellow nail polish"
+ paint = "yellow"
+
+/obj/item/nailpolish/purple
+ name = "purple nail polish"
+ paint = "purple"
+
+/obj/item/nailpolish/Initialize(mapload)
+ . = ..()
+ bottle = mutable_appearance('hyperstation/icons/obj/cosmetic.dmi', "nailpolish")
+ bottle.color = paint
+ add_overlay(bottle)
+
+
+/obj/item/nailpolish/attack(mob/M, mob/user)
+ if(!ismob(M))
+ return
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ if(H.nail_style) //to stop stacking bugs
+ to_chat(user, "Remove the old nail polish first!")
+
+ if(H == user)
+ user.visible_message("[user] does [user.p_their()] nails with \the [src].", \
+ "You take a moment to apply \the [src]. Perfect!")
+ H.nail_style = "nails"
+ H.nail_color = paint
+ H.update_body()
+ else
+ user.visible_message("[user] begins to do [H]'s nails with \the [src].", \
+ "You begin to apply \the [src] on [H]'s nails...")
+ if(do_after(user, 20, target = H))
+ user.visible_message("[user] does [H]'s nails with \the [src].", \
+ "You apply \the [src] on [H]'s nails.")
+ H.nail_style = "nails"
+ H.nail_color = paint
+ H.update_body()
+ else
+ to_chat(user, "Where are the nail on that?")
diff --git a/hyperstation/code/game/objects/items/storage/big_bag.dm b/hyperstation/code/game/objects/items/storage/big_bag.dm
new file mode 100644
index 0000000000..e22a3cb61b
--- /dev/null
+++ b/hyperstation/code/game/objects/items/storage/big_bag.dm
@@ -0,0 +1,44 @@
+/obj/item/storage/backpack/gigantic
+ name = "enormous backpack"
+ desc = "An absolutely massive backpack for particularly large crewmen."
+ icon_state = "explorerpack"
+ item_state = "explorerpack"
+
+/obj/item/storage/backpack/gigantic/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.allow_big_nesting = TRUE
+ STR.max_combined_w_class = 35
+ STR.max_w_class = WEIGHT_CLASS_HUGE
+
+/obj/item/storage/backpack/gigantic/mob_can_equip(mob/living/M, mob/living/equipper, slot, disable_warning, bypass_equip_delay_self)
+ if(!..())
+ return FALSE
+ if(M.size_multiplier >= 2.0)
+ return ..()
+ else
+ return FALSE
+
+/obj/item/storage/backpack/gigantic/proc/fallOff(mob/living/wearer)
+ wearer.dropItemToGround(src, TRUE)
+ playsound(src.loc, 'sound/items/handling/cloth_drop.ogg', 50, TRUE)
+ playsound(src.loc, 'sound/items/handling/toolbelt_drop.ogg', 50, TRUE)
+ wearer.visible_message("The [src.name] slips off [wearer]'s now too-small body and falls to the ground!'", "The [src.name] slips off your now too-small body and falls to the ground!'")
+
+/obj/item/storage/backpack/gigantic/equipped(mob/equipper, slot)
+ . = ..()
+ if(slot ==ITEM_SLOT_BACK)
+ RegisterSignal(equipper, COMSIG_MOBSIZE_CHANGED,PROC_REF(fallOff))
+ else
+ UnregisterSignal(equipper, COMSIG_MOBSIZE_CHANGED)
+
+
+/obj/item/storage/backpack/gigantic/dropped(mob/user, silent)
+ UnregisterSignal(user, COMSIG_MOBSIZE_CHANGED)
+
+
+/obj/item/storage/backpack/gigantic/satchel
+ name = "enormous satchel"
+ desc = "An absolutely massive satchel for particularly large crewmen."
+ icon_state = "satchel-explorer"
+ item_state = "securitypack"
diff --git a/hyperstation/code/game/objects/railings.dm b/hyperstation/code/game/objects/railings.dm
new file mode 100644
index 0000000000..6dd6bd67ca
--- /dev/null
+++ b/hyperstation/code/game/objects/railings.dm
@@ -0,0 +1,162 @@
+//ported from virgo
+
+/obj/structure/railing
+ name = "railing"
+ desc = "A railing to stop people from falling"
+
+ icon = 'hyperstation/icons/obj/railings.dmi'
+ var/icon_modifier = "grey_"
+ icon_state = "grey_railing0"
+
+ density = FALSE
+ layer = 4
+ anchored = TRUE
+ flags_1 = ON_BORDER_1
+ max_integrity = 250
+ var/heat_resistance = 800
+ var/health = 70
+ var/maxhealth = 70
+
+
+ resistance_flags = ACID_PROOF
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100)
+ CanAtmosPass = ATMOS_PASS_PROC
+ var/real_explosion_block //ignore this, just use explosion_block
+ var/breaksound = "shatter"
+ var/hitsound = 'sound/effects/Glasshit.ogg'
+ rad_insulation = RAD_VERY_LIGHT_INSULATION
+ rad_flags = RAD_PROTECT_CONTENTS
+ var/check = 0
+ var/static/list/freepass = typecacheof(list(
+ /obj/singularity,
+ /obj/effect/projectile,
+ /obj/effect/portal,
+ /obj/effect/abstract,
+ /obj/effect/hotspot,
+ /obj/effect/landmark,
+ /obj/effect/temp_visual,
+ /obj/effect/light_emitter/tendril,
+ /obj/effect/collapse,
+ /obj/effect/particle_effect/ion_trails,
+ /obj/effect/dummy/phased_mob,
+ /obj/effect/immovablerod,
+ /obj/effect/crystalline_reentry
+ )) //Gotta make sure certain things can phase through it otherwise the railings also block them.
+
+/obj/structure/railing/CanPass(atom/movable/mover, turf/target)
+ if(istype(mover) && (mover.pass_flags & PASSGLASS) || is_type_in_typecache(mover, freepass))
+ return TRUE
+ if(get_dir(loc, target) == dir)
+ return FALSE
+ return TRUE
+
+/obj/structure/railing/CheckExit(atom/movable/O, turf/target)
+ if(istype(O) && (O.pass_flags & PASSGLASS) || is_type_in_typecache(O, freepass))
+ return TRUE
+ if(get_dir(O.loc, target) == dir)
+ return FALSE
+ return TRUE
+
+/obj/structure/railing/Initialize(mapload)
+ . = ..()
+ if(src.anchored)
+ update_icon(0)
+
+/obj/structure/railing/proc/NeighborsCheck(var/UpdateNeighbors = 1)
+ check = 0
+ //if (!anchored) return
+ var/Rturn = turn(src.dir, -90)
+ var/Lturn = turn(src.dir, 90)
+
+ for(var/obj/structure/railing/R in src.loc)
+ if ((R.dir == Lturn) && R.anchored)
+ check |= 32
+ if (UpdateNeighbors)
+ R.update_icon(0)
+ if ((R.dir == Rturn) && R.anchored)
+ check |= 2
+ if (UpdateNeighbors)
+ R.update_icon(0)
+
+ for (var/obj/structure/railing/R in get_step(src, Lturn))
+ if ((R.dir == src.dir) && R.anchored)
+ check |= 16
+ if (UpdateNeighbors)
+ R.update_icon(0)
+ for (var/obj/structure/railing/R in get_step(src, Rturn))
+ if ((R.dir == src.dir) && R.anchored)
+ check |= 1
+ if (UpdateNeighbors)
+ R.update_icon(0)
+
+ for (var/obj/structure/railing/R in get_step(src, (Lturn + src.dir)))
+ if ((R.dir == Rturn) && R.anchored)
+ check |= 64
+ if (UpdateNeighbors)
+ R.update_icon(0)
+ for (var/obj/structure/railing/R in get_step(src, (Rturn + src.dir)))
+ if ((R.dir == Lturn) && R.anchored)
+ check |= 4
+ if (UpdateNeighbors)
+ R.update_icon(0)
+
+/obj/structure/railing/update_icon(var/UpdateNeighgors = 1)
+ NeighborsCheck(UpdateNeighgors)
+ overlays.Cut()
+ if (!check || !anchored)//|| !anchored
+ icon_state = "[icon_modifier]railing0"
+ else
+ icon_state = "[icon_modifier]railing1"
+ if (check & 32)
+ overlays += image ('hyperstation/icons/obj/railings.dmi', src, "[icon_modifier]corneroverlay")
+ if ((check & 16) || !(check & 32) || (check & 64))
+ overlays += image ('hyperstation/icons/obj/railings.dmi', src, "[icon_modifier]frontoverlay_l")
+ if (!(check & 2) || (check & 1) || (check & 4))
+ overlays += image ('hyperstation/icons/obj/railings.dmi', src, "[icon_modifier]frontoverlay_r")
+ if(check & 4)
+ switch (src.dir)
+ if (NORTH)
+ overlays += image ('hyperstation/icons/obj/railings.dmi', src, "[icon_modifier]mcorneroverlay", pixel_x = 32)
+ if (SOUTH)
+ overlays += image ('hyperstation/icons/obj/railings.dmi', src, "[icon_modifier]mcorneroverlay", pixel_x = -32)
+ if (EAST)
+ overlays += image ('hyperstation/icons/obj/railings.dmi', src, "[icon_modifier]mcorneroverlay", pixel_y = -32)
+ if (WEST)
+ overlays += image ('hyperstation/icons/obj/railings.dmi', src, "[icon_modifier]mcorneroverlay", pixel_y = 32)
+
+/obj/structure/railing/examine(mob/user)
+ . = ..()
+ if(health < maxhealth)
+ switch(health / maxhealth)
+ if(0.0 to 0.5)
+ . += "It looks severely damaged!"
+ if(0.25 to 0.5)
+ . += "It looks damaged!"
+ if(0.5 to 1.0)
+ . += "It has a few scrapes and dents."
+
+/obj/structure/railing/take_damage(amount)
+ health -= amount
+ if(health <= 0)
+ visible_message("\The [src] breaks down!")
+ playsound(src, 'sound/effects/grillehit.ogg', 50, 1)
+ new /obj/item/stack/rods(get_turf(src))
+ qdel(src)
+
+/obj/structure/railing/MouseDrop_T(mob/living/M, mob/living/user)
+ if(!istype(user))
+ return
+ if(!isliving(user))
+ return
+
+ usr.visible_message("[user] starts climbing onto \the [src]!")
+
+ if(!do_after(user, 20))
+ return
+
+ if(get_turf(user) == get_turf(src))
+ usr.forceMove(get_step(src, src.dir))
+ else
+ usr.forceMove(get_turf(src))
+
+ usr.visible_message("[user] climbed over \the [src]!")
diff --git a/hyperstation/code/game/objects/structures/bench.dm b/hyperstation/code/game/objects/structures/bench.dm
new file mode 100644
index 0000000000..ba62136ac5
--- /dev/null
+++ b/hyperstation/code/game/objects/structures/bench.dm
@@ -0,0 +1,11 @@
+/obj/structure/chair/bench
+ name = "wooden bench"
+ icon_state = "bench_middle"
+ icon = 'hyperstation/icons/obj/bench.dmi'
+ buildstackamount = 1
+ buildstacktype = /obj/item/stack/sheet/mineral/wood
+
+/obj/structure/chair/bench/left
+ icon_state = "bench_left"
+/obj/structure/chair/bench/right
+ icon_state = "bench_right"
diff --git a/hyperstation/code/game/objects/structures/ghost_role_spawners.dm b/hyperstation/code/game/objects/structures/ghost_role_spawners.dm
new file mode 100644
index 0000000000..3f3c954f65
--- /dev/null
+++ b/hyperstation/code/game/objects/structures/ghost_role_spawners.dm
@@ -0,0 +1,144 @@
+//Duo malfunctioning cryostasis sleepers: Spawns in big makeshift shelters in lavaland.
+/obj/effect/mob_spawn/human/duohermit
+ name = "malfunctioning cryostasis sleeper"
+ desc = "A humming sleeper with a silhouetted occupant inside. Its stasis function is broken and it's likely being used as a bed."
+ mob_name = "a stranded hermit"
+ job_description = "Lavaland Hermit"
+ icon = 'icons/obj/lavaland/spawners.dmi'
+ icon_state = "cryostasis_sleeper"
+ roundstart = FALSE
+ death = FALSE
+ random = TRUE
+ mob_species = /datum/species/human
+ short_desc = "You are lost."
+ flavour_text = "You and another have been stranded in this planet for quite some time now. Each day you barely scrape by, and between the terrible \
+ conditions of your makeshift shelter, the hostile creatures, and the ash drakes swooping down from the cloudless skies, all you can wish for is the feel of soft grass between your toes and \
+ the fresh air of Earth. These thoughts are dispelled by yet another recollection of how you and your friend got here... "
+ assignedrole = "Hermit"
+ mirrorcanloadappearance = TRUE
+
+/obj/effect/mob_spawn/human/duohermit/Initialize(mapload)
+ . = ..()
+ var/arrpee = rand(1,3)
+ switch(arrpee)
+ if(1)
+ flavour_text += "you were an intern at a rather odd deep space facility. You weren't quite sure how things worked or what they were doing there, but it was your first day on the \
+ job. A day that was abruptly interrupted by gunfire and alarms. Luckily enough, your handy crowbar skills managed to get you to an escape pod before it was too late."
+ outfit.uniform = /obj/item/clothing/under/assistantformal
+ outfit.shoes = /obj/item/clothing/shoes/sneakers/black
+ outfit.back = /obj/item/storage/backpack
+ if(2)
+ flavour_text += "you were a volunteer test subject for a state of the art deep space facility. You didn't care much about who you were working with, but in the end, the paycheck \
+ was really, really good. To this day, you're not quite sure which sort of prototype implants were used on you, as you seem to remember little but the headache that struck you once \
+ the escape pod finally hit the ground and your seatbelt failed to keep you buckled."
+ outfit.uniform = /obj/item/clothing/under/rank/scientist
+ outfit.suit = /obj/item/clothing/suit/toggle/labcoat
+ outfit.shoes = /obj/item/clothing/shoes/sneakers/black
+ outfit.back = /obj/item/storage/backpack
+ if(3)
+ flavour_text += "you were a doctor at a state of the art deep space facility. For who exactly you were conducting research for, not even you are quite sure. Only that the paycheck \
+ at the end of the month was good enough. In the end, when said facility was attacked by Nanotransen, you and another were the only ones to have made it out alive. Or so it seemed."
+ outfit.uniform = /obj/item/clothing/under/rank/medical
+ outfit.suit = /obj/item/clothing/suit/toggle/labcoat
+ outfit.back = /obj/item/storage/backpack/medic
+ outfit.shoes = /obj/item/clothing/shoes/sneakers/black
+
+/obj/effect/mob_spawn/human/duohermit/Destroy()
+ new/obj/structure/fluff/empty_cryostasis_sleeper(get_turf(src))
+ return ..()
+
+/obj/effect/mob_spawn/human/duohermit/special(mob/living/carbon/human/new_spawn)
+ ADD_TRAIT(new_spawn,TRAIT_EXEMPT_HEALTH_EVENTS,GHOSTROLE_TRAIT)
+
+//Exiles: Stranded exiles that have been left in Snowdin. Can be easily adapted for other roles as well.
+/obj/effect/mob_spawn/human/exiled
+ name = "used bed"
+ desc = "Still warm."
+ mob_name = "exiled"
+ job_description = "Exiles"
+ icon = 'icons/obj/objects.dmi'
+ icon_state = "bed"
+ roundstart = FALSE
+ death = FALSE
+ random = TRUE
+ mob_species = /datum/species/human
+ short_desc = "You've been left behind."
+ flavour_text = "As the last escape shuttle left the sector, you were left for dead, stranded in a cold hell where you make do, until hopefully someone finds you. \
+ Every day, you pause and recollect your memories from before it all happened... "
+ assignedrole = "Arctic Exile"
+ mirrorcanloadappearance = TRUE
+
+/obj/effect/mob_spawn/human/exiled/Initialize(mapload)
+ . = ..()
+ var/arrpee = rand(1,1)
+ switch(arrpee)
+ if(1)
+ flavour_text += "You were a lowly engineer, hired by GATO to make sure the turbines from their mining operation remained functional. \
+ You remember the day the mining team descended for the very last time into the depths of the shafts, only to never return. \
+ The agonizing screams from whatever now haunts those mines still brings a shiver down your spine."
+ outfit.uniform = /obj/item/clothing/under/assistantformal
+ outfit.suit = /obj/item/clothing/suit/hooded/wintercoat
+ outfit.shoes = /obj/item/clothing/shoes/winterboots
+ outfit.back = /obj/item/storage/backpack
+ outfit.implants = list(/obj/item/implant/exile) //Made it so they cannot simply exit through the gateway at will.
+
+/obj/effect/mob_spawn/human/exiled/Destroy()
+ new/obj/structure/bed(get_turf(src))
+ return ..()
+
+/obj/effect/mob_spawn/human/exiled/special(mob/living/carbon/human/new_spawn)
+ ADD_TRAIT(new_spawn,TRAIT_EXEMPT_HEALTH_EVENTS,GHOSTROLE_TRAIT)
+
+//Villagers: Once a group of servants that worshiped their 'Gods-Kings', but now finally free after their overlords have abandoned the planet.
+
+/obj/effect/mob_spawn/human/villager
+ name = "used bed"
+ desc = "Still warm."
+ mob_name = "villager"
+ job_description = "Villagers"
+ icon = 'icons/obj/objects.dmi'
+ icon_state = "bed"
+ roundstart = FALSE
+ death = FALSE
+ mob_species = /datum/species/human
+ short_desc = "You're finally free."
+ flavour_text = "After the God-Kings have abandoned the land, your people are finally free from their oppression. \
+ Still, the reasons for their departure are a mystery, and now your people need to work together to survive in this world."
+ assignedrole = "Free villager"
+ mirrorcanloadappearance = TRUE
+
+/obj/effect/mob_spawn/human/villager/Initialize(mapload)
+ . = ..()
+ var/arrpee = rand(1,3)
+ switch(arrpee)
+ if(1)
+ flavour_text += "You were a guard, serving your gods to keep control of you own people. \
+ Now that they left, you work to defend the village."
+ outfit.head = /obj/item/clothing/head/pharaoh
+ outfit.uniform = /obj/item/clothing/under/tunic
+ outfit.shoes = /obj/item/clothing/shoes/sandal
+ outfit.back = /obj/item/storage/backpack/satchel/leather
+ outfit.implants = list(/obj/item/implant/exile) //Made it so they cannot simply exit through the gateway at will.
+ if(2)
+ flavour_text += "You were a farmer, working extensively for years in the field to feed your people. \
+ The work hasn't changed much since the departure of the gods..."
+ outfit.head = /obj/item/clothing/head/rice_hat
+ outfit.uniform = /obj/item/clothing/under/tunic
+ outfit.shoes = /obj/item/clothing/shoes/sandal
+ outfit.back = /obj/item/storage/backpack/satchel/leather
+ outfit.implants = list(/obj/item/implant/exile) //Made it so they cannot simply exit through the gateway at will.
+ if(3)
+ flavour_text += "You were a servant, doing any necessary work that was thrown at you. \
+ At least it gave you experience in a bit of everything, which can be useful in these new times."
+ outfit.uniform = /obj/item/clothing/under/mummy
+ outfit.uniform = /obj/item/clothing/under/tunic
+ outfit.shoes = /obj/item/clothing/shoes/sandal
+ outfit.back = /obj/item/storage/backpack/satchel/leather
+ outfit.implants = list(/obj/item/implant/exile) //Made it so they cannot simply exit through the gateway at will.
+
+/obj/effect/mob_spawn/human/villager/Destroy()
+ new/obj/structure/bed(get_turf(src))
+ return ..()
+
+/obj/effect/mob_spawn/human/villager/special(mob/living/carbon/human/new_spawn)
+ ADD_TRAIT(new_spawn,TRAIT_EXEMPT_HEALTH_EVENTS,GHOSTROLE_TRAIT)
diff --git a/hyperstation/code/game/objects/structures/sauna_oven.dm b/hyperstation/code/game/objects/structures/sauna_oven.dm
new file mode 100644
index 0000000000..6472760611
--- /dev/null
+++ b/hyperstation/code/game/objects/structures/sauna_oven.dm
@@ -0,0 +1,120 @@
+#define SAUNA_H2O_TEMP T20C + 20
+#define SAUNA_LOG_FUEL 150
+#define SAUNA_PAPER_FUEL 5
+#define SAUNA_MAXIMUM_FUEL 3000
+#define SAUNA_WATER_PER_WATER_UNIT 5
+
+/obj/structure/sauna_oven
+ name = "sauna oven"
+ desc = "A modest sauna oven with rocks. Add some fuel, pour some water and enjoy the moment."
+ icon = 'hyperstation/icons/obj/structures/sauna_oven.dmi'
+ icon_state = "sauna_oven"
+ density = TRUE
+ anchored = TRUE
+ resistance_flags = FIRE_PROOF
+ var/lit = FALSE
+ var/fuel_amount = 0
+ var/water_amount = 0
+
+/obj/structure/sauna_oven/examine(mob/user)
+ . = ..()
+ . += "The rocks are [water_amount ? "moist" : "dry"]."
+ . += "There's [fuel_amount ? "some fuel" : "no fuel"] in the oven."
+
+/obj/structure/sauna_oven/Destroy()
+ if(lit)
+ STOP_PROCESSING(SSobj, src)
+ return ..()
+
+/obj/structure/sauna_oven/attack_hand(mob/user)
+ . = ..()
+ if(.)
+ return
+ if(lit)
+ lit = FALSE
+ STOP_PROCESSING(SSobj, src)
+ user.visible_message("[user] turns on [src].", "You turn on [src].")
+ else if (fuel_amount)
+ lit = TRUE
+ START_PROCESSING(SSobj, src)
+ user.visible_message("[user] turns off [src].", "You turn off [src].")
+ update_icon()
+
+/obj/structure/sauna_oven/update_overlays()
+ . = ..()
+ if(lit)
+ . += "sauna_oven_on_overlay"
+
+/obj/structure/sauna_oven/update_icon()
+ ..()
+ icon_state = "[lit ? "sauna_oven_on" : initial(icon_state)]"
+
+/obj/structure/sauna_oven/attackby(obj/item/T, mob/user)
+ if(T.tool_behaviour == TOOL_WRENCH)
+ to_chat(user, "You begin to deconstruct [src].")
+ if(T.use_tool(src, user, 60, volume=50))
+ to_chat(user, "You successfully deconstructed [src].")
+ new /obj/item/stack/sheet/mineral/wood(get_turf(src), 30)
+ qdel(src)
+ return TRUE
+
+ else if(istype(T, /obj/item/reagent_containers))
+ var/obj/item/reagent_containers/reagent_container = T
+ if(!reagent_container.is_open_container())
+ return ..()
+ if(reagent_container.reagents.has_reagent(/datum/reagent/water))
+ reagent_container.reagents.remove_reagent(/datum/reagent/water, 5)
+ user.visible_message("[user] pours some \
+ water into [src].", "You pour \
+ some water to [src].")
+ water_amount += 5 * SAUNA_WATER_PER_WATER_UNIT
+ return TRUE
+ else
+ to_chat(user, "There's no water in [reagent_container]")
+ return FALSE
+
+ else if(istype(T, /obj/item/stack/sheet/mineral/wood))
+ var/obj/item/stack/sheet/mineral/wood/wood = T
+ if(fuel_amount > SAUNA_MAXIMUM_FUEL)
+ to_chat(user, "You can't fit any more of [T] in [src]!")
+ return FALSE
+ fuel_amount += SAUNA_LOG_FUEL * wood.amount
+ wood.use(wood.amount)
+ user.visible_message("[user] tosses some \
+ wood into [src].", "You add \
+ some fuel to [src].")
+ return TRUE
+ else if(istype(T, /obj/item/paper_bin))
+ var/obj/item/paper_bin/paper_bin = T
+ user.visible_message("[user] throws [T] into \
+ [src].", "You add [T] to [src].\
+ ")
+ fuel_amount += SAUNA_PAPER_FUEL * paper_bin.total_paper
+ qdel(paper_bin)
+ return TRUE
+ else if(istype(T, /obj/item/paper))
+ user.visible_message("[user] throws [T] into \
+ [src].", "You throw [T] into [src].\
+ ")
+ fuel_amount += SAUNA_PAPER_FUEL
+ qdel(T)
+ return TRUE
+ return ..()
+
+/obj/structure/sauna_oven/process()
+ if(water_amount)
+ water_amount--
+ var/turf/pos = get_turf(src)
+ if(pos)
+ pos.atmos_spawn_air("water_vapor=10;TEMP=[SAUNA_H2O_TEMP]")
+ fuel_amount--
+ if(fuel_amount <= 0)
+ lit = FALSE
+ STOP_PROCESSING(SSobj, src)
+ update_icon()
+
+#undef SAUNA_H2O_TEMP
+#undef SAUNA_LOG_FUEL
+#undef SAUNA_PAPER_FUEL
+#undef SAUNA_MAXIMUM_FUEL
+#undef SAUNA_WATER_PER_WATER_UNIT
diff --git a/hyperstation/code/gamemode/traitor_lewd.dm b/hyperstation/code/gamemode/traitor_lewd.dm
new file mode 100644
index 0000000000..68c14a4ee8
--- /dev/null
+++ b/hyperstation/code/gamemode/traitor_lewd.dm
@@ -0,0 +1,339 @@
+//Jay Sparrow
+/datum/game_mode
+ var/list/datum/mind/lewd = list()
+
+
+
+/datum/mind/
+ var/sexed = FALSE //General flag for completion check
+
+//TODO - Move this somewhere else
+GLOBAL_LIST_INIT(hyper_special_roles, list(
+ ROLE_LEWD_TRAITOR = /datum/game_mode/traitor/lewd
+))
+
+
+/datum/game_mode/traitor/lewd
+ name = "lewd traitor"
+ config_tag = "lewd traitor"
+ antag_flag = ROLE_LEWD_TRAITOR
+ false_report_weight = 20 //Reports of traitors are pretty common.
+ restricted_jobs = list("Cyborg")
+ protected_jobs = list() //Anyone can be lewd op. Even heads.
+ required_players = 0
+ required_enemies = 0
+
+ announce_span = "danger"
+ announce_text = "There are lewd agents on the station!\n\
+ Traitors: Accomplish your objectives.\n\
+ Crew: Do not let the traitors succeed!"
+
+ //var/list/datum/mind/lewd
+ traitors_possible = 10 //hard limit on traitors if scaling is turned off //Let it be high for now. Testing phase.
+ num_modifier = 0 // Used for gamemodes, that are a child of traitor, that need more than the usual.
+ antag_datum = /datum/antagonist/traitor/lewd //what type of antag to create
+
+/datum/antagonist/traitor/lewd
+ name = "Lewd Traitor"
+ roundend_category = "traitors"
+ antagpanel_category = "Traitor"
+ job_rank = ROLE_LEWD_TRAITOR
+ //antag_moodlet = /datum/mood_event/focused //No special moodlet yet
+ special_role = ROLE_LEWD_TRAITOR
+ employer = "An interested voyeur"
+ give_objectives = TRUE
+ should_give_codewords = FALSE
+ should_equip = TRUE
+ traitor_kind = TRAITOR_HUMAN //Set on initial assignment
+ can_hijack = HIJACK_NEUTRAL
+
+ //Custom uplink list - aaaaaaaaaaaaaa
+ var/list/lewd_uplink_list = list()
+ var/list/lewd_uplink_list_raw = list(
+ /datum/uplink_item/bundles_TC/random,
+ /datum/uplink_item/bundles_TC/telecrystal,
+ /datum/uplink_item/bundles_TC/telecrystal/five,
+ /datum/uplink_item/bundles_TC/telecrystal/twenty,
+ /datum/uplink_item/dangerous/foamsmg,
+ /datum/uplink_item/dangerous/foammachinegun,
+ /datum/uplink_item/dangerous/foampistol,
+ /datum/uplink_item/dangerous/phantomthief,
+ /datum/uplink_item/stealthy_weapons/dart_pistol,
+ /datum/uplink_item/stealthy_weapons/crossbow,
+ /datum/uplink_item/stealthy_weapons/sleepy_pen,
+ /datum/uplink_item/stealthy_weapons/suppressor,
+ /datum/uplink_item/stealthy_weapons/soap,
+ /datum/uplink_item/stealthy_weapons/soap_clusterbang,
+ /datum/uplink_item/ammo/pistolzzz,
+ /datum/uplink_item/ammo/shotgun/stun,
+ /datum/uplink_item/ammo/toydarts,
+ /datum/uplink_item/stealthy_tools/ai_detector,
+ /datum/uplink_item/stealthy_tools/chameleon,
+ /datum/uplink_item/stealthy_tools/chameleon_proj,
+ /datum/uplink_item/stealthy_tools/emplight,
+ /datum/uplink_item/stealthy_tools/syndigaloshes,
+ /datum/uplink_item/stealthy_tools/jammer,
+ /datum/uplink_item/stealthy_tools/smugglersatchel,
+ /datum/uplink_item/device_tools/binary,
+ /datum/uplink_item/device_tools/compressionkit,
+ /datum/uplink_item/device_tools/briefcase_launchpad,
+ /datum/uplink_item/device_tools/camera_bug,
+ /datum/uplink_item/device_tools/military_belt,
+ /datum/uplink_item/device_tools/frame,
+ /datum/uplink_item/device_tools/toolbox,
+ /datum/uplink_item/device_tools/medkit,
+ /datum/uplink_item/device_tools/surgerybag,
+ /datum/uplink_item/device_tools/encryptionkey,
+ /datum/uplink_item/device_tools/thermal,
+ /datum/uplink_item/implants/radio,
+ /datum/uplink_item/implants/reviver,
+ /datum/uplink_item/implants/stealthimplant,
+ /datum/uplink_item/implants/thermals,
+ /datum/uplink_item/implants/uplink,
+ /datum/uplink_item/implants/xray,
+ /datum/uplink_item/role_restricted/goldenbox,
+ /datum/uplink_item/role_restricted/chemical_gun,
+ /datum/uplink_item/badass/syndiecash,
+ /datum/uplink_item/badass/phantomthief,
+ /datum/uplink_item/badass/syndiecards,
+ /datum/uplink_item/badass/syndiecigs)
+
+/datum/game_mode/traitor/lewd/generate_report()
+ return "Although more specific threats are commonplace, you should always remain vigilant for Syndicate agents aboard your station. Syndicate communications have implied that many \
+ GATO employees are Syndicate agents with hidden memories that may be activated at a moment's notice, so it's possible that these agents might not even know their positions."
+
+/datum/mind/proc/make_LewdTraitor()
+ if(!(has_antag_datum(/datum/antagonist/traitor/lewd)))
+ add_antag_datum(/datum/antagonist/traitor/lewd)
+
+/datum/admins/proc/makeLewdtraitors()
+ to_chat(GLOB.admins, "makeLewd_traitors called")
+ var/datum/game_mode/traitor/lewd/temp = new
+
+ //if(CONFIG_GET(flag/protect_roles_from_antagonist))
+ //continue
+
+ //if(CONFIG_GET(flag/protect_assistant_from_antagonist))
+ //continue
+
+ var/list/mob/living/carbon/human/candidates = list()
+ var/mob/living/carbon/human/H = null
+ var/list/mob/living/carbon/human/targets = list()
+ //var/mob/living/carbon/human/T = null
+
+ for(var/mob/living/carbon/human/applicant in GLOB.player_list)
+ if(isReadytoRumble(applicant, ROLE_LEWD_TRAITOR))
+ if(temp.age_check(applicant.client))
+ if(!(applicant.job in temp.restricted_jobs))
+ candidates += applicant
+
+ for(var/mob/living/carbon/human/target in GLOB.player_list)
+ if(target.client?.prefs?.noncon)
+ if(!(target.job in temp.restricted_jobs))
+ targets += target
+
+ if(candidates.len)
+ var/numTraitors = min(candidates.len, targets.len, 1) //This number affects the maximum number of traitors. We want 1 for right now.
+ if(numTraitors == 0)
+ to_chat(GLOB.admins, "No lewd traitors created. Are there any valid targets?")
+ return FALSE
+ for(var/i = 0, iYou are the [owner.special_role].")
+ owner.announce_objectives()
+ if(should_give_codewords)
+ give_codewords()
+
+/datum/antagonist/traitor/lewd/on_gain()
+ if(owner.current && isAI(owner.current))
+ traitor_kind = TRAITOR_AI
+
+ SSticker.mode.traitors += owner
+ owner.special_role = special_role
+ if(give_objectives)
+ forge_objectives()
+ finalize_traitor()
+
+/datum/antagonist/traitor/lewd/finalize_traitor()
+ switch(traitor_kind)
+ if(TRAITOR_AI)
+ add_law_zero()
+ owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/malf.ogg', 100, FALSE, pressure_affected = FALSE)
+ owner.current.grant_language(/datum/language/codespeak)
+ if(TRAITOR_HUMAN)
+ if(should_equip)
+ equip(silent)
+ for(var/obj/I in owner.current.GetAllContents())
+ var/datum/component/uplink/hidden_uplink = I.GetComponent(/datum/component/uplink)
+ if(hidden_uplink)
+ lewd_uplink_list = get_custom_uplink_items(lewd_uplink_list_raw, /datum/game_mode/traitor/lewd, TRUE, FALSE)
+ hidden_uplink.uplink_items = lewd_uplink_list
+ hidden_uplink.telecrystals = 12
+
+
+ owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/tatoralert.ogg', 100, FALSE, pressure_affected = FALSE)
+ greet()
+
+/datum/objective/noncon/New(var/datum/mind/_owner)
+ GLOB.objectives += src // CITADEL EDIT FOR CRYOPODS
+ if(_owner)
+ owner = _owner
+
+
+
+//GS13 - this shit's just fucking weird lol, gtfo
+
+// /datum/objective/noncon/find_target()
+// //..()
+// //to_chat(world, "TEST: noncon/find_target() called")
+// var/list/datum/mind/targets = list()
+// var/list/datum/mind/owners = get_owners()
+// for(var/datum/mind/candidate in SSticker.minds)
+// if (!(candidate in owners) && ishuman(candidate.current) && (candidate.current.client))
+// if(candidate.current.client?.prefs?.noncon == 1)
+// targets += candidate
+// if(targets.len > 0)
+// target = pick(targets)
+// else
+// target = null
+// update_explanation_text()
+// return target
+
+
+// /datum/objective/noncon/check_completion()
+// if(!target)
+// return TRUE
+// else
+// return target.sexed
+
+// /datum/objective/noncon/update_explanation_text()
+// if(target)
+// explanation_text = "Locate [target.name], and sate your lust."
+// else
+// explanation_text = "Free Lewd Objective"
+
+/datum/antagonist/traitor/lewd/roundend_report()
+ var/list/result = list()
+
+ var/traitorwin = TRUE
+
+ result += printplayer(owner)
+
+ var/TC_uses = 0
+ var/uplink_true = FALSE
+ var/purchases = ""
+ LAZYINITLIST(GLOB.uplink_purchase_logs_by_key)
+ var/datum/uplink_purchase_log/H = GLOB.uplink_purchase_logs_by_key[owner.key]
+ if(H)
+ TC_uses = H.total_spent
+ uplink_true = TRUE
+ purchases += H.generate_render(FALSE)
+
+ var/objectives_text = ""
+ if(objectives.len)//If the traitor had no objectives, don't need to process this.
+ var/count = 1
+ for(var/datum/objective/objective in objectives)
+ if(objective.check_completion())
+ objectives_text += "
Objective #[count]: [objective.explanation_text] Success!"
+ else
+ objectives_text += "
Objective #[count]: [objective.explanation_text] Fail."
+ traitorwin = FALSE
+ count++
+
+ if(uplink_true)
+ var/uplink_text = "(used [TC_uses] TC) [purchases]"
+ if(TC_uses==0 && traitorwin)
+ var/static/icon/badass = icon('icons/badass.dmi', "badass")
+ uplink_text += "[icon2html(badass, world)]"
+ result += uplink_text
+
+ result += objectives_text
+
+ var/special_role_text = lowertext(name)
+
+ if(traitorwin)
+ result += "The [special_role_text] was successful!"
+ else
+ result += "The [special_role_text] has failed!"
+ SEND_SOUND(owner.current, 'sound/ambience/ambifailure.ogg')
+
+ return result.Join("
")
+
+
+/proc/get_custom_uplink_items(var/list/custom_uplink_list = null, var/datum/game_mode/gamemode = null, allow_sales = TRUE, allow_restricted = TRUE)
+ var/list/filtered_uplink_items = list()
+ var/list/sale_items = list()
+
+ if(!custom_uplink_list)
+ for(var/path in GLOB.uplink_items)
+ custom_uplink_list += new path
+ for(var/path in custom_uplink_list)
+ var/datum/uplink_item/I = new path
+ if(!I.item)
+ continue
+ if(I.include_modes.len)
+ if(!gamemode && SSticker.mode && !(SSticker.mode.type in I.include_modes))
+ continue
+ if(gamemode && !(gamemode in I.include_modes))
+ continue
+ if(I.exclude_modes.len)
+ if(!gamemode && SSticker.mode && (SSticker.mode.type in I.exclude_modes))
+ continue
+ if(gamemode && (gamemode in I.exclude_modes))
+ continue
+ if(I.player_minimum && I.player_minimum > GLOB.joined_player_list.len)
+ continue
+ if (I.restricted && !allow_restricted)
+ continue
+
+ if(!filtered_uplink_items[I.category])
+ filtered_uplink_items[I.category] = list()
+ filtered_uplink_items[I.category][I.name] = I
+ if(I.limited_stock < 0 && !I.cant_discount && I.item && I.cost > 1)
+ sale_items += I
+ if(allow_sales)
+ for(var/i in 1 to 2) //Smaller list, fewer discounts
+ var/datum/uplink_item/I = pick_n_take(sale_items)
+ var/datum/uplink_item/A = new I.type
+ var/discount = A.get_discount()
+ var/list/disclaimer = list("Void where prohibited.", "Not recommended for children.", "Contains small parts.", "Check local laws for legality in region.", "Do not taunt.", "Not responsible for direct, indirect, incidental or consequential damages resulting from any defect, error or failure to perform.", "Keep away from fire or flames.", "Product is provided \"as is\" without any implied or expressed warranties.", "As seen on TV.", "For recreational use only.", "Use only as directed.", "16% sales tax will be charged for orders originating within Space Nebraska.")
+ A.limited_stock = 1
+ I.refundable = FALSE //THIS MAN USES ONE WEIRD TRICK TO GAIN FREE TC, CODERS HATES HIM!
+ A.refundable = FALSE
+ if(A.cost >= 20) //Tough love for nuke ops
+ discount *= 0.5
+ A.cost = max(round(A.cost * discount),1)
+ A.category = "Discounted Gear"
+ A.name += " ([round(((initial(A.cost)-A.cost)/initial(A.cost))*100)]% off!)"
+ A.desc += " Normally costs [initial(A.cost)] TC. All sales final. [pick(disclaimer)]"
+ A.item = I.item
+
+ if(!filtered_uplink_items[A.category])
+ filtered_uplink_items[A.category] = list()
+ filtered_uplink_items[A.category][A.name] = A
+ return filtered_uplink_items
+
diff --git a/hyperstation/code/gamemode/traitor_thief.dm b/hyperstation/code/gamemode/traitor_thief.dm
new file mode 100644
index 0000000000..56cdde641a
--- /dev/null
+++ b/hyperstation/code/gamemode/traitor_thief.dm
@@ -0,0 +1,84 @@
+//Jay Sparrow
+//A less murder traitor for low chaos dynamic rounds.
+
+/datum/game_mode/traitor/thief
+ name = "thief traitor"
+ config_tag = "thief traitor"
+ antag_flag = ROLE_TRAITOR
+ false_report_weight = 20 //Reports of traitors are pretty common.
+ restricted_jobs = list("AI","Cyborg")
+ protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
+ required_players = 0
+ required_enemies = 0
+
+ announce_span = "danger"
+ announce_text = "There are enemy agents on the station!\n\
+ Traitors: Accomplish your objectives.\n\
+ Crew: Do not let the traitors succeed!"
+
+ traitors_possible = 3 //hard limit on traitors if scaling is turned off //Let it be high for now. Testing phase.
+ num_modifier = 0 // Used for gamemodes, that are a child of traitor, that need more than the usual.
+ antag_datum = /datum/antagonist/traitor/thief //what type of antag to create
+
+/datum/antagonist/traitor/thief
+ name = "Thief Traitor"
+ roundend_category = "traitors"
+ antagpanel_category = "Traitor"
+ job_rank = ROLE_TRAITOR
+ antag_moodlet = /datum/mood_event/focused
+ special_role = ROLE_TRAITOR
+ employer = "The Syndicate"
+ give_objectives = TRUE
+ should_give_codewords = TRUE
+ should_equip = TRUE
+ traitor_kind = TRAITOR_HUMAN //Set on initial assignment
+ can_hijack = HIJACK_NEUTRAL
+
+
+/datum/game_mode/traitor/thief/generate_report()
+ return "Although more specific threats are commonplace, you should always remain vigilant for Syndicate agents aboard your station. Syndicate communications have implied that many \
+ GATO employees are Syndicate agents with hidden memories that may be activated at a moment's notice, so it's possible that these agents might not even know their positions."
+
+/datum/mind/proc/make_ThiefTraitor()
+ if(!(has_antag_datum(/datum/antagonist/traitor/thief)))
+ add_antag_datum(/datum/antagonist/traitor/thief)
+
+
+/datum/antagonist/traitor/thief/proc/forge_objectives()
+ forge_single_objective()
+ forge_single_objective()
+ var/datum/objective/escape/escape_objective = new
+ escape_objective.owner = owner
+ add_objective(escape_objective)
+
+
+/datum/antagonist/traitor/thief/forge_human_objectives()
+ forge_single_objective()
+ forge_single_objective()
+ var/datum/objective/escape/escape_objective = new
+ escape_objective.owner = owner
+ add_objective(escape_objective)
+
+/datum/antagonist/traitor/thief/forge_single_objective()
+ if(prob(15) && !(locate(/datum/objective/download) in owner.objectives) && !(owner.assigned_role in list("Research Director", "Scientist", "Roboticist")))
+ var/datum/objective/download/download_objective = new
+ download_objective.owner = owner
+ download_objective.gen_amount_goal()
+ add_objective(download_objective)
+ else
+ var/datum/objective/steal/steal_objective = new
+ steal_objective.owner = owner
+ steal_objective.find_target()
+ add_objective(steal_objective)
+ return
+
+
+/datum/antagonist/traitor/thief/on_gain()
+ if(owner.current && isAI(owner.current))
+ traitor_kind = TRAITOR_AI
+
+ SSticker.mode.traitors += owner
+ owner.special_role = special_role
+ if(give_objectives)
+ forge_objectives()
+ finalize_traitor()
diff --git a/hyperstation/code/mobs/carrion.dm b/hyperstation/code/mobs/carrion.dm
new file mode 100644
index 0000000000..80dddc71da
--- /dev/null
+++ b/hyperstation/code/mobs/carrion.dm
@@ -0,0 +1,50 @@
+//mob based off the game Carrion.
+//replica sprite made by quotefox
+
+/mob/living/simple_animal/hostile/carrion
+ name = "mass of red tentacles"
+ desc = "A creature composed of tentacles and teeth, you aren't sure where it starts and where it ends."
+ icon = 'hyperstation/icons/mobs/carrion.dmi'
+ icon_state = "c_idle"
+ icon_living = "c_idle"
+ icon_dead = "idle"
+ gender = NEUTER
+ speak_chance = 0
+ turns_per_move = 2
+ turns_per_move = 3
+ maxHealth = 300
+ health = 300
+ see_in_dark = 7
+ response_help = "pets"
+ response_disarm = "pushes aside"
+ response_harm = "attacks"
+ melee_damage_lower = 12
+ melee_damage_upper = 20
+ attacktext = "attacks"
+ attack_sound = 'sound/weapons/bite.ogg'
+ faction = list("hostile")
+ ranged = 1
+ harm_intent_damage = 5
+ obj_damage = 60
+ a_intent = INTENT_HARM
+ ventcrawler = 1
+ death_sound = 'sound/voice/ed209_20sec.ogg'
+ deathmessage = "lets out scream and its tentacles shrivel away..."
+ move_to_delay = 4
+ loot = list(/obj/effect/gibspawner/human)
+
+ atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ minbodytemp = 150
+ maxbodytemp = 500
+ do_footstep = TRUE
+
+/mob/living/simple_animal/hostile/carrion/OpenFire(atom/the_target)
+ var/dist = get_dist(src,the_target)
+ Beam(the_target, "tentacle", time=dist*2, maxdistance=dist, beam_sleep_time = 5)
+ the_target.attack_animal(src)
+
+/mob/living/simple_animal/hostile/carrion/Initialize(mapload)
+//Move the sprite into position, cant use Pixel_X and Y, causes issues with the tenticle sprite!
+ ..()
+ var/matrix/M = src.transform
+ src.transform = M.Translate(-32,-32)
diff --git a/hyperstation/code/mobs/dancer.dm b/hyperstation/code/mobs/dancer.dm
new file mode 100644
index 0000000000..0e727e394c
--- /dev/null
+++ b/hyperstation/code/mobs/dancer.dm
@@ -0,0 +1,56 @@
+/mob/living/dancercaptain
+ name = "Captain Beats"
+ desc = "A captain cursed to dance for all eternity!"
+ icon = 'hyperstation/icons/mobs/dancer/captain.dmi'
+ icon_state = "idle"
+ var/danceaction = 1
+ var/lastaction = 0 //when the last action was!
+ var/actiontime = 4
+ var/list/dancefloor_turfs
+ var/list/dancefloor_turfs_types
+ var/dancefloor_exists = FALSE
+
+/mob/living/dancercaptain/Move(atom/newloc, direct)
+
+ if(!danceaction)
+ if(!(world.time > lastaction))
+ return // no move for you!
+
+ animate(src, pixel_x, pixel_y = pixel_y + 10, time = 0.7, 0)
+ . = ..()
+ danceaction = 0 //you did your move
+ lastaction = world.time+actiontime //next action time
+ sleep(1)
+ animate(src, pixel_x, pixel_y = pixel_y - 10, time = 0.7, 0)
+ sleep(1)
+
+ LAZYINITLIST(dancefloor_turfs)
+ LAZYINITLIST(dancefloor_turfs_types)
+
+ if(dancefloor_exists) //remove the old one!
+ dancefloor_exists = FALSE
+ for(var/i in 1 to dancefloor_turfs.len)
+ var/turf/T = dancefloor_turfs[i]
+ T.ChangeTurf(dancefloor_turfs_types[i])
+ var/list/funky_turfs = RANGE_TURFS(2, src)
+
+
+ dancefloor_exists = TRUE
+ var/i = 1
+ dancefloor_turfs.len = funky_turfs.len
+ dancefloor_turfs_types.len = funky_turfs.len
+ for(var/t in funky_turfs)
+ if(!(istype(t, /turf/closed))) //dont change walls
+ var/turf/T = t
+ dancefloor_turfs[i] = T
+ dancefloor_turfs_types[i] = T.type
+ T.ChangeTurf((i % 2 == 0) ? /turf/open/floor/light/colour_cycle/dancefloor_a : /turf/open/floor/light/colour_cycle/dancefloor_b)
+ i++
+
+
+/mob/living/dancercaptain/Initialize(mapload)
+ . = ..()
+
+ lastaction = round(world.time) //round to the nearest second! to keep in beat!
+
+/mob/living/dancercaptain/proc/mtimer()
diff --git a/hyperstation/code/mobs/emote.dm b/hyperstation/code/mobs/emote.dm
new file mode 100644
index 0000000000..9353e1368c
--- /dev/null
+++ b/hyperstation/code/mobs/emote.dm
@@ -0,0 +1,33 @@
+/datum/emote/living/chirp
+ key = "chirp"
+ key_third_person = "chirps!"
+ message = "chirps!"
+ emote_type = EMOTE_AUDIBLE
+ muzzle_ignore = FALSE
+ restraint_check = FALSE
+ mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai)
+
+/datum/emote/living/chirp/run_emote(mob/living/user, params)
+ if(!(. = ..()))
+ return
+ if(user.nextsoundemote >= world.time)
+ return
+ user.nextsoundemote = world.time + 7
+ playsound(user, 'sound/voice/chirp.ogg', 50, 1, -1)
+
+/datum/emote/living/caw
+ key = "caw"
+ key_third_person = "caws!"
+ message = "caws!"
+ emote_type = EMOTE_AUDIBLE
+ muzzle_ignore = FALSE
+ restraint_check = FALSE
+ mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai)
+
+/datum/emote/living/caw/run_emote(mob/living/user, params)
+ if(!(. = ..()))
+ return
+ if(user.nextsoundemote >= world.time)
+ return
+ user.nextsoundemote = world.time + 7
+ playsound(user, 'sound/voice/caw.ogg', 50, 1, -1)
\ No newline at end of file
diff --git a/hyperstation/code/mobs/hugbot.dm b/hyperstation/code/mobs/hugbot.dm
new file mode 100644
index 0000000000..148680461a
--- /dev/null
+++ b/hyperstation/code/mobs/hugbot.dm
@@ -0,0 +1,311 @@
+//HUGBOT
+//HUGBOT PATHFINDING
+//HUGBOT ASSEMBLY
+
+
+/mob/living/simple_animal/bot/hugbot
+ name = "\improper Hugbot"
+ desc = "A little cudly robot. He looks excited."
+ icon = 'hyperstation/icons/mobs/aibots.dmi'
+ icon_state = "hugbot0"
+ density = FALSE
+ anchored = FALSE
+ health = 20
+ maxHealth = 20
+ pass_flags = PASSMOB
+
+ status_flags = (CANPUSH | CANSTUN)
+
+ bot_type = HUG_BOT
+ model = "Hugbot"
+ bot_core_type = /obj/machinery/bot_core/hugbot
+ window_id = "autohug"
+ window_name = "Automatic Hugging Unit v1.0 Alpha"
+ path_image_color = "#FFDDDD"
+
+ base_speed = 4
+
+ var/stationary_mode = 0 //If enabled, the Hugbot will not move automatically.
+ var/mob/living/carbon/patient = null
+ var/mob/living/carbon/oldpatient = null
+ var/last_found = 0
+
+
+/mob/living/simple_animal/bot/hugbot/update_icon()
+ cut_overlays()
+ if(!on)
+ icon_state = "hugbot0"
+ return
+ if(IsStun())
+ icon_state = "hugbota"
+ return
+ if(mode == BOT_HEALING)
+ icon_state = "hugbots[stationary_mode]"
+ return
+ else if(stationary_mode) //Bot has yellow light to indicate stationary mode.
+ icon_state = "hugbot2"
+ else
+ icon_state = "hugbot1"
+
+/mob/living/simple_animal/bot/medbot/Initialize(mapload, new_skin)
+ . = ..()
+ update_icon()
+
+
+/mob/living/simple_animal/bot/hugbot/update_canmove()
+ . = ..()
+ update_icon()
+
+/mob/living/simple_animal/bot/hugbot/bot_reset()
+ ..()
+ update_icon()
+
+/mob/living/simple_animal/bot/hugbot/proc/soft_reset() //Allows the medibot to still actively perform its medical duties without being completely halted as a hard reset does.
+ path = list()
+ patient = null
+ mode = BOT_IDLE
+ last_found = world.time
+ update_icon()
+
+/mob/living/simple_animal/bot/hugbot/set_custom_texts()
+
+ text_hack = "You bypass [name]'s manipulator pressure sensors."
+ text_dehack = "You rewire [name]'s manipulator pressure sensors."
+ text_dehack_fail = "[name] seems damaged and does not respond to reprogramming!"
+
+/mob/living/simple_animal/bot/hugbot/attack_paw(mob/user)
+ return attack_hand(user)
+
+/mob/living/simple_animal/bot/hugbot/get_controls(mob/user)
+ var/dat
+ dat += hack(user)
+ dat += showpai(user)
+ dat += "Hugging Unit Controls v1.0 Alpha
"
+ dat += "Status: [on ? "On" : "Off"]
"
+ dat += "Maintenance panel panel is [open ? "opened" : "closed"]
"
+ dat += "Behaviour controls are [locked ? "locked" : "unlocked"]
"
+ if(!locked || issilicon(user) || IsAdminGhost(user))
+ dat += "Patrol Station: [auto_patrol ? "Yes" : "No"]
"
+ dat += "Stationary Mode: [stationary_mode ? "Yes" : "No"]
"
+
+ return dat
+
+/mob/living/simple_animal/bot/hugbot/Topic(href, href_list)
+ if(..())
+ return TRUE
+
+ update_controls()
+ return
+
+/mob/living/simple_animal/bot/hugbot/attackby(obj/item/W as obj, mob/user as mob, params)
+ var/current_health = health
+ ..()
+ if(health < current_health) //if medbot took some damage
+ step_to(src, (get_step_away(src,user)))
+
+/mob/living/simple_animal/bot/hugbot/emag_act(mob/user)
+ ..()
+ if(emagged == 2)
+ if(user)
+ to_chat(user, "You short out [src]'s manipulator pressure sensors.")
+ audible_message("[src]'s arm twitches violently!")
+ flick("medibot_spark", src)
+ playsound(src, "sparks", 75, 1)
+
+
+/mob/living/simple_animal/bot/hugbot/proc/assess_patient(mob/living/carbon/C)
+ //Time to see if they need medical help!
+ if(C.stat == DEAD || (HAS_TRAIT(C, TRAIT_FAKEDEATH)))
+ return FALSE //welp too late for them!
+
+ if(!(loc == C.loc) && !(isturf(C.loc) && isturf(loc)))
+ return FALSE
+
+ if(ishuman(C))
+ var/mob/living/carbon/human/H = C
+ var/datum/component/mood/mood = H.GetComponent(/datum/component/mood)
+ if(emagged != 2) // EVERYONE GETS HUGS!
+ for(var/datum/mood_event/i in mood.mood_events)
+ if (i.description == "Hugs are nice.\n" )
+ return FALSE
+ else if (C.IsKnockdown())
+ return FALSE
+ return TRUE
+
+ return FALSE
+
+/mob/living/simple_animal/bot/hugbot/process_scan(mob/living/carbon/human/H)
+ if(H.stat == DEAD)
+ return
+
+ if((H == oldpatient) && (world.time < last_found + 200))
+ return
+
+ if(assess_patient(H))
+ last_found = world.time
+ return H
+ else
+ return
+
+/mob/living/simple_animal/bot/hugbot/handle_automated_action()
+ if(!..())
+ return
+
+ if(mode == BOT_HEALING)
+ medicate_patient(patient)
+ return
+
+ if(IsStun())
+ oldpatient = patient
+ patient = null
+ mode = BOT_IDLE
+ update_icon()
+ return
+
+ if(frustration > 8)
+ oldpatient = patient
+ soft_reset()
+
+ if(QDELETED(patient))
+ var/scan_range = (stationary_mode ? 1 : DEFAULT_SCAN_RANGE) //If in stationary mode, scan range is limited to adjacent patients.
+ patient = scan(/mob/living/carbon/human, oldpatient, scan_range)
+ oldpatient = patient
+
+ if(patient && (get_dist(src,patient) <= 1)) //Patient is next to us, begin treatment!
+ if(mode != BOT_HEALING)
+ mode = BOT_HEALING
+ update_icon()
+ frustration = 0
+ medicate_patient(patient)
+ return
+
+ //Patient has moved away from us!
+ else if(patient && path.len && (get_dist(patient,path[path.len]) > 2))
+ path = list()
+ mode = BOT_IDLE
+ last_found = world.time
+
+ else if(stationary_mode && patient) //Since we cannot move in this mode, ignore the patient and wait for another.
+ soft_reset()
+ return
+
+ if(patient && path.len == 0 && (get_dist(src,patient) > 1))
+ path = get_path_to(src, get_turf(patient), /turf/proc/Distance_cardinal, 0, 30,id=access_card)
+ mode = BOT_MOVING
+ if(!path.len) //try to get closer if you can't reach the patient directly
+ path = get_path_to(src, get_turf(patient), /turf/proc/Distance_cardinal, 0, 30,1,id=access_card)
+ if(!path.len) //Do not chase a patient we cannot reach.
+ soft_reset()
+
+ if(path.len > 0 && patient)
+ if(!bot_move(path[path.len]))
+ oldpatient = patient
+ soft_reset()
+ return
+
+ if(path.len > 8 && patient)
+ frustration++
+
+ if(auto_patrol && !stationary_mode && !patient)
+ if(mode == BOT_IDLE || mode == BOT_START_PATROL)
+ start_patrol()
+
+ if(mode == BOT_PATROL)
+ bot_patrol()
+
+ return
+
+/mob/living/simple_animal/bot/hugbot/UnarmedAttack(atom/A)
+ if(iscarbon(A))
+ var/mob/living/carbon/C = A
+ patient = C
+ mode = BOT_HEALING
+ update_icon()
+ medicate_patient(C)
+ update_icon()
+ else
+ ..()
+
+/mob/living/simple_animal/bot/hugbot/proc/medicate_patient(mob/living/carbon/C)
+ if(!on)
+ return
+
+ if(!istype(C))
+ oldpatient = patient
+ soft_reset()
+ return
+
+ if(C.stat == DEAD || (HAS_TRAIT(C, TRAIT_FAKEDEATH)))
+ oldpatient = patient
+ soft_reset()
+ return
+
+ visible_message("[src] hugs [C] to make [C.p_them()] feel better!", \
+ "You hug [C] to make [C.p_them()] feel better!")
+ SEND_SIGNAL(C, COMSIG_ADD_MOOD_EVENT, "hug", /datum/mood_event/hug)
+
+ if (emagged != 2)
+ C.AdjustStun(-60)
+ C.AdjustKnockdown(-60)
+ C.AdjustUnconscious(-60)
+ C.AdjustSleeping(-100)
+ if(recoveringstam)
+ C.adjustStaminaLoss(-15)
+ else if(resting)
+ C.resting = 0
+ C.update_canmove()
+ else
+ C.Knockdown(100)
+ C.Stun(100)
+ C.update_canmove()
+
+ playsound(C.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
+
+ oldpatient = patient
+ patient = null
+ mode = BOT_IDLE
+ update_icon()
+ return
+
+/mob/living/simple_animal/bot/hugbot/explode()
+ on = FALSE
+ visible_message("[src] blows apart!")
+ do_sparks(3, TRUE, src)
+ ..()
+
+/obj/machinery/bot_core/hugbot
+ req_one_access = list(ACCESS_ROBOTICS)
+
+/obj/item/bot_assembly/hugbot
+ desc = "It's a box of hugs with an arm attached."
+ name = "incomplete hugbot assembly"
+ icon = 'hyperstation/icons/mobs/aibots.dmi'
+ icon_state = "hugbot_arm"
+ created_name = "Hugbot"
+
+/obj/item/bot_assembly/hugbot/attackby(obj/item/W, mob/user, params)
+ ..()
+ if(istype(W, /obj/item/assembly/prox_sensor))
+ if(!can_finish_build(W, user))
+ return
+ var/mob/living/simple_animal/bot/hugbot/A = new(drop_location())
+ A.name = created_name
+ A.robot_arm = W.type
+ to_chat(user, "You add [W] to [src]. Beep boop!")
+ qdel(W)
+ qdel(src)
+
+/obj/item/storage/box/hug/attackby(obj/item/I, mob/user, params)
+ if((istype(I, /obj/item/bodypart/l_arm/robot)) || (istype(I, /obj/item/bodypart/r_arm/robot)))
+ if(contents.len) //prevent accidently deleting contents
+ to_chat(user, "You need to empty [src] out first!")
+ return
+ if(!user.temporarilyRemoveItemFromInventory(I))
+ return
+ qdel(I)
+ to_chat(user, "You add [I] to the [src]! You've got a hugbot assembly now!")
+ var/obj/item/bot_assembly/hugbot/A = new
+ qdel(src)
+ user.put_in_hands(A)
+ else
+ return ..()
diff --git a/hyperstation/code/mobs/mimic.dm b/hyperstation/code/mobs/mimic.dm
new file mode 100644
index 0000000000..1ad5a24abf
--- /dev/null
+++ b/hyperstation/code/mobs/mimic.dm
@@ -0,0 +1,414 @@
+/mob/living/simple_animal/hostile/hs13mimic
+ name = "Mimic"
+ icon = 'hyperstation/icons/mobs/mimic.dmi'
+ desc = "A writhing mass of black flesh, unlikely to be happy to see you."
+ icon_state = "mimic"
+ icon_living = "mimic"
+ icon_dead = "mimic_dead"
+ gender = NEUTER
+ speak_chance = 0
+ maxHealth = 35
+ health = 35
+ turns_per_move = 5
+ move_to_delay = 1
+ speed = 0
+ see_in_dark = 8
+ lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
+ pass_flags = PASSTABLE
+ butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab/xeno = 2)
+ response_help = "prods"
+ response_disarm = "pushes aside"
+ response_harm = "smacks"
+ melee_damage_lower = 8
+ melee_damage_upper = 12
+ attacktext = "stings"
+ attack_sound = 'hyperstation/sound/creatures/mimic/mimic_attack.ogg'
+ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ ventcrawler = VENTCRAWLER_ALWAYS
+ blood_volume = 0
+ faction = list("mimic")
+ gold_core_spawnable = NO_SPAWN
+ vision_range = 1
+ aggro_vision_range = 9
+ wander = TRUE
+ minbodytemp = 250 //weak to cold
+ maxbodytemp = 1500
+ pressure_resistance = 1200
+ sight = SEE_MOBS
+ var/stealthed = TRUE
+ var/knockdown_people = 1
+ var/playerTransformCD = 50
+ var/playerTfTime
+ var/static/mimic_blacklisted_transform_items = typecacheof(list(
+ /obj/item/projectile,
+ /obj/item/radio/intercom,
+ /mob/living/simple_animal/bot))
+ var/transformsound = 'hyperstation/sound/creatures/mimic/mimic_transform.ogg'
+ var/playstyle_string = "You are a mimic, a tricky creature that can take the form of \
+ almost any item nearby by shift-clicking it. While morphed, you move slowly and do less damage. \
+ Finally, you can restore yourself to your original form while morphed by shift-clicking yourself. \
+ Attacking carbon lifeforms will heal you at the cost of destructuring their DNA. \
+ You can also change your form to that of simple animals, but be wary that anyone examining you can \
+ find out."
+
+/mob/living/simple_animal/hostile/hs13mimic/Initialize(mapload)
+ . = ..()
+ trytftorandomobject() // When initialized, make sure they take the form of something.
+
+/mob/living/simple_animal/hostile/hs13mimic/Login()
+ . = ..()
+ SEND_SOUND(src, sound('sound/ambience/antag/ling_aler.ogg'))
+ to_chat(src, src.playstyle_string)
+
+/mob/living/simple_animal/hostile/hs13mimic/attack_hand(mob/living/carbon/human/M)
+ . = ..()
+ if(stealthed && stat == CONSCIOUS)
+ if(M.a_intent == INTENT_HELP)//They're trying to pick us up! We tricked them boys! *plays runescape sea shanty*
+ target = M
+ guaranteedknockdown(M)
+ trigger() // Bring our friends if any!
+
+/mob/living/simple_animal/hostile/hs13mimic/AttackingTarget()
+ . = ..()
+ if(iscarbon(target))
+ var/mob/living/carbon/C = target
+ if(.)
+ if(stealthed && knockdown_people) //Guaranteed knockdown if we get the first hit while disguised. Typically, only players can do this since NPC mimics transform first before attacking.
+ restore()
+ C.Knockdown(40)
+ C.visible_message("\The [src] knocks down \the [C]!", \
+ "\The [src] knocks you down!")
+ else if(knockdown_people && prob(15))
+ C.Knockdown(40)
+ C.visible_message("\The [src] knocks down \the [C]!", \
+ "\The [src] knocks you down!")
+ if(C.nutrition >= 15)
+ C.nutrition -= (rand(7,15)) //They lose 7-15 nutrition
+ adjustBruteLoss(-3) //We heal 3 damage
+ C.adjustCloneLoss(rand(2,4)) //They also take a bit of cellular damage.
+ if(isanimal(target))
+ var/mob/living/simple_animal/A = target
+ if(.)
+ if(stealthed)
+ restore()
+ if(A.stat == CONSCIOUS)
+ adjustBruteLoss(-3) //We heal 3 damage
+
+/mob/living/simple_animal/hostile/hs13mimic/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
+ trigger()
+ . = ..()
+
+/mob/living/simple_animal/hostile/hs13mimic/FindTarget()
+ . = ..()
+ if(.)
+ trigger() //We have a target! Trigger!
+ else if(!target && !stealthed) //Has no target, isn't stealthed, let's search for an object to transform
+ trytftorandomobject()
+
+/mob/living/simple_animal/hostile/hs13mimic/death(gibbed)
+ restore() //We died. Restore form.
+ . = ..()
+
+/mob/living/simple_animal/hostile/hs13mimic/med_hud_set_health()
+ if(stealthed)
+ var/image/holder = hud_list[HEALTH_HUD]
+ holder.icon_state = null
+ return //we hide medical hud while morphed
+ ..()
+
+/mob/living/simple_animal/hostile/hs13mimic/med_hud_set_status()
+ if(stealthed)
+ var/image/holder = hud_list[STATUS_HUD]
+ holder.icon_state = null
+ return //we hide medical hud while morphed
+ ..()
+
+/mob/living/simple_animal/hostile/hs13mimic/proc/mimicTransformList() //The list of default things to transform needs to be bigger, consider this in the future.
+ var/transformitem = rand(1,100)
+ medhudupdate()
+ wander = FALSE
+ vision_range = initial(vision_range)
+ switch(transformitem)
+ if(1 to 10)
+ name = "drinking glass"
+ icon = 'icons/obj/drinks.dmi'
+ icon_state = "glass_empty"
+ desc = "Your standard drinking glass."
+ if(11 to 20)
+ name = "insulated gloves"
+ icon = 'icons/obj/clothing/gloves.dmi'
+ icon_state = "yellow"
+ desc = "These gloves will protect the wearer from electric shock."
+ if(21 to 30)
+ name = "stunbaton"
+ desc = "A stun baton for incapacitating people with."
+ icon = 'icons/obj/items_and_weapons.dmi'
+ icon_state = "stunbaton"
+ if(31 to 40)
+ name = "pen"
+ icon = 'icons/obj/bureaucracy.dmi'
+ icon_state = "pen"
+ desc = "It's a black ink pen, modified for use with both paper and Nanotransen-brand Digital-Readpads™!"
+ if(41 to 50)
+ name = "newspaper"
+ desc = "An issue of The Catpaw, the newspaper circulating aboard GATO stations."
+ icon = 'icons/obj/bureaucracy.dmi'
+ icon_state = "newspaper"
+ if(51 to 60)
+ name = "stechkin pistol" //greytider bait
+ desc = "A small, easily concealable 10mm handgun. Has a threaded barrel for suppressors."
+ icon = 'icons/obj/guns/projectile.dmi'
+ icon_state = "pistol"
+ if(61 to 70)
+ name = "emergency oxygen tank"
+ desc = "Used for emergencies. Contains very little oxygen, so try to conserve it until you actually need it."
+ icon = 'icons/obj/tank.dmi'
+ icon_state = "emergency"
+ if(71 to 80)
+ name = "drinking glass"
+ icon = 'icons/obj/drinks.dmi'
+ icon_state = "glass_empty"
+ desc = "Your standard drinking glass."
+ if(81 to 90)
+ name = "fleshlight"
+ icon = 'hyperstation/icons/obj/fleshlight.dmi'
+ icon_state = "fleshlight_totallynotamimic"
+ desc = "A sex toy disguised as a flashlight, used to stimulate someones penis, complete with colour changing sleeve."
+ if(91 to 100)
+ icon = 'modular_citadel/icons/obj/genitals/dildo.dmi'
+ switch(rand(1,3)) //switch within a switch hmmmmmmmmmm
+ if(1)
+ icon_state = "dildo_knotted_2"
+ name = "small knotted dildo"
+ if(2)
+ icon_state = "dildo_flared_4"
+ name = "huge flared dildo"
+ if(3)
+ icon_state = "dildo_knotted_3"
+ name = "big knotted dildo"
+ desc = "Floppy!"
+
+/mob/living/simple_animal/hostile/hs13mimic/proc/guaranteedknockdown(mob/living/carbon/human/M)
+ M.Knockdown(40)
+ M.visible_message("\The [src] knocks down \the [M]!", \
+ "\The [src] tricks you, knocking you down!")
+
+/mob/living/simple_animal/hostile/hs13mimic/proc/medhudupdate()
+ med_hud_set_health()
+ med_hud_set_status()
+
+/mob/living/simple_animal/hostile/hs13mimic/proc/restore()
+ //back to normal mimic sprite
+ stealthed = FALSE
+ medhudupdate()
+ name = initial(name)
+ icon = 'hyperstation/icons/mobs/mimic.dmi'
+ icon_state = "mimic"
+ desc = initial(desc)
+ speed = initial(speed)
+ wander = TRUE
+ vision_range = 9
+
+/mob/living/simple_animal/hostile/hs13mimic/proc/trigger()
+ if(stealthed && stat == CONSCIOUS)
+ visible_message("The [src] Reveals itself to be a Mimic!")
+ restore()
+ playsound(loc, transformsound, 75, TRUE)
+ triggerOthers(target) // Friends too!
+
+/mob/living/simple_animal/hostile/hs13mimic/proc/triggerOthers(passtarget) //
+ for(var/mob/living/simple_animal/hostile/hs13mimic/C in oview(5, src.loc))
+ if(passtarget && C.target == null && !(isdead(target)))
+ C.target = passtarget
+ C.trigger()
+
+/mob/living/simple_animal/hostile/hs13mimic/proc/trytftorandomobject()
+ stealthed = TRUE
+ medhudupdate()
+ var/list/obj/item/listItems = list()
+ for(var/obj/item/I in oview(9,src.loc))
+ if(allowed(I))
+ listItems += I
+ if(LAZYLEN(listItems))
+ var/obj/item/changedReference = pick(listItems)
+ wander = FALSE
+ vision_range = initial(vision_range)
+ name = changedReference.name
+ icon = changedReference.icon
+ icon_state = changedReference.icon_state
+ desc = changedReference.desc
+ else
+ mimicTransformList() //Couldn't find any valid items, let's go for the default list then.
+
+/mob/living/simple_animal/hostile/hs13mimic/proc/allowed(atom/movable/A)
+ return !is_type_in_typecache(A, mimic_blacklisted_transform_items) && (isitem(A) || isanimal(A))
+
+//One leader mimic spawns per mimic event spawn, they are able to consume and transform themselves into the station's dead pets. Buckle up.
+/mob/living/simple_animal/hostile/hs13mimic/leader
+ var/mob/living/consumptionTarget = null
+ var/consuming = FALSE
+ health = 38 //They have a teeeny tiny more health.
+ maxHealth = 38
+
+/mob/living/simple_animal/hostile/hs13mimic/leader/Life()
+ . = ..()
+ if(!consuming)
+ if(!consumptionTarget)
+ for(var/mob/living/simple_animal/pet/A in oview(5, src))
+ if(A.stat == DEAD)
+ consumptionTarget = A
+ break
+ if(!target && consumptionTarget) //Don't try to consume anything if we're currently attacking something.
+ var/target_distance = get_dist(targets_from, consumptionTarget)
+ if(target_distance > minimum_distance)
+ Goto(consumptionTarget,move_to_delay,minimum_distance)
+ else
+ tryConsume(consumptionTarget)
+
+/mob/living/simple_animal/hostile/hs13mimic/leader/proc/tryConsume(var/mob/living/simple_animal/pet/A)
+ src.visible_message("[A] is being consumed...",
+ "You start to consume the dead [A]...", "You hear strange fleshy sounds.")
+ consuming = TRUE
+ if(do_after(src, 100, target = A))
+ stealthed = TRUE
+ speed = 5
+ wander = TRUE
+ name = A.name
+ desc = A.desc
+ icon = A.icon
+ icon_state = A.icon_living
+ desc += " But something about it seems wrong..."
+ qdel(A)
+ consuming = FALSE
+ consumptionTarget = FALSE
+ return TRUE
+ consuming = FALSE
+ return FALSE
+
+//Player control code
+
+/mob/living/simple_animal/hostile/hs13mimic/ShiftClickOn(atom/movable/A)
+ if(playerTfTime <= world.time && stat == CONSCIOUS)
+ if(A == src)
+ restore()
+ playerTfTime = world.time + playerTransformCD
+ return
+ if(istype(A) && allowed(A))
+ stealthed = TRUE
+ SEND_SOUND(src, sound(transformsound,volume=50))
+ name = A.name
+ icon = A.icon
+ icon_state = A.icon_state
+ desc = A.desc
+ speed = 5
+ playerTfTime = world.time + playerTransformCD
+ if(isanimal(A))
+ var/mob/living/simple_animal/animal = A
+ icon_state = animal.icon_living
+ desc += " But something about it seems wrong..."
+
+ else
+ to_chat(src, "You need to wait a little longer before you can shift into something else!")
+ ..()
+
+//Event control
+
+/datum/round_event_control/mimic_infestation
+ name = "Mimic Infestation"
+ typepath = /datum/round_event/mimic_infestation
+ weight = 5
+ max_occurrences = 1
+ min_players = 15
+
+/datum/round_event/mimic_infestation
+ announceWhen = 200
+ var/static/list/mimic_station_areas_blacklist = typecacheof(/area/space,
+ /area/shuttle,
+ /area/mine,
+ /area/holodeck,
+ /area/ruin,
+ /area/hallway,
+ /area/hallway/primary,
+ /area/hallway/secondary,
+ /area/hallway/secondary/entry,
+ /area/engine/supermatter,
+ /area/engine/atmospherics_engine,
+ /area/engine/engineering/reactor_core,
+ /area/engine/engineering/reactor_control,
+ /area/ai_monitored/turret_protected,
+ /area/layenia/cloudlayer,
+ /area/asteroid/nearstation,
+ /area/science/server,
+ /area/science/explab,
+ /area/science/xenobiology,
+ /area/security/processing)
+ var/spawncount = 1
+ fakeable = FALSE
+
+/datum/round_event/mimic_infestation/setup()
+ announceWhen = rand(announceWhen, announceWhen + 50)
+ spawncount = rand(4, 7)
+
+/datum/round_event/mimic_infestation/announce(fake)
+ priority_announce("Unidentified lifesigns detected aboard [station_name()]. Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", 'sound/ai/aliens.ogg')
+
+/datum/round_event/mimic_infestation/start()
+ var/list/area/stationAreas = list()
+ var/list/area/eligible_areas = list()
+ for(var/area/A in world) // Get the areas in the Z level
+ if(A.z == SSmapping.station_start)
+ stationAreas += A
+ for(var/area/place in stationAreas) // first we check if it's a valid area
+ if(place.outdoors)
+ continue
+ if(place.areasize < 16)
+ continue
+ if(is_type_in_typecache(place, mimic_station_areas_blacklist))
+ continue
+ eligible_areas += place
+ for(var/area/place in eligible_areas) // now we check if there are people in that area
+ var/numOfPeople
+ for(var/mob/living/carbon/H in place)
+ numOfPeople++
+ break
+ if(numOfPeople > 0)
+ eligible_areas -= place
+
+ var/validFound = FALSE
+ var/list/turf/validTurfs = list()
+ var/area/pickedArea
+ while(!validFound || !eligible_areas.len)
+ pickedArea = pick_n_take(eligible_areas)
+ var/list/turf/t = get_area_turfs(pickedArea, SSmapping.station_start)
+ for(var/turf/thisTurf in t) // now we check if it's a closed turf, cold turf or occupied turf and yeet it
+ if(isopenturf(thisTurf))
+ var/turf/open/tempGet = thisTurf
+ if(tempGet.air.temperature <= T0C)
+ t -= thisTurf
+ continue
+ if(isclosedturf(thisTurf))
+ t -= thisTurf
+ else
+ for(var/obj/O in thisTurf)
+ if(O.density && !(istype(O, /obj/structure/table)))
+ t -= thisTurf
+ break
+ if(t.len >= spawncount) //Is the number of available turfs equal or bigger than spawncount?
+ validFound = TRUE
+ validTurfs = t
+
+ if(!eligible_areas.len)
+ message_admins("No eligible areas for spawning mimics.")
+ return WAITING_FOR_SOMETHING
+
+ notify_ghosts("A group of mimics has spawned in [pickedArea]!", source=pickedArea, action=NOTIFY_ATTACK, flashwindow = FALSE)
+ while(spawncount > 0 && validTurfs.len)
+ spawncount--
+ var/turf/pickedTurf = pick_n_take(validTurfs)
+ if(spawncount != 0)
+ var/spawn_type = /mob/living/simple_animal/hostile/hs13mimic
+ spawn_atom_to_turf(spawn_type, pickedTurf, 1, FALSE)
+ else
+ var/spawn_type = /mob/living/simple_animal/hostile/hs13mimic/leader
+ spawn_atom_to_turf(spawn_type, pickedTurf, 1, FALSE)
+ return SUCCESSFUL_SPAWN
diff --git a/hyperstation/code/modules/admin/verbs/hyperverbs.dm b/hyperstation/code/modules/admin/verbs/hyperverbs.dm
new file mode 100644
index 0000000000..615fb14460
--- /dev/null
+++ b/hyperstation/code/modules/admin/verbs/hyperverbs.dm
@@ -0,0 +1,49 @@
+/client/proc/spawn_floor_cluwne()
+ set category = "Fun"
+ set name = "Unleash Targeted Floor Cluwne"
+ set desc = "Pick a specific target. Be warned: spawning more than one may cause issues!"
+ var/target
+
+ if(!check_rights(R_FUN))
+ return
+
+ target = input("Any specific target in mind? Please note only live, non cluwned, human targets are valid.", "Target", target) as null|anything in GLOB.player_list
+ if(target && ishuman(target))
+ var/mob/living/carbon/human/H = target
+ var/mob/living/simple_animal/hostile/floor_cluwne/FC = new /mob/living/simple_animal/hostile/floor_cluwne(H.loc)
+ FC.forced = TRUE
+ FC.Acquire_Victim(H)
+ FC.target = H
+ FC.current_victim = H
+ log_admin("[key_name(usr)] spawned floor cluwne.")
+ message_admins("[key_name(usr)] spawned floor cluwne.")
+ deadchat_broadcast("Floor Cluwne has just been spawned!")
+
+/client/proc/spawn_random_floor_cluwne()
+ set category = "Fun"
+ set name = "Unleash Random Floor Cluwne"
+ set desc = "Goes after a random player in your Z level. Be warned: spawning more than one may cause issues!"
+
+ if(!check_rights(R_FUN))
+ return
+
+ var/turf/T = get_turf(usr)
+ new /mob/living/simple_animal/hostile/floor_cluwne(T)
+ log_admin("[key_name(usr)] spawned a random target floor cluwne.")
+ message_admins("[key_name(usr)] spawned a random target floor cluwne.")
+
+/client/proc/spawn_twitch_plays_clowncar()
+ set category = "Fun"
+ set name = "Spawn Twich Plays: Clown Car Edition"
+ set desc = "A clown car that weights the direction it goes based on the keys ghosts are pushing. Terrible idea? Maybe."
+
+ if(!check_rights(R_FUN))
+ return
+
+ var/turf/T = get_turf(usr)
+ var/obj/vehicle/sealed/car/clowncar/twitch_plays/G = new /obj/vehicle/sealed/car/clowncar/twitch_plays(T)
+ var/area/A = get_area(G)
+ log_admin("[key_name(usr)] spawned a twitch plays clown car.")
+ message_admins("[key_name(usr)] spawned a twitch plays clown car. Some soundtrack is likely recommended for the fuckshit that's about to unfold.")
+ notify_ghosts("A Ghost Controlled Clown Car has been spawned at \the [A.name]! Double click it to orbit and wrestle its direction with other ghosts!", 'yogstation/sound/misc/bikehorn_creepy.ogg', source = G, action = NOTIFY_JUMP, flashwindow = FALSE)
+
diff --git a/hyperstation/code/modules/antagonists/wendigo/datums_hud.dm b/hyperstation/code/modules/antagonists/wendigo/datums_hud.dm
new file mode 100644
index 0000000000..f0ea064184
--- /dev/null
+++ b/hyperstation/code/modules/antagonists/wendigo/datums_hud.dm
@@ -0,0 +1,277 @@
+/*
+Holds things like antag datums, game modes, abilities, and everything
+related to the antag that could be a datum
+*/
+
+//ANTAGONIST
+/datum/antagonist/wendigo
+ name = "wendigo"
+ antagpanel_category = "Wendigo"
+
+/datum/antagonist/wendigo/on_gain()
+ if(istype(owner.current, /mob/living/carbon/human))
+ var/mob/living/carbon/wendigo/new_owner = new/mob/living/carbon/wendigo(get_turf(owner.current))
+ var/mob/current_body = owner.current
+ current_body.transfer_ckey(new_owner)
+ current_body.Destroy()
+ owner = new_owner.mind
+ owner.current = new_owner
+ ..()
+
+
+//HUD
+//Contents: Intentions, Hands, Dropping/Throwing/Pulling, Inventory Equip
+// Health + Souls on the bottom of screen
+//TODO: Health doll, Soul counter (not devil)
+
+/datum/hud/wendigo/New(mob/living/carbon/wendigo/owner)
+ ..()
+ var/obj/screen/using
+ var/obj/screen/inventory/inv_box
+
+ var/widescreenlayout = FALSE //adds support for different hud layouts depending on widescreen pref
+ if(owner.client && owner.client.prefs && owner.client?.prefs?.widescreenpref)
+ widescreenlayout = FALSE
+
+ //CRAFTING
+ using = new /obj/screen/craft
+ using.icon = ui_style
+ if(!widescreenlayout)
+ using.screen_loc = ui_boxcraft
+ static_inventory += using
+
+ //LANG MENU
+ using = new/obj/screen/language_menu
+ using.icon = ui_style
+ if(!widescreenlayout)
+ using.screen_loc = ui_boxlang
+ static_inventory += using
+
+ //AREA EDITOR
+ using = new /obj/screen/area_creator
+ using.icon = ui_style
+ if(!widescreenlayout)
+ using.screen_loc = ui_boxarea
+ static_inventory += using
+
+ using = new /obj/screen/voretoggle()
+ using.icon = tg_ui_icon_to_cit_ui(ui_style)
+ using.screen_loc = ui_voremode
+ if(!widescreenlayout)
+ using.screen_loc = ui_boxvore
+ static_inventory += using
+
+ //INTENTS & ACTIONS
+ action_intent = new /obj/screen/act_intent/segmented
+ action_intent.icon_state = mymob.a_intent
+ static_inventory += action_intent
+
+ using = new /obj/screen/human/equip()
+ using.icon = ui_style
+ using.screen_loc = ui_equip_position(mymob)
+ static_inventory += using
+
+ using = new /obj/screen/mov_intent
+ using.icon = tg_ui_icon_to_cit_ui(ui_style)
+ using.icon_state = (mymob.m_intent == MOVE_INTENT_RUN ? "running" : "walking")
+ using.screen_loc = ui_movi
+ static_inventory += using
+
+ using = new /obj/screen/resist()
+ using.icon = ui_style
+ using.screen_loc = ui_overridden_resist
+ hotkeybuttons += using
+
+ using = new /obj/screen/restbutton()
+ using.icon = tg_ui_icon_to_cit_ui(ui_style)
+ using.screen_loc = ui_pull_resist
+ static_inventory += using
+
+ using = new /obj/screen/combattoggle()
+ using.icon = tg_ui_icon_to_cit_ui(ui_style)
+ using.screen_loc = ui_combat_toggle
+ static_inventory += using
+
+ using = new /obj/screen/drop()
+ using.icon = ui_style
+ using.screen_loc = ui_drop_throw
+ static_inventory += using
+
+ pull_icon = new /obj/screen/pull()
+ pull_icon.icon = ui_style
+ pull_icon.update_icon(mymob)
+ pull_icon.screen_loc = ui_pull_resist
+ static_inventory += pull_icon
+
+ throw_icon = new /obj/screen/throw_catch()
+ throw_icon.icon = ui_style
+ throw_icon.screen_loc = ui_drop_throw
+ hotkeybuttons += throw_icon
+
+//////
+ //INVENTORY
+//////
+ build_hand_slots()
+
+ using = new /obj/screen/human/toggle()
+ using.icon = ui_style
+ using.screen_loc = ui_inventory
+ static_inventory += using
+
+ using = new /obj/screen/swap_hand()
+ using.icon = ui_style
+ using.icon_state = "swap_1"
+ using.screen_loc = ui_swaphand_position(owner,1)
+ static_inventory += using
+
+ using = new /obj/screen/swap_hand()
+ using.icon = ui_style
+ using.icon_state = "swap_2"
+ using.screen_loc = ui_swaphand_position(owner,2)
+ static_inventory += using
+
+ inv_box = new /obj/screen/inventory()
+ inv_box.name = "neck"
+ inv_box.icon = ui_style
+ inv_box.icon_state = "neck"
+ inv_box.screen_loc = ui_neck
+ inv_box.slot_id =ITEM_SLOT_NECK
+ toggleable_inventory += inv_box
+
+ inv_box = new /obj/screen/inventory()
+ inv_box.name = "back"
+ inv_box.icon = ui_style
+ inv_box.icon_state = "back"
+ inv_box.screen_loc = ui_back
+ inv_box.slot_id =ITEM_SLOT_BACK
+ static_inventory += inv_box
+
+ inv_box = new /obj/screen/inventory()
+ inv_box.name = "gloves"
+ inv_box.icon = ui_style
+ inv_box.icon_state = "gloves"
+ inv_box.screen_loc = ui_gloves
+ inv_box.slot_id =ITEM_SLOT_GLOVES
+ toggleable_inventory += inv_box
+
+ inv_box = new /obj/screen/inventory()
+ inv_box.name = "eyes"
+ inv_box.icon = ui_style
+ inv_box.icon_state = "glasses"
+ inv_box.screen_loc = ui_glasses
+ inv_box.slot_id =ITEM_SLOT_GLASSES
+ toggleable_inventory += inv_box
+
+ inv_box = new /obj/screen/inventory()
+ inv_box.name = "ears"
+ inv_box.icon = ui_style
+ inv_box.icon_state = "ears"
+ inv_box.screen_loc = ui_ears
+ inv_box.slot_id =ITEM_SLOT_EARS
+ toggleable_inventory += inv_box
+
+ inv_box = new /obj/screen/inventory()
+ inv_box.name = "head"
+ inv_box.icon = ui_style
+ inv_box.icon_state = "head"
+ inv_box.screen_loc = ui_head
+ inv_box.slot_id =ITEM_SLOT_HEAD
+ toggleable_inventory += inv_box
+
+ inv_box = new /obj/screen/inventory()
+ inv_box.name = "belt"
+ inv_box.icon = ui_style
+ inv_box.icon_state = "belt"
+ inv_box.screen_loc = ui_belt
+ inv_box.slot_id =ITEM_SLOT_BELT
+ static_inventory += inv_box
+
+ //INFO DISPLAY
+
+ internals = new /obj/screen/internals()
+ infodisplay += internals
+
+ healths = new /obj/screen/healths()
+ infodisplay += healths
+
+ healthdoll = new /obj/screen/healthdoll()
+ infodisplay += healthdoll
+
+ zone_select = new /obj/screen/zone_sel()
+ zone_select.icon = ui_style
+ zone_select.update_icon(mymob)
+ static_inventory += zone_select
+
+ for(var/obj/screen/inventory/inv in (static_inventory + toggleable_inventory))
+ if(inv.slot_id)
+ inv.hud = src
+ inv_slots[inv.slot_id] = inv
+ inv.update_icon()
+
+/datum/hud/wendigo/hidden_inventory_update(mob/viewer)
+ if(!mymob)
+ return
+ var/mob/living/carbon/wendigo/W = mymob
+
+ var/mob/screenmob = viewer || W
+
+ if(screenmob.hud_used.inventory_shown && screenmob.hud_used.hud_shown)
+ if(W.gloves)
+ W.gloves.screen_loc = ui_gloves
+ screenmob.client.screen += W.gloves
+ if(W.ears)
+ W.ears.screen_loc = ui_ears
+ screenmob.client.screen += W.ears
+ if(W.glasses)
+ W.glasses.screen_loc = ui_glasses
+ screenmob.client.screen += W.glasses
+ if(W.wear_neck)
+ W.wear_neck.screen_loc = ui_neck
+ screenmob.client.screen += W.wear_neck
+ if(W.head)
+ W.head.screen_loc = ui_head
+ screenmob.client.screen += W.head
+ else
+ if(W.gloves) screenmob.client.screen -= W.gloves
+ if(W.ears) screenmob.client.screen -= W.ears
+ if(W.glasses) screenmob.client.screen -= W.glasses
+ if(W.wear_neck) screenmob.client.screen -= W.wear_neck
+ if(W.head) screenmob.client.screen -= W.head
+
+/datum/hud/wendigo/persistent_inventory_update(mob/viewer)
+ if(!mymob)
+ return
+ ..()
+ var/mob/living/carbon/wendigo/W = mymob
+
+ var/mob/screenmob = viewer || W
+
+ if(screenmob.hud_used)
+ if(screenmob.hud_used.hud_shown)
+ if(W.belt)
+ W.belt.screen_loc = ui_belt
+ screenmob.client.screen += W.belt
+ if(W.back)
+ W.back.screen_loc = ui_back
+ screenmob.client.screen += W.back
+ else
+ if(W.belt)
+ screenmob.client.screen -= W.belt
+ if(W.back)
+ screenmob.client.screen -= W.back
+
+ if(hud_version != HUD_STYLE_NOHUD)
+ for(var/obj/item/I in W.held_items)
+ I.screen_loc = ui_hand_position(W.get_held_index_of_item(I))
+ screenmob.client.screen += I
+ else
+ for(var/obj/item/I in W.held_items)
+ I.screen_loc = null
+ screenmob.client.screen -= I
+
+/datum/hud/wendigo/show_hud(version = 0,mob/viewmob)
+ . = ..()
+ if(!.)
+ return
+ var/mob/screenmob = viewmob || mymob
+ hidden_inventory_update(screenmob)
diff --git a/hyperstation/code/modules/antagonists/wendigo/forest/structures/soul_storage.dm b/hyperstation/code/modules/antagonists/wendigo/forest/structures/soul_storage.dm
new file mode 100644
index 0000000000..e8a54be3be
--- /dev/null
+++ b/hyperstation/code/modules/antagonists/wendigo/forest/structures/soul_storage.dm
@@ -0,0 +1,14 @@
+/obj/structure/soul_storage
+ name = "soul storage"
+ desc = "Stores souls!"
+ var/list/souls = list()
+
+//This is a big WIP. I don't even want it having the name "soul storage" but it'll do for now
+
+/obj/structure/soul_storage/Initialize(mapload)
+ . = ..()
+ GLOB.wendigo_soul_storages += src
+
+/obj/structure/soul_storage/Destroy()
+ GLOB.wendigo_soul_storages -= src
+ return ..()
diff --git a/hyperstation/code/modules/antagonists/wendigo/mob/_on_click.dm b/hyperstation/code/modules/antagonists/wendigo/mob/_on_click.dm
new file mode 100644
index 0000000000..13257de9e9
--- /dev/null
+++ b/hyperstation/code/modules/antagonists/wendigo/mob/_on_click.dm
@@ -0,0 +1,11 @@
+/mob/living/carbon/wendigo/UnarmedAttack(atom/A, proximity)
+ A.attack_hand(src)
+
+/mob/living/carbon/wendigo/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE)
+ if(incapacitated() || lying)
+ to_chat(src, "You can't do that right now!")
+ return FALSE
+ if(be_close && !in_range(M, src))
+ to_chat(src, "You are too far away!")
+ return FALSE
+ return TRUE
diff --git a/hyperstation/code/modules/antagonists/wendigo/mob/defines_init.dm b/hyperstation/code/modules/antagonists/wendigo/mob/defines_init.dm
new file mode 100644
index 0000000000..b535492afe
--- /dev/null
+++ b/hyperstation/code/modules/antagonists/wendigo/mob/defines_init.dm
@@ -0,0 +1,115 @@
+#define DEFAULT_MOVESPEED 1.5
+//The mob itself
+/mob/living/carbon/wendigo
+ name = "wendigo"
+ gender = FEMALE
+ unique_name = FALSE
+ hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPLOYAL_HUD,IMPCHEM_HUD,IMPTRACK_HUD, NANITE_HUD, DIAG_NANITE_FULL_HUD,ANTAG_HUD,GLAND_HUD,SENTIENT_DISEASE_HUD)
+ hud_type = /datum/hud/wendigo
+ possible_a_intents = list(INTENT_HELP, INTENT_DISARM, INTENT_GRAB, INTENT_HARM)
+ appearance_flags = TILE_BOUND|PIXEL_SCALE|LONG_GLIDE
+ status_flags = CANSTUN //cant be knocked down, unconscious, or be pushed
+ maxHealth = 200
+ icon = 'hyperstation/icons/mobs/wendigo.dmi'
+ icon_state = "reference"
+
+ bodyparts = list(/obj/item/bodypart/chest/wendigo, /obj/item/bodypart/head/wendigo,
+ /obj/item/bodypart/l_arm/wendigo, /obj/item/bodypart/r_arm/wendigo,
+ /obj/item/bodypart/l_leg/wendigo, /obj/item/bodypart/r_leg/wendigo)
+ var/obj/item/belt = null
+ var/datum/physiology/physiology
+ var/obj/structure/soul_storage/connected_link
+ var/fake_breast_size = 5 //She doesn't actually have the organ, but we cache her breast size for rp reasons
+ var/fake_penis_size = 8
+
+/mob/living/carbon/wendigo/Initialize(mapload)
+ . = ..()
+ /* //TODO: Uncomment when objectives + forest get finished
+ if(!connected_link)
+ if(!GLOB.wendigo_soul_storages.len)
+ connected_link = new /obj/structure/soul_storage(get_turf(src))
+ else
+ connected_link = pick(GLOB.wendigo_soul_storages)
+ gender = pick(FEMALE, MALE)
+ */
+ if(gender == MALE)
+ fake_breast_size = 0
+ else
+ fake_penis_size = 0
+ real_name = name
+ //DAMAGE RESISTANCE
+ physiology = new()
+ physiology.brute_mod = 0.7
+ physiology.burn_mod = 1.25
+ physiology.tox_mod = -1
+ physiology.oxy_mod = 1
+ physiology.clone_mod = 0
+ physiology.stamina_mod = 0 //Running and attacking, prods
+ physiology.heat_mod = 2 //IM MELTIIINNNGGGG-!
+ physiology.cold_mod = 0
+ physiology.siemens_coeff = 0.2
+ physiology.stun_mod = 0 //prods n aggressive grab
+ //physiology.bleed_mod = 2
+ physiology.speed_mod = 0.9 //Should be faster than a normal person's walking speed
+ physiology.hunger_mod = 2 //We're gonna have a FEAST TONIGHT!
+ physiology.do_after_speed = 1
+
+ //Traits & Bodyparts
+ create_bodyparts()
+ create_internal_organs()
+ ADD_TRAIT(src, TRAIT_NOCLONE, "initialize")
+ add_verb(src, /mob/living/proc/mob_sleep)
+ add_verb(src, /mob/living/proc/lay_down)
+ add_movespeed_modifier("MOVESPEED_DEFAULT", multiplicative_slowdown=DEFAULT_MOVESPEED)
+ update_body_parts()
+
+/mob/living/carbon/wendigo/Destroy()
+ QDEL_NULL(physiology)
+ return ..()
+
+/mob/living/carbon/wendigo/update_body_parts()
+ for(var/X in bodyparts)
+ var/obj/item/bodypart/BP = X
+ BP.update_limb()
+
+/mob/living/carbon/wendigo/update_move_intent_slowdown()
+ var/mod = 0
+ if(m_intent == MOVE_INTENT_WALK)
+ mod = (CONFIG_GET(number/movedelay/walk_delay) / 2) //complete copy besides this part
+ else
+ mod = 1.30
+ if(!isnum(mod))
+ mod = 1
+ add_movespeed_modifier(MOVESPEED_ID_MOB_WALK_RUN_CONFIG_SPEED, TRUE, 100, override = TRUE, multiplicative_slowdown = mod)
+
+/mob/living/carbon/wendigo/update_movespeed(resort=TRUE)
+ return (..() * physiology.speed_mod)
+
+/mob/living/carbon/wendigo/can_hold_items()
+ return TRUE
+/mob/living/carbon/wendigo/IsAdvancedToolUser()
+ return TRUE
+/mob/living/carbon/wendigo/can_be_pulled()
+ return FALSE
+/mob/living/carbon/wendigo/is_literate()
+ return TRUE
+/mob/living/carbon/wendigo/canBeHandcuffed()
+ return TRUE
+/mob/living/carbon/wendigo/update_inv_handcuffed()
+ return
+/mob/living/carbon/wendigo/update_inv_legcuffed()
+ return
+
+//
+//ORGANS
+//
+/mob/living/carbon/wendigo/create_internal_organs()
+ internal_organs += new /obj/item/organ/eyes/wendigo
+ internal_organs += new /obj/item/organ/liver/wendigo
+
+ internal_organs += new /obj/item/organ/tongue
+ internal_organs += new /obj/item/organ/lungs
+ internal_organs += new /obj/item/organ/heart
+ internal_organs += new /obj/item/organ/stomach
+ internal_organs += new /obj/item/organ/ears
+ ..()
diff --git a/hyperstation/code/modules/antagonists/wendigo/mob/examine.dm b/hyperstation/code/modules/antagonists/wendigo/mob/examine.dm
new file mode 100644
index 0000000000..f32f4eb9c6
--- /dev/null
+++ b/hyperstation/code/modules/antagonists/wendigo/mob/examine.dm
@@ -0,0 +1,86 @@
+/mob/living/carbon/wendigo/examine(mob/user)
+ var/t_He = p_they(TRUE)
+ var/t_His = p_their(TRUE)
+ var/t_his = p_their()
+ var/t_him = p_them()
+
+ . = list("*---------*\nThis is [icon2html(src, user)] \a [src]!")
+
+ if(handcuffed)
+ . += "[t_He] is [icon2html(handcuffed, user)] handcuffed!"
+ if(legcuffed)
+ . += "[t_He] has [icon2html(legcuffed, user)] leg cuffs!"
+ if(head)
+ . += "[t_He] is wearing [head.get_examine_string(user)] on [t_his] head."
+ if(wear_neck)
+ . += "[t_He] is wearing [wear_neck.get_examine_string(user)] around [t_his] neck."
+
+ for(var/obj/item/I in held_items)
+ if(!(I.item_flags & ABSTRACT))
+ . += "[t_He] is holding [I.get_examine_string(user)] in [t_his] [get_held_index_name(get_held_index_of_item(I))]."
+
+ if(back)
+ . += "[t_He] has [back.get_examine_string(user)] on [t_his] back."
+
+ if(stat == DEAD)
+ . += "[t_He] is limp and unresponsive, with no signs of life."
+
+ var/list/msg = ("")
+ var/temp_hp = getBruteLoss()
+ if(!(user == src && src.hal_screwyhud == SCREWYHUD_HEALTHY))
+ if(temp_hp)
+ if(temp_hp <= 25) msg += "[t_He] has minor bruising.\n"
+ else if(temp_hp <= 75) msg += "[t_He] has moderate bruising!\n"
+ else msg += "[t_He] has severe bruising!\n"
+ temp_hp = getFireLoss()
+ if(temp_hp)
+ if(temp_hp <= 25) msg += "[t_He] has minor burns.\n"
+ else if(temp_hp <= 50) msg += "[t_He] has moderate burns!\n"
+ else msg += "[t_He] has severe burns!\n"
+ if(fire_stacks > 0)
+ msg += "[t_He] is covered in something flammable.\n"
+ if(fire_stacks < 0)
+ msg += "[t_He] looks a little soaked.\n"
+ if(pulledby)
+ if(pulledby.grab_state)
+ msg += "[t_He] is restrained by [pulledby]'s grip.\n"
+
+ msg += ""
+ . += msg
+
+ if(stat == UNCONSCIOUS)
+ . += "[t_He] isn't responding to anything around [t_him] and seems to be asleep."
+ else if(InCritical())
+ . += "[t_His] breathing is shallow and labored."
+
+
+ if(fake_breast_size < 16)
+ . += "You see a pair of breasts. You estimate them to be [ascii2text(round(fake_breast_size)+63)]-cups."
+ else if(!fake_breast_size && gender == FEMALE)
+ . += "You see a pair of breasts. They're small and flatchested, however."
+ else
+ . += "You see [pick("some serious honkers", "a real set of badonkers", "some dobonhonkeros", "massive dohoonkabhankoloos", "two big old tonhongerekoogers", "a couple of giant bonkhonagahoogs", "a pair of humongous hungolomghnonoloughongous")]. Their volume is way beyond cupsize now, measuring in about [round(fake_breast_size)]cm in diameter."
+
+ if(fake_penis_size)
+ . += "You see a flaccid human penis. You estimate it's about [round(fake_penis_size, 0.25)] inch[round(fake_penis_size, 0.25) != 1 ? "es" : ""] long." //would put girth here but lazy
+
+ if(connected_link)
+ if(connected_link.souls.len > 5)
+ . += "[t_His] eyes are glowing a deadly red."
+ else
+ var/A = ""
+ switch(connected_link.souls.len)
+ if(0)
+ A += "[t_He] looks malnourished and weak."
+ if(1)
+ A += "[t_He] looks hungry."
+ if(2 to 4)
+ A += "[t_He] is salivating."
+ if(5)
+ A += "[t_His] eyes are glowing red."
+ . += "[A]"
+ else
+ . += "[t_He] looks lost."
+
+ SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, .)
+ . += "*---------*"
diff --git a/hyperstation/code/modules/antagonists/wendigo/mob/metabolization.dm b/hyperstation/code/modules/antagonists/wendigo/mob/metabolization.dm
new file mode 100644
index 0000000000..547151cc4f
--- /dev/null
+++ b/hyperstation/code/modules/antagonists/wendigo/mob/metabolization.dm
@@ -0,0 +1,55 @@
+//Snowflake metabolization with naughty organ manipulation
+/mob/living/carbon/wendigo/proc/metabolize_hunger()
+ if(HAS_TRAIT(src, TRAIT_NOHUNGER))
+ return
+
+ if(connected_link)
+ if(connected_link.souls.len > 3)
+ nutrition = min(800, nutrition + (HUNGER_FACTOR*connected_link.souls.len))
+ nutrition = max(0, nutrition - (HUNGER_FACTOR / (physiology.hunger_mod / (connected_link.souls.len + 1))))
+ else
+ nutrition = max(0, nutrition - (HUNGER_FACTOR / physiology.hunger_mod))
+
+ switch(nutrition)
+ if(NUTRITION_LEVEL_WELL_FED to INFINITY)
+ throw_alert("nutrition", /obj/screen/alert/full)
+ if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FED)
+ throw_alert("nutrition", /obj/screen/alert/sated)
+ if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY)
+ throw_alert("nutrition", /obj/screen/alert/hungry)
+ if(0 to NUTRITION_LEVEL_STARVING)
+ throw_alert("nutrition", /obj/screen/alert/starving)
+
+/mob/living/carbon/wendigo/reagent_check(datum/reagent/R)
+ if(istype(R, /datum/reagent/fermi))
+ var/had_changed = FALSE
+ if(R.type == /datum/reagent/fermi/penis_enlarger)
+ fake_penis_size += 0.25
+ had_changed = "penis_enlarger"
+ if(R.type == /datum/reagent/fermi/PEsmaller)
+ fake_penis_size = max(0, fake_penis_size - 0.25)
+ had_changed = "PEsmaller"
+ if(R.type == /datum/reagent/fermi/breast_enlarger)
+ fake_breast_size += 0.25
+ had_changed = "breast_enlarger"
+ if(R.type == /datum/reagent/fermi/BEsmaller)
+ fake_breast_size = max(0, fake_breast_size - 0.25)
+ had_changed = "BEsmaller"
+
+ if(had_changed)
+ R.volume -= R.metabolization_rate
+ if(round(fake_penis_size, 1) == fake_penis_size && fake_penis_size)
+ switch(had_changed)
+ if("penis_enlarger")
+ to_chat(src, "Your [pick(GLOB.gentlemans_organ_names)] [pick("swells up to", "flourishes into", "expands into", "bursts forth into", "grows eagerly into", "amplifys into")] a [fake_penis_size] inch penis.")
+ if("PEsmaller")
+ if(fake_penis_size > 0)
+ to_chat(src, "Your [pick(GLOB.gentlemans_organ_names)] [pick("shrinks down to", "decreases into", "diminishes into", "deflates into", "shrivels regretfully into", "contracts into")] a [fake_penis_size] inch penis.")
+ else if(round(fake_breast_size, 1) == fake_breast_size && fake_breast_size)
+ switch(had_changed)
+ if("breast_enlarger")
+ to_chat(src, "Your breasts [pick("swell up to", "flourish into", "expand into", "burst forth into", "grow eagerly into", "amplify into")] a [ascii2text(round(fake_breast_size)+63)]-cup.")
+ if("BEsmaller)")
+ if(fake_breast_size > 0)
+ to_chat(src, "Your breasts [pick("shrink down to", "decrease into", "diminish into", "deflate into", "shrivel regretfully into", "contracts into")] a [ascii2text(round(fake_breast_size)+63)]-cup.")
+ return TRUE
diff --git a/hyperstation/code/modules/antagonists/wendigo/mob/mob_defense.dm b/hyperstation/code/modules/antagonists/wendigo/mob/mob_defense.dm
new file mode 100644
index 0000000000..d59e4bf479
--- /dev/null
+++ b/hyperstation/code/modules/antagonists/wendigo/mob/mob_defense.dm
@@ -0,0 +1,125 @@
+/mob/living/carbon/wendigo/handle_environment(datum/gas_mixture/environment)
+ if(!environment)
+ return
+
+ var/loc_temp = get_temperature(environment)
+
+ if(stat != DEAD)
+ adjust_bodytemperature(natural_bodytemperature_stabilization())
+
+ if(!on_fire) //If you're on fire, you do not heat up or cool down based on surrounding gases
+ if(loc_temp < bodytemperature)
+ adjust_bodytemperature(max((loc_temp - bodytemperature) / BODYTEMP_COLD_DIVISOR, BODYTEMP_COOLING_MAX))
+ else
+ adjust_bodytemperature(min((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR, BODYTEMP_HEATING_MAX))
+
+
+ if(bodytemperature > BODYTEMP_HEAT_DAMAGE_LIMIT && !HAS_TRAIT(src, TRAIT_RESISTHEAT))
+ switch(bodytemperature)
+ if(280 to 300)
+ throw_alert("temp", /obj/screen/alert/hot, 1)
+ apply_damage(HEAT_DAMAGE_LEVEL_1*physiology.heat_mod, BURN)
+ if(300 to 350)
+ throw_alert("temp", /obj/screen/alert/hot, 2)
+ apply_damage(HEAT_DAMAGE_LEVEL_2*physiology.heat_mod, BURN)
+ if(350 to INFINITY)
+ throw_alert("temp", /obj/screen/alert/hot, 3)
+ if(on_fire)
+ apply_damage(HEAT_DAMAGE_LEVEL_3*physiology.heat_mod, BURN)
+ else
+ apply_damage(HEAT_DAMAGE_LEVEL_2*physiology.heat_mod, BURN)
+
+ else if(bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT && !HAS_TRAIT(src, TRAIT_RESISTCOLD))
+ if(!istype(loc, /obj/machinery/atmospherics/components/unary/cryo_cell))
+ switch(bodytemperature)
+ if(200 to 260)
+ throw_alert("temp", /obj/screen/alert/cold, 1)
+ apply_damage(COLD_DAMAGE_LEVEL_1*physiology.cold_mod, BURN)
+ if(0 to 120)
+ throw_alert("temp", /obj/screen/alert/cold, 2)
+ apply_damage(COLD_DAMAGE_LEVEL_2*physiology.cold_mod, BURN)
+ if(-INFINITY to 0)
+ throw_alert("temp", /obj/screen/alert/cold, 3)
+ apply_damage(COLD_DAMAGE_LEVEL_3*physiology.cold_mod, BURN)
+ else
+ clear_alert("temp")
+
+ else
+ clear_alert("temp")
+
+ //Account for massive pressure differences
+
+ var/pressure = environment.return_pressure()
+ var/adjusted_pressure = calculate_affecting_pressure(pressure) //Returns how much pressure actually affects the mob.
+ switch(adjusted_pressure)
+ if(HAZARD_HIGH_PRESSURE to INFINITY)
+ adjustBruteLoss(min((((adjusted_pressure / HAZARD_HIGH_PRESSURE)-1)*PRESSURE_DAMAGE_COEFFICIENT)*physiology.pressure_mod, MAX_HIGH_PRESSURE_DAMAGE))
+ throw_alert("pressure", /obj/screen/alert/highpressure, 2)
+ if(WARNING_HIGH_PRESSURE to HAZARD_HIGH_PRESSURE)
+ throw_alert("pressure", /obj/screen/alert/highpressure, 1)
+ if(WARNING_LOW_PRESSURE to WARNING_HIGH_PRESSURE)
+ clear_alert("pressure")
+ if(HAZARD_LOW_PRESSURE to WARNING_LOW_PRESSURE)
+ throw_alert("pressure", /obj/screen/alert/lowpressure, 1)
+ else
+ adjustBruteLoss( LOW_PRESSURE_DAMAGE*physiology.pressure_mod )
+ throw_alert("pressure", /obj/screen/alert/lowpressure, 2)
+
+ return
+
+/mob/living/carbon/wendigo/handle_breath_temperature(datum/gas_mixture/breath)
+ if(abs(BODYTEMP_NORMAL - breath.temperature) > 50)
+ switch(breath.temperature)
+ if(-INFINITY to 0)
+ adjustFireLoss(3)
+ if(0 to 120) //We flourish in a cold environment
+ adjustFireLoss(1.5)
+ if(298 to 350) //But hoo boy do we not like the heat
+ adjustFireLoss(2)
+ if(350 to 800)
+ adjustFireLoss(3)
+ if(800 to INFINITY)
+ adjustFireLoss(8)
+
+/mob/living/carbon/wendigo/handle_fire()
+ if(..())
+ return
+ adjust_bodytemperature(BODYTEMP_HEATING_MAX*physiology.heat_mod)
+ return
+
+/mob/living/carbon/wendigo/adjustBruteLoss(amount, updating_health, forced)
+ return ..(amount*physiology.brute_mod, updating_health, forced)
+
+/mob/living/carbon/wendigo/adjustFireLoss(amount, updating_health, forced)
+ return ..(amount*physiology.burn_mod, updating_health, forced)
+
+/mob/living/carbon/wendigo/adjustToxLoss(amount, updating_health, forced)
+ return ..(amount*physiology.tox_mod, updating_health, forced)
+
+/mob/living/carbon/wendigo/adjustOxyLoss(amount, updating_health, forced)
+ return ..(amount*physiology.oxy_mod, updating_health, forced)
+
+/mob/living/carbon/wendigo/adjustCloneLoss(amount, updating_health, forced)
+ return ..(amount*physiology.clone_mod, updating_health, forced)
+
+/mob/living/carbon/wendigo/adjustStaminaLoss(amount, updating_health, forced)
+ return ..(amount*physiology.stamina_mod, updating_health, forced)
+
+/mob/living/carbon/wendigo/do_after_coefficent()
+ . = ..()
+ . *= physiology.do_after_speed
+
+/mob/living/carbon/wendigo/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, override = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
+ return ..(shock_damage, source, physiology.siemens_coeff, safety, override, tesla_shock, illusion, stun)
+
+/mob/living/carbon/wendigo/Stun(amount, updating, ignore_canstun)
+ return ..(amount*physiology.stun_mod, updating, ignore_canstun)
+
+/mob/living/carbon/wendigo/Knockdown(amount, updating, ignore_canknockdown, override_hardstun, override_stamdmg)
+ return ..(amount*physiology.stun_mod, updating, ignore_canknockdown, override_hardstun, override_stamdmg)
+
+/mob/living/carbon/wendigo/Unconscious(amount, updating, ignore_canunconscious)
+ return ..(amount*physiology.stun_mod, updating, ignore_canunconscious)
+
+/mob/living/carbon/wendigo/bleed(amt)
+ return ..(amt*physiology.bleed_mod)
diff --git a/hyperstation/code/modules/antagonists/wendigo/mob/mob_equip.dm b/hyperstation/code/modules/antagonists/wendigo/mob/mob_equip.dm
new file mode 100644
index 0000000000..68c146592e
--- /dev/null
+++ b/hyperstation/code/modules/antagonists/wendigo/mob/mob_equip.dm
@@ -0,0 +1,100 @@
+/mob/living/carbon/wendigo/can_equip(obj/item/I, slot, disable_warning)
+ switch(slot)
+ if(SLOT_HANDS)
+ if(get_empty_held_indexes())
+ return TRUE
+ return FALSE
+ if(SLOT_GLASSES)
+ if(glasses)
+ return FALSE
+ if(!(I.slot_flags & ITEM_SLOT_EYES))
+ return FALSE
+ return TRUE
+ if(SLOT_NECK)
+ if(wear_neck)
+ return FALSE
+ if(!(I.slot_flags & ITEM_SLOT_NECK))
+ return FALSE
+ return TRUE
+ if(SLOT_EARS)
+ if(ears)
+ return FALSE
+ if(!(I.slot_flags & ITEM_SLOT_EARS))
+ return FALSE
+ return TRUE
+ if(SLOT_GLOVES)
+ if(gloves)
+ return FALSE
+ if(!(I.slot_flags & ITEM_SLOT_GLOVES))
+ return FALSE
+ return TRUE
+ if(SLOT_BACK)
+ if(back)
+ return FALSE
+ if(!(I.slot_flags & ITEM_SLOT_BACK))
+ return FALSE
+ return TRUE
+ if(SLOT_IN_BACKPACK)
+ if(back)
+ if(SEND_SIGNAL(back, COMSIG_TRY_STORAGE_CAN_INSERT, I, src, TRUE))
+ return TRUE
+ return FALSE
+ if(SLOT_HEAD)
+ if(head)
+ return FALSE
+ if(!(I.slot_flags & ITEM_SLOT_HEAD))
+ return FALSE
+ return TRUE
+ if(SLOT_BELT)
+ if(belt)
+ return FALSE
+ if(!(I.slot_flags & ITEM_SLOT_BELT))
+ return FALSE
+ return TRUE
+ return FALSE
+
+/mob/living/carbon/wendigo/equip_to_slot(obj/item/I, slot)
+ if(!..())
+ return
+ switch(slot)
+ if(SLOT_GLASSES)
+ glasses = I
+ update_inv_glasses()
+ if(SLOT_EARS)
+ ears = I
+ update_inv_ears()
+ if(SLOT_GLOVES)
+ gloves = I
+ update_inv_gloves()
+ return TRUE
+
+/mob/living/carbon/wendigo/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE)
+ . = ..()
+ if(!. || !I)
+ return
+
+ else if(I == gloves)
+ gloves = null
+ if(!QDELETED(src))
+ update_inv_gloves()
+ else if(I == glasses)
+ glasses = null
+ var/obj/item/clothing/glasses/G = I
+ if(G.tint)
+ update_tint()
+ if(G.vision_correction)
+ if(HAS_TRAIT(src, TRAIT_NEARSIGHT))
+ overlay_fullscreen("nearsighted", /obj/screen/fullscreen/impaired, 1)
+ adjust_eye_damage(0)
+ if(G.vision_flags || G.darkness_view || G.invis_override || G.invis_view || !isnull(G.lighting_alpha))
+ update_sight()
+ if(!QDELETED(src))
+ update_inv_glasses()
+ else if(I == ears)
+ ears = null
+ if(!QDELETED(src))
+ update_inv_ears()
+ else if(I == belt)
+ belt = null
+ if(!QDELETED(src))
+ update_inv_belt()
diff --git a/hyperstation/code/modules/antagonists/wendigo/mob/organs_bodyparts.dm b/hyperstation/code/modules/antagonists/wendigo/mob/organs_bodyparts.dm
new file mode 100644
index 0000000000..aa23dc1c87
--- /dev/null
+++ b/hyperstation/code/modules/antagonists/wendigo/mob/organs_bodyparts.dm
@@ -0,0 +1,58 @@
+/obj/item/organ/liver/wendigo
+ name = "rotten liver"
+ desc = "A liver from a wendigo. It looks like a spoiled tomato."
+ decay_factor = 0
+
+/obj/item/organ/liver/wendigo/on_life()
+ var/mob/living/carbon/C = owner
+ if(istype(C))
+ damage = initial(maxHealth)
+
+ if(filterToxins && !HAS_TRAIT(owner, TRAIT_TOXINLOVER))
+ //handle liver toxin filtration
+ for(var/datum/reagent/toxin/T in C.reagents.reagent_list)
+ var/thisamount = C.reagents.get_reagent_amount(T.type)
+ if (thisamount && thisamount <= toxTolerance)
+ C.reagents.remove_reagent(T.type, 1)
+
+ C.reagents.metabolize(C, can_overdose=FALSE)
+ if(istype(C, /mob/living/carbon/wendigo))
+ var/mob/living/carbon/wendigo/A = C
+ A.metabolize_hunger()
+
+/obj/item/organ/eyes/wendigo
+ sight_flags = SEE_MOBS
+
+/obj/item/bodypart/head/wendigo
+ dismemberable = FALSE
+ max_damage = INFINITY
+ animal_origin = WENDIGO_BODYPART
+
+/obj/item/bodypart/chest/wendigo
+ dismemberable = FALSE
+ max_damage = INFINITY
+ animal_origin = WENDIGO_BODYPART
+
+/obj/item/bodypart/l_arm/wendigo
+ dismemberable = FALSE
+ max_damage = INFINITY
+ attack_verb = list("slashed", "clawed", "mauled")
+ animal_origin = WENDIGO_BODYPART
+
+/obj/item/bodypart/r_arm/wendigo
+ dismemberable = FALSE
+ max_damage = INFINITY
+ attack_verb = list("slashed", "clawed", "mauled")
+ animal_origin = WENDIGO_BODYPART
+
+/obj/item/bodypart/l_leg/wendigo
+ dismemberable = FALSE
+ max_damage = INFINITY
+ attack_verb = list("pounded", "stomped", "stamped", "kicked")
+ animal_origin = WENDIGO_BODYPART
+
+/obj/item/bodypart/r_leg/wendigo
+ dismemberable = FALSE
+ max_damage = INFINITY
+ attack_verb = list("pounded", "stomped", "stamped", "kicked")
+ animal_origin = WENDIGO_BODYPART
diff --git a/hyperstation/code/modules/antagonists/wendigo/mob/update_icons.dm b/hyperstation/code/modules/antagonists/wendigo/mob/update_icons.dm
new file mode 100644
index 0000000000..46a448d46e
--- /dev/null
+++ b/hyperstation/code/modules/antagonists/wendigo/mob/update_icons.dm
@@ -0,0 +1,370 @@
+#define HEAD_X_OFFSET_NORTH 2
+#define HEAD_Y_OFFSET_NORTH 8
+#define HEAD_X_OFFSET_EAST 2
+#define HEAD_Y_OFFSET_EAST 8
+#define HEAD_X_OFFSET_SOUTH 8
+#define HEAD_Y_OFFSET_SOUTH 2
+#define HEAD_X_OFFSET_WEST 8
+#define HEAD_Y_OFFSET_WEST -2
+
+#define LHAND_X_OFFSET_NORTH -5
+#define LHAND_Y_OFFSET_NORTH 3
+#define LHAND_X_OFFSET_EAST -2
+#define LHAND_Y_OFFSET_EAST 3
+#define LHAND_X_OFFSET_SOUTH 8
+#define LHAND_Y_OFFSET_SOUTH 3
+#define LHAND_X_OFFSET_WEST -2
+#define LHAND_Y_OFFSET_WEST 3
+
+#define RHAND_X_OFFSET_NORTH 8
+#define RHAND_Y_OFFSET_NORTH 3
+#define RHAND_X_OFFSET_EAST 2
+#define RHAND_Y_OFFSET_EAST 3
+#define RHAND_X_OFFSET_SOUTH -5
+#define RHAND_Y_OFFSET_SOUTH 3
+#define RHAND_X_OFFSET_WEST 2
+#define RHAND_Y_OFFSET_WEST 3
+
+#define EARS_X_OFFSET_NORTH 0
+#define EARS_Y_OFFSET_NORTH 0
+#define EARS_X_OFFSET_EAST 0
+#define EARS_Y_OFFSET_EAST 0
+#define EARS_X_OFFSET_SOUTH 0
+#define EARS_Y_OFFSET_SOUTH 0
+#define EARS_X_OFFSET_WEST 0
+#define EARS_Y_OFFSET_WEST 0
+
+#define GLASSES_X_OFFSET_NORTH 5
+#define GLASSES_Y_OFFSET_NORTH 10
+#define GLASSES_X_OFFSET_EAST 1
+#define GLASSES_Y_OFFSET_EAST 10
+#define GLASSES_X_OFFSET_SOUTH -2
+#define GLASSES_Y_OFFSET_SOUTH 10
+#define GLASSES_X_OFFSET_WEST -1
+#define GLASSES_Y_OFFSET_WEST 10
+
+#define BELT_X_OFFSET_NORTH 0
+#define BELT_Y_OFFSET_NORTH 10
+#define BELT_X_OFFSET_EAST 0
+#define BELT_Y_OFFSET_EAST 0
+#define BELT_X_OFFSET_SOUTH 0
+#define BELT_Y_OFFSET_SOUTH 0
+#define BELT_X_OFFSET_WEST 0
+#define BELT_Y_OFFSET_WEST 0
+
+#define BACK_X_OFFSET_NORTH 1
+#define BACK_Y_OFFSET_NORTH 8
+#define BACK_X_OFFSET_EAST 0
+#define BACK_Y_OFFSET_EAST 8
+#define BACK_X_OFFSET_SOUTH 1
+#define BACK_Y_OFFSET_SOUTH 8
+#define BACK_X_OFFSET_WEST 0
+#define BACK_Y_OFFSET_WEST 8
+
+#define NECK_X_OFFSET_NORTH 2
+#define NECK_Y_OFFSET_NORTH 10
+#define NECK_X_OFFSET_EAST 0
+#define NECK_Y_OFFSET_EAST 10
+#define NECK_X_OFFSET_SOUTH 2
+#define NECK_Y_OFFSET_SOUTH 10
+#define NECK_X_OFFSET_WEST 0
+#define NECK_Y_OFFSET_WEST 10
+
+/mob/living/carbon/wendigo/setDir(newdir, ismousemovement)
+ if(dir == newdir)
+ return //Don't spend time regenerating icons when we don't move
+ . = ..()
+ regenerate_icons()
+
+/mob/living/carbon/wendigo/regenerate_icons()
+ if(!..())
+ update_inv_head()
+ update_inv_gloves()
+ update_inv_ears()
+ update_inv_glasses()
+ update_inv_belt()
+ update_inv_back()
+ update_inv_neck()
+ update_transform()
+
+/mob/living/carbon/wendigo/update_inv_gloves()
+ if(gloves)
+ gloves.screen_loc = ui_gloves
+ if(client && hud_used && hud_used.hud_shown)
+ if(hud_used.inventory_shown)
+ client.screen += gloves
+
+//GLASSES
+/mob/living/carbon/wendigo/update_inv_glasses()
+ if(glasses)
+ glasses.screen_loc = ui_glasses
+ if(client && hud_used && hud_used.hud_shown)
+ if(hud_used.inventory_shown)
+ client.screen += glasses
+
+//EARS
+/mob/living/carbon/wendigo/update_inv_ears()
+ remove_overlay(EARS_LAYER)
+ if(ears)
+ if(client && hud_used)
+ var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_EARS]
+ inv.update_icon()
+ ears.screen_loc = ui_ears //move the item to the appropriate screen loc
+ if(client && hud_used && hud_used.hud_shown)
+ if(hud_used.inventory_shown)
+ client.screen += ears
+
+ overlays_standing[EARS_LAYER] = ears.build_worn_icon(state = ears.icon_state, default_layer = EARS_LAYER, default_icon_file = ((ears.alternate_worn_icon) ? ears.alternate_worn_icon : 'icons/mob/ears.dmi'))
+ var/mutable_appearance/ears_overlay = overlays_standing[EARS_LAYER]
+ switch(dir)
+ if(NORTH)
+ ears_overlay.pixel_x += EARS_X_OFFSET_NORTH
+ ears_overlay.pixel_y += EARS_Y_OFFSET_NORTH
+ if(EAST)
+ ears_overlay.pixel_x += EARS_X_OFFSET_EAST
+ ears_overlay.pixel_y += EARS_Y_OFFSET_EAST
+ if(SOUTH)
+ ears_overlay.pixel_x += EARS_X_OFFSET_SOUTH
+ ears_overlay.pixel_y += EARS_Y_OFFSET_SOUTH
+ if(WEST)
+ ears_overlay.pixel_x += EARS_X_OFFSET_WEST
+ ears_overlay.pixel_y += EARS_Y_OFFSET_WEST
+ overlays_standing[EARS_LAYER] = ears_overlay
+ apply_overlay(EARS_LAYER)
+
+//BELT
+/mob/living/carbon/wendigo/update_inv_belt()
+ remove_overlay(BELT_LAYER)
+
+ if(belt)
+ belt.screen_loc = ui_belt
+ if(client && hud_used && hud_used.hud_shown)
+ if(hud_used.inventory_shown)
+ client.screen += belt
+
+ var/t_state = belt.item_state
+ if(!t_state)
+ t_state = belt.icon_state
+
+ overlays_standing[BELT_LAYER] = belt.build_worn_icon(state = t_state, default_layer = BELT_LAYER, default_icon_file = ((belt.alternate_worn_icon) ? belt.alternate_worn_icon : 'icons/mob/belt.dmi'))
+ var/mutable_appearance/belt_overlay = overlays_standing[BELT_LAYER]
+ switch(dir)
+ if(NORTH)
+ belt_overlay.pixel_x += BELT_X_OFFSET_NORTH
+ belt_overlay.pixel_y += BELT_Y_OFFSET_NORTH
+ if(EAST)
+ belt_overlay.pixel_x += BELT_X_OFFSET_EAST
+ belt_overlay.pixel_y += BELT_Y_OFFSET_EAST
+ if(SOUTH)
+ belt_overlay.pixel_x += BELT_X_OFFSET_SOUTH
+ belt_overlay.pixel_y += BELT_Y_OFFSET_SOUTH
+ if(WEST)
+ belt_overlay.pixel_x += BELT_X_OFFSET_WEST
+ belt_overlay.pixel_y += BELT_Y_OFFSET_WEST
+ overlays_standing[BELT_LAYER] = belt_overlay
+ apply_overlay(BELT_LAYER)
+
+//BACK
+/mob/living/carbon/wendigo/update_inv_back()
+ ..()
+ if(back)
+ back.screen_loc = ui_back
+ if(client && hud_used && hud_used.hud_shown)
+ if(hud_used.inventory_shown)
+ client.screen += back
+ var/mutable_appearance/back_overlay = overlays_standing[BACK_LAYER]
+ if(back_overlay)
+ remove_overlay(BACK_LAYER)
+ switch(dir)
+ if(NORTH)
+ back_overlay.pixel_x += BACK_X_OFFSET_NORTH
+ back_overlay.pixel_y += BACK_Y_OFFSET_NORTH
+ if(EAST)
+ back_overlay.pixel_x += BACK_X_OFFSET_EAST
+ back_overlay.pixel_y += BACK_Y_OFFSET_EAST
+ if(SOUTH)
+ back_overlay.pixel_x += BACK_X_OFFSET_SOUTH
+ back_overlay.pixel_y += BACK_Y_OFFSET_SOUTH
+ if(WEST)
+ back_overlay.pixel_x += BACK_X_OFFSET_WEST
+ back_overlay.pixel_y += BACK_Y_OFFSET_WEST
+ overlays_standing[BACK_LAYER] = back_overlay
+ apply_overlay(BACK_LAYER)
+
+//NECK
+/mob/living/carbon/wendigo/update_inv_neck()
+ ..()
+ if(wear_neck)
+ wear_neck.screen_loc = ui_neck
+ if(client && hud_used && hud_used.hud_shown)
+ if(hud_used.inventory_shown)
+ client.screen += wear_neck
+ var/mutable_appearance/neck_overlay = overlays_standing[NECK_LAYER]
+ if(neck_overlay)
+ remove_overlay(NECK_LAYER)
+ switch(dir)
+ if(NORTH)
+ neck_overlay.pixel_x += NECK_X_OFFSET_NORTH
+ neck_overlay.pixel_y += NECK_Y_OFFSET_NORTH
+ if(EAST)
+ neck_overlay.pixel_x += NECK_X_OFFSET_EAST
+ neck_overlay.pixel_y += NECK_Y_OFFSET_EAST
+ if(SOUTH)
+ neck_overlay.pixel_x += NECK_X_OFFSET_SOUTH
+ neck_overlay.pixel_y += NECK_Y_OFFSET_SOUTH
+ if(WEST)
+ neck_overlay.pixel_x += NECK_X_OFFSET_WEST
+ neck_overlay.pixel_y += NECK_Y_OFFSET_WEST
+ overlays_standing[NECK_LAYER] = neck_overlay
+ apply_overlay(NECK_LAYER)
+
+//HANDS
+/mob/living/carbon/wendigo/update_inv_hands()
+ remove_overlay(HANDS_LAYER)
+ if (handcuffed)
+ drop_all_held_items()
+ return
+
+ var/list/hands = list()
+ for(var/obj/item/I in held_items)
+ if(client && hud_used && hud_used.hud_version != HUD_STYLE_NOHUD)
+ I.screen_loc = ui_hand_position(get_held_index_of_item(I))
+ client.screen += I
+ if(observers && observers.len)
+ for(var/M in observers)
+ var/mob/dead/observe = M
+ if(observe.client && observe.client.eye == src)
+ observe.client.screen += I
+ else
+ observers -= observe
+ if(!observers.len)
+ observers = null
+ break
+
+ var/t_state = I.item_state
+ if(!t_state)
+ t_state = I.icon_state
+
+ var/icon_file = I.lefthand_file
+ var/righthand = 0
+ if(get_held_index_of_item(I) % 2 == 0)
+ icon_file = I.righthand_file
+ righthand = 1
+
+ var/mutable_appearance/thing = I.build_worn_icon(state = t_state, default_layer = HANDS_LAYER, default_icon_file = icon_file, isinhands = TRUE)
+ if(righthand)
+ switch(dir)
+ if(NORTH)
+ thing.pixel_x += RHAND_X_OFFSET_NORTH
+ thing.pixel_y += RHAND_Y_OFFSET_NORTH
+ if(EAST)
+ thing = null //ghetto
+ if(SOUTH)
+ thing.pixel_x += RHAND_X_OFFSET_SOUTH
+ thing.pixel_y += RHAND_Y_OFFSET_SOUTH
+ if(WEST)
+ thing.pixel_x += RHAND_X_OFFSET_WEST
+ thing.pixel_y += RHAND_Y_OFFSET_WEST
+ else
+ switch(dir)
+ if(NORTH)
+ thing.pixel_x += LHAND_X_OFFSET_NORTH
+ thing.pixel_y += LHAND_Y_OFFSET_NORTH
+ if(EAST)
+ thing.pixel_x += LHAND_X_OFFSET_EAST
+ thing.pixel_y += LHAND_Y_OFFSET_EAST
+ if(SOUTH)
+ thing.pixel_x += LHAND_X_OFFSET_SOUTH
+ thing.pixel_y += LHAND_Y_OFFSET_SOUTH
+ if(WEST)
+ thing = null //ghetto
+
+ hands += thing
+
+ overlays_standing[HANDS_LAYER] = hands
+ apply_overlay(HANDS_LAYER)
+
+//HEAD
+/mob/living/carbon/wendigo/update_inv_head()
+ remove_overlay(HEAD_LAYER)
+
+ if(head)
+ head.screen_loc = ui_head
+ if(client && hud_used && hud_used.hud_shown)
+ if(hud_used.inventory_shown)
+ client.screen += head
+ var/mutable_appearance/head_icon = head.build_worn_icon(state = head.icon_state, default_layer = HEAD_LAYER, default_icon_file = 'icons/mob/head.dmi')
+ switch(dir)
+ if(NORTH)
+ head_icon.pixel_x += HEAD_X_OFFSET_NORTH
+ head_icon.pixel_y += HEAD_Y_OFFSET_NORTH
+ if(EAST)
+ head_icon.pixel_x += HEAD_X_OFFSET_EAST
+ head_icon.pixel_y += HEAD_Y_OFFSET_EAST
+ if(SOUTH)
+ head_icon.pixel_x += HEAD_X_OFFSET_SOUTH
+ head_icon.pixel_y += HEAD_Y_OFFSET_SOUTH
+ if(WEST)
+ head_icon.pixel_x += HEAD_X_OFFSET_WEST
+ head_icon.pixel_y += HEAD_Y_OFFSET_WEST
+ overlays_standing[SLOT_HEAD] = head_icon
+ update_hud_head(head)
+
+ apply_overlay(HEAD_LAYER)
+
+#undef LHAND_X_OFFSET_NORTH
+#undef LHAND_Y_OFFSET_NORTH
+#undef LHAND_X_OFFSET_EAST
+#undef LHAND_Y_OFFSET_EAST
+#undef LHAND_X_OFFSET_SOUTH
+#undef LHAND_Y_OFFSET_SOUTH
+#undef LHAND_X_OFFSET_WEST
+#undef LHAND_Y_OFFSET_WEST
+#undef RHAND_X_OFFSET_NORTH
+#undef RHAND_Y_OFFSET_NORTH
+#undef RHAND_X_OFFSET_EAST
+#undef RHAND_Y_OFFSET_EAST
+#undef RHAND_X_OFFSET_SOUTH
+#undef RHAND_Y_OFFSET_SOUTH
+#undef RHAND_X_OFFSET_WEST
+#undef RHAND_Y_OFFSET_WEST
+#undef EARS_X_OFFSET_NORTH
+#undef EARS_Y_OFFSET_NORTH
+#undef EARS_X_OFFSET_EAST
+#undef EARS_Y_OFFSET_EAST
+#undef EARS_X_OFFSET_SOUTH
+#undef EARS_Y_OFFSET_SOUTH
+#undef EARS_X_OFFSET_WEST
+#undef EARS_Y_OFFSET_WEST
+#undef GLASSES_X_OFFSET_NORTH
+#undef GLASSES_Y_OFFSET_NORTH
+#undef GLASSES_X_OFFSET_EAST
+#undef GLASSES_Y_OFFSET_EAST
+#undef GLASSES_X_OFFSET_SOUTH
+#undef GLASSES_Y_OFFSET_SOUTH
+#undef GLASSES_X_OFFSET_WEST
+#undef GLASSES_Y_OFFSET_WEST
+#undef BELT_X_OFFSET_NORTH
+#undef BELT_Y_OFFSET_NORTH
+#undef BELT_X_OFFSET_EAST
+#undef BELT_Y_OFFSET_EAST
+#undef BELT_X_OFFSET_SOUTH
+#undef BELT_Y_OFFSET_SOUTH
+#undef BELT_X_OFFSET_WEST
+#undef BELT_Y_OFFSET_WEST
+#undef BACK_X_OFFSET_NORTH
+#undef BACK_Y_OFFSET_NORTH
+#undef BACK_X_OFFSET_EAST
+#undef BACK_Y_OFFSET_EAST
+#undef BACK_X_OFFSET_SOUTH
+#undef BACK_Y_OFFSET_SOUTH
+#undef BACK_X_OFFSET_WEST
+#undef BACK_Y_OFFSET_WEST
+#undef NECK_X_OFFSET_NORTH
+#undef NECK_Y_OFFSET_NORTH
+#undef NECK_X_OFFSET_EAST
+#undef NECK_Y_OFFSET_EAST
+#undef NECK_X_OFFSET_SOUTH
+#undef NECK_Y_OFFSET_SOUTH
+#undef NECK_X_OFFSET_WEST
+#undef NECK_Y_OFFSET_WEST
diff --git a/hyperstation/code/modules/arousal/arousalhud.dm b/hyperstation/code/modules/arousal/arousalhud.dm
new file mode 100644
index 0000000000..5f43568bde
--- /dev/null
+++ b/hyperstation/code/modules/arousal/arousalhud.dm
@@ -0,0 +1,704 @@
+//Hyperstation Arousal hud
+//This needs alot of attention, im a bad coder. a shitty attempt at making a more user friendly/modern hud
+//if you wanna use this on your own server go ahead, but alittle credit would always be nice! -quotefox
+//This still uses alot of cits arousal system!
+
+/obj/screen/arousal/ui_interact(mob/user)
+ . = ..()
+ var/dat = {"Genitals
"}
+
+ var/mob/living/carbon/U = user
+ for(var/obj/item/organ/genital/G in U.internal_organs)
+ if(!G.nochange)
+ if(!G.dontlist)
+ // GS13: Fix spelling
+ dat += "[G.mode == "hidden" ? "[G.name] (Hidden)" : (G.mode == "clothes" ? "[G.name] (Hidden by Clothes)" : (G.mode == "visible" ? "[G.name] (Visible)" : "[G.name] (Visible)"))]
"
+
+ dat += {"
Contexual Options
"}
+ var/obj/item/organ/genital/penis/P = user.getorganslot("penis")
+ //Options
+ dat += "Masturbate"
+ dat += "(Stimulate a sexual organ with your hands.)
"
+
+ dat += "Climax"
+ dat += "(Orgasm from a sexual organ.)
"
+
+ dat += "Fill container"
+ dat += "(Use a container in your hand to collect your seminal fluid.)
"
+
+ var/mob/living/carbon/human/C = usr
+ if(C && C.w_uniform || C.wear_suit) //if they are wearing cloths
+ dat += "Explode out of clothes"
+ dat += "(Flex your body to cause your clothes to burst apart.)
"
+
+ if(user.pulling)
+ dat += "Kiss [user.pulling]"
+ dat += "(Kiss a partner, or object.)
"
+ dat += "Feed [user.pulling]"
+ dat += "(Feed a partner.)
"
+ dat += "Feed from [user.pulling]"
+ dat += "(Feed a partner.)
"
+ else
+ dat += "Kiss"
+ dat += "(Requires a partner)
"
+ dat += "Feed others"
+ dat += "(Requires a partner)
"
+ dat += "Feed from others"
+ dat += "(Requires a partner)
"
+ dat += "Feed yourself"
+ dat += "(Feed yourself with your own genitals)
"
+
+ var/obj/item/organ/genital/belly/Belly = user.getorganslot("belly")
+ if(Belly)
+ if(Belly.inflatable)
+ // GS13: Fix description
+ dat += "Decrease belly size"
+ dat += "(Shrink your belly down a size)
"
+ dat += "Increase belly size"
+ dat += "(Bloat your belly up a size)
"
+
+ if(user.pulling)
+ dat += "Climax over [user.pulling]" //you can cum on objects if you really want...
+ dat += "(Orgasm over a person or object.)
"
+ if(isliving(user.pulling))
+ if(iscarbon(user.pulling))
+ dat += "Climax with [user.pulling]"
+ dat += {"(Orgasm with another person.)
"}
+
+ var/mob/living/carbon/human/H = user.pulling
+ if(H.breedable && P && H)
+ dat += "Impregnate [U.pulling] ([clamp(U.impregchance,0,100)]%)"
+ dat += "(Climax inside another person, and attempt to knock them up.)
"
+ else
+ dat += "Climax over"
+ dat += "(Requires a partner)
"
+ dat += "Climax with"
+ dat += "(Requires a partner)
"
+
+ //old code needs to be cleaned
+ if(P)
+ if(P.condom == 1)
+ dat += "Remove condom (penis)
"
+ if(P.sounding == 1)
+ dat += "Remove sounding rod (penis)
"
+ for(var/obj/item/organ/genital/G in U.internal_organs)
+ if(G.equipment) //they have equipment
+ dat += "Remove [G.equipment.name] ([G.name])
"
+
+ dat += {"
"}//Newline for the objects
+ //bottom options
+ dat += "Refresh"
+ dat += "Old Menu"
+ dat += "Toggle Undergarments "
+ dat += "
"
+
+ var/datum/browser/popup = new(user, "arousal", "Arousal Panel")
+ popup.set_content(dat)
+ popup.set_title_image(user.browse_rsc_icon(icon, icon_state), 500,600)
+
+ popup.open()
+
+
+
+
+
+/obj/screen/arousal/Topic(href, href_list)
+ . = ..() //Sanity checks.
+ if(..())
+ return
+
+ var/mob/living/carbon/human/H = usr
+ if (!H)
+ return
+ if(usr.stat==1) //No sleep-masturbation, you're unconscious.
+ to_chat(usr, "You must be conscious to do that!")
+ usr << browse(null, "window=arousal") //closes the window
+ return
+ if(usr.stat==3)
+ to_chat(usr, "You must be alive to do that!")
+ usr << browse(null, "window=arousal") //closes the window
+ return
+
+ if(href_list["hidepenis"])
+ var/obj/item/organ/genital/penis/P = usr.getorganslot("penis")
+ var/picked_visibility = input(usr, "Choose visibility", "Expose/Hide genitals", "Hidden by clothes") in list("Always visible", "Hidden by clothes", "Always hidden")
+ P.toggle_visibility(picked_visibility)
+
+ if(href_list["hidevagina"])
+ var/obj/item/organ/genital/vagina/V = usr.getorganslot("vagina")
+ var/picked_visibility = input(usr, "Choose visibility", "Expose/Hide genitals", "Hidden by clothes") in list("Always visible", "Hidden by clothes", "Always hidden")
+ V.toggle_visibility(picked_visibility)
+
+ if(href_list["hidebreasts"])
+ var/obj/item/organ/genital/breasts/B = usr.getorganslot("breasts")
+ var/picked_visibility = input(usr, "Choose visibility", "Expose/Hide genitals", "Hidden by clothes") in list("Always visible", "Hidden by clothes", "Always hidden")
+ B.toggle_visibility(picked_visibility)
+
+ if(href_list["hidebelly"])
+ var/obj/item/organ/genital/belly/E = usr.getorganslot("belly")
+ var/picked_visibility = input(usr, "Choose visibility", "Expose/Hide genitals", "Hidden by clothes") in list("Always visible", "Hidden by clothes", "Always hidden")
+ E.toggle_visibility(picked_visibility)
+
+ if(href_list["hideanus"])
+ var/obj/item/organ/genital/anus/A = usr.getorganslot("anus")
+ var/picked_visibility = input(usr, "Choose visibility", "Expose/Hide genitals", "Hidden by clothes") in list("Always visible", "Hidden by clothes", "Always hidden")
+ A.toggle_visibility(picked_visibility)
+
+ if(href_list["hidetesticles"])
+ var/obj/item/organ/genital/testicles/T = usr.getorganslot("testicles")
+ var/picked_visibility = input(usr, "Choose visibility", "Expose/Hide genitals", "Hidden by clothes") in list("Always visible", "Hidden by clothes", "Always hidden")
+ T.toggle_visibility(picked_visibility)
+
+ if(href_list["masturbate"])
+ if (H.arousalloss >= (H.max_arousal / 100) * 33) //requires 33% arousal.
+ H.solomasturbate()
+ return
+ else
+ to_chat(usr, "You aren't aroused enough for that! ")
+ return
+
+ if(href_list["container"])
+ if (H.arousalloss >= (H.max_arousal / 100) * 33) //requires 33% arousal.
+ H.cumcontainer()
+ return
+ else
+ to_chat(usr, "You aren't aroused enough for that! ")
+ return
+
+ if(href_list["clothesplosion"])
+ if (H.arousalloss >= (H.max_arousal / 100) * 33) //Requires 33% arousal.
+ H.clothesplosion()
+ return
+ else
+ to_chat(usr, "You aren't aroused enough for that! ")
+ return
+
+ if(href_list["climax"])
+ if (H.arousalloss >= (H.max_arousal / 100) * 33) //requires 33% arousal.
+ H.climaxalone(FALSE)
+ return
+ else
+ to_chat(usr, "You aren't aroused enough for that! ")
+ return
+
+ if(href_list["climaxover"])
+ if (H.arousalloss >= (H.max_arousal / 100) * 33) //requires 33% arousal.
+ H.climaxover(usr.pulling)
+ return
+ else
+ to_chat(usr, "You aren't aroused enough for that! ")
+ return
+
+ if(href_list["climaxwith"])
+ if (H.arousalloss >= (H.max_arousal / 100) * 33) //requires 33% arousal.
+ H.climaxwith(usr.pulling)
+ return
+ else
+ to_chat(usr, "You aren't aroused enough for that! ")
+ return
+
+ if(href_list["impreg"])
+ if (H.arousalloss >= (H.max_arousal / 100) * 33) //requires 33% arousal.
+ H.impregwith(usr.pulling)
+ return
+ else
+ to_chat(usr, "You aren't aroused enough for that! ")
+ return
+
+ if(href_list["kiss"])
+ if(usr.pulling)
+ kiss()
+ else
+ to_chat(usr, "You cannot do this alone!")
+ return
+
+ if(href_list["feed"])
+ if(usr.pulling)
+ feed()
+ else
+ to_chat(usr, "You cannot do this alone!")
+ return
+
+ if(href_list["feedfrom"])
+ if(usr.pulling)
+ feedfrom()
+ else
+ to_chat(usr, "You cannot do this alone!")
+ return
+
+ if(href_list["feedyourself"])
+ feedyourself()
+ return
+
+ if(href_list["shrink_belly"])
+ var/obj/item/organ/genital/belly/E = usr.getorganslot("belly")
+ if(E.size > 0)
+ to_chat(usr, "You feel your belly diminish.")
+ E.size -= 1
+ H.update_genitals()
+ else
+ to_chat(usr, "Your belly is already at the minimum size! ")
+
+ if(href_list["inflate_belly"])
+ var/obj/item/organ/genital/belly/E = usr.getorganslot("belly")
+ if(E.size < 11)
+ to_chat(usr, "You feel your belly bloat out..")
+ E.size += 1
+ H.update_genitals()
+ else
+ to_chat(usr, "Your belly is already at the maximum size! ")
+
+
+ if(href_list["removecondom"])
+ H.menuremovecondom()
+
+ if(href_list["removesound"])
+ H.menuremovesounding()
+
+ if(href_list["removeequipmentpenis"])
+ var/obj/item/organ/genital/penis/O = usr.getorganslot("penis")
+ var/obj/item/I = O.equipment
+ usr.put_in_hands(I)
+ O.equipment = null
+
+ if(href_list["removeequipmentbreasts"])
+ var/obj/item/organ/genital/breasts/O = usr.getorganslot("breasts")
+ var/obj/item/I = O.equipment
+ usr.put_in_hands(I)
+ O.equipment = null
+
+
+ if(href_list["removeequipmentvagina"])
+ var/obj/item/organ/genital/vagina/O = usr.getorganslot("vagina")
+ var/obj/item/I = O.equipment
+ usr.put_in_hands(I)
+ if(istype(I, /obj/item/portalpanties))
+ var/obj/item/portalpanties/P = I
+ P.remove()
+ O.equipment = null
+
+ if(href_list["removeequipmentbelly"])
+ var/obj/item/organ/genital/belly/O = usr.getorganslot("belly")
+ var/obj/item/I = O.equipment
+ usr.put_in_hands(I)
+ O.equipment = null
+
+ if(href_list["removeequipmentanus"])
+ var/obj/item/organ/genital/anus/O = usr.getorganslot("anus")
+ var/obj/item/I = O.equipment
+ usr.put_in_hands(I)
+ O.equipment = null
+
+
+ if(href_list["omenu"])
+ usr << browse(null, "window=arousal") //closes the window
+ H.mob_climax()
+ return
+
+ if(href_list["underwear"])
+ H.underwear_toggle()
+ return
+
+ src.ui_interact(usr)
+
+
+obj/screen/arousal/proc/kiss()
+ if(usr.restrained(TRUE))
+ to_chat(usr, "You can't do that while restrained!")
+ return
+ var/mob/living/carbon/human/H = usr
+ if (H)
+ H.kisstarget(H.pulling)
+
+obj/screen/arousal/proc/feed()
+ if(usr.restrained(TRUE))
+ to_chat(usr, "You can't do that while restrained!")
+ return
+ var/mob/living/carbon/human/H = usr
+ if (H)
+ H.genitalfeed(H.pulling)
+
+obj/screen/arousal/proc/feedfrom()
+ if(usr.restrained(TRUE))
+ to_chat(usr, "You can't do that while restrained!")
+ return
+ var/mob/living/carbon/human/H = usr
+ if (H)
+ H.genitalfeedfrom(H.pulling)
+
+obj/screen/arousal/proc/feedyourself()
+ if(usr.restrained(TRUE))
+ to_chat(usr, "You can't do that while restrained!")
+ return
+ var/mob/living/carbon/human/H = usr
+ if (H)
+ H.genitalfeedyourself()
+
+
+/mob/living/carbon/human/proc/menuremovecondom()
+
+ if(restrained(TRUE))
+ to_chat(src, "You can't do that while restrained!")
+ return
+ var/free_hands = get_num_arms()
+ if(!free_hands)
+ to_chat(src, "You need at least one free arm.")
+ return
+ var/obj/item/organ/genital/penis/P = getorganslot("penis")
+ if(!P.condom)
+ to_chat(src, "You don't have a condom on!")
+ return
+ if(P.condom)
+ to_chat(src, "You tug the condom off the end of your penis!")
+ removecondom()
+ src.ui_interact(usr) //reopen dialog
+ return
+ return
+
+/mob/living/carbon/human/proc/menuremovesounding()
+
+ if(restrained(TRUE))
+ to_chat(src, "You can't do that while restrained!")
+ return
+ var/free_hands = get_num_arms()
+ if(!free_hands)
+ to_chat(src, "You need at least one free arm.")
+ return
+ var/obj/item/organ/genital/penis/P = getorganslot("penis")
+ if(!P.sounding)
+ to_chat(src, "You don't have a rod inside!")
+ return
+ if(P.sounding)
+ to_chat(src, "You pull the rod off from the tip of your penis!")
+ removesounding()
+ src.ui_interact(usr) //reopen dialog
+ return
+ return
+
+/mob/living/carbon/human/proc/solomasturbate()
+ if(restrained(TRUE))
+ to_chat(src, "You can't do that while restrained!")
+ return
+ var/free_hands = get_num_arms()
+ if(!free_hands)
+ to_chat(src, "You need at least one free arm.")
+ return
+ for(var/helditem in held_items)
+ if(isobj(helditem))
+ free_hands--
+ if(free_hands <= 0)
+ to_chat(src, "You're holding too many things.")
+ return
+ //We got hands, let's pick an organ
+ var/obj/item/organ/genital/picked_organ
+ picked_organ = pick_masturbate_genitals()
+ if(picked_organ)
+ src << browse(null, "window=arousal") //closes the window
+ mob_masturbate(picked_organ)
+ return
+ else //They either lack organs that can masturbate, or they didn't pick one.
+ to_chat(src, "You cannot climax without choosing genitals.")
+ return
+
+
+//Kissing target proc
+/mob/living/carbon/human/proc/kisstarget(mob/living/L)
+
+ src << browse(null, "window=arousal") //closes the arousal window, if its open, mainly to stop spam
+ if(isliving(L)) //is your target living? Living people can resist your advances if they want to via moving.
+ if(iscarbon(L))
+ src.visible_message("[src] is about to kiss [L]!", \
+ "You're attempting to kiss [L]!", \
+ "You're attempting to kiss with something!")
+ SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "kissed", /datum/mood_event/kiss) //how cute, affection is nice.
+ //Well done you kissed it/them!
+ src.visible_message("[src] kisses [L]!", \
+ "You kiss [L]!", \
+ "You kiss something!")
+
+/mob/living/carbon/human/proc/climaxalone()
+ //we dont need hands to climax alone, its hands free!
+ var/obj/item/organ/genital/picked_organ
+ picked_organ = pick_climax_genitals()
+ if(picked_organ)
+ src << browse(null, "window=arousal") //closes the window
+ mob_climax_outside(picked_organ)
+ return
+ else //They either lack organs that can masturbate, or they didn't pick one.
+ to_chat(src, "You cannot climax without choosing genitals.")
+ return
+
+/mob/living/carbon/human/proc/climaxwith(mob/living/T)
+
+ var/mob/living/carbon/human/L = pick_partner()
+ var/obj/item/organ/genital/picked_organ
+ picked_organ = pick_climax_genitals()
+ if(picked_organ)
+ var/mob/living/partner = L
+ if(partner)
+ src << browse(null, "window=arousal") //alls fine, we can close the window now.
+ var/obj/item/organ/genital/penis/P = picked_organ
+ var/spillage = "No" //default to no, just incase player has items on to prevent climax
+ if(!P.condom == 1&&!P.sounding == 1) //you cant climax with a condom on or sounding in.
+ spillage = input(src, "Would your fluids spill outside?", "Choose overflowing option", "Yes") as anything in list("Yes", "No")
+ if(spillage == "Yes")
+ mob_climax_partner(picked_organ, partner, TRUE, FALSE, FALSE)
+ else
+ mob_climax_partner(picked_organ, partner, FALSE, FALSE, FALSE)
+ else
+ to_chat(src, "You cannot do this alone.")
+ return
+ else //They either lack organs that can masturbate, or they didn't pick one.
+ to_chat(src, "You cannot climax without choosing genitals.")
+ return
+
+
+
+/mob/living/carbon/human/proc/climaxover(mob/living/T)
+
+ var/mob/living/carbon/human/L = T
+ var/obj/item/organ/genital/picked_organ
+ picked_organ = pick_climax_genitals()
+ if(picked_organ)
+ src << browse(null, "window=arousal") //alls fine, we can close the window now.
+ var/mob/living/partner = L
+ if(partner)
+ var/obj/item/organ/genital/penis/P = picked_organ
+ if(P.condom == 1)
+ to_chat(src, "You cannot do this action with a condom on.")
+ return
+ if(P.sounding == 1)
+ to_chat(src, "You cannot do this action with a sounding in.")
+ return
+ mob_climax_partner(picked_organ, partner, FALSE, FALSE, TRUE)
+ else
+ to_chat(src, "You cannot do this alone.")
+ return
+ else //They either lack organs that can masturbate, or they didn't pick one.
+ to_chat(src, "You cannot climax without choosing genitals.")
+ return
+
+/mob/living/carbon/human/proc/clothesplosion()
+ if(usr.restrained(TRUE))
+ to_chat(usr, "You can't do that while restrained!")
+ return
+ var/mob/living/carbon/human/H = src
+ var/items = H.get_contents()
+ for(var/obj/item/W in items)
+ if(W == H.w_uniform || W == H.wear_suit)
+ H.dropItemToGround(W, TRUE)
+ playsound(H.loc, 'sound/items/poster_ripped.ogg', 50, 1)
+ H.visible_message("[H] explodes out of their clothes!'")
+
+
+
+/mob/living/carbon/human/proc/impregwith(mob/living/T)
+
+ var/mob/living/carbon/human/L = pick_partner()
+ var/obj/item/organ/genital/picked_organ
+ picked_organ = src.getorganslot("penis") //Impregnation must be done with a penis.
+ if(picked_organ)
+ var/mob/living/partner = L
+ if(partner)
+ if(!partner.breedable)//check if impregable.
+ to_chat(src, "Your partner cannot be impregnated.")//some fuckary happening, you shouldnt even get to this point tbh.
+ return
+ var/obj/item/organ/genital/penis/P = picked_organ
+ //you cant impreg with a condom on or sounding in.
+ if(P.condom == 1)
+ to_chat(src, "You cannot do this action with a condom on.")
+ return
+ if(P.sounding == 1)
+ to_chat(src, "You cannot do this action with a sounding in.")
+ return
+ src << browse(null, "window=arousal") //alls fine, we can close the window now.
+ //Keeping this for messy fun
+ var/spillage = input(src, "Would your fluids spill outside?", "Choose overflowing option", "Yes") as anything in list("Yes", "No")
+ if(spillage == "Yes")
+ mob_climax_partner(picked_organ, partner, TRUE, TRUE, FALSE)
+ else
+ mob_climax_partner(picked_organ, partner, FALSE, TRUE, FALSE)
+ else
+ to_chat(src, "You cannot do this alone.")
+ return
+ else //no penis :(
+ to_chat(src, "You cannot impregnate without a penis.")
+ return
+
+/mob/living/carbon/human/proc/cumcontainer(mob/living/T)
+ //We'll need hands and no restraints.
+ if(restrained(TRUE)) //TRUE ignores grabs
+ to_chat(src, "You can't do that while restrained!")
+ return
+ var/free_hands = get_num_arms()
+ if(!free_hands)
+ to_chat(src, "You need at least one free arm.")
+ return
+ for(var/helditem in held_items)//how many hands are free
+ if(isobj(helditem))
+ free_hands--
+ if(free_hands <= 0)
+ to_chat(src, "You're holding too many things.")
+ return
+ //We got hands, let's pick an organ
+ var/obj/item/organ/genital/picked_organ
+ src << browse(null, "window=arousal")
+ picked_organ = pick_climax_genitals() //Gotta be climaxable, not just masturbation, to fill with fluids.
+ if(picked_organ)
+ //Good, got an organ, time to pick a container
+ var/obj/item/reagent_containers/fluid_container = pick_climax_container()
+ if(fluid_container)
+ mob_fill_container(picked_organ, fluid_container)
+ return
+ else
+ to_chat(src, "You cannot do this without anything to fill.")
+ return
+ else //They either lack organs that can climax, or they didn't pick one.
+ to_chat(src, "You cannot fill anything without choosing genitals.")
+ return
+
+/atom/proc/add_cum_overlay() //This can go in a better spot, for now its here.
+ cum_splatter_icon = icon(initial(icon), initial(icon_state), , 1)
+ cum_splatter_icon.Blend("#fff", ICON_ADD)
+ cum_splatter_icon.Blend(icon('hyperstation/icons/effects/cumoverlay.dmi', "cum_obj"), ICON_MULTIPLY)
+ add_overlay(cum_splatter_icon)
+
+/atom/proc/wash_cum()
+ cut_overlay(mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi', "cum_normal"))
+ cut_overlay(mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi', "cum_large"))
+ if(cum_splatter_icon)
+ cut_overlay(cum_splatter_icon)
+ return TRUE
+
+/mob/living/carbon/human/proc/genitalfeed(mob/living/L, mb_time = 30)
+ if(isliving(L)) //is your target living? Living people can resist your advances if they want to via moving.
+ if(iscarbon(L))
+ var/obj/item/organ/genital/picked_organ
+ var/total_fluids = 0
+ var/datum/reagents/fluid_source = null
+ src << browse(null, "window=arousal")
+ picked_organ = pick_climax_genitals() //Gotta be climaxable, not just masturbation, to fill with fluids.
+ if(picked_organ)
+ //Good, got an organ, time to pick a container
+ if(picked_organ.name == "penis")//if the select organ is a penis
+ var/obj/item/organ/genital/penis/P = src.getorganslot("penis")
+ if(P.condom) //if the penis is condomed
+ to_chat(src, "You cannot feed someone when there is a condom over your [picked_organ.name].")
+ return
+ if(P.sounding) //if the penis is sounded
+ to_chat(src, "You cannot feed someone when there is a rod inside your [picked_organ.name].")
+ return
+ if(picked_organ.producing) //Can it produce its own fluids, such as breasts?
+ fluid_source = picked_organ.reagents
+ else
+ if(!picked_organ.linked_organ)
+ to_chat(src, "Your [picked_organ.name] is unable to produce it's own fluids, it's missing the organs for it.")
+ return
+ fluid_source = picked_organ.linked_organ.reagents
+ total_fluids = fluid_source.total_volume
+
+ src.visible_message("[src] starts to feed [L.name] with their [picked_organ.name].", \
+ "You feed [L.name] with your [picked_organ.name].")
+ if(do_after(src, mb_time, target = src) && in_range(src, L))
+ fluid_source.trans_to(L, total_fluids)
+ src.visible_message("[src] uses [p_their()] [picked_organ.name] to feed [L.name]!", \
+ "You used your [picked_organ.name] to feed [L.name] a total of [total_fluids]u's.")
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
+ if(picked_organ.can_climax)
+ setArousalLoss(min_arousal)
+
+ else //They either lack organs that can climax, or they didn't pick one.
+ to_chat(src, "You cannot fill anything without choosing exposed genitals.")
+ return
+
+/mob/living/carbon/human/proc/genitalfeedfrom(mob/living/target, mb_time = 30)
+ var/mob/living/carbon/human/L = target
+ var/obj/item/organ/genital/picked_organ
+ var/total_fluids = 0
+ var/datum/reagents/fluid_source = null
+ src << browse(null, "window=arousal")
+
+ var/list/genitals_list = list()
+ var/list/worn_stuff = L.get_equipped_items()
+
+ for(var/obj/item/organ/genital/G in L.internal_organs)
+ if(G.can_climax) //filter out what you can't masturbate with
+ if(G.is_exposed(worn_stuff)) //Nude or through_clothing
+ if(!G.dontlist)
+ genitals_list += G
+ if(genitals_list.len)
+ picked_organ = input(src, "with what?", "Climax", null) as null|obj in genitals_list
+ else
+ return
+
+ if(picked_organ)
+ //Good, got an organ, time to pick a container
+ if(picked_organ.name == "penis")//if the select organ is a penis
+ var/obj/item/organ/genital/penis/P = L.getorganslot("penis")
+ if(P.condom) //if the penis is condomed
+ to_chat(src, "You cannot feed from [picked_organ.name] when there is a condom over it.")
+ return
+ if(P.sounding) //if the penis is sounded
+ to_chat(src, "You cannot feed from [picked_organ.name] when there is a rod inside it.")
+ return
+ if(picked_organ.producing) //Can it produce its own fluids, such as breasts?
+ fluid_source = picked_organ.reagents
+ else
+ if(!picked_organ.linked_organ)
+ to_chat(src, "The [picked_organ.name] is unable to produce it's own fluids, it's missing the organs for it.")
+ return
+ fluid_source = picked_organ.linked_organ.reagents
+ total_fluids = fluid_source.total_volume
+
+ src.visible_message("[src] starts to feed from [L.name]'s [picked_organ.name].", \
+ "You feed from [L.name]'s '[picked_organ.name].")
+ if(do_after(src, mb_time, target = src) && in_range(src, L))
+ fluid_source.trans_to(src, total_fluids)
+ src.visible_message("[src] feeds from [L.name]'s [picked_organ.name]!", \
+ "You used [L.name]'s [picked_organ.name] to feed with a total of [total_fluids]u's.")
+ SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
+ if(picked_organ.can_climax)
+ L.setArousalLoss(min_arousal)
+
+ else //They either lack organs that can climax, or they didn't pick one.
+ to_chat(src, "You cannot fill anything without choosing exposed genitals.")
+ return
+
+/mob/living/carbon/human/proc/genitalfeedyourself(mb_time = 30)
+ var/obj/item/organ/genital/picked_organ
+ var/total_fluids = 0
+ var/datum/reagents/fluid_source = null
+ src << browse(null, "window=arousal")
+ picked_organ = pick_climax_genitals() //Gotta be climaxable, not just masturbation, to fill with fluids.
+ if(picked_organ)
+ //Good, got an organ, time to pick a container
+ if(picked_organ.name == "penis")//if the select organ is a penis
+ var/obj/item/organ/genital/penis/P = src.getorganslot("penis")
+ if(P.condom) //if the penis is condomed
+ to_chat(src, "You cannot feed yourself when there is a condom over your [picked_organ.name].")
+ return
+ if(P.sounding) //if the penis is sounded
+ to_chat(src, "You cannot feed yourself when there is a rod inside your [picked_organ.name].")
+ return
+ if(picked_organ.producing) //Can it produce its own fluids, such as breasts?
+ fluid_source = picked_organ.reagents
+ else
+ if(!picked_organ.linked_organ)
+ to_chat(src, "Your [picked_organ.name] is unable to produce it's own fluids, it's missing the organs for it.")
+ return
+ fluid_source = picked_organ.linked_organ.reagents
+ total_fluids = fluid_source.total_volume
+
+ src.visible_message("[src] starts to feed themselves with their [picked_organ.name].", \
+ "You feed yourself with your [picked_organ.name].")
+ if(do_after(src, mb_time))
+ fluid_source.trans_to(src, total_fluids)
+ src.visible_message("[src] uses [p_their()] [picked_organ.name] to feed themselves!", \
+ "You used your [picked_organ.name] to feed yourself a total of [total_fluids]u's.")
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
+ if(picked_organ.can_climax)
+ setArousalLoss(min_arousal)
+
+ else //They either lack organs that can climax, or they didn't pick one.
+ to_chat(src, "You cannot fill anything without choosing exposed genitals.")
+ return
diff --git a/hyperstation/code/modules/arousal/creampie.dm b/hyperstation/code/modules/arousal/creampie.dm
new file mode 100644
index 0000000000..4ae6749357
--- /dev/null
+++ b/hyperstation/code/modules/arousal/creampie.dm
@@ -0,0 +1,15 @@
+/mob/living/carbon/human/handle_creampie()
+ if(NOBLOOD in dna.species.species_traits)
+ cumdrip_rate = 0
+ return
+
+ if(cumdrip_rate < 0)
+ cumdrip_rate = 0
+
+ if(bodytemperature >= TCRYO && !(HAS_TRAIT(src, TRAIT_NOCLONE)))
+ cumdrip_rate = cumdrip_rate - 1
+ cumdrip()
+
+/mob/living/carbon/human/proc/cumdrip()
+ if(isturf(loc))
+ new/obj/effect/decal/cleanable/semendrip(get_turf(src))
\ No newline at end of file
diff --git a/hyperstation/code/modules/cargo/exports/engineering.dm b/hyperstation/code/modules/cargo/exports/engineering.dm
new file mode 100644
index 0000000000..0c13f797ab
--- /dev/null
+++ b/hyperstation/code/modules/cargo/exports/engineering.dm
@@ -0,0 +1,4 @@
+/datum/export/plutonium_rod
+ cost = 20000
+ unit_name = "Plutonium Rod"
+ export_types = list(/obj/item/twohanded/required/fuel_rod/plutonium)
diff --git a/hyperstation/code/modules/cargo/exports/sweatshop.dm b/hyperstation/code/modules/cargo/exports/sweatshop.dm
new file mode 100644
index 0000000000..623ee585ab
--- /dev/null
+++ b/hyperstation/code/modules/cargo/exports/sweatshop.dm
@@ -0,0 +1,24 @@
+/datum/export/sweatshop/stool
+ cost = 850
+ unit_name = "custom stool"
+ export_types = list(/obj/item/processed/wood/stool)
+
+/datum/export/sweatshop/cushion
+ cost = 300
+ unit_name = "cloth cushion"
+ export_types = list(/obj/item/cushion)
+
+/datum/export/sweatshop/cushionsilk
+ cost = 500
+ unit_name = "silk cushion"
+ export_types = list(/obj/item/cushion)
+
+/datum/export/sweatshop/stool/cushioncloth
+ cost = 1800
+ unit_name = "cushioned stool (cloth)"
+ export_types = list(/obj/item/processed/wood/stoolcloth)
+
+/datum/export/sweatshop/stool/cushionsilk
+ cost = 2400
+ unit_name = "cushioned stool (cloth)"
+ export_types = list(/obj/item/processed/wood/stoolsilk)
diff --git a/hyperstation/code/modules/cargo/packs/misc.dm b/hyperstation/code/modules/cargo/packs/misc.dm
new file mode 100644
index 0000000000..d1b88903f3
--- /dev/null
+++ b/hyperstation/code/modules/cargo/packs/misc.dm
@@ -0,0 +1,7 @@
+/datum/supply_pack/service/stripperpole
+ name = "Stripper Pole Crate"
+ desc = "No private bar is complete without a stripper pole, show off the goods! Comes with a ready-to-assemble stripper pole, and a complementary wrench to get things set up!"
+ cost = 3550
+ contains = list(/obj/item/polepack/,
+ /obj/item/wrench/)
+ crate_name = "stripper pole crate"
diff --git a/hyperstation/code/modules/cargo/sweatshop/metal.dm b/hyperstation/code/modules/cargo/sweatshop/metal.dm
new file mode 100644
index 0000000000..456dd0c266
--- /dev/null
+++ b/hyperstation/code/modules/cargo/sweatshop/metal.dm
@@ -0,0 +1,6 @@
+/obj/item/processed/metal
+ name = "Heated Metal"
+ desc = "A malleable metal, able to be cut into nails."
+ icon = 'hyperstation/icons/obj/cargo/sweatshop/metal.dmi'
+ icon_state = "metal"
+ sharpness = TRUE
diff --git a/hyperstation/code/modules/cargo/sweatshop/sweatshop.dm b/hyperstation/code/modules/cargo/sweatshop/sweatshop.dm
new file mode 100644
index 0000000000..2bb17bc1c7
--- /dev/null
+++ b/hyperstation/code/modules/cargo/sweatshop/sweatshop.dm
@@ -0,0 +1,256 @@
+//THE TOOLS
+/obj/item/carpentry
+ name = "carpentry"
+ desc = "You shouldn't be seeing this!"
+ icon = 'hyperstation/icons/obj/cargo/sweatshop/sweatshop.dmi'
+ usesound = list('sound/effects/picaxe1.ogg', 'sound/effects/picaxe2.ogg', 'sound/effects/picaxe3.ogg')
+
+/obj/item/carpentry/handsaw
+ name = "handsaw"
+ desc = "A shoddy tool used to process wood into smaller segments."
+ icon_state = "handsaw"
+ slot_flags = ITEM_SLOT_BACK
+ force = 8
+ sharpness = TRUE
+ w_class = WEIGHT_CLASS_HUGE
+ materials = list(MAT_METAL=50)
+ attack_verb = list("slashed", "sawed")
+
+/obj/item/carpentry/hammer
+ name = "hammer"
+ desc = "A tool used to manually bash nails into place."
+ icon_state = "hammer"
+ slot_flags = ITEM_SLOT_BELT
+ force = 7
+ sharpness = FALSE
+ w_class = WEIGHT_CLASS_NORMAL
+ materials = list(MAT_METAL=100)
+ attack_verb = list("bonked", "nailed")
+
+/obj/item/carpentry/glue
+ name = "glue"
+ desc = "Used to haphazardly stick things together; secured by the toughest Monkey Glue(TM)."
+ icon_state = "glue"
+ force = 0
+ sharpness = FALSE
+ w_class = WEIGHT_CLASS_SMALL
+ materials = list(MAT_PLASTIC=25)
+ attack_verb = list("glued", "coughed")
+
+/obj/item/carpentry/borer
+ name = "manual borer"
+ desc = "An incredibly awful tool used to manually drill holes into something... Surely there's a better option."
+ icon_state = "borer"
+ force = 3
+ sharpness = TRUE
+ w_class = WEIGHT_CLASS_SMALL
+ materials = list(MAT_METAL=25)
+ attack_verb = list("bored", "drilled")
+
+/obj/item/carpentry/sandpaper
+ name = "sandpaper strip"
+ desc = "A strip of sandpaper, commonly used for sanding down rough surfaces into a more smooth shape."
+ icon_state = "sandpaper"
+ force = 1
+ sharpness = FALSE
+ w_class = WEIGHT_CLASS_TINY
+ materials = list(MAT_GLASS=1) //lmao
+ attack_verb = list("sanded", "licked")
+
+/obj/item/nails
+ name = "metal nails"
+ desc = "A bunch of nails, used for hammering into things."
+ icon = 'hyperstation/icons/obj/cargo/sweatshop/sweatshop.dmi'
+ icon_state = "nails"
+ force = 0
+ sharpness = TRUE
+ w_class = WEIGHT_CLASS_TINY
+ materials = list(MAT_METAL=10)
+ attack_verb = list("nailed", "screwed")
+
+/obj/item/cushion
+ name = "basic cushion"
+ desc = "Beats sitting on the floor."
+ icon = 'hyperstation/icons/obj/cargo/sweatshop/cloth.dmi'
+ icon_state = "clothcushion"
+ force = 0
+ sharpness = FALSE
+ w_class = WEIGHT_CLASS_NORMAL
+ attack_verb = list("thomped", "thwacked")
+
+/obj/item/cushion/silk
+ name = "silk cushion"
+ desc = "How'd it turn red?!"
+ icon_state = "silkcushion"
+
+//BASIC RECIPES - To do, add sound. As well as refactor everything in a more smart way so we can add the possibility of multiple wood types in the future.
+//saw a plank into two platforms
+/obj/item/processed/wood/plank/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/carpentry/handsaw))
+ to_chat(user," You begin to saw [src] in half...")
+ if(do_after(user, 40) && isturf(loc))
+ new src.sawobj(loc)
+ new src.sawobj(loc) //send help i dont know how to make two in the same line lmfao
+ to_chat(user, " You saw [src] in half.")
+ qdel(src)
+ else
+ to_chat(user, "You need to hold still to saw [src]!")
+ else
+ ..()
+//saw a platform into four blocks
+/obj/item/processed/wood/platform/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/carpentry/handsaw))
+ to_chat(user," You begin cut [src] into smaller pieces...")
+ if(do_after(user, 20) && isturf(loc))
+ new src.sawobj(loc)
+ new src.sawobj(loc)
+ new src.sawobj(loc)
+ new src.sawobj(loc)
+ to_chat(user, " You cut [src] into four pieces.")
+ qdel(src)
+ else
+ to_chat(user, "You need to hold still to saw [src]!")
+ else
+ ..()
+//sand a block into a peg
+/obj/item/processed/wood/block/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/carpentry/sandpaper))
+ to_chat(user," You carefully begin to sand down [src]...")
+ if(do_after(user, 50) && isturf(loc))
+ new src.sandobj(loc)
+ to_chat(user, " You smooth [src] into a peg.")
+ qdel(src)
+ else
+ to_chat(user, "You need to hold still to sand [src]!")
+ else
+ ..()
+//cut heated metal into nails
+/obj/item/processed/metal/attackby(obj/item/I, mob/user, params)
+ if(I.tool_behaviour == TOOL_WIRECUTTER)
+ to_chat(user," You tediously begin to cut [src] into several nails...")
+ if(do_after(user, 80) && isturf(loc))
+ new /obj/item/nails(loc)
+ new /obj/item/nails(loc)
+ to_chat(user, " You make some crude metal nails.")
+ qdel(src)
+ else
+ to_chat(user, "You need to hold still to process [src]!")
+ else
+ ..()
+
+//Covered in glue
+//cover a wooden block in glue
+/obj/item/processed/wood/block/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/carpentry/glue))
+ to_chat(user," You begin to glue down one end of [src]...")
+ if(do_after(user, 10) && isturf(loc))
+ new src.glueobj(loc)
+ to_chat(user, " You slap some glue onto [src].")
+ qdel(src)
+ else
+ to_chat(user, "You need to hold still to glue [src]!")
+ else
+ ..()
+//cover a wooden peg in glue
+/obj/item/processed/wood/peg/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/carpentry/glue))
+ to_chat(user," You begin to glue down one end of the [src]...")
+ if(do_after(user, 10) && isturf(loc))
+ new src.glueobj(loc)
+ to_chat(user, " You slap some glue onto [src].")
+ qdel(src)
+ else
+ to_chat(user, "You need to hold still to glue [src]!")
+ else
+ ..()
+
+//Seats
+//bore a platform into a seat
+/obj/item/processed/wood/platform/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/carpentry/borer))
+ to_chat(user," You begin to cut four holes into [src]...")
+ if(do_after(user, 40) && isturf(loc))
+ new src.boreobj(loc)
+ to_chat(user, " You drill four holes into [src].")
+ qdel(src)
+ else
+ to_chat(user, "You need to hold still to refine [src]!")
+ else
+ ..()
+
+//Stools - Further crafting
+/obj/item/processed/wood/stool1/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/nails))
+ to_chat(user," You place nails into [src]...")
+ if(do_after(user, 20) && isturf(loc))
+ new /obj/item/processed/wood/stool2(loc)
+ to_chat(user, " The nails are ready to be hammered.")
+ qdel(src)
+ qdel(I)
+ else
+ to_chat(user, "You need to hold still to refine [src]!")
+ else
+ ..()
+
+/obj/item/processed/wood/stool2/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/carpentry/hammer))
+ to_chat(user," You begin to hammer the [src]...")
+ if(do_after(user, 30) && isturf(loc))
+ new /obj/item/processed/wood/stool3(loc)
+ to_chat(user, " The nails are hammered into place.")
+ qdel(src)
+ else
+ to_chat(user, "You need to hold still to refine [src]!")
+ else
+ ..()
+
+/obj/item/processed/wood/stool3/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/carpentry/sandpaper))
+ to_chat(user," You begin to sand the [src]...")
+ if(do_after(user, 30) && isturf(loc))
+ new /obj/item/processed/wood/stool4(loc)
+ to_chat(user, " You sand down the [src].")
+ qdel(src)
+ else
+ to_chat(user, "You need to hold still to refine [src]!")
+ else
+ ..()
+
+/obj/item/processed/wood/stool4/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/processed/wood/glueblock))
+ to_chat(user," You add some finishing touches to the [src]...")
+ if(do_after(user, 30) && isturf(loc))
+ new /obj/item/processed/wood/stool(loc)
+ to_chat(user, " You complete the [src].")
+ qdel(src)
+ qdel(I)
+ else
+ to_chat(user, "You need to hold still to refine [src]!")
+ else
+ ..()
+
+/obj/item/processed/wood/stool/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/cushion))
+ to_chat(user," You secure a cloth cushion to [src]...")
+ if(do_after(user, 30) && isturf(loc))
+ new /obj/item/processed/wood/stoolcloth(loc)
+ to_chat(user, " You add a cushion to [src].")
+ qdel(src)
+ qdel(I)
+ else
+ to_chat(user, "You need to hold still to detail [src]!")
+ else
+ ..()
+
+/obj/item/processed/wood/stool/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/cushion/silk))
+ to_chat(user," You secure a silk cushion to [src]...")
+ if(do_after(user, 30) && isturf(loc))
+ new /obj/item/processed/wood/stoolsilk(loc)
+ to_chat(user, " You add a cushion to [src].")
+ qdel(src)
+ qdel(I)
+ else
+ to_chat(user, "You need to hold still to detail [src]!")
+ else
+ ..()
diff --git a/hyperstation/code/modules/cargo/sweatshop/wooden.dm b/hyperstation/code/modules/cargo/sweatshop/wooden.dm
new file mode 100644
index 0000000000..9a6bb52174
--- /dev/null
+++ b/hyperstation/code/modules/cargo/sweatshop/wooden.dm
@@ -0,0 +1,128 @@
+//WOODEN COMPONENTS. honestly i need to move some shit around to allow for easier material swapping, but that's for a later date.
+
+/obj/item/processed/wood
+ name = "Wooden Processed Item"
+ desc = "You shouldn't see this!"
+ icon = 'hyperstation/icons/obj/cargo/sweatshop/wooden.dmi'
+ sharpness = FALSE
+ attack_verb = list("slapped", "thunked")
+ var/sawobj = /obj/item/condom
+ var/glueobj = /obj/item/dildo
+ var/sandobj = /obj/item/carpentry/sandpaper
+ var/boreobj = /obj/item/carpentry/borer
+
+/obj/item/processed/wood/plank
+ name = "processable wooden plank"
+ desc = "A somewhat sturdy refined plank. This can be used in various applications."
+ icon_state = "plank"
+ force = 3
+ w_class = WEIGHT_CLASS_HUGE
+ sawobj = /obj/item/processed/wood/platform
+
+/obj/item/processed/wood/platform
+ name = "wood platform"
+ desc = "A somewhat sturdy cropping of a plank. This one is an alright foundation for chairs and stools."
+ icon_state = "platform"
+ force = 3
+ w_class = WEIGHT_CLASS_NORMAL
+ sawobj = /obj/item/processed/wood/block
+ boreobj = /obj/item/processed/wood/seat
+
+/obj/item/processed/wood/block
+ name = "wood block"
+ desc = "A chopped platform into a wooden block. This one can be used for sanded into pegs, or used as a base on it's own."
+ icon_state = "block"
+ force = 2
+ w_class = WEIGHT_CLASS_SMALL
+ sandobj = /obj/item/processed/wood/peg
+ glueobj = /obj/item/processed/wood/glueblock
+
+/obj/item/processed/wood/peg
+ name = "wood peg"
+ desc = "A wooden peg. Useful for fitting into holes."
+ icon_state = "peg"
+ force = 1
+ w_class = WEIGHT_CLASS_TINY
+ attack_verb = list("donked", "thunked")
+ glueobj = /obj/item/processed/wood/gluepeg
+
+//glue
+
+/obj/item/processed/wood/gluepeg
+ name = "glued wood peg"
+ desc = "A wooden peg. With a bunch of glue used for securing."
+ icon_state = "gluepeg"
+ force = 1
+ w_class = WEIGHT_CLASS_TINY
+ attack_verb = list("pegged", "thunked")
+
+/obj/item/processed/wood/glueblock
+ name = "glued wood block"
+ desc = "A wooden block. With a bunch of glue used for securing."
+ icon_state = "glueblock"
+ force = 2
+ w_class = WEIGHT_CLASS_SMALL
+ attack_verb = list("blocked", "thunked")
+
+//seat
+/obj/item/processed/wood/seat
+ name = "wood seat"
+ desc = "A baseline for crafting seats. Not exactly that comfortable to sit on..."
+ icon_state = "seat"
+ force = 2
+ w_class = WEIGHT_CLASS_SMALL
+ attack_verb = list("slapped", "thunked")
+
+
+//Stool steps. There's probably an easier way to do this, but I cannot be assed rn, I'll refine after PR
+/obj/item/processed/wood/stool1
+ name = "stool base"
+ desc = "A haphazardly made base for a stool. It's not even secured with any nails."
+ icon_state = "stool1"
+ force = 4
+ w_class = WEIGHT_CLASS_BULKY
+
+/obj/item/processed/wood/stool2
+ name = "nailed stool base"
+ desc = "Nails are in position"
+ icon_state = "stool2"
+ force = 4
+ w_class = WEIGHT_CLASS_BULKY
+
+/obj/item/processed/wood/stool3
+ name = "hammered stool base"
+ desc = "A vaguely stool-shaped... Thing. Could use some sandpaper."
+ icon_state = "stool3"
+ force = 4
+ w_class = WEIGHT_CLASS_BULKY
+
+/obj/item/processed/wood/stool4
+ name = "bland stool"
+ desc = "A rather bland stool."
+ icon_state = "stool4"
+ force = 4
+ w_class = WEIGHT_CLASS_BULKY
+
+//The finished product
+/obj/item/processed/wood/stool
+ name = "custom stool"
+ desc = "An intricite, custom stool."
+ icon_state = "stool"
+ force = 10
+ w_class = WEIGHT_CLASS_BULKY
+
+//Let's make it soft and more expensive
+
+/obj/item/processed/wood/stoolcloth
+ name = "cloth-cushioned stool"
+ desc = "A custom stool with a cloth cushion."
+ icon_state = "stoolcloth"
+ force = 10
+ w_class = WEIGHT_CLASS_BULKY
+
+/obj/item/processed/wood/stoolsilk
+ name = "cloth-cushioned stool"
+ desc = "A custom stool with a silk cushion."
+ icon_state = "stoolsilk"
+ force = 11 //lol
+ w_class = WEIGHT_CLASS_BULKY
diff --git a/hyperstation/code/modules/client/loadout/glasses.dm b/hyperstation/code/modules/client/loadout/glasses.dm
new file mode 100644
index 0000000000..11a7f7100c
--- /dev/null
+++ b/hyperstation/code/modules/client/loadout/glasses.dm
@@ -0,0 +1,11 @@
+/datum/gear/glasses/garb
+ name = "polychromic gar glasses"
+ category =ITEM_SLOT_GLASSES
+ path = /obj/item/clothing/glasses/polychromic/garpoly
+ cost = 2
+
+/datum/gear/glasses/gigagarb
+ name = "polychromic giga gar glasses"
+ category =ITEM_SLOT_GLASSES
+ path = /obj/item/clothing/glasses/polychromic/supergarpoly
+ cost = 2
diff --git a/hyperstation/code/modules/client/loadout/gloves.dm b/hyperstation/code/modules/client/loadout/gloves.dm
new file mode 100644
index 0000000000..44acb78c02
--- /dev/null
+++ b/hyperstation/code/modules/client/loadout/gloves.dm
@@ -0,0 +1,11 @@
+/datum/gear/syntech/ring
+ name = "Normalizer Ring"
+ category =ITEM_SLOT_GLOVES
+ path = /obj/item/clothing/gloves/ring/syntech
+ cost = 6
+
+/datum/gear/syntech/band
+ name = "Normalizer Band"
+ category =ITEM_SLOT_GLOVES
+ path = /obj/item/clothing/gloves/ring/syntech/band
+ cost = 6
diff --git a/hyperstation/code/modules/client/loadout/neck.dm b/hyperstation/code/modules/client/loadout/neck.dm
new file mode 100644
index 0000000000..e4d14b12ac
--- /dev/null
+++ b/hyperstation/code/modules/client/loadout/neck.dm
@@ -0,0 +1,17 @@
+/datum/gear/syntech/pendant
+ name = "Normalizer Pendant"
+ category =ITEM_SLOT_NECK
+ path = /obj/item/clothing/neck/syntech
+ cost = 6
+
+/datum/gear/syntech/choker
+ name = "Normalizer Choker"
+ category =ITEM_SLOT_NECK
+ path = /obj/item/clothing/neck/syntech/choker
+ cost = 6
+
+/datum/gear/syntech/collar
+ name = "Normalizer Collar"
+ category =ITEM_SLOT_NECK
+ path = /obj/item/clothing/neck/syntech/collar
+ cost = 6
diff --git a/hyperstation/code/modules/client/loadout/tablet.dm b/hyperstation/code/modules/client/loadout/tablet.dm
new file mode 100644
index 0000000000..fc26cee92f
--- /dev/null
+++ b/hyperstation/code/modules/client/loadout/tablet.dm
@@ -0,0 +1,5 @@
+/datum/gear/backpack/tablet
+ name = "Tablet Computer"
+ category =ITEM_SLOT_IN_BACKPACK
+ path = /obj/item/modular_computer/tablet/preset/cheap/
+ cost = 3
diff --git a/hyperstation/code/modules/clothing/glasses/polychromic_glasses.dm b/hyperstation/code/modules/clothing/glasses/polychromic_glasses.dm
new file mode 100644
index 0000000000..61dac716f7
--- /dev/null
+++ b/hyperstation/code/modules/clothing/glasses/polychromic_glasses.dm
@@ -0,0 +1,39 @@
+/obj/item/clothing/glasses/polychromic
+ name = "polychromic glasses template"
+ desc = "You shouldn't be seeing this. Report it if you do."
+ icon = 'hyperstation/icons/obj/clothing/glasses.dmi'
+ icon_state = "polygar"
+ item_color = "polygar"
+ item_state = "polygar"
+ alternate_worn_icon = 'hyperstation/icons/mob/eyes.dmi' //Because, as it appears, the item itself is normally not directly aware of its worn overlays, so this is about the easiest way, without adding a new var.
+ hasprimary = TRUE
+ primary_color = "#0c0c0c"
+ vision_correction = 1
+
+/obj/item/clothing/glasses/polychromic/worn_overlays(isinhands, icon_file) //this is where the main magic happens. Also mandates that ALL polychromic stuff MUST USE alternate_worn_icon
+ . = ..()
+ if(hasprimary | hassecondary | hastertiary)
+ if(!isinhands) //prevents the worn sprites from showing up if you're just holding them
+ if(hasprimary) //checks if overlays are enabled
+ var/mutable_appearance/primary_worn = mutable_appearance(alternate_worn_icon, "[item_color]-primary") //automagical sprite selection
+ primary_worn.color = primary_color //colors the overlay
+ . += primary_worn //adds the overlay onto the buffer list to draw on the mob sprite.
+ if(hassecondary)
+ var/mutable_appearance/secondary_worn = mutable_appearance(alternate_worn_icon, "[item_color]-secondary")
+ secondary_worn.color = secondary_color
+ . += secondary_worn
+ if(hastertiary)
+ var/mutable_appearance/tertiary_worn = mutable_appearance(alternate_worn_icon, "[item_color]-tertiary")
+ tertiary_worn.color = tertiary_color
+ . += tertiary_worn
+
+/obj/item/clothing/glasses/polychromic/garpoly
+ name = "polychromic gar glasses"
+ desc = "Go beyond impossible and kick reason to the curb! Doesn't seem to have flash protection and doesn't seem sharp either. It is made out of bluespace prescription glass though."
+
+/obj/item/clothing/glasses/polychromic/supergarpoly
+ name = "polychromic giga gar glasses"
+ desc = "Believe in the you who believes in yourself. Also doesn't seem to have flash protection and doesn't seem sharp either. It is made out of bluespace prescription glass though."
+ icon_state = "polysupergar"
+ item_color = "polysupergar"
+ item_state = "polysupergar"
diff --git a/hyperstation/code/modules/clothing/gloves.dm b/hyperstation/code/modules/clothing/gloves.dm
new file mode 100644
index 0000000000..c3ecb21fe1
--- /dev/null
+++ b/hyperstation/code/modules/clothing/gloves.dm
@@ -0,0 +1,13 @@
+/obj/item/clothing/gloves/guncaster
+ name = "fingerless leather gloves"
+ desc = "Sturdy leather gloves with no fingertips, buckled at the wrist."
+ icon_state = "guncaster"
+ item_state = "guncaster"
+ icon = 'hyperstation/icons/obj/clothing/gloves.dmi'
+ alternate_worn_icon = 'hyperstation/icons/mobs/gloves.dmi'
+ item_color = null //So they don't wash.
+ transfer_prints = TRUE
+ strip_delay = 40
+ equip_delay_other = 20
+ cold_protection = HANDS
+ min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT
\ No newline at end of file
diff --git a/hyperstation/code/modules/clothing/head.dm b/hyperstation/code/modules/clothing/head.dm
new file mode 100644
index 0000000000..8a0a5aee12
--- /dev/null
+++ b/hyperstation/code/modules/clothing/head.dm
@@ -0,0 +1,8 @@
+/obj/item/clothing/head/maidband
+ name = "maid head-band"
+ desc = "To complete the maid look."
+ icon_state = "maid_head"
+ item_state = "maid_head"
+ icon = 'hyperstation/icons/obj/clothing/head.dmi'
+ alternate_worn_icon = 'hyperstation/icons/mobs/head.dmi'
+ mutantrace_variation = NONE
diff --git a/hyperstation/code/modules/clothing/sizeaccessories.dm b/hyperstation/code/modules/clothing/sizeaccessories.dm
new file mode 100644
index 0000000000..9a0d65efe2
--- /dev/null
+++ b/hyperstation/code/modules/clothing/sizeaccessories.dm
@@ -0,0 +1,116 @@
+//Clothing vars and procs
+/obj/item/clothing
+ var/normalize_size = RESIZE_NORMAL //This number is used as the "normal" height people will be given when wearing one of these accessories
+ var/natural_size = null //The value of the wearer's body_size var in prefs. Unused for now.
+ var/recorded_size = null //the user's height prior to equipping
+
+//For applying a normalization
+/obj/item/clothing/proc/normalize_mob_size(mob/living/carbon/human/H)
+ if(H.normalized) //First we make a check to see if they're already normalized, from wearing another article of SynTech jewelry
+ to_chat(H, "This accessory buzzes, being overwritten by another.")
+ playsound(H, 'sound/machines/buzz-sigh.ogg', 50, 1)
+ return
+ recorded_size = H.get_effective_size() //If not, grab their current size
+ playsound(H, 'sound/effects/magic.ogg', 50, 1)
+ flash_lighting_fx(3, 3, LIGHT_COLOR_PURPLE)
+ H.visible_message("A flash of purple light engulfs [H], before they change to normal!","You feel warm for a moment, before everything scales to your size...")
+ H.resize(normalize_size) //Then apply the size
+ H.normalized = TRUE //And set normalization
+
+//For removing a normalization, and reverting back to normal
+/obj/item/clothing/proc/denormalize_mob_size(mob/living/carbon/human/H)
+ if(H.normalized) //sanity check
+ playsound(H,'sound/weapons/emitter2.ogg', 50, 1)
+ flash_lighting_fx(3, 3, LIGHT_COLOR_YELLOW)
+ H.visible_message("Golden light engulfs [H], and they shoot back to their default height!","Energy rushes through your body, and you return to normal.")
+ H.resize(recorded_size)
+ H.normalized = FALSE
+
+
+//For storing normalization on mobs
+/mob/living
+ var/normalized = FALSE
+ //normalized is a check for instances where more than one accessory of jewelry is worn. For all intensive purposes, only the first worn accessory stores the user's size.
+ //Anything else is just extra.
+
+//Clothing below. Code could be compressed more, but until I make jewelry slots, this will do. -Dahl
+
+//GLOVE SLOT ITEMS...
+//SynTech ring
+/obj/item/clothing/gloves/ring/syntech
+ name = "normalizer ring"
+ desc = "An expensive, shimmering SynTech ring gilded with golden GATO markings. It will 'normalize' the size of the user to a specified height approved for work-conditions, as long as it is equipped. The artificial violet gem inside twinkles ominously."
+ icon = 'hyperstation/icons/obj/clothing/sizeaccessories.dmi'
+ icon_state = "ring"
+ item_state = "sring" //No use in a unique sprite since it's just one pixel
+ w_class = WEIGHT_CLASS_TINY
+ body_parts_covered = 0
+ transfer_prints = TRUE
+ strip_delay = 40
+ //These are already defined under the parent ring, but I wanna leave em here for reference purposes
+
+//For glove slots
+/obj/item/clothing/gloves/ring/syntech/equipped(mob/living/user, slot)
+ if(ishuman(user))
+ var/mob/living/carbon/human/human_target = user
+ if(slot ==ITEM_SLOT_GLOVES)
+
+ if(human_target.custom_body_size)
+ normalize_mob_size(human_target)
+
+/obj/item/clothing/gloves/ring/syntech/dropped(mob/living/user, slot)
+ if(ishuman(user))
+ var/mob/living/carbon/human/human_target = user
+
+ if(human_target.normalized)
+ denormalize_mob_size(human_target)
+
+
+
+//SynTech Wristband
+/obj/item/clothing/gloves/ring/syntech/band
+ name = "normalizer wristband"
+ desc = "An expensive technological wristband cast in SynTech purples with shimmering GATO hues. It will 'normalize' the size of the user to a specified height for approved work-conditions, as long as it is equipped. There is a small screen buzzing with information."
+ icon_state = "wristband"
+ item_state = "syntechband"
+
+
+//NECK SLOT ITEMS...
+//Syntech Pendant
+/obj/item/clothing/neck/syntech
+ name = "normalizer pendant"
+ desc = "A vibrant violet jewel cast in silvery-gold metals, sporting the elegance of GATO with SynTech prowess. It will 'normalize' the size of the user to a specified height for approved work-conditions, as long as it is equipped. The artificial violet gem inside twinkles ominously."
+ icon = 'hyperstation/icons/obj/clothing/sizeaccessories.dmi'
+ icon_state = "pendant"
+ item_state = "pendant"
+ w_class = WEIGHT_CLASS_SMALL //Gainstation Edit: Small, not normal sized.
+
+//For neck items
+/obj/item/clothing/neck/syntech/equipped(mob/living/user, slot)
+ if(ishuman(user))
+ var/mob/living/carbon/human/human_target = user
+ if(slot ==ITEM_SLOT_NECK)
+
+ if(human_target.custom_body_size)
+ normalize_mob_size(human_target)
+
+/obj/item/clothing/neck/syntech/dropped(mob/living/user, slot)
+ if(ishuman(user))
+ var/mob/living/carbon/human/human_target = user
+
+ if(human_target.normalized)
+ denormalize_mob_size(human_target)
+
+//Syntech Choker
+/obj/item/clothing/neck/syntech/choker
+ name = "normalizer choker"
+ desc = "A sleek, tight-fitting choker embezzled with silver to gold, adorned with vibrant purple studs; combined technology of GATO and SynTech. It will 'normalize' the size of the user to a specified height for approved work-conditions, as long as it is equipped. There is a small screen buzzing with information."
+ icon_state = "choker"
+ item_state = "collar"
+
+//Syntech Collar
+/obj/item/clothing/neck/syntech/collar
+ name = "normalizer collar"
+ desc = "A cute pet collar, technologically designed with vibrant purples and smooth silvers. There is a small gem bordered by gold at the front, reading 'SYNTECH' engraved within the metal. It will 'normalize' the size of the user to a specified height for approved work-conditions, as long as it is equipped. The artificial violet gem inside twinkles ominously."
+ icon_state = "collar"
+ item_state = "collar"
diff --git a/hyperstation/code/modules/clothing/spacesuits/hardsuit.dm b/hyperstation/code/modules/clothing/spacesuits/hardsuit.dm
new file mode 100644
index 0000000000..619c5526b0
--- /dev/null
+++ b/hyperstation/code/modules/clothing/spacesuits/hardsuit.dm
@@ -0,0 +1,178 @@
+/obj/item/clothing/head/helmet/space/hardsuit/rd/hev
+ name = "HEV Suit helmet"
+ desc = "A Hazardous Environment Helmet. It fits snug over the suit and has a heads-up display for researchers. The flashlight seems broken, fitting considering this was made before the start of the milennium."
+ icon_state = "hev"
+ item_state = "hev"
+ item_color = "rd"
+ resistance_flags = ACID_PROOF | FIRE_PROOF
+ max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
+ armor = list("melee" = 30, "bullet" = 10, "laser" = 10, "energy" = 5, "bomb" = 80, "bio" = 100, "rad" = 100, "fire" = 60, "acid" = 60)
+ actions_types = list(/datum/action/item_action/toggle_research_scanner)
+
+/obj/item/clothing/head/helmet/space/hardsuit/rd/hev/no_scanner
+ actions_types = list()
+
+/obj/item/clothing/suit/space/hardsuit/rd/hev
+ name = "HEV Suit"
+ desc = "The hazard suit. It was designed to protect scientists from the blunt trauma, radiation, energy discharge that hazardous materials might produce or entail. Fits you like a glove. The automatic medical system seems broken... They're waiting for you, Gordon. In the test chamberrrrrr."
+ icon_state = "hev"
+ item_state = "hev"
+ resistance_flags = ACID_PROOF | FIRE_PROOF
+ max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT //Same as an emergency firesuit. Not ideal for extended exposure.
+ flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT
+ allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/gun/energy/wormhole_projector,
+ /obj/item/hand_tele, /obj/item/aicard)
+ armor = list("melee" = 30, "bullet" = 10, "laser" = 10, "energy" = 5, "bomb" = 80, "bio" = 100, "rad" = 100, "fire" = 60, "acid" = 60)
+ helmettype = /obj/item/clothing/head/helmet/space/hardsuit/rd/hev
+ tauric = FALSE //Citadel Add for tauric hardsuits
+ taurmode = NOT_TAURIC
+ var/firstpickup = TRUE
+ var/pickupsound = TRUE
+
+/obj/item/clothing/suit/space/hardsuit/rd/hev/no_sound
+ pickupsound = FALSE
+
+/obj/item/clothing/suit/space/hardsuit/rd/hev/equipped(mob/user, slot)
+ . = ..()
+ if(!pickupsound)
+ return
+ if(!ishuman(user))
+ return
+ if(slot ==ITEM_SLOT_WEAR_SUIT)
+ if(!firstpickup)
+ SEND_SOUND(user, sound('hyperstation/sound/halflife/hevsuit_pickup.ogg', volume = 50))
+ else
+ firstpickup = FALSE
+ SEND_SOUND(user, sound('hyperstation/sound/halflife/hevsuit_firstpickup.ogg', volume = 50))
+ SEND_SOUND(user, sound('hyperstation/sound/halflife/anomalous_materials.ogg', volume = 50))
+ return
+
+/obj/item/clothing/suit/space/hardsuit/shielded/goldenpa
+ name = "GATO Power Armor"
+ desc = "An advanced armor with built in energy shielding, developed by GATO via unknown means. It belongs by only few exclusive members of the corporation."
+ icon_state = "golden_pa"
+ item_state = "golden_pa"
+ max_charges = 4
+ current_charges = 4
+ recharge_delay = 15
+ armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
+ strip_delay = 130
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+ allowed = list(/obj/item/gun, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/melee/transforming/energy/sword/saber, /obj/item/restraints/handcuffs, /obj/item/tank/internals)
+ helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded/goldenpa
+ slowdown = 0
+ tauric = TRUE //Citadel Add for tauric hardsuits
+
+/obj/item/clothing/suit/space/hardsuit/shielded/goldenpa/Initialize(mapload)
+ jetpack = new /obj/item/tank/jetpack/suit(src)
+ . = ..()
+
+/obj/item/clothing/head/helmet/space/hardsuit/shielded/goldenpa
+ name = "GATO Power Helmet"
+ desc = "An advanced armor helmet with built in energy shielding, developed by GATO via unknown means. It belongs by only few exclusive members of the corporation."
+ icon_state = "hardsuit0-goldenpa"
+ item_state = "hardsuit0-goldenpa"
+ item_color = "goldenpa"
+ armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
+ strip_delay = 130
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+
+/obj/item/clothing/suit/space/hardsuit/teslapa
+ name = "Tesla Power Armor"
+ desc = "An advanced power armor, with built-in tesla technology. You're sure this will fry whoever dares attack in close quarters."
+ icon_state = "tesla_pa"
+ item_state = "tesla_pa"
+ item_color = "tesla_pa"
+ armor = list("melee" = 70, "bullet" = 70, "laser" = 90, "energy" = 90, "bomb" = 70, "bio" = 100, "rad" = 40, "fire" = 100, "acid" = 100)
+ strip_delay = 300
+ equip_delay_self = 300
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+ allowed = list(/obj/item/gun, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/melee/transforming/energy/sword/saber, /obj/item/restraints/handcuffs, /obj/item/tank/internals)
+ helmettype = /obj/item/clothing/head/helmet/space/hardsuit/teslapahelmet
+ slowdown = 1
+ siemens_coefficient = -1
+ blood_overlay_type = "armor"
+ resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+ hit_reaction_chance = 50
+ rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE
+ var/teslapa_cooldown = 20
+ var/teslapa_cooldown_duration = 10
+ var/tesla_power = 20000
+ var/tesla_range = 4
+ var/tesla_flags = TESLA_MOB_DAMAGE | TESLA_OBJ_DAMAGE
+ var/legacy = FALSE
+ var/legacy_dmg = 35
+
+/obj/item/clothing/suit/space/hardsuit/teslapa/Initialize(mapload)
+ jetpack = new /obj/item/tank/jetpack/suit(src)
+ . = ..()
+
+/obj/item/clothing/suit/space/hardsuit/teslapa/dropped(mob/user)
+ ..()
+ if(istype(user))
+ user.flags_1 &= ~TESLA_IGNORE_1
+
+/obj/item/clothing/suit/space/hardsuit/teslapa/equipped(mob/user, slot)
+ ..()
+ if(slot_flags & slotdefine2slotbit(slot)) //Was equipped to a valid slot for this item?
+ user.flags_1 |= TESLA_IGNORE_1
+
+/obj/item/clothing/suit/space/hardsuit/teslapa/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
+ if(prob(hit_reaction_chance))
+ if(world.time < teslapa_cooldown_duration)
+ var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread
+ sparks.set_up(1, 1, src)
+ sparks.start()
+ owner.visible_message("The tesla capacitors on [owner]'s Tesla Power Armor are still recharging! The armor merely emits some sparks.")
+ return
+ owner.visible_message("[src] blocks [attack_text], sending out arcs of lightning!")
+ if(!legacy)
+ tesla_zap(owner, tesla_range, tesla_power, tesla_flags)
+ else
+ for(var/mob/living/M in view(2, owner))
+ if(M == owner)
+ continue
+ owner.Beam(M,icon_state="purple_lightning",icon='icons/effects/effects.dmi',time=5)
+ M.adjustFireLoss(legacy_dmg)
+ playsound(M, 'sound/machines/defib_zap.ogg', 50, 1, -1)
+ teslapa_cooldown = world.time + teslapa_cooldown_duration
+ return TRUE
+
+/obj/item/clothing/head/helmet/space/hardsuit/teslapahelmet
+ name = "Tesla Power Armor Helmet"
+ desc = "An advanced power armor, with built-in tesla technology. You're sure this will fry whoever dares attack in close quarters."
+ icon_state = "teslaup"
+ item_state = "teslaup"
+ armor = list("melee" = 70, "bullet" = 70, "laser" = 90, "energy" = 90, "bomb" = 70, "bio" = 100, "rad" = 10, "fire" = 100, "acid" = 100)
+ strip_delay = 130
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+
+/obj/item/clothing/suit/space/hardsuit/advancedpa
+ name = "Advanced Power Armor"
+ desc = "An advanced power armor. You're sure this is near to impossible to penetrate in close quarters."
+ icon_state = "advanced_pa"
+ item_state = "advanced_pa"
+ item_color = "advanced_pa"
+ armor = list("melee" = 95, "bullet" = 95, "laser" = 70, "energy" = 80, "bomb" = 70, "bio" = 100, "rad" = 40, "fire" = 100, "acid" = 100)
+ strip_delay = 300 //chonky armor means chonky strip
+ equip_delay_self = 300
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+ allowed = list(/obj/item/gun, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/melee/transforming/energy/sword/saber, /obj/item/restraints/handcuffs, /obj/item/tank/internals)
+ helmettype = /obj/item/clothing/head/helmet/space/hardsuit/advancedpahelmet
+ slowdown = 0
+ blood_overlay_type = "armor"
+ resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+ rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE
+
+/obj/item/clothing/suit/space/hardsuit/advancedpa/Initialize(mapload)
+ jetpack = new /obj/item/tank/jetpack/suit(src)
+ . = ..()
+
+/obj/item/clothing/head/helmet/space/hardsuit/advancedpahelmet
+ name = "Advanced Power Armor Helmet"
+ desc = "An advanced power armor. You're sure this is almost impenetrable in close quarters."
+ icon_state = "adv_pa"
+ item_state = "adv_pa"
+ armor = list("melee" = 95, "bullet" = 90, "laser" = 70, "energy" = 80, "bomb" = 70, "bio" = 100, "rad" = 40, "fire" = 100, "acid" = 100)
+ strip_delay = 300
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
diff --git a/hyperstation/code/modules/clothing/suits/misc.dm b/hyperstation/code/modules/clothing/suits/misc.dm
new file mode 100644
index 0000000000..0d62f28d3e
--- /dev/null
+++ b/hyperstation/code/modules/clothing/suits/misc.dm
@@ -0,0 +1,24 @@
+/obj/item/clothing/suit/kaminacape
+ name = "Kamina's Cape"
+ desc = "Don't believe in yourself, dumbass. Believe in me. Believe in the Kamina who believes in you."
+ icon_state = "kaminacape"
+ item_state = "kaminacape"
+ body_parts_covered = 0
+ icon = 'hyperstation/icons/obj/clothing/suits.dmi'
+ alternate_worn_icon = 'hyperstation/icons/mobs/suits.dmi'
+ mutantrace_variation = NONE
+
+/obj/item/clothing/suit/gcvest
+ name = "\improper Guncaster's Vest"
+ desc = "An open leather vest with battlescarred metal shoulderpads, perfect for hunting interdimensional wazards. Smells of gunpowder and plasma."
+ icon_state = "guncaster"
+ item_state = "guncaster"
+ icon = 'hyperstation/icons/obj/clothing/suits.dmi'
+ alternate_worn_icon = 'hyperstation/icons/mobs/suits.dmi'
+ mutantrace_variation = NONE
+
+/obj/item/clothing/suit/gcvest/alt
+ name = "\improper Hellraider's Vest"
+ desc = "An open leather vest with battlescarred metal shoulderpads, discovered in a dimensional anomaly. Smells of gunpowder and plasma."
+ icon_state = "guncaster_alt"
+ item_state = "guncaster_alt"
diff --git a/hyperstation/code/modules/clothing/under/under.dm b/hyperstation/code/modules/clothing/under/under.dm
new file mode 100644
index 0000000000..2f3002ebe7
--- /dev/null
+++ b/hyperstation/code/modules/clothing/under/under.dm
@@ -0,0 +1,9 @@
+/obj/item/clothing/under/lumberjack
+ name = "lumberjack outfit"
+ desc = "I am a lumberjack and I am ok, I sleep all night and I work all day."
+ icon = 'hyperstation/icons/obj/clothing/suits.dmi'
+ alternate_worn_icon = 'hyperstation/icons/mobs/suits.dmi'
+ icon_state = "lumberjack"
+ item_state = "lumberjack"
+ can_adjust = FALSE
+ mutantrace_variation = NONE
diff --git a/hyperstation/code/modules/economy/account.dm b/hyperstation/code/modules/economy/account.dm
new file mode 100644
index 0000000000..039ab4482e
--- /dev/null
+++ b/hyperstation/code/modules/economy/account.dm
@@ -0,0 +1,29 @@
+#define DUMPTIME 3000
+
+/datum/bank_account
+ var/account_holder = "Some pleb"
+ var/account_balance = 0
+ var/account_offstation_balance = 0
+ var/account_pin = 0
+ var/account_dna = ""
+ var/datum/job/account_job
+ var/list/bank_cards = list()
+ var/add_to_accounts = TRUE
+ var/transferable = TRUE
+ var/account_id
+ var/withdrawDelay = 0
+
+/datum/bank_account/New(newname, job)
+ if(add_to_accounts)
+ if(!SSeconomy)
+ log_world("Wack")
+ SSeconomy.bank_accounts += src
+ account_holder = newname
+ account_job = job
+ account_id = rand(111111,999999)
+
+/datum/bank_account/Destroy()
+ if(add_to_accounts)
+ SSeconomy.bank_accounts -= src
+ return ..()
+
diff --git a/hyperstation/code/modules/events/crystalline_reentry.dm b/hyperstation/code/modules/events/crystalline_reentry.dm
new file mode 100644
index 0000000000..f3a6d1e05a
--- /dev/null
+++ b/hyperstation/code/modules/events/crystalline_reentry.dm
@@ -0,0 +1,309 @@
+/datum/round_event_control/crystalline_reentry
+ name = "Crystalline Asteroid"
+ typepath = /datum/round_event/crystalline_reentry
+ min_players = 15
+ max_occurrences = 5
+ var/atom/special_target
+
+/datum/round_event_control/crystalline_reentry/admin_setup()
+ if(!check_rights(R_FUN))
+ return
+ var/aimed = alert("Aimed at current location?","Snipe", "Yes", "No")
+ if(aimed == "Yes")
+ special_target = get_turf(usr)
+
+/datum/round_event/crystalline_reentry
+ announceWhen = 0
+ startWhen = 10
+ fakeable = FALSE
+
+/datum/round_event/crystalline_reentry/announce(fake)
+ priority_announce("A crystalline asteroid has suffered a violent atmospheric entry. Brace for possible impact.", "General Alert")
+
+/datum/round_event/crystalline_reentry/start()
+ var/datum/round_event_control/crystalline_reentry/C = control
+ var/startside = pick(GLOB.cardinals)
+ var/z = pick(SSmapping.levels_by_trait(ZTRAIT_STATION))
+ var/turf/startT = spaceDebrisStartLoc(startside, z)
+ var/turf/endT = spaceDebrisFinishLoc(startside, z)
+ new /obj/effect/crystalline_reentry(startT, endT, C.special_target)
+
+/datum/round_event_control/crystalline_wave
+ name = "Catastrophic Crystalline Asteroid Wave"
+ typepath = /datum/round_event/crystalline_wave
+ min_players = 35
+ max_occurrences = 0 //This is only an admin spawn. Ergo, wrath of the gods.
+ var/atom/special_target
+
+/datum/round_event_control/crystalline_wave/admin_setup()
+ if(!check_rights(R_FUN))
+ return
+ /* No special target for you
+ var/aimed = alert("Aimed at current location?","Snipe", "Yes", "No")
+ if(aimed == "Yes")
+ special_target = get_turf(usr)
+ */
+ var/randselect = pick("https://youtu.be/S0HTqqwZq-o","https://youtu.be/Liv4CvpMdRA","https://youtu.be/9XZyQ12qt7w")
+ message_admins("A crystalline asteroid wave has been triggered. Maybe you should add some music for the players? Consider this random selection: [randselect]")
+
+/datum/round_event/crystalline_wave
+ announceWhen = 0
+ startWhen = 15
+ endWhen = 60 //45 seconds of pain
+ fakeable = FALSE
+
+/datum/round_event/crystalline_wave/announce(fake)
+ priority_announce("Several crystalline asteroids have been detected en route with the station. All hands, brace for impact. Organic signals have been detected contained within some of the asteroids.", title = "Priority Alert", sound = 'sound/misc/voyalert.ogg')
+
+/datum/round_event/crystalline_wave/tick()
+ if(ISMULTIPLE(activeFor, 3))
+ spawn_asteroids(rand(4,5)) // A looooot of asteroids...
+
+/datum/round_event/crystalline_wave/proc/spawn_asteroids(number = 5)
+ var/datum/round_event_control/crystalline_reentry/C = control
+ for(var/i = 0; i < number; i++)
+ var/startside = pick(GLOB.cardinals)
+ var/z = pick(SSmapping.levels_by_trait(ZTRAIT_STATION))
+ var/turf/startT = spaceDebrisStartLoc(startside, z)
+ var/turf/endT = spaceDebrisFinishLoc(startside, z)
+ new /obj/effect/crystalline_reentry/notendrilalert(startT, endT, C.special_target)
+
+/obj/effect/crystalline_reentry
+ name = "dense ice asteroid"
+ desc = "Oh shit."
+ icon = 'icons/obj/meteor.dmi'
+ icon_state = "ice"
+ throwforce = 100
+ move_force = INFINITY
+ move_resist = INFINITY
+ pull_force = INFINITY
+ density = TRUE
+ anchored = TRUE
+ flags_1 = PREVENT_CONTENTS_EXPLOSION_1
+ var/asteroidhealth = 150
+ var/z_original = 0
+ var/destination
+ var/notify = TRUE
+ var/tendrilnotify = TRUE
+ var/atom/special_target
+ var/dropamt = 5
+ var/droptype = list(/obj/item/stack/ore/bluespace_crystal)
+ var/meteorsound = 'sound/effects/meteorimpact.ogg'
+
+/obj/effect/crystalline_reentry/New(atom/start, atom/end, aimed_at)
+ ..()
+ SSaugury.register_doom(src, 2000)
+ z_original = z
+ destination = end
+ special_target = aimed_at
+ asteroidhealth = rand(150,300)
+ if(notify)
+ notify_ghosts("\A [src] is inbound!",
+ enter_link="(Click to orbit)",
+ source=src, action=NOTIFY_ORBIT)
+ GLOB.poi_list += src
+
+ var/special_target_valid = FALSE
+ if(special_target)
+ var/turf/T = get_turf(special_target)
+ if(T.z == z_original)
+ special_target_valid = TRUE
+ if(special_target_valid)
+ walk_towards(src, special_target, 1)
+ else if(end && end.z==z_original)
+ walk_towards(src, destination, 1)
+
+/obj/effect/crystalline_reentry/Topic(href, href_list)
+ if(href_list["orbit"])
+ var/mob/dead/observer/ghost = usr
+ if(istype(ghost))
+ ghost.ManualFollow(src)
+
+/obj/effect/crystalline_reentry/Destroy()
+ GLOB.poi_list -= src
+ . = ..()
+
+/obj/effect/crystalline_reentry/Moved()
+ if((z != z_original) || (loc == destination))
+ qdel(src)
+ if(special_target && loc == get_turf(special_target))
+ complete_trajectory()
+ return ..()
+
+/obj/effect/crystalline_reentry/proc/complete_trajectory()
+ //We hit what we wanted to hit, time to go boom!
+ collision_effect()
+
+/obj/effect/crystalline_reentry/ex_act(severity, target)
+ return FALSE
+
+/obj/effect/crystalline_reentry/singularity_act()
+ return
+
+/obj/effect/crystalline_reentry/singularity_pull()
+ return
+
+/obj/effect/crystalline_reentry/Bump(atom/clong)
+ if(!special_target)//If it has a special target, THERE ARE NO BRAKES ON THE ADMINBUS, BABY
+ asteroidhealth = asteroidhealth - rand(7,14)
+
+ if(prob(10))
+ playsound(src, 'sound/effects/bang.ogg', 50, 1)
+ audible_message("You hear a BONK!")
+
+ if(clong && prob(25))
+ x = clong.x
+ y = clong.y
+
+ if(special_target && clong == special_target)
+ complete_trajectory()
+
+ if(isturf(clong) || isobj(clong))
+ if(clong.density)
+ clong.ex_act(EXPLODE_HEAVY)
+
+ else if(isliving(clong))
+ penetrate(clong)
+ else if(istype(clong, type))
+ var/obj/effect/crystalline_reentry/other = clong
+ visible_message("[src] collides with [other]!\
+ ")
+ var/datum/effect_system/smoke_spread/smoke = new
+ smoke.set_up(2, get_turf(src))
+ smoke.start()
+ qdel(src)
+ qdel(other)
+ if(asteroidhealth <= 0)
+ collision_effect()
+ else
+ atmos_spawn_air("water_vapor=75;TEMP=0") //brr
+
+/obj/effect/crystalline_reentry/proc/penetrate(mob/living/L)
+ L.visible_message("[L] is smashed by a crystalline asteroid!" , "The crystalline asteroid smashes you!" , "You hear a BONK!")
+ if(ishuman(L))
+ var/mob/living/carbon/human/H = L
+ H.adjustBruteLoss(160)
+ if(special_target && loc == get_turf(special_target)) // just in case. Otherwise the asteroid can stop if it penetrates someone that is standing exactly on that spot and that is bad
+ complete_trajectory()
+
+/obj/effect/crystalline_reentry/proc/make_debris()
+ for(var/throws = dropamt, throws > 0, throws--)
+ var/thing_to_spawn = pick(droptype)
+ new thing_to_spawn(get_turf(src))
+
+/obj/effect/crystalline_reentry/proc/collision_effect()
+ make_debris()
+ explosion(src.loc, 0, 0, 5, 3, 1, 0, 0, 0, 0)
+ var/sound/meteor_sound = sound(meteorsound)
+ var/random_frequency = get_rand_frequency()
+ for(var/mob/M in GLOB.player_list)
+ if((M.orbiting) && (SSaugury.watchers[M]))
+ continue
+ var/turf/T = get_turf(M)
+ if(!T || T.z != src.z)
+ continue
+ var/dist = get_dist(M.loc, src.loc)
+ shake_camera(M, dist > 20 ? 2 : 4, dist > 20 ? 1 : 3)
+ M.playsound_local(src.loc, null, 50, 1, random_frequency, 10, S = meteor_sound)
+ atmos_spawn_air("water_vapor=1250;TEMP=0") //brr
+ switch(rand(1,100))
+ if(1 to 30)
+ var/obj/structure/spawner/crystalline/M = new(src.loc)
+ visible_message("A [M] emerges from the asteroid's rubble!")
+ if(prob(50) && tendrilnotify)
+ priority_announce("Unknown organic entities have been detected in the vincinity of [station_name()]. General caution is advised.", "General Alert")
+ if(31 to 99)
+ visible_message("The asteroid collapses into nothing...")
+ if(100)
+ var/mob/living/simple_animal/bot/hugbot/M = new(src.loc)
+ visible_message("A [M] emerges from the asteroid's rubble! Wait... What?")
+ qdel(src)
+
+/obj/effect/crystalline_reentry/notendrilalert
+ tendrilnotify = FALSE
+
+/mob/living/simple_animal/hostile/asteroid/basilisk/tendril
+ fromtendril = TRUE
+
+//Crystalline Tendrils, which spawn crystalline monsters
+/obj/structure/spawner/crystalline
+ name = "crystalline tendril"
+ desc = "A vile tendril of corruption, originating from gods know where. Terrible monsters are pouring out of it."
+ icon = 'icons/mob/nest.dmi'
+ icon_state = "tendril"
+ faction = list("mining")
+ max_mobs = 3
+ max_integrity = 9000 // Don't worry, the value is normalized within Initialize
+ obj_integrity = 9000 // Same as above
+ mob_types = list(/mob/living/simple_animal/hostile/asteroid/basilisk/tendril)
+ move_resist = INFINITY // just killing it tears a massive hole in the ground, let's not move it
+ anchored = TRUE
+ resistance_flags = INDESTRUCTIBLE | FIRE_PROOF | LAVA_PROOF
+ var/gps = null
+ var/obj/effect/light_emitter/tendril/emitted_light
+
+/obj/structure/spawner/crystalline/Initialize(mapload)
+ . = ..()
+ emitted_light = new(loc)
+ for(var/F in RANGE_TURFS(1, src))
+ if(iswallturf(F))
+ var/turf/closed/M = F
+ M.ScrapeAway(null, CHANGETURF_IGNORE_AIR)
+ if(iscloudturf(F))
+ var/turf/open/chasm/cloud/M = F
+ M.TerraformTurf(/turf/open/floor/plating/asteroid/layenia, /turf/open/floor/plating/asteroid/layenia)
+ gps = new /obj/item/gps/internal(src)
+ addtimer(CALLBACK(src,PROC_REF(delayedInitialize)), 4 SECONDS)
+
+/obj/structure/spawner/crystalline/deconstruct(disassembled)
+ new /obj/effect/cloud_collapse(loc)
+ new /obj/structure/closet/crate/necropolis/tendril(loc)
+ return ..()
+
+/obj/structure/spawner/crystalline/Destroy()
+ QDEL_NULL(emitted_light)
+ QDEL_NULL(gps)
+ return ..()
+
+/obj/structure/spawner/crystalline/proc/delayedInitialize()
+ //Why is this needed? Simple, because apparently explosion is so slow that it triggers after the spawner spawns and kills it on the spot. This just makes it killable.
+ resistance_flags = FIRE_PROOF | LAVA_PROOF
+ max_integrity = 225
+ obj_integrity = 225
+
+/obj/effect/light_emitter/crystalline
+ set_luminosity = 4
+ set_cap = 2.5
+ light_color = LIGHT_COLOR_LIGHT_CYAN
+
+/obj/effect/cloud_collapse
+ name = "collapsing crystalline tendril"
+ desc = "Get clear!"
+ layer = TABLE_LAYER
+ icon = 'icons/mob/nest.dmi'
+ icon_state = "tendril"
+ anchored = TRUE
+ density = TRUE
+ var/obj/effect/light_emitter/crystalline/emitted_light
+
+/obj/effect/cloud_collapse/Initialize(mapload)
+ . = ..()
+ emitted_light = new(loc)
+ visible_message("The tendril writhes in fury as the earth around it begins to crack and break apart! Get back!")
+ visible_message("Something falls free of the tendril!")
+ playsound(loc,'sound/effects/tendril_destroyed.ogg', 200, 0, 50, 1, 1)
+ addtimer(CALLBACK(src,PROC_REF(collapse)), 50)
+
+/obj/effect/cloud_collapse/Destroy()
+ QDEL_NULL(emitted_light)
+ return ..()
+
+/obj/effect/cloud_collapse/proc/collapse()
+ for(var/mob/M in range(7,src))
+ shake_camera(M, 15, 1)
+ playsound(get_turf(src),'sound/effects/explosionfar.ogg', 200, 1)
+ visible_message("The tendril falls into the clouds below!")
+ for(var/turf/T in range(1,src))
+ if(!T.density)
+ T.TerraformTurf(/turf/open/chasm/cloud, /turf/open/chasm/cloud)
+ qdel(src)
diff --git a/hyperstation/code/modules/food_and_drinks/recipes/drinks_recipes.dm b/hyperstation/code/modules/food_and_drinks/recipes/drinks_recipes.dm
new file mode 100644
index 0000000000..05fedabf72
--- /dev/null
+++ b/hyperstation/code/modules/food_and_drinks/recipes/drinks_recipes.dm
@@ -0,0 +1,5 @@
+/datum/chemical_reaction/pilk
+ name = "Pilk"
+ id = /datum/reagent/consumable/pilk
+ results = list(/datum/reagent/consumable/pilk = 2)
+ required_reagents = list(/datum/reagent/consumable/milk = 1, /datum/reagent/consumable/space_cola = 1)
diff --git a/hyperstation/code/modules/integrated_electronics/input.dm b/hyperstation/code/modules/integrated_electronics/input.dm
new file mode 100644
index 0000000000..3c229bfe3a
--- /dev/null
+++ b/hyperstation/code/modules/integrated_electronics/input.dm
@@ -0,0 +1,80 @@
+/* Doesnt fucking work, dont know why, everything I add in this game has some kinda of issue.
+
+
+/obj/item/integrated_circuit/input/gmeasurement
+ name = "body measurement"
+ desc = "Used to get a measurement of a refs genitals size and body size."
+ icon_state = "medscan"
+ complexity = 5
+ extended_desc = "Upon activation, the circuit will attempt to measure all body parts on the refs body within one tile away."
+ inputs = list("target" = IC_PINTYPE_REF)
+ outputs = list(
+ "penis length" = IC_PINTYPE_NUMBER,
+ "breast size" = IC_PINTYPE_NUMBER,
+ "testicle size" = IC_PINTYPE_NUMBER,
+ "body size" = IC_PINTYPE_NUMBER
+ )
+ activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_RESEARCH
+ power_draw_per_use = 40
+ cooldown_per_use = 20
+ ext_cooldown = 25
+
+/obj/item/integrated_circuit/input/gmeasurement/do_work()
+
+ var/mob/living/L = get_pin_data_as_type(IC_INPUT, 1, /mob/living)
+
+ if(!istype(L) || !L.Adjacent(get_turf(src)) ) //Invalid input
+ return
+
+ var/obj/item/organ/genital/penis/P = L.getorganslot("penis")
+ var/obj/item/organ/genital/breasts/B = L.getorganslot("breasts")
+ var/obj/item/organ/genital/testicles/T = L.getorganslot("testicles")
+
+//reset data, just incase they dont have that genitle
+ set_pin_data(IC_OUTPUT, 1, 0)
+ set_pin_data(IC_OUTPUT, 2, 0)
+ set_pin_data(IC_OUTPUT, 3, 0)
+ set_pin_data(IC_OUTPUT, 4, 0)
+
+//get sizes
+ set_pin_data(IC_OUTPUT, 1, P.length)
+ set_pin_data(IC_OUTPUT, 2, B.cached_size)
+ set_pin_data(IC_OUTPUT, 3, T.cached_size )
+ set_pin_data(IC_OUTPUT, 4, L.size_multiplier*100)
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/input/pregtest
+ name = "pregnancy tester"
+ desc = "A circuit used to determine whether someone is pregnant or not and if they possess the ability to be impregnated."
+ icon_state = "medscan"
+ complexity = 5
+ inputs = list("target" = IC_PINTYPE_REF)
+ outputs = list(
+ "is pregnant" = IC_PINTYPE_BOOLEAN,
+ "breedable" = IC_PINTYPE_BOOLEAN
+ )
+ activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_RESEARCH
+ power_draw_per_use = 40
+ cooldown_per_use = 20
+ ext_cooldown = 25
+
+/obj/item/integrated_circuit/input/pregtest/do_work()
+
+ var/mob/living/L = get_pin_data_as_type(IC_INPUT, 1, /mob/living)
+
+ if(!istype(L) || !L.Adjacent(get_turf(src)) ) //Invalid input
+ return
+
+ var/obj/item/organ/genital/womb/W = L.getorganslot("womb")
+
+
+ set_pin_data(IC_OUTPUT, 1, null)
+ set_pin_data(IC_OUTPUT, 2, null)
+ set_pin_data(IC_OUTPUT, 1, W.pregnant)
+ set_pin_data(IC_OUTPUT, 2, L.breedable)
+ push_data()
+ activate_pin(2)
+*/
\ No newline at end of file
diff --git a/hyperstation/code/modules/jobs/job_types/roleplay.dm b/hyperstation/code/modules/jobs/job_types/roleplay.dm
new file mode 100644
index 0000000000..9becaf9b8c
--- /dev/null
+++ b/hyperstation/code/modules/jobs/job_types/roleplay.dm
@@ -0,0 +1,13 @@
+/*
+Roleplay Jobs, Currently borked.
+
+/datum/job/rpcentral
+ title = "Central Command Inspector"
+ faction = "Station"
+ total_positions = 1
+ spawn_positions = 1
+ minimal_player_age = 1
+ supervisors = "central command"
+ selection_color = "#ffeef0"
+ custom_spawn_text = "You are from central command doing a routine inspection of the station, please fill out the provided information and submit it to central command by the end of the shift."
+*/
\ No newline at end of file
diff --git a/hyperstation/code/modules/mining/equipment/survival_pod.dm b/hyperstation/code/modules/mining/equipment/survival_pod.dm
new file mode 100644
index 0000000000..9bc33102f8
--- /dev/null
+++ b/hyperstation/code/modules/mining/equipment/survival_pod.dm
@@ -0,0 +1,8 @@
+// yes, I COULD make it a seperate object from the surv capsule but the code needed is indeticle soo...
+
+/obj/item/survivalcapsule/reactor // the not-so-survival capsule
+ name = "RMBK Reactor Beacon"
+ desc = "A special bluespace beacon designed to implement a reactor into the hull of the ship or station that it is activated on."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "beacon"
+ template_id = "reactor"
diff --git a/hyperstation/code/modules/mining/shelters.dm b/hyperstation/code/modules/mining/shelters.dm
new file mode 100644
index 0000000000..cae63da171
--- /dev/null
+++ b/hyperstation/code/modules/mining/shelters.dm
@@ -0,0 +1,7 @@
+// yes, I COULD make it a seperate object from the surv capsule but the code needed is indeticle soo...
+
+/datum/map_template/shelter/reactor
+ name = "RBMK Reactor"
+ shelter_id = "reactor"
+ description = "A reactor core, coolant and moderator loop not included."
+ mappath = "_maps/templates/reactor_1.dmm"
diff --git a/hyperstation/code/modules/mob/dead/new_player/cosmetic_parts.dm b/hyperstation/code/modules/mob/dead/new_player/cosmetic_parts.dm
new file mode 100644
index 0000000000..7b71ff33e6
--- /dev/null
+++ b/hyperstation/code/modules/mob/dead/new_player/cosmetic_parts.dm
@@ -0,0 +1,163 @@
+/*
+COSMETIC PARTS
+
+this system allows you to change the _base_ appearance of a limb independent
+of species or markings. this, for example, allows us to create "hybrids" like a
+mammal with avian legs.
+
+keep in mind that this does not change the species of a leg! in the above example,
+the mammal's bird-legs are still mammal legs. this matters in some places, like
+body part transplants; mis-matched species will cause extra damage if the surgery
+fails and the part is rejected. so you can't attach mammal-bird legs to avians
+safely.
+*/
+
+/datum/cosmetic_part
+ /** A unique string that is used to identify this part for save files. Allows name changes without breaking saves */
+ var/id
+ /** The name of the cosmetic part. This shows up in the preferences dropdown. */
+ var/name
+ var/icon = 'hyperstation/icons/mob/char_parts.dmi'
+ var/icon_state
+ /** How colors are determined for this part. MUTCOLORS for base color, MATRIXED to allow multiple channels. */
+ var/color_src = MUTCOLORS
+ /** Whether or not this cosmetic part has an alternate form for digitigrade legs. */
+ var/support_digitigrade = TRUE
+ /**
+ WE
+ HATE
+ GENDER!!!!!!!!!!
+ */
+ var/support_gender = null
+ /** Species IDs that support this cosmetic part. Bypassed with "show mismatched markings" option. */
+ var/list/supported_species
+
+/datum/cosmetic_part/head
+/datum/cosmetic_part/chest
+/datum/cosmetic_part/arms
+/datum/cosmetic_part/legs
+
+// HEADS
+// =========================================
+
+/datum/cosmetic_part/head/default
+ id = "default"
+ name = "default"
+ icon = null
+
+/datum/cosmetic_part/head/ipc_round
+ id = "ipc_round"
+ name = "round ipc head"
+ icon_state = "ipc_round"
+ supported_species = list("ipc")
+ support_gender = FALSE
+
+
+// CHESTS
+// =========================================
+
+/datum/cosmetic_part/chest/default
+ id = "default"
+ name = "default"
+ icon = null
+
+/datum/cosmetic_part/chest/ipc_sleek
+ id = "ipc_sleek"
+ name = "sleek ipc chest"
+ icon_state = "ipc_sleek"
+ supported_species = list("ipc")
+ color_src = MATRIXED
+ support_gender = FALSE
+
+/datum/cosmetic_part/chest/ipc_jointed
+ id = "ipc_jointed"
+ name = "jointed ipc chest"
+ icon_state = "ipc_jointed"
+ supported_species = list("ipc")
+ color_src = MATRIXED
+ support_gender = FALSE
+
+
+// ARMS
+// =========================================
+
+/datum/cosmetic_part/arms/default
+ id = "default"
+ name = "default"
+ icon = null
+
+/datum/cosmetic_part/arms/avian_alt
+ id = "avian_alt"
+ name = "avian claws"
+ icon_state = "avian_alt"
+ supported_species = list("mammal", "avian", "aquatic", "insect", "xeno", "synthliz")
+
+/datum/cosmetic_part/arms/insect
+ id = "insect"
+ name = "insect arms"
+ icon = 'modular_citadel/icons/mob/mutant_bodyparts.dmi'
+ icon_state = "insect"
+ supported_species = list("mammal", "avian", "aquatic", "insect", "xeno", "synthliz")
+
+/datum/cosmetic_part/arms/ipc_sleek
+ id = "ipc_sleek"
+ name = "sleek ipc arms"
+ icon_state = "ipc_sleek"
+ supported_species = list("ipc")
+ color_src = MATRIXED
+
+/datum/cosmetic_part/arms/ipc_jointed
+ id = "ipc_jointed"
+ name = "jointed ipc arms"
+ icon_state = "ipc_jointed"
+ supported_species = list("ipc")
+ color_src = MATRIXED
+
+
+// LEGS
+// =========================================
+
+/datum/cosmetic_part/legs/default
+ id = "default"
+ name = "default"
+ icon = null
+
+/datum/cosmetic_part/legs/avian
+ id = "avian"
+ name = "avian legs"
+ icon = 'modular_citadel/icons/mob/mutant_bodyparts.dmi'
+ icon_state = "avian"
+ supported_species = list("mammal", "avian", "aquatic", "insect", "xeno", "synthliz")
+ support_digitigrade = FALSE
+
+/datum/cosmetic_part/legs/mammal
+ id = "mammal"
+ name = "mammal legs"
+ icon = 'modular_citadel/icons/mob/mutant_bodyparts.dmi'
+ icon_state = "mammal"
+ supported_species = list("mammal", "avian", "aquatic", "insect", "xeno", "synthliz")
+ support_digitigrade = TRUE
+
+/datum/cosmetic_part/legs/insect
+ id = "insect"
+ name = "insect legs"
+ icon = 'modular_citadel/icons/mob/mutant_bodyparts.dmi'
+ icon_state = "insect"
+ supported_species = list("mammal", "avian", "aquatic", "insect", "xeno", "synthliz")
+ support_digitigrade = TRUE
+
+/datum/cosmetic_part/legs/ipc_sleek
+ id = "ipc_sleek"
+ name = "sleek ipc legs"
+ icon_state = "ipc_sleek"
+ supported_species = list("ipc")
+ color_src = MATRIXED
+ support_digitigrade = FALSE
+
+/datum/cosmetic_part/legs/ipc_jointed
+ id = "ipc_jointed"
+ name = "jointed ipc legs"
+ icon_state = "ipc_jointed"
+ supported_species = list("ipc")
+ color_src = MATRIXED
+ support_digitigrade = FALSE
diff --git a/hyperstation/code/modules/mob/dead/new_player/roleplay.dm b/hyperstation/code/modules/mob/dead/new_player/roleplay.dm
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/hyperstation/code/modules/mob/dead/new_player/sprite_accessories.dm b/hyperstation/code/modules/mob/dead/new_player/sprite_accessories.dm
new file mode 100644
index 0000000000..932d47808f
--- /dev/null
+++ b/hyperstation/code/modules/mob/dead/new_player/sprite_accessories.dm
@@ -0,0 +1,575 @@
+/*
+from modular_citadel/code/modules/mob/dead/new_player/sprite_accessories.dm:
+/datum/sprite_accessory
+ var/extra = FALSE
+ var/extra_color_src = MUTCOLORS2 //The color source for the extra overlay.
+ var/extra2 = FALSE
+ var/extra_icon = 'modular_citadel/icons/mob/mam_tails.dmi'
+ var/extra2_icon = 'modular_citadel/icons/mob/mam_tails.dmi'
+ var/extra2_color_src = MUTCOLORS3
+ var/list/ckeys_allowed
+
+we use the "citadel" versions of mutant/species parts, which usually (but not always)
+begins with "mam_" somewhere. stuff that's built for only humans/lizards in other parts
+of the code are from TG, don't use those.
+
+keep everything in alphabetical order, please!
+*/
+
+// TODO: code it so that we don't have to type in "icon = 'hyperstation/icons/mob/etc'"
+// manually for hyperstation parts
+
+
+/*
+SNOUTS
+==========================================================
+*/
+
+/datum/sprite_accessory/mam_snouts/bigmandible // sarcoph @ hyperstation, march 2022
+ name = "Big Mandibles (Hyper)"
+ icon_state = "bigmandible"
+ icon = 'hyperstation/icons/mob/char_snouts.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_snouts/proboscis // sarcoph @ hyperstation, march 2022
+ name = "Proboscis (Hyper)"
+ icon_state = "proboscis"
+ icon = 'hyperstation/icons/mob/char_snouts.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_snouts/rhinobeetle // sarcoph @ hyperstation, march 2022
+ name = "Rhino Beetle (Hyper)"
+ icon_state = "rhinobeetle"
+ icon = 'hyperstation/icons/mob/char_snouts.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_snouts/scarab // sarcoph @ hyperstation, march 2022
+ name = "Scarab Beetle (Hyper)"
+ icon_state = "scarab"
+ icon = 'hyperstation/icons/mob/char_snouts.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_snouts/smallmandible // sarcoph @ hyperstation, march 2022
+ name = "Small Mandibles (Hyper)"
+ icon_state = "smallmandible"
+ icon = 'hyperstation/icons/mob/char_snouts.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_snouts/spider // sarcoph @ hyperstation, march 2022
+ name = "Spider (Hyper)"
+ icon_state = "spider"
+ icon = 'hyperstation/icons/mob/char_snouts.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_snouts/tarantula // sarcoph @ hyperstation, march 2022
+ name = "Tarantula (Hyper)"
+ icon_state = "tarantula"
+ icon = 'hyperstation/icons/mob/char_snouts.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_snouts/easterndragon
+ name = "Eastern Dragon (Hyper)"
+ icon_state = "easterndw"
+ icon = 'hyperstation/icons/mob/char_snouts.dmi'
+
+/datum/sprite_accessory/mam_snouts/feasterndragon
+ name = "Eastern Dragon (Top) (Hyper)"
+ icon_state = "feasterndw"
+ icon = 'hyperstation/icons/mob/char_snouts.dmi'
+
+/datum/sprite_accessory/mam_snouts/easterndragonnowhiskers
+ name = "Eastern Dragon - No Whiskers (Hyper)"
+ icon_state = "easterndnw"
+ icon = 'hyperstation/icons/mob/char_snouts.dmi'
+
+/datum/sprite_accessory/mam_snouts/feasterndragonnowhiskers
+ name = "Eastern Dragon - No Whiskers (Top) (Hyper)"
+ icon_state = "feasterndnw"
+ icon = 'hyperstation/icons/mob/char_snouts.dmi'
+
+/datum/sprite_accessory/mam_snouts/knshuttle // Dahlular and Arcstaisia @ hyperstation, april 2022. Feel free to remove comment. Just remember this is snowflakey Kinaris stuff.
+ name = "Kinaris - Shuttle (Hyper)"
+ icon_state = "knshuttle"
+ icon = 'hyperstation/icons/mob/char_snouts.dmi'
+
+/datum/sprite_accessory/mam_snouts/fchemlight
+ name = "RadDog (Top) (Hyper)"
+ icon_state = "fchemlight"
+ icon = 'hyperstation/icons/mob/char_snouts.dmi'
+
+/datum/sprite_accessory/mam_snouts/chemlight
+ name = "RadDog (Hyper)"
+ icon_state = "chemlight"
+ icon = 'hyperstation/icons/mob/char_snouts.dmi'
+
+/datum/sprite_accessory/mam_snouts/pinata
+ name = "pinata (Hyper)"
+ icon_state = "pinata"
+ icon = 'hyperstation/icons/mob/char_snouts.dmi'
+
+/datum/sprite_accessory/mam_snouts/pinatasmile
+ name = "pinata Smile (Hyper)"
+ icon_state = "pinatasmile"
+ icon = 'hyperstation/icons/mob/char_snouts.dmi'
+
+/*
+EARS
+==========================================================
+*/
+
+/datum/sprite_accessory/mam_ears/faceant // sarcoph @ hyperstation, march 2022
+ name = "Face Antennae (Hyper)"
+ icon_state = "faceant"
+ icon = 'hyperstation/icons/mob/char_ears.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_ears/faceant2 // sarcoph @ hyperstation, march 2022
+ name = "Face Antennae 2 (Hyper)"
+ icon_state = "faceant2"
+ icon = 'hyperstation/icons/mob/char_ears.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_ears/moth
+ name = "Moth Antennae (Hyper)"
+ icon_state = "moth"
+ icon = 'hyperstation/icons/mob/char_ears.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_ears/plumeant // sarcoph @ hyperstation, march 2022
+ name = "Plume Antennae (Hyper)"
+ icon_state = "plumeant"
+ icon = 'hyperstation/icons/mob/char_ears.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_ears/roundant // sarcoph @ hyperstation, march 2022
+ name = "Round Antennae (Hyper)"
+ icon_state = "roundant"
+ icon = 'hyperstation/icons/mob/char_ears.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_ears/thinant // sarcoph @ hyperstation, march 2022
+ name = "Thin Antennae (Hyper)"
+ icon_state = "thinant"
+ icon = 'hyperstation/icons/mob/char_ears.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_ears/easterndragon
+ name = "Eastern Dragon (Hyper)"
+ icon_state = "easternd"
+ icon = 'hyperstation/icons/mob/char_ears.dmi'
+
+/datum/sprite_accessory/mam_ears/knshuttle // Dahlular and Arcstaisia @ hyperstation, april 2022. Feel free to remove comment. Just remember this is snowflakey Kinaris stuff.
+ name = "Kinaris - Shuttle (Hyper)"
+ icon_state = "knshuttle"
+ icon = 'hyperstation/icons/mob/char_ears.dmi'
+
+/datum/sprite_accessory/mam_ears/chemlight
+ name = "RadDog (Hyper)"
+ icon_state = "chemlight"
+ icon = 'hyperstation/icons/mob/char_ears.dmi'
+
+/*
+WINGS
+==========================================================
+*/
+
+/datum/sprite_accessory/deco_wings/beetle // sarcoph @ hyperstation, march 2022
+ name = "Beetle (Hyper)"
+ icon_state = "beetle"
+ icon = 'hyperstation/icons/mob/char_wings.dmi'
+
+/datum/sprite_accessory/moth_wings/beetle
+ name = "Beetle (Hyper)"
+ icon_state = "beetle"
+ icon = 'hyperstation/icons/mob/char_wings.dmi'
+
+/datum/sprite_accessory/deco_wings/beetle2 // sarcoph @ hyperstation, march 2022
+ name = "Beetle - 2-toned (Hyper)"
+ icon_state = "beetle2"
+ icon = 'hyperstation/icons/mob/char_wings.dmi'
+ color_src = MATRIXED
+
+/datum/sprite_accessory/moth_wings/beetle2
+ name = "Beetle - 2-toned (Hyper)"
+ icon_state = "beetle2"
+ icon = 'hyperstation/icons/mob/char_wings.dmi'
+ color_src = MATRIXED
+
+/datum/sprite_accessory/deco_wings/insect // sarcoph @ hyperstation, march 2022
+ name = "Insect (Hyper)"
+ icon_state = "insect"
+ icon = 'hyperstation/icons/mob/char_wings.dmi'
+
+/datum/sprite_accessory/moth_wings/insect
+ name = "Insect (Hyper)"
+ icon_state = "insect"
+ icon = 'hyperstation/icons/mob/char_wings.dmi'
+
+/datum/sprite_accessory/deco_wings/knshuttle // Dahlular and Arcstaisia @ hyperstation, april 2022. Feel free to remove comment. Just remember this is snowflakey Kinaris stuff.
+ name = "Kinaris - Shuttle (Hyper)"
+ icon_state = "knshuttle"
+ icon = 'hyperstation/icons/mob/char_wings.dmi'
+ //recommended_species = list("synthliz") need to give synths a deco wing slot
+ color_src = MATRIXED
+
+/datum/sprite_accessory/deco_wings/minibat // sarcoph @ hyperstation, march 2022
+ name = "Mini Bat (Hyper)"
+ icon_state = "minibat"
+ icon = 'hyperstation/icons/mob/char_wings.dmi'
+
+/datum/sprite_accessory/deco_wings/minifeather // sarcoph @ hyperstation, march 2022
+ name = "Mini Feather (Hyper)"
+ icon_state = "minifeather"
+ icon = 'hyperstation/icons/mob/char_wings.dmi'
+
+/datum/sprite_accessory/deco_wings/tinybat // sarcoph @ hyperstation, march 2022
+ name = "Tiny Bat (Hyper)"
+ icon_state = "tinybat"
+ icon = 'hyperstation/icons/mob/char_wings.dmi'
+
+/datum/sprite_accessory/deco_wings/tinyfeather // sarcoph @ hyperstation, march 2022
+ name = "Tiny Feather (Hyper)"
+ icon_state = "tinyfeather"
+ icon = 'hyperstation/icons/mob/char_wings.dmi'
+
+/datum/sprite_accessory/deco_wings/sylveon // Aphast @ hyperstation, november 2022
+ name = "Sylveon Ribbons"
+ icon_state = "sylveon_bow"
+ icon = 'hyperstation/icons/mob/char_wings.dmi'
+ color_src = MATRIXED
+
+/datum/sprite_accessory/horns/sylveon // Aphast @ hyperstation, november 2022, best place to put this due to being a pair and no horn slots here
+ name = "Sylveon Bow"
+ icon = 'modular_citadel/icons/mob/mutant_bodyparts.dmi'
+ icon_state = "sylveon_bow"
+ color_src = MATRIXED
+
+/*
+TAILS + ANIMATED TAILS
+==========================================================
+*/
+
+/datum/sprite_accessory/mam_tails/bee // sarcoph @ hyperstation, march 2022
+ name = "Bee (Hyper)"
+ icon_state = "bee"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_tails_animated/bee
+ name = "Bee (Hyper)"
+ icon_state = "bee"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_tails/bee2 // sarcoph @ hyperstation, march 2022
+ name = "Bee w/ Stinger (Hyper)"
+ icon_state = "bee2"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_tails_animated/bee2
+ name = "Bee w/ Stinger (Hyper)"
+ icon_state = "bee2"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+ recommended_species = list("insect")
+
+// "fan" bird tail, short
+/datum/sprite_accessory/mam_tails/shorthawk // sarcoph @ hyperstation, jan 2022
+ name = "Hawk - Short (Hyper)"
+ icon_state = "shorthawk"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+
+/datum/sprite_accessory/mam_tails_animated/shorthawk
+ name = "Hawk - Short (Hyper)"
+ icon_state = "shorthawk"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+
+/datum/sprite_accessory/mam_tails/insect // sarcoph @ hyperstation, march 2022
+ name = "Insect (Hyper)"
+ icon_state = "insect"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_tails_animated/insect
+ name = "Insect (Hyper)"
+ icon_state = "insect"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_tails/bug2tone // sarcoph @ hyperstation, march 2022
+ name = "Insect - 2-tone (Hyper)"
+ icon_state = "bug2tone"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_tails_animated/bug2tone
+ name = "Insect - 2-tone (Hyper)"
+ icon_state = "bug2tone"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+ recommended_species = list("insect")
+
+// "narrow" bird tail, long
+/datum/sprite_accessory/mam_tails/longpigeon // sarcoph @ hyperstation, jan 2022
+ name = "Pigeon - Long (Hyper)"
+ icon_state = "longpigeon"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+
+/datum/sprite_accessory/mam_tails_animated/longpigeon
+ name = "Pigeon - Long (Hyper)"
+ icon_state = "longpigeon"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+
+// "narrow" bird tail, short
+/datum/sprite_accessory/mam_tails/shortpigeon // sarcoph @ hyperstation, jan 2022
+ name = "Pigeon - Short (Hyper)"
+ icon_state = "shortpigeon"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+
+/datum/sprite_accessory/mam_tails_animated/shortpigeon
+ name = "Pigeon - Short (Hyper)"
+ icon_state = "shortpigeon"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+
+// "forked" bird tail, long
+/datum/sprite_accessory/mam_tails/swallow // sarcoph @ hyperstation, jan 2022
+ name = "Swallow (Hyper)"
+ icon_state = "swallow"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+
+/datum/sprite_accessory/mam_tails_animated/swallow
+ name = "Swallow (Hyper)"
+ icon_state = "swallow"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+
+// forked bird tail, long; special stripe markings
+/datum/sprite_accessory/mam_tails/swallowstriped // sarcoph @ hyperstation, jan 2022
+ name = "Swallow - Striped (Hyper)"
+ icon_state = "swallowstriped"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+
+/datum/sprite_accessory/mam_tails_animated/swallowstriped
+ name = "Swallow - Striped (Hyper)"
+ icon_state = "swallowstriped"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+
+/datum/sprite_accessory/mam_tails/easterndragon //Pulled base from Virgo, seriously love the server and love you guys, stay lovely.
+ name = "Eastern Dragon (Hyper)"
+ icon_state = "easternd"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+
+/datum/sprite_accessory/mam_tails_animated/easterndragon
+ name = "Eastern Dragon (Hyper)"
+ icon_state = "easternd"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+
+/datum/sprite_accessory/mam_tails/knshuttle // Dahlular and Arcstaisia @ hyperstation, april 2022. Feel free to remove comment. Just remember this is snowflakey Kinaris stuff.
+ name = "Kinaris - Shuttle (Hyper)"
+ icon_state = "knshuttle"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+
+/datum/sprite_accessory/mam_tails_animated/knshuttle
+ name = "Kinaris - Shuttle (Hyper)"
+ icon_state = "knshuttle"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+
+/datum/sprite_accessory/mam_tails/chemlight
+ name = "RadDog (Hyper)"
+ icon_state = "chemlight"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+
+/datum/sprite_accessory/mam_tails_animated/chemlight
+ name = "RadDog (Hyper)"
+ icon_state = "chemlight"
+ icon = 'hyperstation/icons/mob/char_tails.dmi'
+
+
+/*
+BODY MARKINGS
+==========================================================
+*/
+
+/*
+from modular_citadel/code/modules/mob/dead/new_player/sprite_accessories.dm:
+/datum/sprite_accessory/mam_body_markings
+ extra = FALSE
+ extra2 = FALSE
+ color_src = MATRIXED
+ gender_specific = 0
+ icon = 'modular_citadel/icons/mob/mam_markings.dmi'
+ recommended_species = list("mammal", "xeno", "slimeperson", "podweak", "avian", "aquatic")
+*/
+
+/datum/sprite_accessory/mam_body_markings/bee // sarcoph @ hyperstation, march 2022
+ name = "Bee (Hyper)"
+ icon_state = "bee"
+ icon = 'hyperstation/icons/mob/char_markings.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_body_markings/bee_fluff // sarcoph @ hyperstation, march 2022
+ name = "Bee - Fluffy (Hyper)"
+ icon_state = "bee_fluff"
+ icon = 'hyperstation/icons/mob/char_markings.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_body_markings/bug3tone // sarcoph @ hyperstation, march 2022
+ name = "Beetle - 3-tone (Hyper)"
+ icon_state = "bug3tone"
+ icon = 'hyperstation/icons/mob/char_markings.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_body_markings/moth // sarcoph @ hyperstation, jan 2022
+ name = "Moth (Hyper)"
+ icon_state = "moth"
+ icon = 'hyperstation/icons/mob/char_markings.dmi'
+ recommended_species = list("insect")
+
+/datum/sprite_accessory/mam_body_markings/empty
+ name = "None (Hyper)"
+ icon_state = "empty"
+ recommended_species = list("podweak", /*"mammal",*/ "avian", "aquatic", "insect", "xeno", "synthliz", "slimeperson")
+ // mammals are cursed to no empty markings until they get their heads fixed.
+
+/datum/sprite_accessory/mam_body_markings/pigeon // sarcoph @ hyperstation, jan 2022
+ name = "Pigeon (Hyper)"
+ icon_state = "pigeon"
+ icon = 'hyperstation/icons/mob/char_markings.dmi'
+ recommended_species = list("avian")
+
+/datum/sprite_accessory/mam_body_markings/shrike // sarcoph @ hyperstation, jan 2022
+ name = "Shrike (Hyper)"
+ icon_state = "shrike"
+ icon = 'hyperstation/icons/mob/char_markings.dmi'
+ recommended_species = list("avian")
+
+/datum/sprite_accessory/mam_body_markings/easterndragon
+ name = "Eastern Dragon (Hyper)"
+ icon_state = "easternd"
+ icon = 'hyperstation/icons/mob/char_markings.dmi'
+
+/datum/sprite_accessory/mam_body_markings/knshuttle // Dahlular and Arcstaisia @ hyperstation, april 2022. Feel free to remove comment. Just remember this is snowflakey Kinaris stuff.
+ name = "Kinaris - Shuttle (Hyper)"
+ icon_state = "knshuttle"
+ icon = 'hyperstation/icons/mob/char_markings.dmi'
+
+//doged was here
+/datum/sprite_accessory/mam_body_markings/chemlight
+ name = "RadDog (Hyper)"
+ icon_state = "chemlight"
+ icon = 'hyperstation/icons/mob/char_markings.dmi'
+
+//racc do a code maybe it won't explode
+/datum/sprite_accessory/mam_body_markings/raccalt
+ name = "RaccAlt (Hyper)"
+ icon_state = "raccalt"
+ icon = 'hyperstation/icons/mob/char_markings.dmi'
+
+/datum/sprite_accessory/mam_body_markings/pinata
+ name = "pinata (Hyper)"
+ icon_state = "pinata"
+ icon = 'hyperstation/icons/mob/char_markings.dmi'
+
+/*
+TAUR BODIES
+==========================================================
+*/
+
+/*
+from modular_citadel/code/modules/mob/dead/new_player/sprite_accessories.dm:
+/datum/sprite_accessory/taur
+ icon = 'modular_citadel/icons/mob/mam_taur.dmi'
+ extra_icon = 'modular_citadel/icons/mob/mam_taur.dmi'
+ extra = TRUE
+ extra2_icon = 'modular_citadel/icons/mob/mam_taur.dmi'
+ extra2 = TRUE
+ center = TRUE
+ dimension_x = 64
+ var/taur_mode = NOT_TAURIC
+ color_src = MATRIXED
+ recommended_species = list("human", "lizard", "insect", "mammal", "xeno", "jelly", "slimeperson", "podweak", "avian", "aquatic")
+*/
+/datum/sprite_accessory/taur/chemnaga //Chemlight experimental sprites for future spriting
+ name = "RadDog Naga (Hyper)"
+ icon_state = "chemnaga"
+ taur_mode = SNEK_TAURIC
+ ckeys_allowed = list("chemlight")
+
+/datum/sprite_accessory/taur/chemlight
+ name = "RadDog Taur (Hyper)"
+ icon_state = "chemlight"
+ taur_mode = PAW_TAURIC
+ ckeys_allowed = list("chemlight")
+
+
+/*
+IPCS
+==========================================================
+*/
+
+/*
+from modular_citadel/code/modules/mob/dead/new_player/sprite_accessories.dm:
+
+/datum/sprite_accessory/antenna
+ icon = 'modular_citadel/icons/mob/ipc_antennas.dmi'
+ color_src = MUTCOLORS3
+ recommended_species = list("ipc")
+
+/datum/sprite_accessory/screen
+ icon = 'modular_citadel/icons/mob/ipc_screens.dmi'
+ color_src = null
+*/
+
+/datum/sprite_accessory/antenna/idog_down
+ name = "Dog, down (Hyper)"
+ icon_state = "idog_down"
+ color_src = MATRIXED
+
+/datum/sprite_accessory/antenna/idog_up
+ name = "Dog, up (Hyper)"
+ icon_state = "idog_up"
+ color_src = MATRIXED
+
+/datum/sprite_accessory/antenna/headset
+ name = "Headphones (Hyper)"
+ icon_state = "headset"
+ color_src = MATRIXED
+
+/datum/sprite_accessory/antenna/aquatic
+ name = "Fins (Hyper)"
+ icon_state = "aquatic"
+ color_src = MATRIXED
+
+/datum/sprite_accessory/screen/ghost
+ name = "Ghost (Hyper)"
+ icon_state = "ghost"
+
+/datum/sprite_accessory/screen/heartbeat
+ name = "Heartbeat (Hyper)"
+ icon_state = "heartbeat"
+
+/datum/sprite_accessory/screen/ocean
+ name = "Ocean (Hyper)"
+ icon_state = "ocean"
+
+/datum/sprite_accessory/antenna/catears
+ name = "Pointy Ears (Hyper)"
+ icon_state = "catears"
+ color_src = MATRIXED
+
+/datum/sprite_accessory/screen/valley
+ name = "Valley (Hyper)"
+ icon_state = "valley"
+
+/*
+BEARDS
+==========================================================
+*/
+
+/*
+/datum/sprite_accessory/facial_hair
+ icon = 'icons/mob/human_face.dmi'
+ gender = MALE // barf (unless you're a dorf, dorfs dig chix w/ beards :P)
+*/
+
+/datum/sprite_accessory/facial_hair/mane
+ name = "Neckfluff (Hyper)"
+ icon_state = "facial_neckfluff"
diff --git a/hyperstation/code/modules/mob/living/silicon/robot/robot.dm b/hyperstation/code/modules/mob/living/silicon/robot/robot.dm
new file mode 100644
index 0000000000..4752f0c95a
--- /dev/null
+++ b/hyperstation/code/modules/mob/living/silicon/robot/robot.dm
@@ -0,0 +1,3 @@
+//This file is empty right now, but leaves room for people to put shit here in the future for those who are lazy
+/mob/living/silicon/robot
+ var/datum/action/cyborg_small_sprite/small_sprite_action //This gets replaced every time the cyborg changes modules --Cyanosis
\ No newline at end of file
diff --git a/hyperstation/code/modules/mob/living/silicon/robot/robot_modules.dm b/hyperstation/code/modules/mob/living/silicon/robot/robot_modules.dm
new file mode 100644
index 0000000000..dbef0ad93c
--- /dev/null
+++ b/hyperstation/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -0,0 +1,18 @@
+//This file is empty right now, but leaves room for people to put shit here in the future for those who are lazy
+
+//This proc gets called whenever a new robot module is being applied --Cyanosis
+/obj/item/robot_module/proc/handle_sprite_action(mob/living/silicon/robot/R, is_huge = FALSE)
+ ASSERT(istype(R))
+ if(R.small_sprite_action)
+ QDEL_NULL(R.small_sprite_action)
+ if(!is_huge) //only from the expander upgrade. The borg won't have an action if they don't have the expander upgrade
+ return
+ for(var/P in typesof(/datum/action/cyborg_small_sprite))
+ var/datum/action/cyborg_small_sprite/action = P
+ if(initial(action.designated_module) == name)
+ R.small_sprite_action = new action()
+ R.small_sprite_action.Grant(R)
+ break
+ if(!R.small_sprite_action)
+ R.small_sprite_action = new()
+ R.small_sprite_action.Grant(R)
diff --git a/hyperstation/code/modules/mob/living/status_indicators.dm b/hyperstation/code/modules/mob/living/status_indicators.dm
new file mode 100644
index 0000000000..b22337df49
--- /dev/null
+++ b/hyperstation/code/modules/mob/living/status_indicators.dm
@@ -0,0 +1,51 @@
+//Ported from virgo, with some touch ups to make it work with our code.
+
+/mob/living
+ var/list/status_indicators = null // Will become a list as needed.
+
+/mob/living/proc/add_status_indicator(mutable_appearance/thing)
+ if(get_status_indicator(thing))
+ return
+
+ if(!istype(thing, /mutable_appearance))
+ thing = mutable_appearance('hyperstation/icons/mob/status_indicators.dmi', icon_state = thing)
+
+ LAZYADD(status_indicators, thing)
+ handle_status_indicators()
+
+/mob/living/proc/remove_status_indicator(mutable_appearance/thing)
+ thing = get_status_indicator(thing)
+
+ cut_overlay(thing)
+ LAZYREMOVE(status_indicators, thing)
+ handle_status_indicators()
+
+/mob/living/proc/get_status_indicator(mutable_appearance/thing)
+ if(!istype(thing, /mutable_appearance))
+ for(var/mutable_appearance/I in status_indicators)
+ if(I.icon_state == thing)
+ return I
+ return LAZYACCESS(status_indicators, LAZYFIND(status_indicators, thing))
+
+/mob/living/proc/handle_status_indicators()
+ // First, get rid of all the overlays.
+ for(var/thing in status_indicators)
+ cut_overlay(thing)
+
+ if(!LAZYLEN(status_indicators))
+ return
+
+ if(stat == DEAD)
+ return
+
+ // Now the indicator row can actually be built.
+ for(var/thing in status_indicators)
+ var/mutable_appearance/I = thing
+
+ I.appearance_flags = PIXEL_SCALE|KEEP_APART
+ I.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ I.plane = HUD_PLANE
+ I.layer = ABOVE_FLY_LAYER
+ I.pixel_y = 18
+ I.pixel_x = 18
+ add_overlay(I)
diff --git a/hyperstation/code/modules/mob/mob_helpers.dm b/hyperstation/code/modules/mob/mob_helpers.dm
new file mode 100644
index 0000000000..de5372ffb7
--- /dev/null
+++ b/hyperstation/code/modules/mob/mob_helpers.dm
@@ -0,0 +1,29 @@
+mob/proc/checkloadappearance()
+ var/mob/living/carbon/human/H = src
+ //This will be where the person gets to select their appearance instead of the random character
+ if (world.time <= (H.time_initialized + 900) && H.mirrorcanloadappearance == TRUE)
+ SEND_SOUND(H, 'sound/misc/server-ready.ogg')
+ to_chat(H, "This ghost role allows you to select your loaded character's appearance. Make sure you have your ID in your ID slot, if you have one.")
+ if(alert(H, "Would you like to load your currently loaded character's appearance?", "This can only be done up until 90s after you spawn.", "Yes", "No") == "Yes" && world.time <= (H.time_initialized + 900))
+ if(alert(H, "You should only load a character that has not currently died in the round. Do you accept this?", "Warning", "Yes", "No") == "Yes" && world.time <= (H.time_initialized + 900))
+ H.client?.prefs?.copy_to(H)
+ if (H.custom_body_size) //Do they have a custom size set?
+ H.resize(H.custom_body_size * 0.01)
+ H.real_name = H.client?.prefs?.real_name
+ H.mind.name = H.real_name //Makes sure to change their mind name to their real name.
+ SSquirks.AssignQuirks(H, H.client, TRUE, FALSE, H.job, FALSE)//This Assigns the selected character's quirks
+ H.dna.update_dna_identity() //This makes sure their DNA is updated.
+ var/obj/item/card/id/idCard = H.get_idcard() //Time to change their ID card as well if they have one.
+ if (idCard != null)
+ idCard.update_label(H.real_name, idCard.assignment)
+ idCard.registered_name = H.real_name
+ H.mirrorcanloadappearance = FALSE //Prevents them from using the mirror again.
+ ADD_TRAIT(H,TRAIT_EXEMPT_HEALTH_EVENTS,GHOSTROLE_TRAIT) //Makes sure they are exempt from health events.
+ SEND_SOUND(H, 'sound/magic/charge.ogg') //Fluff
+ to_chat(H, "Your head aches for a second. You feel like this is how things should have been.")
+ log_game("[key_name(H)] has loaded their default appearance for a ghost role.")
+ message_admins("[ADMIN_LOOKUPFLW(H)] has loaded their default appearance for a ghost role.")
+ return
+ else
+ to_chat(H, "You either took too long or chose not to change. Alrighty. Remember, you have 90 seconds from spawn to get to a mirror and still do it if you wish.")
+ return
diff --git a/hyperstation/code/modules/patreon/patreon.dm b/hyperstation/code/modules/patreon/patreon.dm
new file mode 100644
index 0000000000..932ccd737f
--- /dev/null
+++ b/hyperstation/code/modules/patreon/patreon.dm
@@ -0,0 +1,15 @@
+#define PATREONFILE "[global.config.directory]/patreons.txt"
+
+GLOBAL_LIST_EMPTY(patreons)
+
+/proc/load_patreons()
+ GLOB.patreons = list()
+ for(var/line in world.file2list(PATREONFILE))
+ if(!line)
+ continue
+ GLOB.patreons += ckey(line)
+
+/proc/check_patreons(var/ckey)
+ if(!GLOB.patreons)
+ return FALSE
+ . = (ckey in GLOB.patreons)
diff --git a/hyperstation/code/modules/pen.dm b/hyperstation/code/modules/pen.dm
new file mode 100644
index 0000000000..497d39676d
--- /dev/null
+++ b/hyperstation/code/modules/pen.dm
@@ -0,0 +1,45 @@
+/obj/item/pen/attack(mob/living/M, mob/user,stealth)
+ if(!istype(M))
+ return
+
+ if(!force)
+ if(M.can_inject(user, 1))
+ if(user.a_intent == "harm") //old poke requires harm intent.
+ to_chat(user, "You stab [M] with the pen.")
+ if(!stealth)
+ to_chat(M, "You feel a tiny prick!")
+ . = 1
+
+ else //writing time
+ var/mob/living/carbon/human/T = M
+ if(!T) //not human.
+ return
+ if(!T.is_chest_exposed())
+ to_chat(user, "You cannot write on someone with their clothes on.")
+ return
+
+ var/writting = input(user, "Add writing, doesn't replace current text", "Writing on [T]") as text|null
+ if(!writting)
+ return
+
+ var/obj/item/bodypart/BP = T.get_bodypart(user.zone_selected)
+
+ if(!(user==T))
+ src.visible_message("[user] begins to write on [T]'s [BP.name].")
+ else
+ to_chat(user, "You begin to write on your [BP.name].")
+
+ if(do_mob(user, T, 4 SECONDS))
+ if((length(BP.writtentext))+(length(writting)) < 100) //100 character limmit to stop spamming.
+ BP.writtentext += html_encode(writting) //you can add to text, not remove it.
+ else
+ to_chat(user, "There isnt enough space to write that on [T]'s [BP.name].")
+ return
+
+ if(!(user==T))
+ to_chat(user, "You write on [T]'s [BP.name].")
+ else
+ to_chat(user, "You write on your [BP.name].")
+ else
+ . = ..()
+
diff --git a/hyperstation/code/modules/power/reactor/fuel_rods.dm b/hyperstation/code/modules/power/reactor/fuel_rods.dm
new file mode 100644
index 0000000000..f5b9b2c3bf
--- /dev/null
+++ b/hyperstation/code/modules/power/reactor/fuel_rods.dm
@@ -0,0 +1,229 @@
+/obj/item/twohanded/required/fuel_rod
+ var/rad_strength = 500
+ var/half_life = 2000 // how many depletion ticks are needed to half the fuel_power (1 tick = 1 second)
+ var/time_created = 0
+ var/og_fuel_power = 0.20 //the original fuel power value
+ var/process = FALSE
+ // The depletion where depletion_final() will be called (and does something)
+ var/depletion_threshold = 100
+ // How fast this rod will deplete
+ var/depletion_speed_modifier = 1
+ var/depleted_final = FALSE // depletion_final should run only once
+ var/depletion_conversion_type = "plutonium"
+
+/obj/item/twohanded/required/fuel_rod/Initialize(mapload)
+ . = ..()
+ time_created = world.time
+ AddComponent(/datum/component/radioactive, rad_strength, src) // This should be temporary for it won't make rads go lower than 350
+ if(process)
+ START_PROCESSING(SSobj, src)
+
+/obj/item/twohanded/required/fuel_rod/Destroy()
+ if(process)
+ STOP_PROCESSING(SSobj, src)
+ var/obj/machinery/atmospherics/components/trinary/nuclear_reactor/N = loc
+ if(istype(N))
+ N.fuel_rods -= src
+ . = ..()
+
+// This proc will try to convert your fuel rod if you don't override this proc
+// So, ideally, you should write an override of this for every fuel rod you want to create
+/obj/item/twohanded/required/fuel_rod/proc/depletion_final(result_rod = "")
+ if(result_rod)
+ var/obj/machinery/atmospherics/components/trinary/nuclear_reactor/N = loc
+ // Rod conversion is moot when you can't find the reactor
+ if(istype(N))
+ var/obj/item/twohanded/required/fuel_rod/R
+
+ // You can add your own depletion scheme and not override this proc if you are going to convert a fuel rod into another type
+ switch(result_rod)
+ if("plutonium")
+ R = new /obj/item/twohanded/required/fuel_rod/plutonium(loc)
+ R.depletion = depletion
+ if("depleted")
+ if(fuel_power < 10)
+ fuel_power = 0
+ playsound(loc, 'sound/effects/supermatter.ogg', 100, TRUE)
+ R = new /obj/item/twohanded/required/fuel_rod/depleted(loc)
+ R.depletion = depletion
+
+ // Finalization of conversion
+ if(istype(R))
+ N.fuel_rods += R
+ qdel(src)
+ else
+ depleted_final = FALSE // Maybe try again later?
+
+/obj/item/twohanded/required/fuel_rod/deplete(amount=0.035) // override for the one in rmbk.dm
+ depletion += amount * depletion_speed_modifier
+ if(depletion >= depletion_threshold && !depleted_final)
+ depleted_final = TRUE
+ depletion_final(depletion_conversion_type)
+
+/obj/item/twohanded/required/fuel_rod/plutonium
+ fuel_power = 0.20
+ name = "Plutonium-239 Fuel Rod"
+ desc = "A highly energetic titanium sheathed rod containing a sizeable measure of weapons grade plutonium, it's highly efficient as nuclear fuel, but will cause the reaction to get out of control if not properly utilised."
+ icon_state = "inferior"
+ rad_strength = 1500
+ process = TRUE // for half life code
+ depletion_threshold = 300
+ depletion_conversion_type = "depleted"
+
+/obj/item/twohanded/required/fuel_rod/plutonium/process()
+ fuel_power = og_fuel_power * 0.5**((world.time - time_created) / half_life SECONDS) // halves the fuel power every half life (33 minutes)
+
+/obj/item/twohanded/required/fuel_rod/depleted
+ name = "Depleted Fuel Rod"
+ desc = "A highly radioactive fuel rod which has expended most of it's useful energy."
+ icon_state = "normal"
+ rad_strength = 6000 // smelly
+ depletion_conversion_type = "" // It means that it won't turn into anything
+
+// Master type for material optional (or requiring, wyci) and/or producing rods
+/obj/item/twohanded/required/fuel_rod/material
+ // Whether the rod has been harvested. Should be set in expend().
+ var/expended = FALSE
+ // The material that will be inserted and then multiplied (or not). Should be some sort of /obj/item/stack
+ var/material_type
+ // The name of material that'll be used for texts
+ var/material_name
+ var/material_name_singular
+ var/initial_amount = 0
+ // The maximum amount of material the rod can hold
+ var/max_initial_amount = 10
+ var/grown_amount = 0
+ // The multiplier for growth. 1 for the same 2 for double etc etc
+ var/multiplier = 2
+ // After this depletion, you won't be able to add new materials
+ var/material_input_deadline = 25
+ // Material fuel rods generally don't get converted into another fuel object
+ depletion_conversion_type = ""
+
+// Called when the rod is fully harvested
+/obj/item/twohanded/required/fuel_rod/material/proc/expend()
+ expended = TRUE
+
+// Basic checks for material rods
+/obj/item/twohanded/required/fuel_rod/material/proc/check_material_input(mob/user)
+ if(depletion >= material_input_deadline)
+ to_chat(user, "The sample slots have sealed themselves shut, it's too late to add [material_name] now!") // no cheesing in crystals at 100%
+ return FALSE
+ if(expended)
+ to_chat(user, "\The [src]'s material slots have already been used.")
+ return FALSE
+ return TRUE
+
+// The actual growth
+/obj/item/twohanded/required/fuel_rod/material/depletion_final(result_rod)
+ if(result_rod)
+ ..() // So if you put anything into depletion_conversion_type then your fuel rod will be converted (or not) and *won't grow*
+ else
+ grown_amount = initial_amount * multiplier
+
+/obj/item/twohanded/required/fuel_rod/material/attackby(obj/item/W, mob/user, params)
+ var/obj/item/stack/M = W
+ if(istype(M, material_type))
+ if(!check_material_input(user))
+ return
+ if(initial_amount < max_initial_amount)
+ var/adding = min((max_initial_amount - initial_amount), M.amount)
+ M.amount -= adding
+ initial_amount += adding
+ if (adding == 1)
+ to_chat(user, "You insert [adding] [material_name_singular] into \the [src].")
+ else
+ to_chat(user, "You insert [adding] [material_name] into \the [src].")
+ M.zero_amount()
+ else
+ to_chat(user, "\The [src]'s material slots are full!")
+ return
+ else
+ return ..()
+
+/obj/item/twohanded/required/fuel_rod/material/attack_self(mob/user)
+ if(expended)
+ to_chat(user, "You have already removed [material_name] from \the [src].")
+ return
+
+ if(depleted_final)
+ new material_type(user.loc, grown_amount)
+ if (grown_amount == 1)
+ to_chat(user, "You harvest [grown_amount] [material_name_singular] from \the [src].") // Unlikely
+ else
+ to_chat(user, "You harvest [grown_amount] [material_name] from \the [src].")
+ grown_amount = 0
+ expend()
+ else if(depletion)
+ to_chat(user, "\The [src] has not fissiled enough to fully grow the sample. The progress bar shows it is [min(depletion/depletion_threshold*100,100)]% complete.")
+ else if(initial_amount)
+ new material_type(user.loc, initial_amount)
+ if (initial_amount == 1)
+ to_chat(user, "You remove [initial_amount] [material_name_singular] from \the [src].")
+ else
+ to_chat(user, "You remove [initial_amount] [material_name] from \the [src].")
+ initial_amount = 0
+
+/obj/item/twohanded/required/fuel_rod/material/examine(mob/user)
+ . = ..()
+ if(expended)
+ . += "The material slots have been slagged by the extreme heat, you can't grow [material_name] in this rod again..."
+ return
+ else if(depleted_final)
+ if(grown_amount == 1)
+ . += "This fuel rod's [material_name] are now fully grown, and it currently bears [grown_amount] harvestable [material_name_singular]."
+ else
+ . += "This fuel rod's [material_name] are now fully grown, and it currently bears [grown_amount] harvestable [material_name]."
+ return
+ if(depletion)
+ . += "The sample is [min(depletion/depletion_threshold*100,100)]% fissiled."
+ . += "[initial_amount]/[max_initial_amount] of the slots for [material_name] are full."
+
+/obj/item/twohanded/required/fuel_rod/material/telecrystal
+ name = "Telecrystal Fuel Rod"
+ desc = "A disguised titanium sheathed rod containing several small slots infused with uranium dioxide. Permits the insertion of telecrystals to grow more. Fissiles much faster than its standard counterpart"
+ icon_state = "telecrystal"
+ fuel_power = 0.30 // twice as powerful as a normal rod, you're going to need some engineering autism if you plan to mass produce TC
+ depletion_speed_modifier = 3 // headstart, otherwise it takes two hours
+ rad_strength = 1500
+ max_initial_amount = 8
+ multiplier = 3
+ material_type = /obj/item/stack/telecrystal
+ material_name = "telecrystals"
+ material_name_singular = "telecrystal"
+
+/obj/item/twohanded/required/fuel_rod/material/telecrystal/depletion_final(result_rod)
+ ..()
+ if(!result_rod)
+ fuel_power = 0.60 // thrice as powerful as plutonium, you'll want to get this one out quick!
+ name = "Exhausted Telecrystal Fuel Rod"
+ desc = "A highly energetic, disguised titanium sheathed rod containing a number of slots filled with greatly expanded telecrystals which can be removed by hand. It's extremely efficient as nuclear fuel, but will cause the reaction to get out of control if not properly utilised."
+ icon_state = "telecrystal_used"
+ AddComponent(/datum/component/radioactive, 3000, src)
+
+/obj/item/twohanded/required/fuel_rod/material/bananium
+ name = "Bananium Fuel Rod"
+ desc = "A hilarious heavy-duty fuel rod which fissiles a bit slower than its cowardly counterparts. However, its cutting-edge cosmic clown technology allows rooms for extraordinarily exhilarating extraterrestrial element called bananium to menacingly multiply."
+ icon_state = "bananium"
+ fuel_power = 0.15
+ depletion_speed_modifier = 3
+ rad_strength = 350
+ max_initial_amount = 10
+ multiplier = 3
+ material_type = /obj/item/stack/sheet/mineral/bananium
+ material_name = "sheets of bananium"
+ material_name_singular = "sheet of bananium"
+
+/obj/item/twohanded/required/fuel_rod/material/bananium/deplete(amount=0.035)
+ ..()
+ if(initial_amount == max_initial_amount && prob(10))
+ playsound(src, pick('sound/items/bikehorn.ogg', 'yogstation/sound/misc/bikehorn_creepy.ogg'), 50) // HONK
+
+/obj/item/twohanded/required/fuel_rod/material/bananium/depletion_final(result_rod)
+ ..()
+ if(!result_rod)
+ fuel_power = 0.3 // Be warned
+ name = "Fully Grown Bananium Fuel Rod"
+ desc = "A hilarious heavy-duty fuel rod which fissiles a bit slower than it cowardly counterparts. Its greatly grimacing grwoth stage is now over, and bananium outgrowth hums as if it's blatantly honking bike horns."
+ icon_state = "bananium_used"
+ AddComponent(/datum/component/radioactive, 1250, src)
diff --git a/hyperstation/code/modules/power/reactor/rbmk.dm b/hyperstation/code/modules/power/reactor/rbmk.dm
new file mode 100644
index 0000000000..f2735b7e94
--- /dev/null
+++ b/hyperstation/code/modules/power/reactor/rbmk.dm
@@ -0,0 +1,1039 @@
+//For my sanity :))
+
+#define COOLANT_INPUT_GATE airs[1]
+#define MODERATOR_INPUT_GATE airs[2]
+#define COOLANT_OUTPUT_GATE airs[3]
+
+#define RBMK_TEMPERATURE_OPERATING 640 //Celsius
+#define RBMK_TEMPERATURE_CRITICAL 810 //At this point the entire ship/station is alerted to a meltdown. This may need altering
+#define RBMK_TEMPERATURE_MELTDOWN 900
+
+#define RBMK_NO_COOLANT_TOLERANCE 5 //How many process()ing ticks the reactor can sustain without coolant before slowly taking damage
+
+#define RBMK_PRESSURE_OPERATING 1000 //PSI
+#define RBMK_PRESSURE_CRITICAL 1469.59 //PSI
+
+#define RBMK_MAX_CRITICALITY 3 //No more criticality than N for now.
+
+//#define RBMK_POWER_FLAVOURISER 1000 //To turn those KWs into something usable
+#define RBMK_POWER_FLAVOURISER 8000 //To turn those KWs into something usable
+
+//Math. Lame.
+#define KPA_TO_PSI(A) (A/6.895)
+#define PSI_TO_KPA(A) (A*6.895)
+#define KELVIN_TO_CELSIUS(A) (A-273.15)
+#define MEGAWATTS /1e+6
+#define CELSIUS_TO_KELVIN(T_K) ((T_K) + T0C)
+
+//Reference: Heaters go up to 500K.
+//Hot plasmaburn: 14164.95 C.
+
+/**
+
+What is this?
+
+Moderators list (Not gonna keep this accurate forever):
+Fuel Type:
+Oxygen: Power production multiplier. Allows you to run a low plasma, high oxy mix, and still get a lot of power.
+Plasma: Power production gas. More plasma -> more power, but it enriches your fuel and makes the reactor much, much harder to control.
+Tritium: Extremely efficient power production gas. Will cause chernobyl if used improperly.
+
+Moderation Type:
+N2: Helps you regain control of the reaction by increasing control rod effectiveness, will massively boost the rad production of the reactor.
+CO2: Super effective shutdown gas for runaway reactions. MASSIVE RADIATION PENALTY!
+Pluoxium: Same as N2, but no cancer-rads!
+
+Permeability Type:
+BZ: Increases your reactor's ability to transfer its heat to the coolant, thus letting you cool it down faster (but your output will get hotter)
+Water Vapour: More efficient permeability modifier
+Hyper Noblium: Extremely efficient permeability increase. (10x as efficient as bz)
+
+Depletion type:
+Nitryl: When you need weapons grade plutonium yesterday. Causes your fuel to deplete much, much faster. Not a huge amount of use outside of sabotage.
+
+Sabotage:
+
+Meltdown:
+Flood reactor moderator with plasma, they won't be able to mitigate the reaction with control rods.
+Shut off coolant entirely. Raise control rods.
+Swap all fuel out with spent fuel, as it's way stronger.
+
+Blowout:
+Shut off exit valve for quick overpressure.
+Cause a pipefire in the coolant line (LETHAL).
+Tack heater onto coolant line (can also cause straight meltdown)
+
+Tips:
+Be careful to not exhaust your plasma supply. I recommend you DON'T max out the moderator input when youre running plasma + o2, or you're at a tangible risk of running out of those gasses from atmos.
+The reactor CHEWS through moderator. It does not do this slowly. Be very careful with that!
+
+*/
+
+//Remember kids. If the reactor itself is not physically powered by an APC, it cannot shove coolant in!
+/var/datum/looping_sound/rbmk_reactor/soundloop
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor
+ name = "Advanced Gas-Cooled Nuclear Reactor"
+ desc = "A tried and tested design which can output stable power at an acceptably low risk. The moderator can be changed to provide different effects."
+ icon = 'hyperstation/icons/obj/machinery/rbmk.dmi'
+ icon_state = "reactor_map"
+ pixel_x = -32
+ pixel_y = -32
+ density = FALSE //It burns you if you're stupid enough to walk over it.
+ anchored = TRUE
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF
+ light_color = LIGHT_COLOR_CYAN
+ dir = 8 //Less headache inducing :))
+ var/id = null //Change me mappers
+ //Variables essential to operation
+ var/temperature = 0 //Lose control of this -> Meltdown
+ var/vessel_integrity = 400 //How long can the reactor withstand overpressure / meltdown? This gives you a fair chance to react to even a massive pipe fire
+ var/pressure = 0 //Lose control of this -> Blowout
+ var/K = 0 //Rate of reaction.
+ var/desired_k = 0
+ var/control_rod_effectiveness = 0.65 //Starts off with a lot of control over K. If you flood this thing with plasma, you lose your ability to control K as easily.
+ var/power = 0 //0-100%. A function of the maximum heat you can achieve within operating temperature
+ var/power_modifier = 1 //Upgrade me with parts, science! Flat out increase to physical power output when loaded with plasma.
+ var/list/fuel_rods = list()
+ //Secondary variables.
+ var/next_slowprocess = 0
+ var/gas_absorption_effectiveness = 0.5
+ var/gas_absorption_constant = 0.5 //We refer to this one as it's set on init, randomized.
+ var/minimum_coolant_level = 2
+ var/warning = FALSE //Have we begun warning the crew of their impending death?
+ var/next_warning = 0 //To avoid spam.
+ var/last_power_produced = 0 //For logging purposes
+ var/next_flicker = 0 //Light flicker timer
+ var/base_power_modifier = RBMK_POWER_FLAVOURISER
+ var/slagged = FALSE //Is this reactor even usable any more?
+ //Console statistics.
+ var/last_coolant_temperature = 0
+ var/last_output_temperature = 0
+ var/last_heat_delta = 0 //For administrative cheating only. Knowing the delta lets you know EXACTLY what to set K at.
+ var/no_coolant_ticks = 0 //How many times in succession did we not have enough coolant? Decays twice as fast as it accumulates.
+ var/last_user = null
+ var/current_desired_k = null
+ var/datum/powernet/powernet = null
+
+//Use this in your maps if you want everything to be preset.
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/preset
+ id = "default_reactor_for_lazy_mappers"
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/examine(mob/user)
+ . = ..()
+ if(Adjacent(src, user))
+ if(do_after(user, 1 SECONDS, target=src))
+ var/percent = vessel_integrity / initial(vessel_integrity) * 100
+ var/msg = "The reactor looks operational."
+ switch(percent)
+ if(0 to 10)
+ msg = "[src]'s seals are dangerously warped and you can see cracks all over the reactor vessel! "
+ if(10 to 40)
+ msg = "[src]'s seals are heavily warped and cracked! "
+ if(40 to 60)
+ msg = "[src]'s seals are holding, but barely. You can see some micro-fractures forming in the reactor vessel."
+ if(60 to 80)
+ msg = "[src]'s seals are in-tact, but slightly worn. There are no visible cracks in the reactor vessel."
+ if(80 to 90)
+ msg = "[src]'s seals are in good shape, and there are no visible cracks in the reactor vessel."
+ if(95 to 100)
+ msg = "[src]'s seals look factory new, and the reactor's in excellent shape."
+ . += msg
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/attackby(obj/item/W, mob/user, params)
+ if(istype(W, /obj/item/twohanded/required/fuel_rod))
+ if(power >= 20)
+ to_chat(user, "You cannot insert fuel into [src] when it has been raised above 20% power.")
+ return FALSE
+ if(fuel_rods.len >= 5)
+ to_chat(user, "[src] is already at maximum fuel load.")
+ return FALSE
+ to_chat(user, "You start to insert [W] into [src]...")
+ radiation_pulse(src, temperature)
+ if(do_after(user, 5 SECONDS, target=src))
+ if(!fuel_rods.len)
+ start_up() //That was the first fuel rod. Let's heat it up.
+ message_admins("Reactor first started up by [ADMIN_LOOKUPFLW(user)] in [ADMIN_VERBOSEJMP(src)]")
+ investigate_log("Reactor first started by [key_name(user)] at [AREACOORD(src)]", INVESTIGATE_SINGULO)
+ fuel_rods += W
+ W.forceMove(src)
+ radiation_pulse(src, temperature) //Wear protective equipment when even breathing near a reactor!
+ investigate_log("Rod added to reactor by [key_name(user)] at [AREACOORD(src)]", INVESTIGATE_SINGULO)
+ return TRUE
+ if(istype(W, /obj/item/sealant))
+ if(power >= 20)
+ to_chat(user, "You cannot repair [src] while it is running at above 20% power.")
+ return FALSE
+ if(vessel_integrity >= 350)
+ to_chat(user, "[src]'s seals are already in-tact, repairing them further would require a new set of seals.")
+ return FALSE
+ if(vessel_integrity <= 0.5 * initial(vessel_integrity)) //Heavily damaged.
+ to_chat(user, "[src]'s reactor vessel is cracked and worn, you need to repair the cracks with a welder before you can repair the seals.")
+ return FALSE
+ if(do_after(user, 5 SECONDS, target=src))
+ if(vessel_integrity >= 350) //They might've stacked doafters
+ to_chat(user, "[src]'s seals are already in-tact, repairing them further would require a new set of seals.")
+ return FALSE
+ playsound(src, 'sound/effects/spray2.ogg', 50, 1, -6)
+ user.visible_message("[user] applies sealant to some of [src]'s worn out seals.", "You apply sealant to some of [src]'s worn out seals.")
+ vessel_integrity += 10
+ vessel_integrity = CLAMP(vessel_integrity, 0, initial(vessel_integrity))
+ return TRUE
+ return ..()
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/welder_act(mob/living/user, obj/item/I)
+ if(power >= 20)
+ to_chat(user, "You can't repair [src] while it is running at above 20% power.")
+ return FALSE
+ if(vessel_integrity > 0.5 * initial(vessel_integrity))
+ to_chat(user, "[src] is free from cracks. Further repairs must be carried out with flexi-seal sealant.")
+ return FALSE
+ if(I.use_tool(src, user, 0, volume=40))
+ if(vessel_integrity > 0.5 * initial(vessel_integrity))
+ to_chat(user, "[src] is free from cracks. Further repairs must be carried out with flexi-seal sealant.")
+ return FALSE
+ vessel_integrity += 20
+ to_chat(user, "You weld together some of [src]'s cracks. This'll do for now.")
+ return TRUE
+
+//Admin procs to mess with the reaction environment.
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/lazy_startup()
+ for(var/I=0;I<5;I++)
+ fuel_rods += new /obj/item/twohanded/required/fuel_rod(src)
+ message_admins("Reactor started up by admins in [ADMIN_VERBOSEJMP(src)]")
+ start_up()
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/deplete()
+ for(var/obj/item/twohanded/required/fuel_rod/FR in fuel_rods)
+ FR.depletion = 100
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/Initialize(mapload)
+ . = ..()
+ connect_to_network()
+ icon_state = "reactor_off"
+ gas_absorption_effectiveness = rand(5, 6)/10 //All reactors are slightly different. This will result in you having to figure out what the balance is for K.
+ gas_absorption_constant = gas_absorption_effectiveness //And set this up for the rest of the round.
+ STOP_PROCESSING(SSmachines, src) //We'll handle this one ourselves.
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/Crossed(atom/movable/AM, oldloc)
+ . = ..()
+ if(isliving(AM) && temperature > 0)
+ var/mob/living/L = AM
+ L.adjust_bodytemperature(CLAMP(temperature, BODYTEMP_COOLING_MAX, BODYTEMP_HEATING_MAX)) //If you're on fire, you heat up!
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/process()
+ update_parents() //Update the pipenet to register new gas mixes
+ if(next_slowprocess < world.time)
+ slowprocess()
+ next_slowprocess = world.time + 1 SECONDS //Set to wait for another second before processing again, we don't need to process more than once a second
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/has_fuel()
+ return fuel_rods?.len
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/slowprocess()
+ //Let's get our gasses sorted out.
+ var/datum/gas_mixture/coolant_input = COOLANT_INPUT_GATE
+ var/datum/gas_mixture/moderator_input = MODERATOR_INPUT_GATE
+ var/datum/gas_mixture/coolant_output = COOLANT_OUTPUT_GATE
+
+ //Firstly, heat up the reactor based off of K.
+ var/input_moles = coolant_input.total_moles() //Firstly. Do we have enough moles of coolant?
+ if(input_moles >= minimum_coolant_level)
+ last_coolant_temperature = KELVIN_TO_CELSIUS(coolant_input.return_temperature())
+ //Important thing to remember, once you slot in the fuel rods, this thing will not stop making heat, at least, not unless you can live to be thousands of years old which is when the spent fuel finally depletes fully.
+ var/heat_delta = (KELVIN_TO_CELSIUS(coolant_input.return_temperature()) / 100) * gas_absorption_effectiveness //Take in the gas as a cooled input, cool the reactor a bit. The optimum, 100% balanced reaction sits at K=1, coolant input temp of 200K / -73 celsius.
+ last_heat_delta = heat_delta
+ temperature += heat_delta
+ coolant_output.merge(coolant_input) //And now, shove the input into the output.
+ coolant_input.gases = list() //Clear out anything left in the input gate.
+ color = null
+ no_coolant_ticks = max(0, no_coolant_ticks-2) //Needs half as much time to recover the ticks than to acquire them
+ else
+ if(has_fuel())
+ no_coolant_ticks++
+ if(no_coolant_ticks > RBMK_NO_COOLANT_TOLERANCE)
+ temperature += temperature / 500 //This isn't really harmful early game, but when your reactor is up to full power, this can get out of hand quite quickly.
+ vessel_integrity -= temperature / 200 //Think fast chucklenuts!
+ take_damage(10) //Just for the sound effect, to let you know you've fucked up.
+ color = "[COLOR_RED]"
+ investigate_log("Reactor taking damage from the lack of coolant", INVESTIGATE_SINGULO)
+ //Now, heat up the output and set our pressure.
+ coolant_output.temperature = CELSIUS_TO_KELVIN(temperature) //Heat the coolant output gas that we just had pass through us.
+ last_output_temperature = KELVIN_TO_CELSIUS(coolant_output.return_temperature())
+ pressure = KPA_TO_PSI(coolant_output.return_pressure())
+ power = (temperature / RBMK_TEMPERATURE_CRITICAL) * 100
+ var/radioactivity_spice_multiplier = 1 //Some gasses make the reactor a bit spicy.
+ var/depletion_modifier = 0.035 //How rapidly do your rods decay
+ gas_absorption_effectiveness = gas_absorption_constant
+ //Next up, handle moderators!
+ if(moderator_input.total_moles() >= minimum_coolant_level)
+ var/total_fuel_moles = moderator_input.gases[/datum/gas/plasma] + (moderator_input.gases[/datum/gas/tritium]*10) //Constricted plasma is 50% more efficient as fuel than plasma, but is harder to produce
+ var/power_modifier = max((moderator_input.gases[/datum/gas/oxygen] / moderator_input.total_moles() * 10), 1) //You can never have negative IPM. For now.
+ if(total_fuel_moles >= minimum_coolant_level) //You at least need SOME fuel.
+ var/power_produced = max((total_fuel_moles / moderator_input.total_moles() * 10), 1)
+ last_power_produced = max(0,((power_produced*power_modifier)*moderator_input.total_moles()))
+ last_power_produced *= (power/100) //Aaaand here comes the cap. Hotter reactor => more power.
+ last_power_produced *= base_power_modifier //Finally, we turn it into actual usable numbers.
+ radioactivity_spice_multiplier += moderator_input.gases[/datum/gas/tritium] / 5 //Chernobyl 2.
+ var/turf/T = get_turf(src)
+ if(power >= 20)
+ coolant_output.gases[/datum/gas/nitryl] += total_fuel_moles/50 //Shove out nitryl into the air when it's fuelled. You need to filter this off, or you're gonna have a bad time.
+ var/obj/structure/cable/C = T.get_cable_node()
+ if(!C?.powernet)
+ //message_admins("No cable or cable has no powernet!")
+ return
+ else
+ //C.powernet.newavail += last_power_produced //hacky wtf
+ //message_admins("Adding [last_power_produced] to powernet.")
+ add_avail(last_power_produced)
+ var/total_control_moles = moderator_input.gases[/datum/gas/nitrogen] + (moderator_input.gases[/datum/gas/carbon_dioxide]*2) + (moderator_input.gases[/datum/gas/pluoxium]*3) //N2 helps you control the reaction at the cost of making it absolutely blast you with rads. Pluoxium has the same effect but without the rads!
+ if(total_control_moles >= minimum_coolant_level)
+ var/control_bonus = total_control_moles / 250 //1 mol of n2 -> 0.002 bonus control rod effectiveness, if you want a super controlled reaction, you'll have to sacrifice some power.
+ control_rod_effectiveness = initial(control_rod_effectiveness) + control_bonus
+ radioactivity_spice_multiplier += moderator_input.gases[/datum/gas/nitrogen] / 25 //An example setup of 50 moles of n2 (for dealing with spent fuel) leaves us with a radioactivity spice multiplier of 3.
+ radioactivity_spice_multiplier += moderator_input.gases[/datum/gas/carbon_dioxide] / 12.5
+ var/total_permeability_moles = moderator_input.gases[/datum/gas/bz] + (moderator_input.gases[/datum/gas/water_vapor]*2) + (moderator_input.gases[/datum/gas/hypernoblium]*10)
+ if(total_permeability_moles >= minimum_coolant_level)
+ var/permeability_bonus = total_permeability_moles / 500
+ gas_absorption_effectiveness = gas_absorption_constant + permeability_bonus
+ var/total_degradation_moles = moderator_input.gases[/datum/gas/nitryl] //Because it's quite hard to get.
+ if(total_degradation_moles >= minimum_coolant_level*0.5) //I'll be nice.
+ depletion_modifier += total_degradation_moles / 15 //Oops! All depletion. This causes your fuel rods to get SPICY.
+ playsound(src, pick('hyperstation/sound/machines/sm/accent/normal/1.ogg','hyperstation/sound/machines/sm/accent/normal/2.ogg','hyperstation/sound/machines/sm/accent/normal/3.ogg','hyperstation/sound/machines/sm/accent/normal/4.ogg','hyperstation/sound/machines/sm/accent/normal/5.ogg'), 100, TRUE)
+ //From this point onwards, we clear out the remaining gasses.
+ moderator_input.gases = list() //Woosh. And the soul is gone.
+ K += total_fuel_moles / 1000
+ var/fuel_power = 0 //So that you can't magically generate K with your control rods.
+ if(!has_fuel()) //Reactor must be fuelled and ready to go before we can heat it up boys.
+ K = 0
+ else
+ for(var/obj/item/twohanded/required/fuel_rod/FR in fuel_rods)
+ K += FR.fuel_power
+ fuel_power += FR.fuel_power
+ FR.deplete(depletion_modifier)
+ //Firstly, find the difference between the two numbers.
+ var/difference = abs(K - desired_k)
+ //Then, hit as much of that goal with our cooling per tick as we possibly can.
+ difference = CLAMP(difference, 0, control_rod_effectiveness) //And we can't instantly zap the K to what we want, so let's zap as much of it as we can manage....
+ if(difference > fuel_power && desired_k > K)
+ message_admins("Not enough fuel to get [difference]. We have fuel [fuel_power]")
+ investigate_log("Reactor has not enough fuel to get [difference]. We have fuel [fuel_power]", INVESTIGATE_SINGULO)
+ difference = fuel_power //Again, to stop you being able to run off of 1 fuel rod.
+ if(K != desired_k)
+ if(desired_k > K)
+ K += difference
+ else if(desired_k < K)
+ K -= difference
+ if(K == desired_k && last_user && current_desired_k != desired_k)
+ current_desired_k = desired_k
+ message_admins("Reactor desired criticality set to [desired_k] by [ADMIN_LOOKUPFLW(last_user)] in [ADMIN_VERBOSEJMP(src)]")
+ investigate_log("reactor desired criticality set to [desired_k] by [key_name(last_user)] at [AREACOORD(src)]", INVESTIGATE_SINGULO)
+
+ K = CLAMP(K, 0, RBMK_MAX_CRITICALITY)
+ if(has_fuel())
+ temperature += K
+ else
+ temperature -= 10 //Nothing to heat us up, so.
+ handle_alerts() //Let's check if they're about to die, and let them know.
+ update_icon()
+ radiation_pulse(src, temperature*radioactivity_spice_multiplier)
+ if(power >= 93 && world.time >= next_flicker) //You're overloading the reactor. Give a more subtle warning that power is getting out of control.
+ next_flicker = world.time + 2 MINUTES
+ for(var/obj/machinery/light/L in GLOB.machines)
+ if(prob(25) && L.z == z) //If youre running the reactor cold though, no need to flicker the lights.
+ L.flicker()
+ investigate_log("Reactor overloading at [power]% power", INVESTIGATE_SINGULO)
+ for(var/atom/movable/I in get_turf(src))
+ if(isliving(I))
+ var/mob/living/L = I
+ if(temperature > 0)
+ L.adjust_bodytemperature(CLAMP(temperature, BODYTEMP_COOLING_MAX, BODYTEMP_HEATING_MAX)) //If you're on fire, you heat up!
+ if(istype(I, /obj/item/reagent_containers/food) && !istype(I, /obj/item/reagent_containers/food/drinks))
+ playsound(src, pick('sound/machines/fryer/deep_fryer_1.ogg', 'sound/machines/fryer/deep_fryer_2.ogg'), 100, TRUE)
+ var/obj/item/reagent_containers/food/grilled_item = I
+ if(prob(80))
+ return //To give the illusion that it's actually cooking omegalul.
+ switch(power)
+ if(20 to 39)
+ grilled_item.name = "grilled [initial(grilled_item.name)]"
+ grilled_item.desc = "[initial(I.desc)] It's been grilled over a nuclear reactor."
+ if(!grilled_item.foodtype & FRIED)
+ grilled_item.foodtype |= FRIED
+ if(40 to 70)
+ grilled_item.name = "heavily grilled [initial(grilled_item.name)]"
+ grilled_item.desc = "[initial(I.desc)] It's been heavily grilled through the magic of nuclear fission."
+ if(!grilled_item.foodtype & FRIED)
+ grilled_item.foodtype |= FRIED
+ if(70 to 95)
+ grilled_item.name = "Three-Mile Nuclear-Grilled [initial(grilled_item.name)]"
+ grilled_item.desc = "A [initial(grilled_item.name)]. It's been put on top of a nuclear reactor running at extreme power by some badass engineer."
+ if(!grilled_item.foodtype & FRIED)
+ grilled_item.foodtype |= FRIED
+ if(95 to INFINITY)
+ grilled_item.name = "Ultimate Meltdown Grilled [initial(grilled_item.name)]"
+ grilled_item.desc = "A [initial(grilled_item.name)]. A grill this perfect is a rare technique only known by a few engineers who know how to perform a 'controlled' meltdown whilst also having the time to throw food on a reactor. I'll bet it tastes amazing."
+ if(!grilled_item.foodtype & FRIED)
+ grilled_item.foodtype |= FRIED
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/relay(var/sound, var/message=null, loop = FALSE, channel = null) //Sends a sound + text message to the crew of a ship
+ for(var/mob/M in GLOB.player_list)
+ if(M.z == z)
+ if(!isinspace(M))
+ if(sound)
+ if(channel) //Doing this forbids overlapping of sounds
+ SEND_SOUND(M, sound(sound, repeat = loop, wait = 0, volume = 70, channel = channel))
+ else
+ SEND_SOUND(M, sound(sound, repeat = loop, wait = 0, volume = 70))
+ if(message)
+ to_chat(M, message)
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/stop_relay(channel) //Stops all playing sounds for crewmen on N channel.
+ for(var/mob/M in GLOB.player_list)
+ if(M.z == z)
+ M.stop_sound_channel(channel)
+
+//Method to handle sound effects, reactor warnings, all that jazz.
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/handle_alerts()
+ var/alert = FALSE //If we have an alert condition, we'd best let people know.
+ if(K <= 0 && temperature <= 0)
+ shut_down()
+ //First alert condition: Overheat
+ if(temperature >= RBMK_TEMPERATURE_CRITICAL)
+ alert = TRUE
+ investigate_log("Reactor reaching critical temperature at [temperature] C with desired criticality at [desired_k]", INVESTIGATE_SINGULO)
+ message_admins("Reactor reaching critical temperature at [ADMIN_VERBOSEJMP(src)]")
+ if(temperature >= RBMK_TEMPERATURE_MELTDOWN)
+ var/temp_damage = min(temperature/100, initial(vessel_integrity)/40) //40 seconds to meltdown from full integrity, worst-case. Bit less than blowout since it's harder to spike heat that much.
+ vessel_integrity -= temp_damage
+ if(vessel_integrity <= temp_damage)
+ investigate_log("Reactor melted down at [temperature] C with desired criticality at [desired_k]", INVESTIGATE_SINGULO)
+ meltdown() //Oops! All meltdown
+ return
+ else
+ alert = FALSE
+ if(temperature < -200) //That's as cold as I'm letting you get it, engineering.
+ color = COLOR_CYAN
+ temperature = -200
+ else
+ color = null
+ //Second alert condition: Overpressurized (the more lethal one)
+ if(pressure >= RBMK_PRESSURE_CRITICAL)
+ alert = TRUE
+ investigate_log("Reactor reaching critical pressure at [pressure] PSI with desired criticality at [desired_k]", INVESTIGATE_SINGULO)
+ message_admins("Reactor reaching critical pressure at [ADMIN_VERBOSEJMP(src)]")
+ shake_animation(0.5)
+ playsound(loc, 'sound/machines/clockcult/steam_whoosh.ogg', 100, TRUE)
+ var/turf/T = get_turf(src)
+ T.atmos_spawn_air("water_vapor=[pressure/100];TEMP=[CELSIUS_TO_KELVIN(temperature)]")
+ var/pressure_damage = min(pressure/100, initial(vessel_integrity)/45) //You get 45 seconds (if you had full integrity), worst-case. But hey, at least it can't be instantly nuked with a pipe-fire.. though it's still very difficult to save.
+ vessel_integrity -= pressure_damage
+ if(vessel_integrity <= pressure_damage) //It wouldn't
+ investigate_log("Reactor blowout at [pressure] PSI with desired criticality at [desired_k]", INVESTIGATE_SINGULO)
+ blowout()
+ return
+ if(warning)
+ if(!alert) //Congrats! You stopped the meltdown / blowout.
+ stop_relay(CHANNEL_REACTOR_ALERT)
+ warning = FALSE
+ set_light(0)
+ light_color = LIGHT_COLOR_CYAN
+ set_light(10)
+ else
+ if(!alert)
+ return
+ if(world.time < next_warning)
+ return
+ next_warning = world.time + 30 SECONDS //To avoid engis pissing people off when reaaaally trying to stop the meltdown or whatever.
+ warning = TRUE //Start warning the crew of the imminent danger.
+ relay('hyperstation/sound/effects/rbmk/alarm.ogg', null, loop=TRUE, channel = CHANNEL_REACTOR_ALERT)
+ set_light(0)
+ light_color = LIGHT_COLOR_RED
+ set_light(10)
+
+//Failure condition 1: Meltdown. Achieved by having heat go over tolerances. This is less devastating because it's easier to achieve.
+//Results: Engineering becomes unusable and your engine irreparable
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/meltdown()
+ set waitfor = FALSE
+ SSair.atmos_machinery -= src //Annd we're now just a useless brick.
+ slagged = TRUE
+ color = null
+ update_icon()
+ STOP_PROCESSING(SSmachines, src)
+ icon_state = "reactor_slagged"
+ AddComponent(/datum/component/radioactive, 15000 , src, 0)
+ var/obj/effect/landmark/nuclear_waste_spawner/NSW = new /obj/effect/landmark/nuclear_waste_spawner/strong(get_turf(src))
+ relay('hyperstation/sound/effects/rbmk/meltdown.ogg', "You hear a horrible metallic hissing.")
+ stop_relay(CHANNEL_REACTOR_ALERT)
+ NSW.fire() //This will take out engineering for a decent amount of time as they have to clean up the sludge.
+ for(var/obj/machinery/power/apc/apc in GLOB.apcs_list)
+ if((apc.z == z) && prob(70))
+ apc.overload_lighting()
+ var/datum/gas_mixture/coolant_input = COOLANT_INPUT_GATE
+ var/datum/gas_mixture/moderator_input = MODERATOR_INPUT_GATE
+ var/datum/gas_mixture/coolant_output = COOLANT_OUTPUT_GATE
+ var/turf/T = get_turf(src)
+ coolant_input.temperature = CELSIUS_TO_KELVIN(temperature)*2
+ moderator_input.temperature = CELSIUS_TO_KELVIN(temperature)*2
+ coolant_output.temperature = CELSIUS_TO_KELVIN(temperature)*2
+ T.assume_air(coolant_input)
+ T.assume_air(moderator_input)
+ T.assume_air(coolant_output)
+ explosion(get_turf(src), 0, 5, 10, 20, TRUE, TRUE)
+ empulse(get_turf(src), 25, 15)
+ QDEL_NULL(soundloop)
+
+//Failure condition 2: Blowout. Achieved by reactor going over-pressured. This is a round-ender because it requires more fuckery to achieve.
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/blowout()
+ explosion(get_turf(src), GLOB.MAX_EX_DEVESTATION_RANGE, GLOB.MAX_EX_HEAVY_RANGE, GLOB.MAX_EX_LIGHT_RANGE, GLOB.MAX_EX_FLASH_RANGE)
+ meltdown() //Double kill.
+ SSweather.run_weather("nuclear fallout")
+ for(var/X in GLOB.landmarks_list)
+ if(istype(X, /obj/effect/landmark/nuclear_waste_spawner))
+ var/obj/effect/landmark/nuclear_waste_spawner/WS = X
+ if(is_station_level(WS.z)) //Begin the SLUDGING
+ WS.fire()
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/update_icon()
+ icon_state = "reactor_off"
+ switch(temperature)
+ if(0 to 200)
+ icon_state = "reactor_on"
+ if(200 to RBMK_TEMPERATURE_OPERATING)
+ icon_state = "reactor_hot"
+ if(RBMK_TEMPERATURE_OPERATING to 750)
+ icon_state = "reactor_veryhot"
+ if(750 to RBMK_TEMPERATURE_CRITICAL) //Point of no return.
+ icon_state = "reactor_overheat"
+ if(RBMK_TEMPERATURE_CRITICAL to INFINITY)
+ icon_state = "reactor_meltdown"
+ if(!has_fuel())
+ icon_state = "reactor_off"
+ if(slagged)
+ icon_state = "reactor_slagged"
+
+
+//Startup, shutdown
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/start_up()
+ START_PROCESSING(SSmachines, src)
+ desired_k = 1
+ set_light(10)
+ var/startup_sound = pick('hyperstation/sound/effects/ship/reactor/startup.ogg', 'hyperstation/sound/effects/ship/reactor/startup2.ogg')
+ playsound(loc, startup_sound, 100)
+ soundloop = new(list(src), TRUE)
+ if(!powernet)
+ message_admins("No powernet for the Nuclear Reactor! Trying to add.")
+ connect_to_network()
+ if(!powernet)
+ message_admins("Powernet add fail. This reactor will never produce power.")
+
+//Shuts off the fuel rods, ambience, etc. Keep in mind that your temperature may still go up!
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/shut_down()
+ STOP_PROCESSING(SSmachines, src)
+ set_light(0)
+ K = 0
+ desired_k = 0
+ temperature = 0
+ update_icon()
+ QDEL_NULL(soundloop)
+
+/obj/item/twohanded/required/fuel_rod
+ name = "Uranium-235 Fuel Rod"
+ desc = "A titanium sheathed rod containing a measure of enriched uranium-dioxide powder inside, and a breeding blanket of uranium-238 around it, used to kick off a fission reaction and breed plutonium fuel respectivly."
+ icon = 'hyperstation/icons/obj/control_rod.dmi'
+ icon_state = "irradiated"
+ w_class = WEIGHT_CLASS_BULKY
+ var/depletion = 0 //Each fuel rod will deplete in around 30 minutes.
+ var/fuel_power = 0.10
+
+/obj/item/twohanded/required/fuel_rod/proc/deplete(amount=0.035)
+ depletion += amount
+ if(depletion >= 100)
+ fuel_power = 0.20
+ name = "Plutonium-239 Fuel Rod"
+ desc = "A highly energetic titanium sheathed rod containing a sizeable measure of weapons grade plutonium, it's highly efficient as nuclear fuel, but will cause the reaction to get out of control if not properly utilised."
+ icon_state = "inferior"
+ AddComponent(/datum/component/radioactive, 1500 , src)
+ else
+ fuel_power = 0.10
+
+/obj/item/twohanded/required/fuel_rod/Initialize(mapload)
+ .=..()
+ AddComponent(/datum/component/radioactive, 350 , src)
+
+//Controlling the reactor.
+
+/obj/machinery/computer/reactor
+ name = "Reactor control console"
+ desc = "Scream"
+ light_color = "#55BA55"
+ light_power = 1
+ light_range = 3
+ icon = 'hyperstation/icons/obj/rbmk_computer.dmi'
+ icon_state = "oldcomp"
+ icon_screen = "library"
+ icon_keyboard = null
+ var/obj/machinery/atmospherics/components/trinary/nuclear_reactor/reactor = null
+ var/id = "default_reactor_for_lazy_mappers"
+
+/obj/machinery/computer/reactor/Initialize(mapload, obj/item/circuitboard/C)
+ . = ..()
+ addtimer(CALLBACK(src,PROC_REF(link_to_reactor)), 10 SECONDS)
+
+/obj/machinery/computer/reactor/wrench_act(mob/living/user, obj/item/I)
+ to_chat(user, "You start [anchored ? "un" : ""]securing [name]...")
+ if(I.use_tool(src, user, 40, volume=75))
+ to_chat(user, "You [anchored ? "un" : ""]secure [name].")
+ setAnchored(!anchored)
+ return TRUE
+ return FALSE
+
+/obj/machinery/computer/reactor/proc/link_to_reactor()
+ for(var/obj/machinery/atmospherics/components/trinary/nuclear_reactor/asdf in GLOB.machines)
+ if(asdf.id && asdf.id == id)
+ reactor = asdf
+ return TRUE
+ return FALSE
+
+#define FREQ_RBMK_CONTROL 1439.69
+
+/obj/machinery/computer/reactor/control_rods
+ name = "Control rod management computer"
+ desc = "A computer which can remotely raise / lower the control rods of a reactor."
+ icon_screen = "rbmk_rods"
+
+/obj/machinery/computer/reactor/control_rods/attack_hand(mob/living/user)
+ . = ..()
+ ui_interact(user)
+
+/obj/machinery/computer/reactor/control_rods/ui_interact(mob/user, ui_key, datum/tgui/ui, force_open, datum/tgui/master_ui, datum/ui_state/state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "rbmkcontrolrods", name, 300, 300, master_ui, state)
+ ui.open()
+
+/obj/machinery/computer/reactor/control_rods/ui_act(action, params)
+ if(..())
+ return
+ if(!reactor)
+ return
+ if(action == "input")
+ var/input = text2num(params["target"])
+ reactor.last_user = usr
+ reactor.desired_k = CLAMP(input, 0, 3)
+
+/obj/machinery/computer/reactor/control_rods/ui_data(mob/user)
+ var/list/data = list()
+ data["control_rods"] = 0
+ data["k"] = 0
+ data["desiredK"] = 0
+ if(reactor)
+ data["k"] = reactor.K
+ data["desiredK"] = reactor.desired_k
+ data["control_rods"] = 100 - (reactor.desired_k / 3 * 100) //Rod insertion is extrapolated as a function of the percentage of K
+ return data
+
+/obj/machinery/computer/reactor/stats
+ name = "Reactor Statistics Console"
+ desc = "A console for monitoring the statistics of a nuclear reactor."
+ icon_screen = "rbmk_stats"
+ var/next_stat_interval = 0
+ var/list/psiData = list()
+ var/list/powerData = list()
+ var/list/tempInputData = list()
+ var/list/tempOutputdata = list()
+
+/obj/machinery/computer/reactor/stats/attack_hand(mob/living/user)
+ . = ..()
+ ui_interact(user)
+
+/obj/machinery/computer/reactor/stats/ui_interact(mob/user, ui_key, datum/tgui/ui, force_open, datum/tgui/master_ui, datum/ui_state/state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "RbmkStats", name, 350, 500, master_ui, state)
+ ui.open()
+
+/obj/machinery/computer/reactor/stats/process()
+ if(world.time >= next_stat_interval)
+ next_stat_interval = world.time + 1 SECONDS //You only get a slow tick.
+ psiData += (reactor) ? reactor.pressure : 0
+ if(psiData.len > 100) //Only lets you track over a certain timeframe.
+ psiData.Cut(1, 2)
+ powerData += (reactor) ? reactor.power*10 : 0 //We scale up the figure for a consistent:tm: scale
+ if(powerData.len > 100) //Only lets you track over a certain timeframe.
+ powerData.Cut(1, 2)
+ tempInputData += (reactor) ? reactor.last_coolant_temperature : 0 //We scale up the figure for a consistent:tm: scale
+ if(tempInputData.len > 100) //Only lets you track over a certain timeframe.
+ tempInputData.Cut(1, 2)
+ tempOutputdata += (reactor) ? reactor.last_output_temperature : 0 //We scale up the figure for a consistent:tm: scale
+ if(tempOutputdata.len > 100) //Only lets you track over a certain timeframe.
+ tempOutputdata.Cut(1, 2)
+
+/obj/machinery/computer/reactor/stats/ui_data(mob/user)
+ var/list/data = list()
+ data["powerData"] = powerData
+ data["psiData"] = psiData
+ data["tempInputData"] = tempInputData
+ data["tempOutputdata"] = tempOutputdata
+ data["coolantInput"] = reactor ? reactor.last_coolant_temperature : 0
+ data["coolantOutput"] = reactor ? reactor.last_output_temperature : 0
+ data["power"] = reactor ? reactor.power : 0
+ data ["psi"] = reactor ? reactor.pressure : 0
+ return data
+
+/obj/machinery/computer/reactor/fuel_rods
+ name = "Reactor Fuel Management Console"
+ desc = "A console which can remotely raise fuel rods out of nuclear reactors."
+ icon_screen = "rbmk_fuel"
+
+/obj/machinery/computer/reactor/fuel_rods/attack_hand(mob/living/user)
+ . = ..()
+ if(!reactor)
+ return FALSE
+ if(reactor.power > 20)
+ to_chat(user, "You cannot remove fuel from [reactor] when it is above 20% power.")
+ return FALSE
+ if(!reactor.fuel_rods.len)
+ to_chat(user, "[reactor] does not have any fuel rods loaded.")
+ return FALSE
+ var/atom/movable/fuel_rod = input(usr, "Select a fuel rod to remove", "[src]", null) as null|anything in reactor.fuel_rods
+ if(!fuel_rod)
+ return
+ playsound(src, pick('hyperstation/sound/effects/rbmk/switch.ogg','hyperstation/sound/effects/rbmk/switch2.ogg','hyperstation/sound/effects/rbmk/switch3.ogg'), 100, FALSE)
+ playsound(reactor, 'hyperstation/sound/effects/ship/freespace2/crane_1.wav', 100, FALSE)
+ fuel_rod.forceMove(get_turf(reactor))
+ reactor.fuel_rods -= fuel_rod
+
+//Preset pumps for mappers. You can also set the id tags yourself.
+/obj/machinery/atmospherics/components/binary/pump/rbmk_input
+ id = "rbmk_input"
+ frequency = FREQ_RBMK_CONTROL
+
+/obj/machinery/atmospherics/components/binary/pump/rbmk_output
+ id = "rbmk_output"
+ frequency = FREQ_RBMK_CONTROL
+
+/obj/machinery/atmospherics/components/binary/pump/rbmk_moderator
+ id = "rbmk_moderator"
+ frequency = FREQ_RBMK_CONTROL
+
+/obj/machinery/computer/reactor/pump
+ name = "Reactor inlet valve computer"
+ desc = "A computer which controls valve settings on an advanced gas cooled reactor. Alt click it to remotely set pump pressure."
+ icon_screen = "rbmk_input"
+ id = "rbmk_input"
+ var/datum/radio_frequency/radio_connection
+ var/on = FALSE
+
+/obj/machinery/computer/reactor/pump/AltClick(mob/user)
+ . = ..()
+ var/newPressure = input(user, "Set new output pressure (kPa)", "Remote pump control", null) as num
+ if(!newPressure)
+ return
+ newPressure = clamp(newPressure, 0, MAX_OUTPUT_PRESSURE) //Number sanitization is not handled in the pumps themselves, only during their ui_act which this doesn't use.
+ signal(on, newPressure)
+
+/obj/machinery/computer/reactor/attack_robot(mob/user)
+ . = ..()
+ attack_hand(user)
+
+/obj/machinery/computer/reactor/attack_ai(mob/user)
+ . = ..()
+ attack_hand(user)
+
+/obj/machinery/computer/reactor/pump/attack_hand(mob/living/user)
+ . = ..()
+ if(!is_operational())
+ return FALSE
+ playsound(loc, pick('hyperstation/sound/effects/rbmk/switch.ogg','hyperstation/sound/effects/rbmk/switch2.ogg','hyperstation/sound/effects/rbmk/switch3.ogg'), 100, FALSE)
+ visible_message("[src]'s switch flips [on ? "off" : "on"].")
+ on = !on
+ signal(on)
+
+/obj/machinery/computer/reactor/pump/Initialize(mapload, obj/item/circuitboard/C)
+ . = ..()
+ radio_connection = SSradio.add_object(src, FREQ_RBMK_CONTROL,filter=RADIO_ATMOSIA)
+
+/obj/machinery/computer/reactor/pump/proc/signal(power, set_output_pressure=null)
+ var/datum/signal/signal
+ if(!set_output_pressure) //Yes this is stupid, but technically if you pass through "set_output_pressure" onto the signal, it'll always try and set its output pressure and yeahhh...
+ signal = new(list(
+ "tag" = id,
+ "frequency" = FREQ_RBMK_CONTROL,
+ "timestamp" = world.time,
+ "power" = power,
+ "sigtype" = "command"
+ ))
+ else
+ signal = new(list(
+ "tag" = id,
+ "frequency" = FREQ_RBMK_CONTROL,
+ "timestamp" = world.time,
+ "power" = power,
+ "set_output_pressure" = set_output_pressure,
+ "sigtype" = "command"
+ ))
+ radio_connection.post_signal(src, signal, filter=RADIO_ATMOSIA)
+
+//Preset subtypes for mappers
+/obj/machinery/computer/reactor/pump/rbmk_input
+ name = "Reactor inlet valve computer"
+ icon_screen = "rbmk_input"
+ id = "rbmk_input"
+
+/obj/machinery/computer/reactor/pump/rbmk_output
+ name = "Reactor output valve computer"
+ icon_screen = "rbmk_output"
+ id = "rbmk_output"
+
+/obj/machinery/computer/reactor/pump/rbmk_moderator
+ name = "Reactor moderator valve computer"
+ icon_screen = "rbmk_moderator"
+ id = "rbmk_moderator"
+
+//Monitoring program.
+/datum/computer_file/program/nuclear_monitor
+ filename = "rbmkmonitor"
+ filedesc = "Nuclear Reactor Monitoring"
+ ui_header = "smmon_0.gif"
+ program_icon_state = "smmon_0"
+ extended_desc = "This program connects to specially calibrated sensors to provide information on the status of nuclear reactors."
+ requires_ntnet = TRUE
+ transfer_access = ACCESS_CONSTRUCTION
+ network_destination = "rbmk monitoring system"
+ size = 2
+ tgui_id = "ntosrbmkstats"
+ ui_x = 350
+ ui_y = 550
+ var/active = TRUE //Easy process throttle
+ var/next_stat_interval = 0
+ var/list/psiData = list()
+ var/list/powerData = list()
+ var/list/tempInputData = list()
+ var/list/tempOutputdata = list()
+ var/obj/machinery/atmospherics/components/trinary/nuclear_reactor/reactor //Our reactor.
+
+/datum/computer_file/program/nuclear_monitor/process_tick()
+ ..()
+ if(!reactor || !active)
+ return FALSE
+ var/stage = 0
+ //This is dirty but i'm lazy wahoo!
+ if(reactor.power > 0)
+ stage = 1
+ if(reactor.power >= 40)
+ stage = 2
+ if(reactor.temperature >= RBMK_TEMPERATURE_OPERATING)
+ stage = 3
+ if(reactor.temperature >= RBMK_TEMPERATURE_CRITICAL)
+ stage = 4
+ if(reactor.temperature >= RBMK_TEMPERATURE_MELTDOWN)
+ stage = 5
+ if(reactor.vessel_integrity <= 100) //Bye bye! GET OUT!
+ stage = 6
+ ui_header = "smmon_[stage].gif"
+ program_icon_state = "smmon_[stage]"
+ if(istype(computer))
+ computer.update_icon()
+ if(world.time >= next_stat_interval)
+ next_stat_interval = world.time + 1 SECONDS //You only get a slow tick.
+ psiData += (reactor) ? reactor.pressure : 0
+ if(psiData.len > 100) //Only lets you track over a certain timeframe.
+ psiData.Cut(1, 2)
+ powerData += (reactor) ? reactor.power*10 : 0 //We scale up the figure for a consistent:tm: scale
+ if(powerData.len > 100) //Only lets you track over a certain timeframe.
+ powerData.Cut(1, 2)
+ tempInputData += (reactor) ? reactor.last_coolant_temperature : 0 //We scale up the figure for a consistent:tm: scale
+ if(tempInputData.len > 100) //Only lets you track over a certain timeframe.
+ tempInputData.Cut(1, 2)
+ tempOutputdata += (reactor) ? reactor.last_output_temperature : 0 //We scale up the figure for a consistent:tm: scale
+ if(tempOutputdata.len > 100) //Only lets you track over a certain timeframe.
+ tempOutputdata.Cut(1, 2)
+
+/datum/computer_file/program/nuclear_monitor/run_program(mob/living/user)
+ . = ..(user)
+ //No reactor? Go find one then.
+ if(!reactor)
+ for(var/obj/machinery/atmospherics/components/trinary/nuclear_reactor/R in GLOB.machines)
+ if(R.z == usr.z)
+ reactor = R
+ break
+ active = TRUE
+
+/datum/computer_file/program/nuclear_monitor/kill_program(forced = FALSE)
+ active = FALSE
+ ..()
+
+/datum/computer_file/program/nuclear_monitor/ui_data()
+ var/list/data = get_header_data()
+ data["powerData"] = powerData
+ data["psiData"] = psiData
+ data["tempInputData"] = tempInputData
+ data["tempOutputdata"] = tempOutputdata
+ data["coolantInput"] = reactor ? reactor.last_coolant_temperature : 0
+ data["coolantOutput"] = reactor ? reactor.last_output_temperature : 0
+ data["power"] = reactor ? reactor.power : 0
+ data ["psi"] = reactor ? reactor.pressure : 0
+ return data
+
+/datum/computer_file/program/nuclear_monitor/ui_act(action, params)
+ if(..())
+ return TRUE
+
+ switch(action)
+ if("swap_reactor")
+ var/list/choices = list()
+ for(var/obj/machinery/atmospherics/components/trinary/nuclear_reactor/R in GLOB.machines)
+ if(R.z != usr.z)
+ continue
+ choices += R
+ reactor = input(usr, "What reactor do you wish to monitor?", "[src]", null) as null|anything in choices
+ powerData = list()
+ psiData = list()
+ tempInputData = list()
+ tempOutputdata = list()
+ return TRUE
+
+/obj/effect/decal/nuclear_waste
+ name = "Plutonium sludge"
+ desc = "A writhing pool of heavily irradiated, spent reactor fuel. You probably shouldn't step through this..."
+ icon = 'hyperstation/icons/obj/machinery/reactor_parts.dmi'
+ icon_state = "nuclearwaste"
+ alpha = 150
+ light_color = LIGHT_COLOR_CYAN
+ color = "#ff9eff"
+
+/obj/effect/decal/nuclear_waste/Initialize(mapload)
+ . = ..()
+ for(var/obj/A in get_turf(src))
+ if(istype(A, /obj/structure))
+ qdel(src) //It is more processing efficient to do this here rather than when searching for available turfs.
+ set_light(3)
+ AddComponent(/datum/component/radioactive, 1000, src, 0)
+
+/obj/effect/decal/nuclear_waste/epicenter //The one that actually does the irradiating. This is to avoid every bit of sludge PROCESSING
+ name = "Dense nuclear sludge"
+
+/obj/effect/landmark/nuclear_waste_spawner //Clean way of spawning nuclear gunk after a reactor core meltdown.
+ name = "Nuclear waste spawner"
+ var/range = 5 //5 tile radius to spawn goop
+
+/obj/effect/landmark/nuclear_waste_spawner/strong
+ range = 10
+
+/obj/effect/landmark/nuclear_waste_spawner/proc/fire()
+ playsound(loc, 'sound/effects/gib_step.ogg', 100)
+ new /obj/effect/decal/nuclear_waste/epicenter(get_turf(src))
+ for(var/turf/open/floor in orange(range, get_turf(src)))
+ if(prob(35)) //Scatter the sludge, don't smear it everywhere
+ new /obj/effect/decal/nuclear_waste (floor)
+ qdel(src)
+
+/obj/effect/decal/nuclear_waste/epicenter/Initialize(mapload)
+ . = ..()
+
+/obj/effect/decal/nuclear_waste/Crossed(atom/movable/AM)
+ . = ..()
+ if(isliving(AM))
+ var/mob/living/L = AM
+ playsound(loc, 'sound/effects/gib_step.ogg', HAS_TRAIT(L, TRAIT_LIGHT_STEP) ? 20 : 50, 1)
+ radiation_pulse(src, 500, 5) //MORE RADS
+
+/obj/effect/decal/nuclear_waste/attackby(obj/item/tool, mob/user)
+ if(tool.tool_behaviour == TOOL_SHOVEL)
+ radiation_pulse(src, 1000, 5) //MORE RADS
+ to_chat(user, "You start to clear [src]...")
+ if(tool.use_tool(src, user, 50, volume=100))
+ to_chat(user, "You clear [src]. ")
+ qdel(src)
+ return
+ . = ..()
+
+/datum/weather/nuclear_fallout
+ name = "nuclear fallout"
+ desc = "Irradiated dust falls down everywhere."
+ telegraph_duration = 50
+ telegraph_message = "The air suddenly becomes dusty.."
+ weather_message = "You feel a wave of hot ash fall down on you."
+ weather_overlay = "light_ash"
+ telegraph_overlay = "light_snow"
+ weather_duration_lower = 600
+ weather_duration_upper = 1500
+ weather_color = "green"
+ telegraph_sound = null
+ weather_sound = 'hyperstation/sound/effects/ship/reactor/falloutwind.ogg'
+ end_duration = 100
+ area_type = /area
+ protected_areas = list(/area/maintenance, /area/ai_monitored/turret_protected/ai_upload, /area/ai_monitored/turret_protected/ai_upload_foyer,
+ /area/ai_monitored/turret_protected/ai, /area/storage/emergency/starboard, /area/storage/emergency/port, /area/shuttle)
+ target_trait = ZTRAIT_STATION
+ end_message = "The ash stops falling."
+ immunity_type = "rad"
+
+/datum/weather/nuclear_fallout/weather_act(mob/living/L)
+ L.rad_act(100)
+
+/datum/weather/nuclear_fallout/telegraph()
+ ..()
+ status_alarm(TRUE)
+
+/datum/weather/nuclear_fallout/proc/status_alarm(active) //Makes the status displays show the radiation warning for those who missed the announcement.
+ var/datum/radio_frequency/frequency = SSradio.return_frequency(FREQ_STATUS_DISPLAYS)
+ if(!frequency)
+ return
+
+ var/datum/signal/signal = new
+ if (active)
+ signal.data["command"] = "alert"
+ signal.data["picture_state"] = "radiation"
+ else
+ signal.data["command"] = "shuttle"
+
+ var/atom/movable/virtualspeaker/virt = new(null)
+ frequency.post_signal(virt, signal)
+
+/datum/weather/nuclear_fallout/end()
+ if(..())
+ return
+ status_alarm(FALSE)
+
+/obj/item/sealant
+ name = "Flexi seal"
+ desc = "A neat spray can that can repair torn inflatable segments, and more!"
+ icon = 'hyperstation/icons/obj/inflatable.dmi'
+ icon_state = "sealant"
+ w_class = 1
+
+/area/engine/engineering/reactor_core
+ name = "Nuclear Reactor Core"
+
+/area/engine/engineering/reactor_control
+ name = "Reactor Control Room"
+
+
+//Procs shamelessly taken from machinery/power
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/connect_to_network()
+ var/turf/T = src.loc
+ if(!T || !istype(T))
+ return FALSE
+
+ var/obj/structure/cable/C = T.get_cable_node() //check if we have a node cable on the machine turf, the first found is picked
+ if(!C || !C.powernet)
+ return FALSE
+
+ C.powernet.add_machine(src)
+ return TRUE
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/disconnect_from_network()
+ if(!powernet)
+ return FALSE
+ powernet.remove_machine(src)
+ return TRUE
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/proc/add_avail(amount)
+ if(powernet)
+ powernet.newavail += amount
+ return TRUE
+ else
+ return FALSE
diff --git a/hyperstation/code/modules/power/reactor/reactor_cargo.dm b/hyperstation/code/modules/power/reactor/reactor_cargo.dm
new file mode 100644
index 0000000000..5548e88f8d
--- /dev/null
+++ b/hyperstation/code/modules/power/reactor/reactor_cargo.dm
@@ -0,0 +1,37 @@
+// modular shitcode but it works:tm:
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/multitool_act(mob/living/user, obj/item/multitool/I)
+ if(istype(I))
+ to_chat(user, "You add \the [src]'s ID into the multitool's buffer.")
+ I.buffer = src.id
+ return TRUE
+/obj/machinery/computer/reactor/multitool_act(mob/living/user, obj/item/multitool/I)
+ if(istype(I))
+ to_chat(user, "You add the reactor's ID to \the [src]>")
+ src.id = I.buffer
+ link_to_reactor()
+ return TRUE
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/cargo // easier on the brain
+
+/obj/machinery/atmospherics/components/trinary/nuclear_reactor/cargo/New()
+ . = ..()
+ id = rand(1, 9999999) // cmon, what are the chances? The chances are... Very low friend... But maybe we can make this a bit better.
+
+// Cargo variants can be wrenched down and don't start linked to the default RMBK reactor
+
+/obj/machinery/computer/reactor/control_rods/cargo
+ anchored = FALSE
+ id = null
+
+/obj/machinery/computer/reactor/stats/cargo
+ anchored = FALSE
+ id = null
+
+/obj/machinery/computer/reactor/fuel_rods/cargo
+ anchored = FALSE
+ id = null
+
+/obj/item/paper/fluff/rbmkcargo
+ name = "Nuclear Reactor Instructions"
+ info = "Make sure a 5x5 area is completely clear of pipes, cables and machinery when using the beacon. Those will be provided automatically with the beacon's bluespace decompression. Use a multitool on the reactor then on the computers provided to link them together. Also make sure the reactor has a proper pipeline filled with cooling gas before inserting fuel rods. Good luck!"
diff --git a/hyperstation/code/modules/reagents/chemistry/reagents/drink_reagents.dm b/hyperstation/code/modules/reagents/chemistry/reagents/drink_reagents.dm
new file mode 100644
index 0000000000..3656de1581
--- /dev/null
+++ b/hyperstation/code/modules/reagents/chemistry/reagents/drink_reagents.dm
@@ -0,0 +1,23 @@
+/datum/reagent/consumable/pilk
+ name = "Pilk"
+ description = "A forbidden mixture that dates back to the early days of space civilization, its creation is known to have caused at least one or two massacres."
+ color = "#cf9f6f"
+ taste_description = "heresy"
+ glass_icon_state = "whiskeycolaglass"
+ glass_name = "glass of Pilk"
+ glass_desc = "Why would you do this to yourself?"
+ hydration = 3
+
+/datum/reagent/consumable/pilk/on_mob_life(mob/living/carbon/M)
+ M.drowsyness = max(0,M.drowsyness-3)
+ M.adjust_bodytemperature(-3 * TEMPERATURE_DAMAGE_COEFFICIENT, BODYTEMP_NORMAL)
+ if(HAS_TRAIT(M, TRAIT_CALCIUM_HEALER))
+ M.heal_bodypart_damage(0.75,0, 0)
+ . = 1
+ else
+ if(M.getBruteLoss() && prob(20))
+ M.heal_bodypart_damage(0.5,0, 0)
+ . = 1
+ if(holder.has_reagent(/datum/reagent/consumable/capsaicin))
+ holder.remove_reagent(/datum/reagent/consumable/capsaicin, 1)
+ ..()
diff --git a/hyperstation/code/modules/reagents/chemistry/reagents/food_reagents.dm b/hyperstation/code/modules/reagents/chemistry/reagents/food_reagents.dm
new file mode 100644
index 0000000000..d8cda7c0cf
--- /dev/null
+++ b/hyperstation/code/modules/reagents/chemistry/reagents/food_reagents.dm
@@ -0,0 +1,8 @@
+/datum/reagent/consumable/alienhoney //for players who want bee character bewbs that produce honey.
+ name = "alien honey"
+ description = "Sweet honey that came from a alien source. This honey doesn't possess the same healing or nutrition properties as earth-bound bee honey."
+ color = "#d3a308"
+ value = 3 //plentiful, so wouldnt be worth as much.
+ nutriment_factor = 5 * REAGENTS_METABOLISM
+ metabolization_rate = 1 * REAGENTS_METABOLISM
+ taste_description = "sweetness"
diff --git a/hyperstation/code/modules/reagents/chemistry/reagents/hydroponics_reactions.dm b/hyperstation/code/modules/reagents/chemistry/reagents/hydroponics_reactions.dm
new file mode 100644
index 0000000000..b69dd876eb
--- /dev/null
+++ b/hyperstation/code/modules/reagents/chemistry/reagents/hydroponics_reactions.dm
@@ -0,0 +1,294 @@
+/*
+ * If you ever decide to add more reactions, please remember to keep these
+ * in alphabetical order, according to reagent names and not their datums.
+ * It could be hard to find reactions when code diving.
+ * Due note, unlike before this change, that subtypes will apply to the tray the same as their parents
+ * -DT
+ */
+
+#define DEFAULT_ACT_MUTAGEN switch(rand(100)){\
+ if(91 to 100){\
+ T.adjustHealth(-10);\
+ to_chat(user, "The plant shrivels and burns.");}\
+ if(81 to 90)\
+ T.mutatespecie();\
+ if(66 to 80)\
+ T.hardmutate();\
+ if(41 to 65)\
+ T.mutate();\
+ if(21 to 41)\
+ to_chat(user, "The plants don't seem to react...");\
+ if(11 to 20)\
+ T.mutateweed();\
+ if(1 to 10)\
+ T.mutatepest(user);\
+ else\
+ to_chat(user, "Nothing happens...");}
+
+//Ammonia
+/datum/reagent/ammonia/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ T.adjustHealth(round(volume * 0.5))
+ T.adjustNutri(round(volume * 1))
+ if(..())
+ T.myseed.adjust_yield(round(volume * 0.01))
+ return TRUE
+
+//Ash
+/datum/reagent/ash/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ // Ash is used IRL in gardening, as a fertilizer enhancer and weed killer
+ T.adjustHealth(round(volume * 0.25))
+ T.adjustNutri(round(volume * 0.5))
+ T.adjustWeeds(-round(volume / 10))
+ return TRUE
+
+//Beer
+/datum/reagent/consumable/ethanol/beer/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ // Beer is a chemical composition of alcohol and various other things. It's a shitty nutrient but hey, it's still one. Also alcohol is bad, mmmkay?
+ T.adjustHealth(-round(volume * 0.05))
+ T.adjustNutri(round(volume * 0.25))
+ T.adjustWater(round(volume * 0.7))
+ return TRUE
+
+//Blood
+/datum/reagent/blood/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ T.adjustNutri(round(volume * 0.6))
+ T.adjustHealth(-round(volume))
+ T.adjustPests(rand(2,4)) //they HUNGER
+ return TRUE
+
+//Charcoal
+/datum/reagent/medicine/charcoal/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ T.adjustToxic(-round(volume * 2)) //Antitoxin binds shit pretty well. So the tox goes significantly down
+ return TRUE
+
+//Chlorine
+/datum/reagent/chlorine/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ T.adjustHealth(-round(volume * 1))
+ T.adjustToxic(round(volume * 1.5))
+ T.adjustWater(-round(volume * 0.5))
+ T.adjustWeeds(-rand(1,volume * 0.125))
+ return TRUE
+
+//Diethylamine
+/datum/reagent/diethylamine/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ //Corrosive to pests, but not to plants
+ if(..())
+ T.myseed.adjust_yield(round(volume * 0.02))
+ T.adjustHealth(round(volume))
+ T.adjustNutri(round(volume * 2))
+ T.adjustPests(-rand(1,round(volume / 30)))
+ return TRUE
+
+//Earthsblood
+/datum/reagent/medicine/earthsblood/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ T.self_sufficiency_progress += volume
+ if(T.self_sufficiency_progress >= T.self_sufficiency_req)
+ T.become_self_sufficient()
+ else
+ to_chat(user, "[T] warms as it might on a spring day under a genuine Sun.")
+ return TRUE
+
+//Enduro-Grow
+/datum/reagent/plantnutriment/endurogrow/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ if(!..())
+ return -1
+ T.myseed.adjust_potency(-round(volume / 10))
+ T.myseed.adjust_yield(-round(volume / 30))
+ T.myseed.adjust_endurance(round(volume / 30))
+ return TRUE
+
+//E-Z Nutrient
+/datum/reagent/plantnutriment/eznutriment/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ T.yieldmod = 1
+ T.mutmod = 1
+ return ..()
+
+//Flourine
+/datum/reagent/fluorine/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ // Fluorine one of the most corrosive and deadly gasses
+ T.adjustHealth(-round(volume * 2))
+ T.adjustToxic(round(volume * 2.5))
+ T.adjustWater(-round(volume * 0.5))
+ T.adjustWeeds(-rand(1, volume * 0.25))
+ return TRUE
+
+//Fluorosulfuric acid
+/datum/reagent/toxin/acid/fluacid/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ T.adjustHealth(-round(volume * 2))
+ T.adjustToxic(round(volume * 3))
+ T.adjustWeeds(-rand(1,volume * 0.5))
+ return TRUE
+
+//Holy Water
+/datum/reagent/water/holywater/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ //Mostly the same as water, it also heals the plant a little with the power of the spirits~
+ T.adjustWater(round(volume))
+ T.adjustHealth(round(volume * 0.1))
+ return TRUE
+
+//Left-4-Zed
+/datum/reagent/plantnutriment/left4zednutriment/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ T.yieldmod = 0
+ T.mutmod = 2
+ return ..()
+
+//Liquid Earthquake
+/datum/reagent/plantnutriment/liquidearthquake/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ if(!..())
+ return -1
+ T.myseed.adjust_weed_chance(round(volume / 10))
+ T.myseed.adjust_weed_rate(round(volume / 30))
+ T.myseed.adjust_production(-round(volume / 30))
+ return TRUE
+
+//Milk
+/datum/reagent/consumable/milk/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ T.adjustNutri(round(volume * 0.2))
+ T.adjustWater(round(volume * 0.5))
+ if(..()) // Milk is good for humans, but bad for plants. The sugars canot be used by plants, and the milk fat fucks up growth.
+ if(!T.myseed.get_gene(/datum/plant_gene/trait/plant_type/fungal_metabolism)) //Not shrooms though
+ T.adjustHealth(-round(volume / 2))
+ return TRUE
+
+//Napalm
+/datum/reagent/napalm/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ if(..())
+ if(T.myseed.resistance_flags & FIRE_PROOF)
+ return TRUE
+ T.adjustHealth(-round(volume * 6))
+ T.adjustToxic(round(volume * 7))
+ T.adjustWeeds(-rand(5,9))
+ return TRUE
+
+//Nutriment
+/datum/reagent/consumable/nutriment/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ //Compost, effectively
+ T.adjustHealth(round(volume * 0.5))
+ T.adjustNutri(round(volume))
+ return TRUE
+
+//Pest Killer
+/datum/reagent/toxin/pestkiller/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ T.adjustToxic(round(volume * 0.5))
+ T.adjustPests(-rand(1,volume / 5))
+ return TRUE
+
+//Phosphorus
+/datum/reagent/phosphorus/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ // White Phosphorous + water -> phosphoric acid. That's not a good thing really.
+ // Phosphoric salts are beneficial though. And even if the plant suffers, in the long run the tray gets some nutrients. The benefit isn't worth that much.
+ T.adjustHealth(-round(volume * 0.75))
+ T.adjustNutri(round(volume * 0.1))
+ T.adjustWater(-round(volume * 0.5))
+ T.adjustWeeds(-rand(1, volume * 0.1))
+ return TRUE
+
+//Plant-B-Gone
+/datum/reagent/toxin/plantbgone/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ T.adjustHealth(-round(volume * 5))
+ T.adjustToxic(round(volume * 6))
+ T.adjustWeeds(-rand(4,8))
+ return TRUE
+
+//Plant Base
+//For subtypes of /datum/reagent/plantnutriment/
+/datum/reagent/plantnutriment/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ T.adjustNutri(round(volume))
+ return ..()
+
+//Radium
+/datum/reagent/radium/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ if(volume >= 10)
+ DEFAULT_ACT_MUTAGEN
+ else if(volume >= 5)
+ T.hardmutate()
+ else if(volume >= 2)
+ T.mutate()
+
+ T.adjustHealth(-round(volume))
+ T.adjustToxic(round(volume * 3)) // Radium is harsher (OOC: also easier to produce)
+ return TRUE
+
+//Robust Harvest
+/datum/reagent/plantnutriment/robustharvestnutriment/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ T.yieldmod = 1.3
+ T.mutmod = 0
+ return ..()
+
+//Saltpetre
+/datum/reagent/saltpetre/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ // Saltpetre is used for gardening IRL. To simplify highly, it speeds up growth and strengthens plants
+ T.adjustHealth(round(volume * 0.25))
+ if (..())
+ T.myseed.adjust_production(-round(volume/100)-prob(volume%100))
+ T.myseed.adjust_potency(round(volume*0.5))
+ return TRUE
+
+//Soda Water
+/datum/reagent/consumable/sodawater/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ // A variety of nutrients are dissolved in club soda, without sugar.
+ // These nutrients include carbon, oxygen, hydrogen, phosphorous, potassium, sulfur and sodium, all of which are needed for healthy plant growth.
+ T.adjustWater(round(volume) * 0.9)
+ T.adjustHealth(round(volume * 0.1))
+ T.adjustNutri(round(volume * 0.1))
+ return TRUE
+
+//Sugar
+/datum/reagent/consumable/sugar/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ T.adjustWeeds(round(rand(1, volume / 5)))
+ T.adjustPests(round(rand(1, volume / 5)))
+ return TRUE
+
+//Sulphuric Acid
+/datum/reagent/toxin/acid/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ T.adjustHealth(-round(volume * 1))
+ T.adjustToxic(round(volume * 1.5))
+ T.adjustWeeds(-rand(1,volume * 0.25))
+ return TRUE
+
+//Toxin
+/datum/reagent/toxin/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ T.adjustToxic(round(volume * 2))
+ return TRUE
+
+//Unstable Mutagen
+/datum/reagent/toxin/mutagen/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ if(volume >= 5)
+ DEFAULT_ACT_MUTAGEN
+ else if(volume >= 2)
+ T.hardmutate()
+ else if(volume >= 1)
+ T.mutate()
+ return TRUE
+
+//Uranium
+/datum/reagent/uranium/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ if(volume >= 10)
+ DEFAULT_ACT_MUTAGEN
+ else if(volume >= 5)
+ T.hardmutate()
+ else if(volume >= 2)
+ T.mutate()
+
+ T.adjustHealth(-round(volume))
+ T.adjustToxic(round(volume * 2))
+ return TRUE
+
+//Virus Food
+/datum/reagent/toxin/mutagen/mutagenvirusfood/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ T.adjustNutri(round(volume * 0.5))
+ T.adjustHealth(-round(volume * 0.5))
+ return TRUE
+
+//Water
+/datum/reagent/water/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ T.adjustWater(round(volume))
+ return TRUE
+
+//Weed Killer
+/datum/reagent/toxin/plantbgone/weedkiller/on_tray(obj/machinery/hydroponics/T, volume, mob/user)
+ T.adjustToxic(round(volume / 2))
+ T.adjustWeeds(-rand(1,volume / 5))
+ return TRUE
+
+#undef DEFAULT_ACT_MUTAGEN
diff --git a/hyperstation/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/hyperstation/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
new file mode 100644
index 0000000000..3db0e93f20
--- /dev/null
+++ b/hyperstation/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
@@ -0,0 +1,29 @@
+/datum/reagent/medicine/copium
+ name = "copium"
+ description = "A heavy scented substance meant to help one deal with loss."
+ reagent_state = LIQUID
+ color = "#a8707e"
+ overdose_threshold = 21
+ metabolization_rate = 0.75 * REAGENTS_METABOLISM
+ pH = 4
+
+/datum/reagent/medicine/copium/on_mob_life(mob/living/carbon/M)
+ var/datum/component/mood/mood = M.GetComponent(/datum/component/mood)
+ if(mood.sanity <= SANITY_NEUTRAL && current_cycle >= 5)
+ mood.setSanity(min(mood.sanity+5, SANITY_NEUTRAL))
+ SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "copium", /datum/mood_event/copium)
+ ..()
+
+/datum/reagent/medicine/copium/overdose_process(mob/living/carbon/M) //copious amounts of copium
+ M.blur_eyes(10)
+ if(prob(50))
+ M.adjustToxLoss(1, 0)
+ if(prob(5))
+ M.say(pick("That's okay. Things are going to be okay.", "This is fine.", "I'm okay with the events that are unfolding currently.", "World of Spacecraft is still a good game...", "I'm winning.", "Diesels are far more powerful."))
+ if(prob(5))
+ M.emote("me", 1, "seems to be crying.")
+ if(prob(4))
+ M.emote("me", 1, "falls over, crying.")
+ M.Knockdown(40)
+
+//To-do: /datum/reagent/medicine/seethium
diff --git a/hyperstation/code/modules/reagents/chemistry/recipes/medicine.dm b/hyperstation/code/modules/reagents/chemistry/recipes/medicine.dm
new file mode 100644
index 0000000000..7a542dc5f0
--- /dev/null
+++ b/hyperstation/code/modules/reagents/chemistry/recipes/medicine.dm
@@ -0,0 +1,6 @@
+/datum/chemical_reaction/copium
+ name = "copium"
+ id = /datum/reagent/medicine/copium
+ results = list(/datum/reagent/medicine/copium = 5)
+ required_reagents = list(/datum/reagent/medicine/mannitol = 2, /datum/reagent/water = 1, /datum/reagent/medicine/epinephrine = 1, /datum/reagent/consumable/sugar = 1)
+ required_temp = 266
diff --git a/hyperstation/code/modules/resize/resize_action.dm b/hyperstation/code/modules/resize/resize_action.dm
new file mode 100644
index 0000000000..eeb7f54a4f
--- /dev/null
+++ b/hyperstation/code/modules/resize/resize_action.dm
@@ -0,0 +1,34 @@
+//This action is technically a duplicate of /action/small_sprite
+//However, we do our own things...
+
+/datum/action/sizecode_resize
+ name = "Toggle Giant Sprite"
+ desc = "Others will continue to see you giant."
+ icon_icon = 'icons/mob/screen_gen_old.dmi'
+ button_icon_state = "health1" //You can change this if you want
+ background_icon_state = "bg_alien" //But keep this as a distinct background
+ var/small = FALSE
+ var/image/small_icon
+
+/datum/action/sizecode_resize/Grant(mob/M, safety=FALSE)
+ if(ishuman(M) && !safety) //this probably gets called before a person gets overlays on roundstart, so try again
+ if(!LAZYLEN(M.overlays))
+ addtimer(CALLBACK(src,PROC_REF(Grant), M, TRUE), 5) //https://www.youtube.com/watch?v=QQ-aYZzlDeo
+ return
+
+ ..()
+ if(!owner)
+ return
+ var/image/I = image(icon=owner.icon,icon_state=owner.icon_state,loc=owner,layer=owner.layer,pixel_x=owner.pixel_x,pixel_y=owner.pixel_y)
+ I.overlays += owner.overlays
+ I.override = TRUE
+ small_icon = I
+
+/datum/action/sizecode_resize/Trigger()
+ ..()
+ if(!small)
+ owner.add_alt_appearance(/datum/atom_hud/alternate_appearance/basic, "sizecode_smallsprite", small_icon)
+ else
+ owner.remove_alt_appearance("sizecode_smallsprite")
+ small = !small
+ return TRUE
diff --git a/hyperstation/code/modules/resize/resizing.dm b/hyperstation/code/modules/resize/resizing.dm
new file mode 100644
index 0000000000..294086826f
--- /dev/null
+++ b/hyperstation/code/modules/resize/resizing.dm
@@ -0,0 +1,271 @@
+//I am not a coder. Please fucking tear apart my code, and insult me for how awful I am at coding. Please and thank you. -Dahlular
+//alright bet -BoxBoy
+#define RESIZE_MACRO 6
+#define RESIZE_HUGE 4
+#define RESIZE_BIG 2
+#define RESIZE_NORMAL 1
+#define RESIZE_SMALL 0.75
+#define RESIZE_TINY 0.50
+#define RESIZE_MICRO 0.25
+
+//Moving these here - Jay
+/mob/living
+ var/size_multiplier = 1 //multiplier for the mob's icon size atm
+ var/previous_size = 1
+
+//Cyanosis - Action that resizes the sprite for the client but nobody else. Say goodbye to attacking yourself when someone's above you lmao
+ var/datum/action/sizecode_resize/small_sprite
+
+#define MOVESPEED_ID_SIZE "SIZECODE"
+#define MOVESPEED_ID_STOMP "STEPPY"
+
+//averages
+#define RESIZE_A_MACROHUGE (RESIZE_MACRO + RESIZE_HUGE) / 2
+#define RESIZE_A_HUGEBIG (RESIZE_HUGE + RESIZE_BIG) / 2
+#define RESIZE_A_BIGNORMAL (RESIZE_BIG + RESIZE_NORMAL) / 2
+#define RESIZE_A_NORMALSMALL (RESIZE_NORMAL + RESIZE_SMALL) / 2
+#define RESIZE_A_SMALLTINY (RESIZE_SMALL + RESIZE_TINY) / 2
+#define RESIZE_A_TINYMICRO (RESIZE_TINY + RESIZE_MICRO) / 2
+
+//Scale up a mob's icon by the size_multiplier
+/mob/living/update_transform()
+ ASSERT(!iscarbon(src)) //the source isnt carbon should always be true
+ var/matrix/M = matrix() //matrix (M) variable
+ M.Scale(size_multiplier)
+ M.Translate(0, 16*(size_multiplier-1)) //translate by 16 * size_multiplier - 1 on Y axis
+ src.transform = M //the source of transform is M
+
+/mob/proc/get_effective_size()
+ return 100000
+
+/mob/living/get_effective_size()
+ return src.size_multiplier
+
+/datum/movespeed_modifier/size
+ id = MOVESPEED_ID_SIZE
+
+/mob/living/proc/resize(var/new_size, var/animate = TRUE)
+ size_multiplier = new_size //This will be passed into update_transform() and used to change health and speed.
+ if(size_multiplier == previous_size)
+ return TRUE
+ src.update_transform() //WORK DAMN YOU
+ src.update_mobsize()
+ //Going to change the health and speed values too
+ src.remove_movespeed_modifier(/datum/movespeed_modifier/size)
+ src.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/size, multiplicative_slowdown = (abs(size_multiplier - 1) * 0.3 )) //GS Change, reduces speed penalties for size from *.4 to *.3
+ var/healthmod_old = ((previous_size * 35) - 75) //Get the old value to see what we must change.
+ var/healthmod_new = ((size_multiplier * 35) - 75) //A size of one would be zero. Big boys get health, small ones lose health. //GS Change, reduces health penalties/buffs for size from *75 to *35
+ var/healthchange = healthmod_new - healthmod_old //Get ready to apply the new value, and subtract the old one. (Negative values become positive)
+ src.maxHealth += healthchange
+ src.health += healthchange
+ if(iscarbon(src))
+ var/mob/living/carbon/C = src
+ for(var/obj/item/organ/genital/G in C.internal_organs)
+ G.update_appearance()
+ //if(src.size_multiplier >= RESIZE_A_HUGEBIG || src.size_multiplier <= RESIZE_A_TINYMICRO) Will remove clothing when too big or small. Will do later.
+ previous_size = size_multiplier //And, change this now that we are finally done.
+
+ SEND_SIGNAL(src, COMSIG_MOBSIZE_CHANGED, src) //This SHOULD allow other shit to check when a mob changes size -Capn
+
+ //Now check if the mob can get the size action
+ if(!small_sprite)
+ small_sprite = new(src)
+ small_sprite.Remove(src)
+ if(size_multiplier >= 1.25) //Anything bigger will start to block things
+ small_sprite.Grant(src)
+
+//handle the big steppy, except nice
+/mob/living/proc/handle_micro_bump_helping(var/mob/living/tmob)
+ if(ishuman(src))
+ var/mob/living/carbon/human/H = src
+
+ if(tmob.pulledby == H)
+ return FALSE
+
+ //Micro is on a table.
+ var/turf/steppyspot = tmob.loc
+ for(var/thing in steppyspot.contents)
+ if(istype(thing, /obj/structure/table))
+ return TRUE
+
+ //Both small.
+ if(H.get_effective_size() <= RESIZE_A_TINYMICRO && tmob.get_effective_size() <= RESIZE_A_TINYMICRO)
+ now_pushing = 0
+ H.forceMove(tmob.loc)
+ return TRUE
+
+ //Doing messages
+ if(abs(get_effective_size()/tmob.get_effective_size()) >= 2) //if the initiator is twice the size of the micro
+ now_pushing = 0
+ H.forceMove(tmob.loc)
+
+ //Smaller person being stepped on
+ if(get_effective_size() > tmob.get_effective_size() && iscarbon(src))
+ // GS13: Import Fat Naga & Alt Naga from VoreStation
+ if(istype(H) && H.dna.features["taur"] == "Naga" || H.dna.features["taur"] == "Tentacle" || H.dna.features["taur"] == "Fat Naga" || H.dna.features["taur"] == "Alt Naga")
+ tmob.visible_message("[src] carefully slithers around [tmob].", "[src]'s huge tail slithers besides you.")
+ else
+ tmob.visible_message("[src] carefully steps over [tmob].", "[src] steps over you carefully.")
+
+ return TRUE
+
+ //Smaller person stepping under a larger person
+ if(abs(tmob.get_effective_size()/get_effective_size()) >= 2)
+ H.forceMove(tmob.loc)
+ now_pushing = 0
+ micro_step_under(tmob)
+ return TRUE
+
+//Stepping on disarm intent -- TO DO, OPTIMIZE ALL OF THIS SHIT
+/mob/living/proc/handle_micro_bump_other(var/mob/living/tmob)
+ ASSERT(isliving(tmob))
+ if(ishuman(src))
+ var/mob/living/carbon/human/H = src
+
+ if(tmob.pulledby == H)
+ return FALSE
+
+ //If on a table, don't
+ var/turf/steppyspot = tmob.loc
+ for(var/thing in steppyspot.contents)
+ if(istype(thing, /obj/structure/table))
+ return TRUE
+
+ //Both small
+ if(H.get_effective_size() <= RESIZE_A_TINYMICRO && tmob.get_effective_size() <= RESIZE_A_TINYMICRO)
+ now_pushing = 0
+ H.forceMove(tmob.loc)
+ return TRUE
+
+ if(abs(get_effective_size()/tmob.get_effective_size()) >= 2)
+ if(H.a_intent == "disarm" && H.canmove && !H.buckled)
+ now_pushing = 0
+ H.forceMove(tmob.loc)
+ sizediffStamLoss(tmob)
+ H.add_movespeed_modifier(MOVESPEED_ID_STOMP, multiplicative_slowdown = 10) //Full stop
+ addtimer(CALLBACK(H, TYPE_PROC_REF(/mob/, remove_movespeed_modifier), MOVESPEED_ID_STOMP), 3) //0.3 seconds
+ if(get_effective_size() > tmob.get_effective_size() && iscarbon(H))
+ if(istype(H) && H.dna.features["taur"] == "Naga" || H.dna.features["taur"] == "Tentacle" || H.dna.features["taur"] == "Fat Naga" || H.dna.features["taur"] == "Alt Naga")
+ tmob.visible_message("[src] carefully rolls their tail over [tmob]!", "[src]'s huge tail rolls over you!")
+ else
+ tmob.visible_message("[src] carefully steps on [tmob]!", "[src] steps onto you with force!")
+
+ //horny traits
+ /*
+ if(HAS_TRAIT(src, TRAIT_MICROPHILE))
+ src.adjust_arousal(8)
+ if (src.getArousalLoss() >= 100 && ishuman(tmob) && tmob.has_dna())
+ src.mob_climax(forced_climax=TRUE)
+
+ if(HAS_TRAIT(tmob, TRAIT_MACROPHILE))
+ tmob.adjust_arousal(10)
+ if (tmob.getArousalLoss() >= 100 && ishuman(tmob) && tmob.has_dna())
+ tmob.mob_climax(forced_climax=TRUE)
+
+ return TRUE
+ * commenting out for now.*/
+
+ if(H.a_intent == "harm" && H.canmove && !H.buckled)
+ now_pushing = 0
+ H.forceMove(tmob.loc)
+ sizediffStamLoss(tmob)
+ sizediffBruteloss(tmob)
+ playsound(loc, 'sound/misc/splort.ogg', 50, 1)
+ H.add_movespeed_modifier(MOVESPEED_ID_STOMP, multiplicative_slowdown = 10)
+ addtimer(CALLBACK(H, TYPE_PROC_REF(/mob/, remove_movespeed_modifier), MOVESPEED_ID_STOMP), 10) //1 second
+ //H.Stun(20)
+ if(get_effective_size() > tmob.get_effective_size() && iscarbon(H))
+ if(istype(H) && H.dna.features["taur"] == "Naga" || H.dna.features["taur"] == "Tentacle" || H.dna.features["taur"] == "Fat Naga" || H.dna.features["taur"] == "Alt Naga")
+ tmob.visible_message("[src] mows down [tmob] under their tail!", "[src] plows their tail over you mercilessly!")
+ else
+ tmob.visible_message("[src] slams their foot down on [tmob], crushing them!", "[src] crushes you under their foot!")
+
+ //horny traits
+ /*
+ if(HAS_TRAIT(src, TRAIT_MICROPHILE))
+ src.adjust_arousal((get_effective_size()/tmob.get_effective_size()*3))
+ if (src.getArousalLoss() >= 100 && ishuman(tmob) && tmob.has_dna())
+ src.mob_climax(forced_climax=TRUE)
+
+ if(HAS_TRAIT(tmob, TRAIT_MACROPHILE))
+ tmob.adjust_arousal((get_effective_size()/tmob.get_effective_size()*3))
+ if (tmob.getArousalLoss() >= 100 && ishuman(tmob) && tmob.has_dna())
+ tmob.mob_climax(forced_climax=TRUE)
+
+ commenting out for now */
+ return TRUE
+
+ // if(H.a_intent == "grab" && H.canmove && !H.buckled)
+ // now_pushing = 0
+ // H.forceMove(tmob.loc)
+ // sizediffStamLoss(tmob)
+ // sizediffStun(tmob)
+ // H.add_movespeed_modifier(MOVESPEED_ID_STOMP, multiplicative_slowdown = 10)
+ // addtimer(CALLBACK(H, TYPE_PROC_REF(/mob/, remove_movespeed_modifier), MOVESPEED_ID_STOMP), 7)//About 3/4th a second
+ // if(get_effective_size() > tmob.get_effective_size() && iscarbon(H))
+ // var/feetCover = (H.wear_suit && (H.wear_suit.body_parts_covered & FEET)) || (H.w_uniform && (H.w_uniform.body_parts_covered & FEET) || (H.shoes && (H.shoes.body_parts_covered & FEET)))
+ // if(feetCover)
+ // if(istype(H) && H.dna.features["taur"] == "Naga" || H.dna.features["taur"] == "Tentacle" || H.dna.features["taur"] == "Fat Naga" || H.dna.features["taur"] == "Alt Naga")
+ // tmob.visible_message("[src] pins [tmob] under their tail!", "[src] pins you beneath their tail!")
+ // else
+ // tmob.visible_message("[src] pins [tmob] helplessly underfoot!", "[src] pins you underfoot!")
+ // return TRUE
+ // else
+ // if(istype(H) && H.dna.features["taur"] == "Naga" || H.dna.features["taur"] == "Tentacle" || H.dna.features["taur"] == "Fat Naga" || H.dna.features["taur"] == "Alt Naga")
+ // tmob.visible_message("[src] snatches up [tmob] underneath their tail!", "[src]'s tail winds around you and snatches you in its coils!")
+ // //tmob.mob_pickup_micro_feet(H)
+ // SEND_SIGNAL(tmob, COMSIG_MICRO_PICKUP_FEET, H)
+ // else
+ // tmob.visible_message("[src] stomps down on [tmob], curling their toes and picking them up!", "[src]'s toes pin you down and curl around you, picking you up!")
+ // //tmob.mob_pickup_micro_feet(H)
+ // SEND_SIGNAL(tmob, COMSIG_MICRO_PICKUP_FEET, H)
+ // return TRUE
+
+ if(abs(tmob.get_effective_size()/get_effective_size()) >= 2)
+ H.forceMove(tmob.loc)
+ now_pushing = 0
+ micro_step_under(tmob)
+ return TRUE
+
+//smaller person stepping under another person... TO DO, fix and allow special interactions with naga legs to be seen
+/mob/living/proc/micro_step_under(var/mob/living/tmob)
+ if(istype(tmob) && istype(tmob, /datum/sprite_accessory/taur/naga))
+ src.visible_message("[src] bounds over [tmob]'s tail.", "You jump over [tmob]'s thick tail.")
+ else
+ src.visible_message("[src] runs between [tmob]'s legs.", "You run between [tmob]'s legs.")
+
+//Proc for scaling stamina damage on size difference
+/mob/living/proc/sizediffStamLoss(var/mob/living/tmob)
+ var/S = (get_effective_size()/tmob.get_effective_size()*25) //macro divided by micro, times 25
+ tmob.Knockdown(S) //final result in stamina knockdown
+
+//Proc for scaling stuns on size difference (for grab intent)
+/mob/living/proc/sizediffStun(var/mob/living/tmob)
+ var/T = (get_effective_size()/tmob.get_effective_size()*2) //Macro divided by micro, times 2
+ tmob.Stun(T)
+
+//Proc for scaling brute damage on size difference
+/mob/living/proc/sizediffBruteloss(var/mob/living/tmob)
+ var/B = (get_effective_size()/tmob.get_effective_size()*2) //macro divided by micro, times 3 //GS change, *2
+ tmob.adjustBruteLoss(B) //final result in brute loss
+
+//Proc for changing mob_size to be grabbed for item weight classes
+/mob/living/proc/update_mobsize(var/mob/living/tmob)
+ if(size_multiplier <= 0.50)
+ mob_size = 0
+ else if(size_multiplier < 1)
+ mob_size = 1
+ else if(size_multiplier <= 2)
+ mob_size = 2 //the default human size
+ else if(size_multiplier > 2)
+ mob_size = 3
+
+//Proc for instantly grabbing valid size difference. Code optimizations soon(TM)
+/*
+/mob/living/proc/sizeinteractioncheck(var/mob/living/tmob)
+ if(abs(get_effective_size()/tmob.get_effective_size())>=2.0 && get_effective_size()>tmob.get_effective_size())
+ return FALSE
+ else
+ return TRUE
+*/
+//Clothes coming off at different sizes, and health/speed/stam changes as well
diff --git a/hyperstation/code/modules/resize/sizechems.dm b/hyperstation/code/modules/resize/sizechems.dm
new file mode 100644
index 0000000000..67fe8472f8
--- /dev/null
+++ b/hyperstation/code/modules/resize/sizechems.dm
@@ -0,0 +1,134 @@
+//Size Chemicals, now with better and less cringy names.
+//TO DO: USE BETTER FERMICHEM TO MAKE ALL OF THESE CHEMICALS MORE INTERACTIVE
+
+//Sizechem reagent
+/datum/reagent/sizechem
+ name = "Cell-Volume Altering Base"
+ description = "A stabilized compound liquid, used as a basis for increasing or decreasing the size of living matter with more recipes."
+ color = "#C900CC"
+ taste_description = "regret"
+ can_synth = FALSE
+ metabolization_rate = 0.25
+
+//Sizechem reaction
+/datum/chemical_reaction/sizechem
+ name = "Cell-Volume Altering Base"
+ id = /datum/reagent/sizechem
+ mix_message = "the reaction rapidly alters in size!"
+ required_reagents = list(/datum/reagent/growthserum = 0.15, /datum/reagent/medicine/clonexadone = 0.15, /datum/reagent/gold = 0.15, /datum/reagent/acetone = 0.15)
+ results = list(/datum/reagent/sizechem = 0.3)
+ required_temp = 1
+ //Fermichem vars
+ OptimalTempMin = 600 // Lower area of bell curve for determining heat based rate reactions
+ OptimalTempMax = 630 // Upper end for above
+ ExplodeTemp = 635 // Temperature at which reaction explodes
+ OptimalpHMin = 5 // Lowest value of pH determining pH a 1 value for pH based rate reactions (Plateu phase)
+ OptimalpHMax = 5.5 // Higest value for above
+ ReactpHLim = 2 // How far out pH wil react, giving impurity place (Exponential phase)
+ CatalystFact = 0 // How much the catalyst affects the reaction (0 = no catalyst)
+ CurveSharpT = 4 // How sharp the temperature exponential curve is (to the power of value)
+ CurveSharppH = 4 // How sharp the pH exponential curve is (to the power of value)
+ ThermicConstant = -10 // Temperature change per 1u produced
+ HIonRelease = 0.02 // pH change per 1u reaction (inverse for some reason)
+ RateUpLim = 1 // Optimal/max rate possible if all conditions are perfect
+ FermiChem = TRUE // If the chemical uses the Fermichem reaction mechanics
+ FermiExplode = FALSE // If the chemical explodes in a special way
+ PurityMin = 0.2
+
+//Growthchem reagent
+/datum/reagent/growthchem
+ name = "Prospacillin"
+ description = "A stabilized altercation of size-altering liquids, this one appears to increase cell volume."
+ color = "#E70C0C"
+ taste_description = "a sharp, fiery and intoxicating flavour"
+ overdose_threshold = 10
+ metabolization_rate = 0.25
+ can_synth = FALSE //DO NOT MAKE THIS SNYTHESIZABLE, THESE CHEMS ARE SUPPOSED TO NOT BE USED COMMONLY
+
+//Growthchem reaction
+/datum/chemical_reaction/growthchem
+ name = "Prospacillin"
+ id = /datum/reagent/growthchem
+ mix_message = "the reaction appears to grow!"
+ required_reagents = list(/datum/reagent/sizechem = 0.15, /datum/reagent/consumable/condensedcapsaicin = 0.15, /datum/reagent/drug/aphrodisiac = 0.30)
+ results = list(/datum/reagent/growthchem = 0.25)
+ required_temp = 1
+ OptimalTempMin = 700 // Lower area of bell curve for determining heat based rate reactions
+ OptimalTempMax = 730 // Upper end for above
+ ExplodeTemp = 735 // Temperature at which reaction explodes
+ OptimalpHMin = 3 // Lowest value of pH determining pH a 1 value for pH based rate reactions (Plateu phase)
+ OptimalpHMax = 3.5 // Higest value for above
+ ReactpHLim = 2 // How far out pH wil react, giving impurity place (Exponential phase)
+ CatalystFact = 0 // How much the catalyst affects the reaction (0 = no catalyst)
+ CurveSharpT = 4 // How sharp the temperature exponential curve is (to the power of value)
+ CurveSharppH = 4 // How sharp the pH exponential curve is (to the power of value)
+ ThermicConstant = -10 // Temperature change per 1u produced
+ HIonRelease = 0.02 // pH change per 1u reaction (inverse for some reason)
+ RateUpLim = 1 // Optimal/max rate possible if all conditions are perfect
+ FermiChem = TRUE // If the chemical uses the Fermichem reaction mechanics
+ FermiExplode = FALSE // If the chemical explodes in a special way
+ PurityMin = 0.2
+
+//Growthchem effects
+/datum/reagent/growthchem/on_mob_add(mob/living/carbon/M)
+ . = ..()
+ if(ishuman(M))
+ log_game("SIZECODE: [M] ckey: [M.key] has ingested growthchem.")
+
+/datum/reagent/growthchem/on_mob_life(mob/living/carbon/M)
+ if(!ishuman(M))
+ return..()
+ if(M.size_multiplier < RESIZE_MACRO)
+ M.resize(M.size_multiplier+0.025)
+ M.visible_message("[pick("[M] grows!", "[M] expands in size!", "[M] pushes outwards in stature!")]", "[pick("You feel your body fighting for space and growing!", "The world contracts inwards in every direction!", "You feel your muscles expand, and your surroundings shrink!")]")
+ ..()
+ . = 1
+
+//Shrinkchem reagent
+/datum/reagent/shrinkchem
+ name = "Diminicillin"
+ description = "A stabilized altercation of size-altering liquids, this one appears to decrease cell volume."
+ color = "#0C26E7"
+ taste_description = "a pungent, acidic and jittery flavour"
+ overdose_threshold = 10
+ metabolization_rate = 0.50
+ can_synth = FALSE //SAME STORY AS ABOVE
+
+//Shrinchem reaction
+/datum/chemical_reaction/shrinkchem
+ name = "Diminicillin"
+ id = /datum/reagent/shrinkchem
+ mix_message = "the reaction appears to shrink!"
+ required_reagents = list(/datum/reagent/sizechem = 0.15, /datum/reagent/consumable/frostoil = 0.15, /datum/reagent/drug/aphrodisiac = 0.30)
+ results = list(/datum/reagent/shrinkchem = 0.25)
+ required_temp = 1
+ OptimalTempMin = 100 // Lower area of bell curve for determining heat based rate reactions
+ OptimalTempMax = 150 // Upper end for above
+ ExplodeTemp = 350 // Temperature at which reaction explodes
+ OptimalpHMin = 3 // Lowest value of pH determining pH a 1 value for pH based rate reactions (Plateu phase)
+ OptimalpHMax = 4.5 // Higest value for above
+ ReactpHLim = 2 // How far out pH wil react, giving impurity place (Exponential phase)
+ CatalystFact = 0 // How much the catalyst affects the reaction (0 = no catalyst)
+ CurveSharpT = 4 // How sharp the temperature exponential curve is (to the power of value)
+ CurveSharppH = 4 // How sharp the pH exponential curve is (to the power of value)
+ ThermicConstant = -10 // Temperature change per 1u produced
+ HIonRelease = 0.02 // pH change per 1u reaction (inverse for some reason)
+ RateUpLim = 1 // Optimal/max rate possible if all conditions are perfect
+ FermiChem = TRUE // If the chemical uses the Fermichem reaction mechanics
+ FermiExplode = FALSE // If the chemical explodes in a special way
+ PurityMin = 0.2
+
+//Shrinkchem effects
+/datum/reagent/shrinkchem/on_mob_add(mob/living/carbon/M)
+ . = ..()
+ if(ishuman(M))
+ log_game("SIZECODE: [M] ckey: [M.key] has ingested shrinkchem.")
+
+/datum/reagent/shrinkchem/on_mob_life(mob/living/carbon/M)
+ if(!ishuman(M))
+ return..()
+ if(M.size_multiplier > RESIZE_MICRO)
+ M.resize(M.size_multiplier-0.025)
+ M.visible_message("[pick("[M] shrinks down!", "[M] dwindles in size!", "[M] compresses down!")]", "[pick("You feel your body compressing in size!", "The world pushes outwards in every direction!", "You feel your muscles contract, and your surroundings grow!")]")
+ ..()
+ . = 1
diff --git a/hyperstation/code/modules/resize/sizegun.dm b/hyperstation/code/modules/resize/sizegun.dm
new file mode 100644
index 0000000000..9ebbf74d0a
--- /dev/null
+++ b/hyperstation/code/modules/resize/sizegun.dm
@@ -0,0 +1,88 @@
+/obj/item/projectile/sizelaser
+ name = "sizeray laser"
+ icon_state = "omnilaser"
+ hitsound = null
+ damage = 5
+ damage_type = STAMINA
+ flag = "laser"
+ pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
+
+//TO DO: make the size ray have a selection of a minimum size and maximum size, rather than two modes.
+/obj/item/projectile/sizelaser/shrinkray
+ icon_state="bluelaser"
+
+/obj/item/projectile/sizelaser/growthray
+ icon_state="laser"
+
+/obj/item/projectile/sizelaser/shrinkray/on_hit(var/atom/target, var/blocked = 0)
+ if(istype(target, /mob/living/carbon/human))
+ var/mob/living/M = target
+ switch(M.size_multiplier)
+ if(RESIZE_MACRO to INFINITY)
+ M.resize(RESIZE_HUGE)
+ if(RESIZE_HUGE to RESIZE_MACRO)
+ M.resize(RESIZE_BIG)
+ if(RESIZE_BIG to RESIZE_HUGE)
+ M.resize(RESIZE_NORMAL)
+ if(RESIZE_NORMAL to RESIZE_BIG)
+ M.resize(RESIZE_SMALL)
+ if(RESIZE_SMALL to RESIZE_NORMAL)
+ M.resize(RESIZE_TINY)
+ if(RESIZE_TINY to RESIZE_SMALL)
+ M.resize(RESIZE_MICRO)
+ if((0 - INFINITY) to RESIZE_NORMAL)
+ M.resize(RESIZE_MICRO)
+ M.update_transform()
+ return TRUE
+
+/obj/item/projectile/sizelaser/growthray/on_hit(var/atom/target, var/blocked = 0 )
+ if(istype(target, /mob/living/carbon/human))
+ var/mob/living/M = target
+ switch(M.size_multiplier)
+ if(RESIZE_HUGE to RESIZE_MACRO)
+ M.resize(RESIZE_MACRO)
+ if(RESIZE_BIG to RESIZE_HUGE)
+ M.resize(RESIZE_HUGE)
+ if(RESIZE_NORMAL to RESIZE_BIG)
+ M.resize(RESIZE_BIG)
+ if(RESIZE_SMALL to RESIZE_NORMAL)
+ M.resize(RESIZE_NORMAL)
+ if(RESIZE_TINY to RESIZE_SMALL)
+ M.resize(RESIZE_SMALL)
+ if(RESIZE_MICRO to RESIZE_TINY)
+ M.resize(RESIZE_TINY)
+ if((0 - INFINITY) to RESIZE_MICRO)
+ M.resize(RESIZE_MICRO)
+ M.update_transform()
+ return TRUE
+
+/obj/item/ammo_casing/energy/laser/growthray
+ projectile_type = /obj/item/projectile/sizelaser/growthray
+ select_name = "Growth"
+
+/obj/item/ammo_casing/energy/laser/shrinkray
+ projectile_type = /obj/item/projectile/sizelaser/shrinkray
+ select_name = "Shrink"
+
+//Gun
+/obj/item/gun/energy/laser/sizeray
+ name = "size ray"
+ icon = 'icons/obj/guns/energy.dmi'
+ icon_state = "sizeshrink"
+ desc = "An extremely-dangerous, almost-illegal size-altering device. You've either stolen this, or have the permits for it."
+ item_state = null
+ ammo_type = list(/obj/item/ammo_casing/energy/laser/shrinkray, /obj/item/ammo_casing/energy/laser/growthray)
+ selfcharge = EGUN_SELFCHARGE
+ charge_delay = 5
+ ammo_x_offset = 2
+ clumsy_check = 1
+
+ attackby(obj/item/W, mob/user)
+ if(W==src)
+ if(icon_state=="sizeshrink")
+ icon_state="sizegrow"
+ ammo_type = list(/obj/item/ammo_casing/energy/laser/growthray)
+ else
+ icon_state="sizeshrink"
+ ammo_type = list(/obj/item/ammo_casing/energy/laser/shrinkray)
+ return ..()
diff --git a/hyperstation/code/modules/surgery/organs/augments_arms.dm b/hyperstation/code/modules/surgery/organs/augments_arms.dm
new file mode 100644
index 0000000000..8430fcac5e
--- /dev/null
+++ b/hyperstation/code/modules/surgery/organs/augments_arms.dm
@@ -0,0 +1,25 @@
+/obj/item/organ/cyberimp/arm/mblade
+ name = "arm-mounted scytheblade"
+ desc = "An extremely dangerous implant which can be used in a variety of ways. Mostly killing."
+ contents = newlist(/obj/item/melee/mblade)
+
+/obj/item/organ/cyberimp/arm/mblade/l
+ zone = BODY_ZONE_L_ARM
+
+/obj/item/melee/mblade
+ name = "mounted scytheblade"
+ desc = "An extremely dangerous implant which can be used in a variety of ways. Mostly killing."
+ icon = 'icons/obj/items_and_weapons.dmi'
+ icon_state = "mblade"
+ item_state = "mblade"
+ lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
+ force = 45
+ hitsound = 'sound/weapons/bladeslice.ogg'
+ throwforce = 45
+ block_chance = 70
+ armour_penetration = 80
+ flags_1 = CONDUCT_1
+ w_class = WEIGHT_CLASS_BULKY
+ sharpness = IS_SHARP_ACCURATE
+ attack_verb = list("slashed", "cut")
diff --git a/hyperstation/code/modules/traits.dm b/hyperstation/code/modules/traits.dm
new file mode 100644
index 0000000000..60d97af45a
--- /dev/null
+++ b/hyperstation/code/modules/traits.dm
@@ -0,0 +1,34 @@
+
+
+//Jay Sparrow
+/datum/quirk/cum_plus
+ name = "Extra productive genitals"
+ desc = "Your lower bits produce more and hold more than normal."
+ value = 0
+ category = CATEGORY_SEXUAL
+ mob_trait = TRAIT_CUM_PLUS
+ gain_text = "You feel pressure in your groin."
+ lose_text = "You feel a weight lifted from your groin."
+ medical_record_text = "Patient has greatly increased production of sexual fluids"
+
+/datum/quirk/cum_plus/add()
+ var/mob/living/carbon/M = quirk_holder
+ if(M?.getorganslot("testicles"))
+ var/obj/item/organ/genital/testicles/T = M?.getorganslot("testicles")
+ T?.fluid_mult = 1.5 //Base is 1
+ T?.fluid_max_volume = 5
+
+/datum/quirk/cum_plus/remove()
+ var/mob/living/carbon/M = quirk_holder
+ if(quirk_holder.getorganslot("testicles"))
+ var/obj/item/organ/genital/testicles/T = M?.getorganslot("testicles")
+ T?.fluid_mult = 1 //Base is 1
+ T?.fluid_max_volume = 3 //Base is 3
+
+/datum/quirk/cum_plus/on_process()
+ var/mob/living/carbon/M = quirk_holder //If you get balls later, then this will still proc
+ if(M.getorganslot("testicles"))
+ var/obj/item/organ/genital/testicles/T = M?.getorganslot("testicles")
+ if(T?.fluid_max_volume <= 5 || T?.fluid_mult <= 0.2) //INVALID EXPRESSION?
+ T?.fluid_mult = 1.5 //Base is 0.133
+ T?.fluid_max_volume = 5
diff --git a/hyperstation/code/modules/uplink/uplink_items.dm b/hyperstation/code/modules/uplink/uplink_items.dm
new file mode 100644
index 0000000000..83a12d0f9b
--- /dev/null
+++ b/hyperstation/code/modules/uplink/uplink_items.dm
@@ -0,0 +1,8 @@
+/datum/uplink_item/device_tools/tc_rod
+ name = "Telecrystal Fuel Rod"
+ desc = "This special fuel rod has eight material slots that can be inserted with telecrystals, \
+ once the rod has been fully depleted, you will be able to harvest the extra telecrystals. \
+ Please note: This Rod fissiles much faster than it's regular counterpart, it doesn't take \
+ much to overload the reactor with these..."
+ item = /obj/item/twohanded/required/fuel_rod/material/telecrystal
+ cost = 7
diff --git a/hyperstation/code/obj/ZaoCorp/hat.dm b/hyperstation/code/obj/ZaoCorp/hat.dm
new file mode 100644
index 0000000000..f84461070d
--- /dev/null
+++ b/hyperstation/code/obj/ZaoCorp/hat.dm
@@ -0,0 +1,50 @@
+/obj/item/clothing/head/zao
+ name = "zao police cap"
+ desc = "Zao Corps standard issue police force headwear, designed with the overcoat to help with the cold weather. The headwear also features a holographic projector, allowing itself to blend in with the user to create a better face to face interaction with civilians."
+ icon = 'hyperstation/icons/obj/clothing/head.dmi'
+ icon_state = "zaohat"
+ item_state = "helmet"
+ item_color = "zaohat"
+ alternate_worn_icon = 'hyperstation/icons/mobs/head.dmi'
+ armor = list("melee" = 40, "bullet" = 40, "laser" = 10,"energy" = 20, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 60)
+ cold_protection = HEAD
+ min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT
+ strip_delay = 60
+ resistance_flags = FLAMMABLE
+
+ var/cloaked = 0
+
+/obj/item/clothing/head/zao/dropped()
+ src.icon_state = "zaohat"
+ src.cloaked=0
+ ..()
+
+/obj/item/clothing/head/zao/verb/cloakcap()
+ set category = "Object"
+ set name = "Cloak hat"
+
+ cloak(usr)
+
+
+/obj/item/clothing/head/zao/AltClick(mob/user)
+ . = ..()
+ if(!user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
+ return
+ cloak(user)
+ return TRUE
+
+
+/obj/item/clothing/head/zao/proc/cloak(mob/user)
+ if(!user.incapacitated())
+ src.cloaked = !src.cloaked
+ if(src.cloaked)
+ icon_state = "zaohat_active"
+ to_chat(user, "You toggle the hat\'s cloaking.")
+ else
+ icon_state = "zaohat"
+ to_chat(user, "You reveal the hat again.")
+ usr.update_inv_head() //so our mob-overlays update
+
+/obj/item/clothing/head/zao/examine(mob/user)
+ . = ..()
+ . += "Alt-click the hat to toggle it\'s cloaking [cloaked ? "off" : "on"]."
\ No newline at end of file
diff --git a/hyperstation/code/obj/ZaoCorp/suit.dm b/hyperstation/code/obj/ZaoCorp/suit.dm
new file mode 100644
index 0000000000..ddc4cd273c
--- /dev/null
+++ b/hyperstation/code/obj/ZaoCorp/suit.dm
@@ -0,0 +1,32 @@
+/obj/item/clothing/suit/toggle/zao
+ name = "zao overcoat"
+ desc = "Zao Corps signature navy-blue overcoat with a golden highlight finish. Found among police forces as the suit was designed with riot control in mind, it also serves as modest winter coat insulated padding inside."
+ icon = 'hyperstation/icons/obj/clothing/suits.dmi'
+ icon_state = "zaocoat"
+ item_state = "zaocoat"
+ item_color = "zaocoat"
+ alternate_worn_icon = 'hyperstation/icons/mobs/suits.dmi'
+ togglename = "zipper"
+ body_parts_covered = CHEST|ARMS|LEGS
+ cold_protection = CHEST|GROIN|LEGS|ARMS
+ min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
+ strip_delay = 70
+ equip_delay_other = 40
+ max_integrity = 250
+ resistance_flags = FLAMMABLE
+ armor = list("melee" = 25, "bullet" = 25, "laser" = 5, "energy" = 15, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 10, "acid" = 30)
+ // armor = list("melee" = 50, "bullet" = 50, "laser" = 10, "energy" = 30, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 60)
+ allowed = list(/obj/item/ammo_box,
+ /obj/item/ammo_casing,
+ /obj/item/flashlight,
+ /obj/item/storage/fancy/cigarettes,
+ /obj/item/gun/ballistic,
+ /obj/item/gun/energy,
+ /obj/item/lighter,
+ /obj/item/melee/baton,
+ /obj/item/melee/classic_baton/telescopic,
+ /obj/item/reagent_containers/spray/pepper,
+ /obj/item/restraints/handcuffs,
+ /obj/item/tank/internals/emergency_oxygen,
+ /obj/item/tank/internals/plasmaman,
+ /obj/item/toy) // Will be modifying/removing/adding more at a later date.
\ No newline at end of file
diff --git a/hyperstation/code/obj/ZaoCorp/uniform.dm b/hyperstation/code/obj/ZaoCorp/uniform.dm
new file mode 100644
index 0000000000..4865c10524
--- /dev/null
+++ b/hyperstation/code/obj/ZaoCorp/uniform.dm
@@ -0,0 +1,8 @@
+/obj/item/clothing/under/rank/security/zao
+ name = "zao uniform"
+ desc = "The standard-issue police force uniform of Zao Corp. Made with a more formal approach to a dress shirt attire while retaining the protective fibers and identity with the blue arm bands. Waistcoat encouraged."
+ icon = 'hyperstation/icons/obj/clothing/uniforms.dmi'
+ icon_state = "zaounder"
+ item_state = "zaounder"
+ item_color = "zaounder"
+ alternate_worn_icon = 'hyperstation/icons/mobs/uniforms.dmi'
\ No newline at end of file
diff --git a/hyperstation/code/obj/ZaoCorp/visor.dm b/hyperstation/code/obj/ZaoCorp/visor.dm
new file mode 100644
index 0000000000..d3a5fed523
--- /dev/null
+++ b/hyperstation/code/obj/ZaoCorp/visor.dm
@@ -0,0 +1,45 @@
+/obj/item/clothing/glasses/hud/toggle/zao
+ name = "zao security visor"
+ desc = "A security visor from ZaoCorp designed to deploy and retract it's visor on a whim."
+ icon = 'hyperstation/icons/obj/clothing/glasses.dmi'
+ icon_state = "zaovisor"
+ item_state = "trayson-meson"
+ flash_protect = 1
+ tint = 1
+ flags_cover = GLASSESCOVERSEYES
+ visor_flags_cover = GLASSESCOVERSEYES
+ alternate_worn_icon = 'hyperstation/icons/mobs/eyes.dmi'
+ hud_type = DATA_HUD_SECURITY_ADVANCED
+ actions_types = list(/datum/action/item_action/switch_hud)
+ glass_colour_type = /datum/client_colour/glass_colour/lightyellow
+
+/obj/item/clothing/glasses/hud/toggle/zao/attack_self(mob/user)
+ if(!ishuman(user))
+ return
+ var/mob/living/carbon/human/wearer = user
+ if (wearer.glasses != src)
+ return
+
+ if (hud_type)
+ var/datum/atom_hud/H = GLOB.huds[hud_type]
+ H.remove_hud_from(user)
+
+ if (hud_type == DATA_HUD_SECURITY_ADVANCED)
+ icon_state = "zaovisor_off"
+ item_state = "zaovisor_off"
+ hud_type = null
+ flash_protect = 0
+ tint = 0
+ change_glass_color(user, /datum/client_colour/glass_colour/white)
+ else
+ hud_type = DATA_HUD_SECURITY_ADVANCED
+ icon_state = "zaovisor"
+ item_state = "zaovisor"
+ flash_protect = 1
+ tint = 1
+ change_glass_color(user, /datum/client_colour/glass_colour/lightyellow)
+
+ if (hud_type)
+ var/datum/atom_hud/H = GLOB.huds[hud_type]
+ H.add_hud_to(user)
+ user.update_inv_glasses()
diff --git a/hyperstation/code/obj/ashtray.dm b/hyperstation/code/obj/ashtray.dm
new file mode 100644
index 0000000000..5246b6c77d
--- /dev/null
+++ b/hyperstation/code/obj/ashtray.dm
@@ -0,0 +1,17 @@
+/obj/item/ashtray
+ name = "ashtray"
+ icon = 'hyperstation/icons/obj/objects.dmi'
+ icon_state = "ashtray"
+
+/obj/item/ashtray/attackby(obj/item/I, mob/user, params)
+ if (istype(I,/obj/item/clothing/mask/cigarette/))
+ var/obj/item/clothing/mask/cigarette/cig = I
+ if (cig.lit == 1)
+ src.visible_message("[user] crushes [cig] in \the [src], putting it out.")
+ else if (cig.lit == 0)
+ to_chat(user, "You place [cig] in [src].")
+ qdel(cig) //drop it in.
+
+ if (istype(I,/obj/item/cigbutt))
+ to_chat(user, "You place [I] in [src].")
+ qdel(I) //drop it in.
\ No newline at end of file
diff --git a/hyperstation/code/obj/bluespace sewing kit.dm b/hyperstation/code/obj/bluespace sewing kit.dm
new file mode 100644
index 0000000000..1d209191cb
--- /dev/null
+++ b/hyperstation/code/obj/bluespace sewing kit.dm
@@ -0,0 +1,36 @@
+//Jay Sparrow
+#define ROOMY = "ROOMY"
+//Sorry jay, I killed it.
+
+/obj/item/bluespace_thread
+ name = "Bluespace Sewing Kit"
+ desc = "Thread infused with bluespace dust to make your clothes a little more roomy. Current sewing technology already incorporates this, so thankfully, the days of needing these are long gone. Good souvenir though."
+ icon = 'hyperstation/icons/obj/bluespace_thread.dmi'
+ icon_state = "thread"
+ item_state = "thread"
+ var/uses = 5 //Give it a few charges so it can last through the shift
+ w_class = WEIGHT_CLASS_TINY
+
+/obj/item/bluespace_thread/attack_obj(obj/O, mob/living/user)
+ . = ..()
+ if(!istype(O, /obj/item/clothing))
+ user.show_message("You find yourself unable to stitch this.", 1)
+ return
+
+/obj/item/bluespace_thread/attack_self(mob/living/user)
+ user.show_message("The spool has [uses] uses remaining.", 1)
+
+//Let's add this to the loadout screen
+/datum/gear/bluespace_thread
+ name = "Bluespace Sewing Kit"
+ category =ITEM_SLOT_IN_BACKPACK
+ path = /obj/item/bluespace_thread
+
+//Crafting recipe
+/datum/crafting_recipe/bluespace_thread
+ name = "Bluespace Sewing Kit"
+ result = /obj/item/bluespace_thread
+ time = 40
+ reqs = list(/obj/item/stack/ore/bluespace_crystal = 1,
+ /obj/item/stack/sheet/cloth = 3)
+ category = CAT_MISC
diff --git a/hyperstation/code/obj/condom.dm b/hyperstation/code/obj/condom.dm
new file mode 100644
index 0000000000..de0471639f
--- /dev/null
+++ b/hyperstation/code/obj/condom.dm
@@ -0,0 +1,128 @@
+//Made by quotefox
+//Really needs some work, mainly because condoms should be a container for semen, but I dont know how that works yet. Feel free to improve upon.
+
+/obj/item/condom
+ name = "condom"
+ desc = "Dont be silly, cover your willy!"
+ icon = 'hyperstation/icons/obj/condom.dmi'
+ throwforce = 0
+ icon_state = "b_condom_wrapped"
+ var/unwrapped = 0
+ w_class = WEIGHT_CLASS_TINY
+ price = 1
+
+obj/item/condom/Initialize(mapload)
+ create_reagents(300, DRAWABLE|NO_REACT)
+ ..()
+
+obj/item/condom/update_icon()
+ switch(reagents.total_volume)
+ if(0 to 49)
+ icon_state = "b_condom_inflated"
+ if(50 to 100)
+ icon_state = "b_condom_inflated_med"
+ if(101 to 249)
+ icon_state = "b_condom_inflated_large"
+ if(250 to 300)
+ icon_state = "b_condom_inflated_huge"
+ ..()
+
+/obj/item/condom/attack_self(mob/user) //Unwrap The Condom in hands
+ if(!istype(user))
+ return
+ if(isliving(user))
+ if(unwrapped == 0)
+ icon_state = "b_condom"
+ unwrapped = 1
+ to_chat(user, "You unwrap the condom.")
+ playsound(user, 'sound/items/poster_ripped.ogg', 50, 1, -1)
+ return
+// if(unwrapped == 1)
+// new /obj/item/clothing/head/condom(usr.loc)
+// qdel(src)
+// to_chat(user, "You roll the condom out.")
+// playsound(user, 'sound/lewd/latex.ogg', 50, 1, -1)
+// return
+
+/obj/item/condom/attack(mob/living/carbon/C, mob/living/user) //apply the johnny on another person or yourself
+
+ if(unwrapped == 0 )
+ to_chat(user, "You must remove the condom from the package first!")
+ return
+ var/obj/item/organ/genital/penis/P = C.getorganslot("penis")
+ if(P&&P.is_exposed())
+ if(P.condom)
+ to_chat(user, "They already have a condom on!")
+ return
+ if(isliving(C)&&isliving(user)&&unwrapped == 1)
+ C.visible_message("[user] is trying to put a condom on [C]!",\
+ "[user] is trying to put a condom on you!")
+ if(!do_mob(user, C, 4 SECONDS)) //if Failed to put the condom on
+ return
+ var/mob/living/carbon/human/L = C
+ playsound(C, 'sound/lewd/latex.ogg', 50, 1, -1)
+ P.condom = 1 //apply condom
+ P.colourtint = "87ceeb"
+ if(L)
+ L.update_genitals() // apply the colour!
+ to_chat(C, "Your penis feels more safe!")
+ qdel(src)
+
+ return
+ to_chat(user, "You can't find anywhere to put the condom on.") //Trying to put it on something without/or with a hidden
+
+/obj/item/clothing/head/condom //this is ss13, it would be a sin to not include this..
+ name = "condom"
+ icon = 'hyperstation/icons/obj/condom.dmi'
+ desc = "Looks like someone had abit of some fun!"
+ alternate_worn_icon = 'hyperstation/icons/obj/clothing/head.dmi'
+ icon_state = "b_condom_out"
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 5, "rad" = 0, "fire" = 0, "acid" = 0)
+
+
+/mob/living/carbon/human/proc/removecondom()
+
+ var/obj/item/organ/genital/penis/P = src.getorganslot("penis")
+ if(P.condom)
+ new /obj/item/clothing/head/condom(usr.loc)
+ to_chat(src, "The condom slips off the end of your penis.")
+ P.condom = 0
+ P.colourtint = ""
+ src.update_genitals()
+
+/obj/item/condom/filled
+ name = "filled condom"
+ icon_state = "b_condom_inflated"
+ unwrapped = 2
+ w_class = WEIGHT_CLASS_TINY
+
+/obj/item/condom/filled/throw_impact(atom/hit_atom)
+ . = ..()
+ if(!.) //if we're not being caught
+ splat(hit_atom)
+
+/obj/item/condom/filled/proc/splat(atom/movable/hit_atom)
+ if(isliving(loc))
+ return
+ var/turf/T = get_turf(hit_atom)
+ new/obj/effect/decal/cleanable/semen(T)
+ playsound(T, "sound/misc/splort", 50, TRUE)
+ qdel(src)
+
+/mob/living/carbon/human/proc/condomclimax()
+ var/obj/item/organ/genital/penis/P = src.getorganslot("penis")
+ if(!istype(P))
+ return
+ if(!P.condom)
+ return
+
+ var/obj/item/condom/filled/C = new
+ P.linked_organ.reagents.trans_to(C, P.linked_organ.reagents.total_volume)
+ C.loc = loc
+ P.condom = FALSE
+ P.colourtint = ""
+ update_genitals()
+ C.update_icon()
+ to_chat(src, "The condom bubbles outwards and fills with your cum.")
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
+ setArousalLoss(0)
diff --git a/hyperstation/code/obj/decal.dm b/hyperstation/code/obj/decal.dm
new file mode 100644
index 0000000000..0d9f57820d
--- /dev/null
+++ b/hyperstation/code/obj/decal.dm
@@ -0,0 +1,233 @@
+/obj/decal/stencil
+ name = "stencil"
+ desc = "A letter, one of many used to make words."
+ icon = 'hyperstation/icons/decals/decal_new.dmi'
+ icon_state = "a"
+ anchored = TRUE
+ layer = WALL_OBJ_LAYER
+
+ left
+ pixel_x = -3 //fine-tune from this offset
+ pixel_y = 8
+ alpha = 200
+ a
+ name = "a"
+ icon_state = "a"
+ b
+ name = "b"
+ icon_state = "b"
+ c
+ name = "c"
+ icon_state = "c"
+ d
+ name = "d"
+ icon_state = "d"
+ e
+ name = "e"
+ icon_state = "e"
+ f
+ name = "f"
+ icon_state = "f"
+ g
+ name = "g"
+ icon_state = "g"
+ h
+ name = "h"
+ icon_state = "h"
+ i
+ name = "i"
+ icon_state = "i"
+ j
+ name = "j"
+ icon_state = "j"
+ k
+ name = "k"
+ icon_state = "k"
+ l
+ name = "l"
+ icon_state = "l"
+ m
+ name = "m"
+ icon_state = "m"
+ n
+ name = "n"
+ icon_state = "n"
+ o
+ name = "o"
+ icon_state = "o"
+ p
+ name = "p"
+ icon_state = "p"
+ q
+ name = "q"
+ icon_state = "q"
+ r
+ name = "r"
+ icon_state = "r"
+ s
+ name = "s"
+ icon_state = "s"
+ t
+ name = "t"
+ icon_state = "t"
+ u
+ name = "u"
+ icon_state = "u"
+ v
+ name = "v"
+ icon_state = "v"
+ w
+ name = "w"
+ icon_state = "w"
+ x
+ name = "x"
+ icon_state = "x"
+ y
+ name = "y"
+ icon_state = "y"
+ z
+ name = "z"
+ icon_state = "z"
+ one
+ name = "one"
+ icon_state = "1"
+ two
+ name = "two"
+ icon_state = "2"
+ three
+ name = "three"
+ icon_state = "3"
+ four
+ name = "four"
+ icon_state = "4"
+ five
+ name = "five"
+ icon_state = "5"
+ six
+ name = "six"
+ icon_state = "6"
+ seven
+ name = "seven"
+ icon_state = "7"
+ eight
+ name = "eight"
+ icon_state = "8"
+ nine
+ name = "nine"
+ icon_state = "9"
+ zero
+ name = "zero"
+ icon_state = "0"
+
+ right
+ pixel_x = 13 // fine-tune from this offset
+ pixel_y = 8
+ alpha = 200
+ a
+ name = "a"
+ icon_state = "a"
+ b
+ name = "b"
+ icon_state = "b"
+ c
+ name = "c"
+ icon_state = "c"
+ d
+ name = "d"
+ icon_state = "d"
+ e
+ name = "e"
+ icon_state = "e"
+ f
+ name = "f"
+ icon_state = "f"
+ g
+ name = "g"
+ icon_state = "g"
+ h
+ name = "h"
+ icon_state = "h"
+ i
+ name = "i"
+ icon_state = "i"
+ j
+ name = "j"
+ icon_state = "j"
+ k
+ name = "k"
+ icon_state = "k"
+ l
+ name = "l"
+ icon_state = "l"
+ m
+ name = "m"
+ icon_state = "m"
+ n
+ name = "n"
+ icon_state = "n"
+ o
+ name = "o"
+ icon_state = "o"
+ p
+ name = "p"
+ icon_state = "p"
+ q
+ name = "q"
+ icon_state = "q"
+ r
+ name = "r"
+ icon_state = "r"
+ s
+ name = "s"
+ icon_state = "s"
+ t
+ name = "t"
+ icon_state = "t"
+ u
+ name = "u"
+ icon_state = "u"
+ v
+ name = "v"
+ icon_state = "v"
+ w
+ name = "w"
+ icon_state = "w"
+ x
+ name = "x"
+ icon_state = "x"
+ y
+ name = "y"
+ icon_state = "y"
+ z
+ name = "z"
+ icon_state = "z"
+ one
+ name = "one"
+ icon_state = "1"
+ two
+ name = "two"
+ icon_state = "2"
+ three
+ name = "three"
+ icon_state = "3"
+ four
+ name = "four"
+ icon_state = "4"
+ five
+ name = "five"
+ icon_state = "5"
+ six
+ name = "six"
+ icon_state = "6"
+ seven
+ name = "seven"
+ icon_state = "7"
+ eight
+ name = "eight"
+ icon_state = "8"
+ nine
+ name = "nine"
+ icon_state = "9"
+ zero
+ name = "zero"
+ icon_state = "0"
diff --git a/hyperstation/code/obj/economy.dm b/hyperstation/code/obj/economy.dm
new file mode 100644
index 0000000000..511c99d24e
--- /dev/null
+++ b/hyperstation/code/obj/economy.dm
@@ -0,0 +1,169 @@
+//Hyper station economy. Because coding it yourself is easier than port sometimes.
+
+/obj/machinery/atm
+ name = "automated teller machine"
+ desc = "a wall mounted electronic banking outlet for accessing your bank account."
+ icon = 'hyperstation/icons/obj/economy.dmi'
+ icon_state = "atm"
+ max_integrity = 250
+ integrity_failure = 100
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 30)
+ use_power = IDLE_POWER_USE
+ idle_power_usage = 0
+ active_power_usage = 0
+ power_channel = ENVIRON
+ resistance_flags = FIRE_PROOF
+ var/obj/item/card/held_card
+ var/user = ""
+ light_power = 0
+ light_range = 7
+ light_color = "#ff3232"
+ var/pin = 0
+
+/obj/machinery/atm/ui_interact(mob/user)
+ . = ..()
+ var/dat = {""}
+ dat += ""
+ dat += "
ATM
"
+ dat += "Welcome to General Station 13's Automated Teller Service."
+ dat += "
"
+ if(!held_card)
+ dat += "
Welcome, please insert your ID to continue."
+ else
+ dat += "
Welcome user, [held_card ? user : "------"]
"
+ var/obj/item/card/id/idcard = held_card
+ if(idcard.registered_account)
+ dat += "
Account ID: ([idcard.registered_account.account_id])"
+ else
+ dat += "
Error, this account number does not exsist, please contact your local administration."
+
+ if(idcard.registered_account)
+ if(!idcard.registered_account.account_pin || pin == idcard.registered_account.account_pin)
+ dat += "
Balance: $[idcard.registered_account.account_balance]"
+ //dat += "
Offstation Balance: "
+ dat += "
"
+ dat += "Withdraw"
+ dat += "Change Pin"
+ //dat += "Account Settings"
+ dat += "Eject"
+ else
+ dat += "
Please enter your bank pin to continue!"
+ dat += "
"
+ dat += "[pin ? pin : "----"]
"
+
+ dat += "
"
+
+ dat += "
"
+
+
+ var/datum/browser/popup = new(user, "atm", "ATM")
+ popup.set_content(dat)
+ popup.set_title_image(user.browse_rsc_icon(icon, icon_state), 500,300)
+
+ popup.open()
+
+/obj/machinery/atm/attackby(obj/item/I, mob/living/user)
+ if(istype(I, /obj/item/card)) //input id!
+ if(!held_card)
+ var/obj/item/card/id/idcard = I
+ if(!user.transferItemToLoc(I, src)) //check if you can put it in
+ return
+ held_card = idcard
+ user = idcard.registered_name
+ pin = ""
+ playsound(src, 'sound/machines/button.ogg', 50, FALSE)
+ src.ui_interact(usr)
+
+ if(istype(I, /obj/item/stack/credits)) //feed money back into the machine! dont need a pin to donate stuff.
+ if(held_card)
+ var/obj/item/stack/credits/cred = I
+ var/obj/item/card/id/idcard = held_card
+ idcard.registered_account.account_balance = (idcard.registered_account.account_balance+cred.amount)
+ to_chat(usr, "You insert [cred] into the ATM.")
+ src.ui_interact(usr)
+ del(cred)
+
+/obj/machinery/atm/Topic(href, href_list)
+ . = ..()
+ if(..())
+ return
+
+ if(href_list["card"])
+ if(held_card)
+ if(usr.CanReach(src))
+ playsound(src, 'sound/machines/button.ogg', 50, FALSE)
+ if(usr.put_in_hands(held_card))
+ to_chat(usr, "You take the ID out of the slot.")
+ held_card = null
+ else
+ to_chat(usr, "The machine drops the ID onto the floor!")
+ held_card = null
+ pin = ""
+ user = ""
+
+ if(href_list["pin"])
+ playsound(src, get_sfx("terminal_type"), 25, 1)
+ var/pininput = input(user, "Input pin", "Pin Number") as num|null
+ if(pininput)
+ if(pininput > 9999 || pininput < 1000)
+ to_chat(usr, "[src.name] buzzes, you must input a 4 digit number between 1000 and 9999.")
+ return
+ pin = max(min( round(text2num(pininput)), 9999),1000) //4 numbers or less.
+ var/obj/item/card/id/idcard = held_card
+ if(pin == idcard.registered_account.account_pin)
+ to_chat(usr, "[src.name] beeps, accepting the pin.")
+ else
+ to_chat(usr, "[src.name] buzzes, denying the pin.")
+
+ if(href_list["changepin"])
+ playsound(src, get_sfx("terminal_type"), 25, 1)
+ var/pinchange = input(user, "Input pin", "Pin Number") as num|null
+ if(pinchange > 9999 || pinchange < 1000)
+ to_chat(usr, "[src.name], you must have a 4 digit number for a pin and be between 1000 and 9999.")
+ return
+ if(pinchange)
+ var/pinchange2 = input(user, "Confirm pin", "Confirm pin") as num|null //time to confirm!
+ if(pinchange == pinchange2)
+ var/obj/item/card/id/idcard = held_card
+ idcard.registered_account.account_pin = pinchange
+ to_chat(usr, "[src.name] beeps, your pin has been changed to [pinchange]!.")
+ else
+ to_chat(usr, "[src.name] buzzes, your pins did not match!")
+ pin = ""
+
+ if(href_list["withdraw"])
+ playsound(src, get_sfx("terminal_type"), 25, 1)
+ if(held_card)
+ var/obj/item/card/id/idcard = held_card
+ if(idcard.registered_account)
+ var/amount = input(user, "Choose amount", "Withdraw") as num|null
+ if(amount>0)
+ amount = max(min( round(text2num(amount)), idcard.registered_account.account_balance),0) //make sure they aint taking out more then what they have
+ to_chat(usr, "The machine prints out [amount] credits.")
+ idcard.registered_account.account_balance = (idcard.registered_account.account_balance-amount) //subtract the amount they took out.
+ var/obj/item/stack/credits/C = new /obj/item/stack/credits/(loc)
+ C.amount = amount
+ if(usr.put_in_hands(C))
+ to_chat(usr, "You take [C] out of the ATM.")
+
+ src.ui_interact(usr)
+
+
+//Money, Well, get back, I'm all right Jack, Keep your hands off of my stack.
+//making our own currency, just to stop exploits (for now)
+
+/obj/item/stack/credits
+ name = "credits"
+ singular_name = "credit"
+ desc = "Legal tender, a bundle of shiny metalic looking notes."
+ icon = 'hyperstation/icons/obj/economy.dmi'
+ icon_state = "cash"
+ amount = 1
+ max_amount = 99999999
+ throwforce = 0
+ throw_speed = 2
+ throw_range = 2
+ w_class = WEIGHT_CLASS_TINY
+ full_w_class = WEIGHT_CLASS_TINY
+ resistance_flags = FLAMMABLE
+ var/value = 1
diff --git a/hyperstation/code/obj/fleshlight.dm b/hyperstation/code/obj/fleshlight.dm
new file mode 100644
index 0000000000..413267713e
--- /dev/null
+++ b/hyperstation/code/obj/fleshlight.dm
@@ -0,0 +1,392 @@
+//Hyperstation 13 fleshlight
+//Humbley request this doesnt get ported to other code bases, we strive to make things unique on our server and we dont have alot of coders
+//but if you absolutely must. please give us some credit~ <3
+//made by quotefox
+
+/obj/item/fleshlight
+ name = "fleshlight"
+ desc = "A sex toy disguised as a flashlight, used to stimulate someones penis, complete with colour changing sleeve."
+ icon = 'hyperstation/icons/obj/fleshlight.dmi'
+ icon_state = "fleshlight_base"
+ item_state = "fleshlight"
+ w_class = WEIGHT_CLASS_SMALL
+ var/sleevecolor = "#ffcbd4" //pink
+ price = 8
+ var/mutable_appearance/sleeve
+ var/inuse = 0
+
+/obj/item/fleshlight/examine(mob/user)
+ . = ..()
+ . += "Alt-Click \the [src.name] to customize it."
+
+/obj/item/fleshlight/Initialize(mapload)
+ . = ..()
+ sleeve = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "vagina")
+ sleeve.color = sleevecolor
+ add_overlay(sleeve)
+
+/obj/item/fleshlight/AltClick(mob/user)
+ . = ..()
+ var/style = input(usr, "Choose style", "Customize Fleshlight", "vagina") in list("vagina", "anus")
+ var/new_color = input(user, "Choose color.", "Customize Fleshlight", sleevecolor) as color|null
+ if(new_color)
+ cut_overlays()
+ sleeve = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', style)
+ sleevecolor = new_color
+ sleeve.color = new_color
+ add_overlay(sleeve)
+ return TRUE
+
+/obj/item/fleshlight/attack(mob/living/carbon/C, mob/living/user)
+ var/obj/item/organ/genital/penis/P = C.getorganslot("penis")
+ if(inuse == 1) //just to stop stacking and causing people to cum instantly
+ return
+ if(P&&P.is_exposed())
+ inuse = 1
+ if(!(C == user)) //if we are targeting someone else.
+ C.visible_message("[user] is trying to use [src] on [C]'s penis.", "[user] is trying to use [src] on your penis.")
+
+ if(!do_mob(user, C, 3 SECONDS)) //3 second delay
+ inuse = 0
+ return
+
+ //checked if not used on yourself, if not, carry on.
+ playsound(src, 'sound/lewd/slaps.ogg', 30, 1, -1) //slapping sound
+ inuse = 0
+ if(!(C == user)) //lewd flavour text
+ C.visible_message("[user] pumps [src] on [C]'s penis.", "[user] pumps [src] up and down on your penis.")
+ else
+ user.visible_message("[user] pumps [src] on their penis.", "You pump the fleshlight on your penis.")
+
+ if(prob(30)) //30% chance to make them moan.
+ C.emote("moan")
+
+ C.do_jitter_animation()
+ C.adjust_arousal(20) //make the target more aroused.
+ if (C.getArousalLoss() >= 100 && ishuman(C) && C.has_dna())
+ C.mob_climax(forced_climax=TRUE) //make them cum if they are over the edge.
+
+ return
+
+ else
+ to_chat(user, "You don't see anywhere to use this on.")
+
+ inuse = 0
+ ..()
+
+
+//Hyperstation 13 portal fleshlight
+//kinky!
+
+/obj/item/portallight
+ name = "portal fleshlight"
+ desc = "A silver love(TM) fleshlight, used to stimulate someones penis, with bluespace tech that allows lovers to hump at a distance."
+ icon = 'hyperstation/icons/obj/fleshlight.dmi'
+ icon_state = "unpaired"
+ item_state = "fleshlight"
+ w_class = WEIGHT_CLASS_SMALL
+ var/partnercolor = "#ffcbd4"
+ var/partnerbase = "normal"
+ var/partnerorgan = "portal_vag"
+ price = 20
+ var/mutable_appearance/sleeve
+ var/mutable_appearance/organ
+ var/inuse = 0
+ var/paired = 0
+ var/obj/item/portalunderwear
+ var/useable = FALSE
+ var/option = ""
+
+/obj/item/portallight/examine(mob/user)
+ . = ..()
+ if(!portalunderwear)
+ . += "The device is unpaired, to pair, swipe against a pair of portal panties. "
+ else
+ . += "The device is paired, and awaiting input. "
+
+/obj/item/portallight/attack(mob/living/carbon/C, mob/living/user) //use portallight! nearly the same as the fleshlight apart from you have a buddy!
+ var/obj/item/organ/genital/penis/P = C.getorganslot("penis")
+
+ if(inuse) //just to stop stacking and causing people to cum instantly
+ return
+ if(!useable)
+ to_chat(user, "It seems the device has failed or your partner is not wearing their device.")
+
+ if(C == user)//if your using it on yourself, more options! otherwise, just fuck.
+ option = input(usr, "Choose action", "Portal Fleshlight", "Fuck") in list("Fuck", "Lick", "Touch")
+ else
+ option = "Fuck"
+
+ var/obj/item/organ/genital/G
+ if(istype(portalunderwear.loc, /obj/item/organ/genital)) //Sanity check. Without this it will runtime error.
+ G = portalunderwear.loc
+ if(!G)
+ return
+ var/mob/living/carbon/human/M = G.owner
+
+ if(option == "Fuck"&&!P.is_exposed()) //we are trying to fuck with no penis!
+ to_chat(user, "You don't see anywhere to use this on.")
+ return
+ else //other options dont need checks
+ inuse = 1
+ if(!(C == user)) //if we are targeting someone else.
+ C.visible_message("[user] is trying to use [src] on [C]'s penis.", "[user] is trying to use [src] on your penis.")
+
+ if(!do_mob(user, C, 3 SECONDS)) //3 second delay
+ inuse = 0
+ return
+
+ //checked if not used on yourself, if not, carry on.
+ if(option == "Fuck")
+ playsound(src, 'sound/lewd/slaps.ogg', 30, 1, -1) //slapping sound for fuck.
+
+ inuse = 0
+ if(!(C == user))
+ C.visible_message("[user] pumps [src] on [C]'s penis.", "[user] pumps [src] up and down on your penis.")
+ else
+ if(option == "Fuck")
+ user.visible_message("[user] pumps [src] on their penis.", "You pump the fleshlight on your penis.")
+ if(option == "Lick")
+ user.visible_message("[user] licks into [src].", "You lick into [src].")
+ if(option == "Touch")
+ user.visible_message("[user] touches softly against [src].", "You touch softly on [src].")
+
+
+ if(prob(30)) //30% chance to make your partner moan.
+ M.emote("moan")
+
+ if(option == "Fuck")// normal fuck
+ to_chat(M, "You feel a [P.length] inch, [P.shape] shaped penis pumping through the portal into your [G.name].")//message your partner, and kinky!
+ if(prob(30)) //30% chance to make them moan.
+ C.emote("moan")
+ if(prob(30)) //30% chance to make your partner moan.
+ M.emote("moan")
+ C.adjust_arousal(20)
+ M.adjust_arousal(20)
+ M.do_jitter_animation() //make your partner shake too!
+
+ if (M.getArousalLoss() >= 100 && ishuman(M) && prob(5))//Why not have a probability to cum when someone's getting nailed with max arousal?~
+ if(G.is_exposed()) //Oh yea, if vagina is not exposed, the climax will not cause a spill
+ M.mob_climax_outside(G, spillage = TRUE)
+ else
+ M.mob_climax_outside(G, spillage = FALSE)
+
+ if (C.getArousalLoss() >= 100 && ishuman(C) && C.has_dna())
+ var/mob/living/carbon/human/O = C
+
+ if( (P.condom == 1) || (P.sounding == 1)) //If coundomed and/or sounded, do not fire impreg chance
+ O.mob_climax_partner(P, M, FALSE, FALSE, FALSE, TRUE)
+ else //Else, fire impreg chance
+ if(G.name == "vagina") //no more spontaneous impregnations through the butt!
+ O.mob_climax_partner(P, M, FALSE, TRUE, FALSE, TRUE)
+ else
+ O.mob_climax_partner(P, M, FALSE, FALSE, FALSE, TRUE)
+
+ if(option == "Lick")
+ to_chat(M, "You feel a tongue lick you through the portal against your [G.name].")
+ M.adjust_arousal(10)
+ if(option == "Touch")
+ to_chat(M, "You feel someone touching your [G.name] through the portal.")
+ M.adjust_arousal(5)
+
+ return
+ ..()
+
+/obj/item/portallight/proc/updatesleeve()
+ //get their looks and vagina colour!
+ cut_overlays()//remove current overlays
+
+ var/obj/item/organ/genital/G
+ if(istype(portalunderwear.loc, /obj/item/organ/genital)) //Sanity check. Without this it will runtime.
+ G = portalunderwear.loc
+ if(!G)
+ useable = FALSE
+ return
+
+ var/mob/living/carbon/human/H = G.owner
+
+ if(H) //if the portal panties are on someone.
+ sleeve = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_sleeve_normal")
+ if(H.dna.species.name == "Lizardperson") // lizard nerd
+ sleeve = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_sleeve_lizard")
+
+ if(H.dna.species.name == "Slimeperson") // slime nerd
+ sleeve = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_sleeve_slime")
+
+ if(H.dna.species.name == "Avian") // bird nerd
+ sleeve = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_sleeve_avian")
+
+ sleeve.color = "#" + H.dna.features["mcolor"]
+ add_overlay(sleeve)
+
+ if(G.name == "vagina")
+ organ = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_vag")
+ organ.color = portalunderwear.loc.color
+ if(G.name == "anus")
+ organ = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_anus")
+ organ.color = "#" + H.dna.features["mcolor"]
+
+ useable = TRUE
+ add_overlay(organ)
+ else
+ useable = FALSE
+
+//Hyperstation 13 portal underwear
+//can be attached to vagina or anus, just like the vibrator, still requires pairing with the portallight
+
+/obj/item/portalpanties
+ name = "portal panties"
+ desc = "A silver love(TM) pair of portal underwear, with bluespace tech allows lovers to hump at a distance. Needs to be paired with a portal fleshlight before use."
+ icon = 'hyperstation/icons/obj/fleshlight.dmi'
+ icon_state = "portalpanties"
+ item_state = "fleshlight"
+ w_class = WEIGHT_CLASS_SMALL
+ var/obj/item/portallight
+ var/attached = FALSE
+ var/shapetype = "vagina"
+
+/obj/item/portalpanties/examine(mob/user)
+ . = ..()
+ if(!portallight)
+ . += "The device is unpaired, to pair, swipe the fleshlight against this pair of portal panties(TM). "
+ else
+ . += "The device is paired, and awaiting attachment. "
+
+
+/obj/item/portalpanties/attackby(obj/item/I, mob/living/user) //pairing
+ if(istype(I, /obj/item/portallight))
+ var/obj/item/portallight/P = I
+ if(!P.portalunderwear) //make sure it aint linked to someone else
+ portallight = P //pair the fleshlight
+ P.portalunderwear = src //pair the panties on the fleshlight.
+ P.icon_state = "paired" //we are paired!
+ playsound(src, 'sound/machines/ping.ogg', 50, FALSE)
+ to_chat(user, "[P] has been linked up successfully.")
+ else
+ to_chat(user, "[P] has already been linked to another pair of underwear.")
+ else
+ ..() //just allows people to hit it with other objects, if they so wished.
+
+/obj/item/portalpanties/attack(mob/living/carbon/C, mob/living/user)
+
+ if(!portallight) //we arent paired yet! noobie trap, let them know.
+ to_chat(user, "[src] can only be attached once paired with a portal fleshlight.")
+ return
+
+ var/obj/item/organ/genital/picked_organ
+ var/mob/living/carbon/human/S = user
+ var/mob/living/carbon/human/T = C
+ picked_organ = S.target_genitals(T)
+ if(picked_organ)
+ C.visible_message("[user] is trying to attach [src] to [T]!",\
+ "[user] is trying to put [src] on you!")
+ if(!do_mob(user, C, 3 SECONDS))//warn them and have a delay of 5 seconds to apply.
+ return
+
+ if((picked_organ.name == "vagina")||(picked_organ.name == "anus")) //only fits on a vagina or anus
+
+ src.shapetype = picked_organ.name
+ if(!picked_organ.equipment)
+ to_chat(user, "You wrap [src] around [T]'s [picked_organ.name].")
+ else
+ to_chat(user, "They already have [picked_organ.equipment.name] there.")
+ return
+
+ if(!user.transferItemToLoc(src, picked_organ)) //check if you can put it in
+ return
+ src.attached = TRUE
+ picked_organ.equipment = src
+
+ var/obj/item/portallight/P = portallight
+ //now we need to send what they look like, but saddly if the person changes colour for what ever reason, it wont update. but dont tell people shh.
+ if(P) //just to make sure
+ P.updatesleeve()
+ else
+ to_chat(user, "[src] can only be attached to a vagina or anus.")
+ return
+ else
+ to_chat(user, "You don't see anywhere to attach this.")
+
+/obj/item/portalpanties/proc/remove() //if taken off update it.
+ if(portallight)
+ var/obj/item/portallight/P = portallight
+ P.updatesleeve()
+
+/obj/item/storage/box/portallight
+ name = "Portal Fleshlight and Underwear"
+ icon = 'hyperstation/icons/obj/fleshlight.dmi'
+ desc = "A small silver box with Silver Love Co embossed."
+ icon_state = "box"
+ price = 15
+
+// portal fleshlight box
+/obj/item/storage/box/portallight/PopulateContents()
+ new /obj/item/portallight/(src)
+ new /obj/item/portalpanties/(src)
+ new /obj/item/paper/fluff/portallight(src)
+
+/obj/item/paper/fluff/portallight
+ name = "Portal Fleshlight Instructions"
+ info = "Thank you for purchasing the Silver Love Portal Fleshlight!
To use, simply register your new portal fleshlight with the provided underwear to link them together. The ask your lover to wear the underwear.
Have fun lovers,
Wilhelmina Steiner."
+
+//Happy Halloween!
+//Can be crafted with a knife and a pumpkin in the crafting menu (misc category).
+//I have no regrets.
+
+// /obj/item/twohanded/required/cumpkin
+
+// name = "Cumpkin"
+// desc = "A carved pumpkin with a suspicious inviting hole behind it, maybe you could 'use' it for a while..."
+// icon = 'hyperstation/icons/obj/fleshlight.dmi'
+// icon_state = "cumpkin"
+// item_state = "cumpkin"
+// lefthand_file = 'hyperstation/icons/mob/item_lefthand.dmi'
+// righthand_file = 'hyperstation/icons/mob/item_righthand.dmi'
+// w_class = WEIGHT_CLASS_SMALL
+// price = 10
+// var/inuse = 0
+
+// /obj/item/twohanded/required/cumpkin/attack(mob/living/carbon/C, mob/living/user)
+// var/obj/item/organ/genital/penis/P = C.getorganslot("penis")
+// if(inuse == 1) //just to stop stacking and causing people to cum instantly
+// return
+// if(P&&P.is_exposed())
+// inuse = 1
+// if(!(C == user)) //if we are targeting someone else.
+// C.visible_message("[user] is trying to use [src] on [C]'s penis.", "[user] is trying to use [src] on your penis.")
+
+// if(!do_mob(user, C, 3 SECONDS)) //3 second delay
+// inuse = 0
+// return
+
+// //checked if not used on yourself, if not, carry on.
+// playsound(src, 'sound/lewd/slaps.ogg', 30, 1, -1) //slapping sound
+// inuse = 0
+// if(!(C == user)) //lewd flavour text
+// C.visible_message("[user] pumps [src] on [C]'s penis.", "[user] pumps [src] up and down on your penis.")
+// else
+// user.visible_message("[user] pumps [src] on their penis.", "You pump the cumpkin on your penis.")
+
+// if(prob(30)) //30% chance to make them moan.
+// C.emote("moan")
+
+// C.do_jitter_animation()
+// C.adjust_arousal(20) //make the target more aroused.
+// if (C.getArousalLoss() >= 100 && ishuman(C) && C.has_dna())
+// C.mob_climax(forced_climax=TRUE) //make them cum if they are over the edge.
+
+// return
+
+// else
+// to_chat(user, "You don't see anywhere to use this on.")
+
+// inuse = 0
+// ..()
+
+// /datum/crafting_recipe/cumpkin
+// name = "Cumpkin"
+// time = 30
+// reqs = list(/obj/item/reagent_containers/food/snacks/grown/pumpkin = 1)
+// tools = list(/obj/item/kitchen/knife)
+// result = /obj/item/twohanded/required/cumpkin
+// category = CAT_MISC
diff --git a/hyperstation/code/obj/fluff.dm b/hyperstation/code/obj/fluff.dm
new file mode 100644
index 0000000000..43d744590c
--- /dev/null
+++ b/hyperstation/code/obj/fluff.dm
@@ -0,0 +1,17 @@
+/obj/structure/medscreen/
+ name = "medical screen"
+ desc = "No peaking now!"
+ icon_state = "screen"
+ icon = 'hyperstation/icons/obj/medical.dmi'
+ anchored = TRUE
+ resistance_flags = FLAMMABLE
+ max_integrity = 100
+ integrity_failure = 30
+ var/buildstacktype = /obj/item/stack/sheet/metal
+ var/buildstackamount = 2
+ density = TRUE
+
+/obj/structure/medscreen/Moved()
+ . = ..()
+ if(has_gravity())
+ playsound(src, 'sound/effects/roll.ogg', 100, 1)
diff --git a/hyperstation/code/obj/kinkyclothes.dm b/hyperstation/code/obj/kinkyclothes.dm
new file mode 100644
index 0000000000..b9ccda1bc4
--- /dev/null
+++ b/hyperstation/code/obj/kinkyclothes.dm
@@ -0,0 +1,131 @@
+/obj/item/clothing/gloves/latexsleeves
+ name = "latex sleeves"
+ desc = "A pair of shiny latex sleeves that covers ones arms."
+ icon_state = "latex"
+ item_state = "latex"
+ icon = 'hyperstation/icons/obj/clothing/gloves.dmi'
+ alternate_worn_icon = 'hyperstation/icons/mobs/gloves.dmi'
+ mutantrace_variation = NONE
+ price = 5
+
+/obj/item/clothing/gloves/latexsleeves/security
+ name = "security sleeves"
+ desc = "A pair of latex sleeves, with a band of red above the elbows denoting that the wearer is part of the security team."
+ icon_state = "latexsec"
+ item_state = "latexsec"
+ price = 5
+
+/obj/item/clothing/head/dominatrixcap
+ name = "dominatrix cap"
+ desc = "A sign of authority, over the body."
+ icon_state = "dominatrix"
+ item_state = "dominatrix"
+ icon = 'hyperstation/icons/obj/clothing/head.dmi'
+ alternate_worn_icon = 'hyperstation/icons/mobs/head.dmi'
+ mutantrace_variation = NONE
+
+/obj/item/clothing/shoes/highheels
+ name = "high heels"
+ desc = "They make the wearer appear taller, and more noisey!"
+ icon_state = "highheels"
+ item_state = "highheels"
+ icon = 'hyperstation/icons/obj/clothing/shoes.dmi'
+ alternate_worn_icon = 'hyperstation/icons/mobs/feet.dmi'
+
+
+/obj/item/clothing/shoes/highheels/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/squeak, list('sound/effects/footstep/highheel1.ogg' = 1,'sound/effects/footstep/highheel2.ogg' = 1), 20)
+//the classic click clack
+
+obj/item/clothing/neck/stole
+ name = "white boa"
+ desc = "Fluffy neck wear to keep you warm, and attract others."
+ icon = 'hyperstation/icons/obj/clothing/neck.dmi'
+ w_class = WEIGHT_CLASS_SMALL
+ icon_state = "stole"
+ item_state = "" //no inhands
+ price = 3
+
+obj/item/clothing/neck/stole/black
+ name = "black boa"
+ desc = "Fluffy neck wear to keep you warm, and attract others."
+ icon = 'hyperstation/icons/obj/clothing/neck.dmi'
+ w_class = WEIGHT_CLASS_SMALL
+ icon_state = "stole"
+ item_state = "" //no inhands
+ color = "#3d3d3d"
+ price = 3
+
+/obj/item/clothing/suit/fluffyhalfcrop
+ name = "fluffy half-crop jacket"
+ desc = "A fluffy synthetic fur half-cropped jacket, less about warmth, more about style!"
+ icon_state = "fluffy"
+ item_state = "fluffy"
+ icon = 'hyperstation/icons/obj/clothing/suits.dmi'
+ alternate_worn_icon = 'hyperstation/icons/mobs/suits.dmi'
+ body_parts_covered = CHEST|LEGS|ARMS
+ min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
+ mutantrace_variation = NONE
+
+/obj/item/clothing/under/latexfull
+ name = "full latex jumpsuit"
+ desc = "A tight fitting jumpsuit made of latex."
+ icon = 'hyperstation/icons/obj/clothing/suits.dmi'
+ icon_state = "latex"
+ alternate_worn_icon = 'hyperstation/icons/mobs/suits.dmi'
+ item_state = "r_suit"
+ can_adjust = FALSE
+
+/obj/item/clothing/under/latexhalf
+ name = "latex bodysuit"
+ desc = "A tight fitting outfit made of latex, that covers the wearers torso."
+ icon = 'hyperstation/icons/obj/clothing/suits.dmi'
+ icon_state = "latexhalf"
+ alternate_worn_icon = 'hyperstation/icons/mobs/suits.dmi'
+ item_state = "r_suit"
+ can_adjust = FALSE
+ mutantrace_variation = NONE
+ do_not_cover_butt = TRUE
+
+/obj/item/clothing/under/sexynursesuit
+ name = "Sexy nurse outfit"
+ desc = "A very revealing nurse's outfit. Not very sanitary. Does it even count as clothing?"
+ icon = 'hyperstation/icons/obj/clothing/suits.dmi'
+ icon_state = "sexynursesuit"
+ alternate_worn_icon = 'hyperstation/icons/mobs/suits.dmi'
+ item_state = "r_suit"
+ can_adjust = FALSE
+ mutantrace_variation = NONE
+
+/obj/item/clothing/under/centcomdress
+ name = "Centcom Dress Uniform"
+ desc = "A stylish yet revealing dress uniform worn in extravagent black and gold, worthy of those who sit around and watch cameras all day in an office."
+ icon = 'hyperstation/icons/obj/clothing/suits.dmi'
+ icon_state = "ccdress"
+ alternate_worn_icon = 'hyperstation/icons/mobs/suits.dmi'
+ item_state = "r_suit"
+ can_adjust = FALSE
+ //We will never know why CC can make their skimpy outfits tough as nails
+ body_parts_covered = CHEST|GROIN|ARMS
+ min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
+ cold_protection = CHEST|GROIN|ARMS
+ max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
+ heat_protection = CHEST|GROIN|ARMS
+ armor = list("melee" = 60, "bullet" = 80, "laser" = 80, "energy" = 90, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 50)
+
+/obj/item/clothing/under/centcomdressvk
+ name = "Virginkiller Centcom Dress Uniform"
+ desc = "This black and gold beauty does not help paperwork get done, it seems."
+ icon = 'hyperstation/icons/obj/clothing/suits.dmi'
+ icon_state = "ccdressvk"
+ alternate_worn_icon = 'hyperstation/icons/mobs/suits.dmi'
+ item_state = "r_suit"
+ can_adjust = FALSE
+ //We will never know why CC can make their skimpy outfits tough as nails
+ body_parts_covered = CHEST|GROIN|ARMS
+ min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
+ cold_protection = CHEST|GROIN|ARMS
+ max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
+ heat_protection = CHEST|GROIN|ARMS
+ armor = list("melee" = 60, "bullet" = 80, "laser" = 80, "energy" = 90, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 50)
diff --git a/hyperstation/code/obj/leash.dm b/hyperstation/code/obj/leash.dm
new file mode 100644
index 0000000000..ac9f174322
--- /dev/null
+++ b/hyperstation/code/obj/leash.dm
@@ -0,0 +1,426 @@
+//Jay Sparrow
+//TODO
+/*
+Icons, maybe?
+*/
+
+#define STATUS_EFFECT_LEASH_PET /datum/status_effect/leash_pet
+#define STATUS_EFFECT_LEASH_DOM /datum/status_effect/leash_dom
+#define STATUS_EFFECT_LEASH_FREEPET /datum/status_effect/leash_freepet
+#define MOVESPEED_ID_LEASH "LEASH"
+
+/////STATUS EFFECTS/////
+//These are mostly used as flags for the states each member can be in
+
+/datum/status_effect/leash_dom
+ status_type = STATUS_EFFECT_UNIQUE
+ alert_type = /obj/screen/alert/status_effect/leash_dom
+
+/obj/screen/alert/status_effect/leash_dom
+ name = "Leash Master"
+ desc = "You've got a leash, and a cute pet on the other end."
+ icon_state = "leash_master" //These call icons that don't exist, so no icon comes up. Which is good.
+ //As a result, the descriptions also don't proc, which is fine.
+
+/datum/status_effect/leash_freepet
+ status_type = STATUS_EFFECT_UNIQUE
+ alert_type = /obj/screen/alert/status_effect/leash_freepet
+
+/obj/screen/alert/status_effect/leash_freepet
+ name = "Escaped Pet"
+ desc = "You're on a leash, but you've no master. If anyone grabs the leash they'll gain control!"
+ icon_state = "leash_freepet"
+
+
+/datum/status_effect/leash_pet
+ id = "leashed"
+ status_type = STATUS_EFFECT_UNIQUE
+ var/mob/redirect_component
+ alert_type = /obj/screen/alert/status_effect/leash_pet
+
+/obj/screen/alert/status_effect/leash_pet
+ name = "Leashed Pet"
+ desc = "You're on the hook now! Be good for your master."
+ icon_state = "leash_pet"
+
+
+/datum/status_effect/leash_pet/on_apply()
+ //redirect_component = WEAKREF(owner.AddComponent(/datum/component/redirect, list(COMSIG_LIVING_RESIST = CALLBACK(src,PROC_REF(owner_resist)))))
+ RegisterSignal(owner, COMSIG_LIVING_RESIST,PROC_REF(owner_resist))
+ redirect_component = owner
+ if(!owner.stat)
+ to_chat(owner, "You have been leashed!")
+ return ..()
+
+//This lets the pet resist their leash
+/datum/status_effect/leash_pet/proc/owner_resist()
+ to_chat(owner, "You reach for the hook on your collar...")
+ //Determine how long it takes to remove the leash
+ var/deleash = 15
+ //if(owner.get_item_by_slot(SLOT_HANDCUFFED)) //Commented out because there is no clear way to make this proc BEFORE decuff on resist.
+ //deleash = 100
+ if(do_mob(owner, owner, deleash))//do_mob creates a progress bar and then enacts the code after. Owner, owner, because it's an act on themself
+ if(!QDELETED(src))
+ to_chat(owner, "[owner] has removed their leash!")
+ owner.remove_status_effect(/datum/status_effect/leash_pet)
+
+///// OBJECT /////
+//The leash object itself
+//The component variables are used for hooks, used later.
+
+/obj/item/leash
+ name = "leash"
+ desc = "A simple tether that can easily be hooked onto a collar. Perfect for your pet."
+ icon = 'hyperstation/icons/obj/leash.dmi'
+ icon_state = "leash"
+ item_state = "leash"
+ throw_range = 4
+ slot_flags = ITEM_SLOT_BELT
+ force = 1
+ throwforce = 1
+ w_class = WEIGHT_CLASS_SMALL
+ var/leash_used = 0 //A flag to see if the leash has been used yet, because for some reason picking up an unused leash is weird
+ var/mob/living/leash_pet = "null" //Variable to store our pet later
+ var/mob/living/leash_master = "null" //And our master too
+ var/mob/mobhook_leash_pet
+ var/mob/mobhook_leash_master //Needed to watch for these entities to move
+ var/mob/mobhook_leash_freepet
+ var/leash_location[3] //Three digit list for us to store coordinates later
+
+//Called when someone is clicked with the leash
+/obj/item/leash/attack(mob/living/carbon/C, mob/living/user) //C is the target, user is the one with the leash
+ if(C.has_status_effect(/datum/status_effect/leash_pet)) //If the pet is already leashed, do not leash them. For the love of god.
+ to_chat(user, "[C] has already been leashed.")
+ return
+ if(istype(C.get_item_by_slot(SLOT_NECK), /obj/item/clothing/neck/petcollar) || istype(C.get_item_by_slot(SLOT_NECK), /obj/item/electropack/shockcollar))
+ var/leashtime = 50
+ if(C.handcuffed)
+ leashtime = 5
+ if(do_mob(user, C, leashtime)) //do_mob adds a progress bar, but then we also check to see if they have a collar
+ log_combat(user, C, "leashed", addition="playfully")
+ //TODO: Figure out how to make an easy breakout for leashed leash_pets
+ C.apply_status_effect(/datum/status_effect/leash_pet)//Has now been leashed
+ user.apply_status_effect(/datum/status_effect/leash_dom) //Is the leasher
+ leash_pet = C //Save pet reference for later
+ leash_master = user //Save dom reference for later
+ //mobhook_leash_pet = leash_pet.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src,PROC_REF(on_pet_move))))
+ RegisterSignal(leash_pet, COMSIG_MOVABLE_MOVED,PROC_REF(on_pet_move))
+ mobhook_leash_pet = leash_pet
+ //mobhook_leash_master = leash_master.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src,PROC_REF(on_master_move))))
+ RegisterSignal(leash_master, COMSIG_MOVABLE_MOVED,PROC_REF(on_master_move))
+ mobhook_leash_master = leash_master
+ leash_used = 1
+ if(!leash_pet.has_status_effect(/datum/status_effect/leash_dom)) //Add slowdown if the pet didn't leash themselves
+ leash_pet.add_movespeed_modifier(MOVESPEED_ID_LEASH, multiplicative_slowdown = 5)
+ for(var/mob/viewing in viewers(user, null))
+ if(viewing == leash_master)
+ to_chat(leash_master, "You have hooked a leash onto [leash_pet]!")
+ else
+ viewing.show_message("[leash_pet] has been leashed by [leash_master]!", 1)
+ if(leash_pet.has_status_effect(/datum/status_effect/leash_dom)) //Pet leashed themself. They are not the dom
+ leash_pet.apply_status_effect(/datum/status_effect/leash_freepet)
+ leash_pet.remove_status_effect(/datum/status_effect/leash_dom)
+ while(1) //While true loop. The mark of a genius coder. ##MAINLOOP START
+ sleep(2) //Check every other tick
+ if(leash_pet == "null") //No pet, break loop
+ return
+ if(!(leash_pet.get_item_by_slot(SLOT_NECK))) //The pet has slipped their collar and is not the pet anymore.
+ for(var/mob/viewing in viewers(user, null))
+ viewing.show_message("[leash_pet] has slipped out of their collar!!", 1)
+ to_chat(leash_pet, "You have slipped out of your collar!")
+ to_chat(loc, "[leash_pet] has slipped out of their collar!")
+ leash_pet.remove_status_effect(/datum/status_effect/leash_pet)
+
+ if(!leash_pet.has_status_effect(/datum/status_effect/leash_pet)) //If there is no pet, there is no dom. Loop breaks.
+ //QDEL_NULL(mobhook_leash_master)
+ UnregisterSignal(mobhook_leash_master, COMSIG_MOVABLE_MOVED)
+ //QDEL_NULL(mobhook_leash_pet)
+ UnregisterSignal(mobhook_leash_pet, COMSIG_MOVABLE_MOVED)
+ //QDEL_NULL(mobhook_leash_freepet)
+ UnregisterSignal(mobhook_leash_freepet, COMSIG_MOVABLE_MOVED)
+ if(leash_pet.has_status_effect(/datum/status_effect/leash_freepet))
+ leash_pet.remove_status_effect(/datum/status_effect/leash_freepet)
+ if(leash_pet.has_movespeed_modifier(MOVESPEED_ID_LEASH))
+ leash_pet.remove_movespeed_modifier(MOVESPEED_ID_LEASH)
+ if(!leash_master == "null")
+ leash_master.remove_status_effect(/datum/status_effect/leash_dom)
+ leash_used = 0 //reset the leash to neutral
+ leash_pet = "null"
+ return
+
+ else //No collar, no fun
+ var/leash_message = pick("Your pet needs a collar")
+ to_chat(user, "[leash_message]")
+
+//Called when the leash is used in hand
+//Tugs the pet closer
+/obj/item/leash/attack_self(mob/living/user)
+ if(!leash_pet == "null") //No pet, no tug.
+ return
+ //Yank the pet. Yank em in close.
+ if(leash_pet.x > leash_master.x + 1)
+ step(leash_pet, WEST, 1) //"1" is the speed of movement. We want the tug to be faster than their slow current walk speed.
+ if(leash_pet.y > leash_master.y)//Check the other axis, and tug them into alignment so they are behind the master
+ step(leash_pet, SOUTH, 1)
+ if(leash_pet.y < leash_master.y)
+ step(leash_pet, NORTH, 1)
+ if(leash_pet.x < leash_master.x - 1)
+ step(leash_pet, EAST, 1)
+ if(leash_pet.y > leash_master.y)//Check the other axis, and tug them into alignment so they are behind the master
+ step(leash_pet, SOUTH, 1)
+ if(leash_pet.y < leash_master.y)
+ step(leash_pet, NORTH, 1)
+ if(leash_pet.y > leash_master.y + 1)
+ step(leash_pet, SOUTH, 1)
+ if(leash_pet.x > leash_master.x)//Check the other axis, and tug them into alignment so they are behind the master
+ step(leash_pet, WEST, 1)
+ if(leash_pet.x < leash_master.x)
+ step(leash_pet, EAST, 1)
+ if(leash_pet.y < leash_master.y - 1)
+ step(leash_pet, NORTH, 1)
+ if(leash_pet.x > leash_master.x)//Check the other axis, and tug them into alignment so they are behind the master
+ step(leash_pet, WEST, 1)
+ if(leash_pet.x < leash_master.x)
+ step(leash_pet, EAST, 1)
+
+/obj/item/leash/proc/on_master_move()
+ //Make sure the dom still has a pet
+ if(leash_master == "null") //There must be a master
+ return
+ if(leash_pet == "null") //There must be a pet
+ return
+ if(leash_pet == leash_master) //Pet is the master
+ return
+ if(!leash_pet.has_status_effect(/datum/status_effect/leash_pet))
+ //QDEL_NULL(mobhook_leash_master) //Probably redundant, but it's nice to be safe
+ UnregisterSignal(mobhook_leash_master, COMSIG_MOVABLE_MOVED)
+ leash_master.remove_status_effect(/datum/status_effect/leash_dom)
+ return
+
+ //If the master moves, pull the pet in behind
+ sleep(2) //A small sleep so the pet kind of bounces back after they make the step
+ //Also, the sleep means that the distance check for master happens before the pet, to prevent both from proccing.
+
+ if(leash_master == "null") //Just to stop error messages
+ return
+ if(leash_pet == "null")
+ return
+ if(leash_pet.x > leash_master.x + 2)
+ step(leash_pet, WEST, 1) //"1" is the speed of movement. We want the tug to be faster than their slow current walk speed.
+ if(leash_pet.y > leash_master.y)//Check the other axis, and tug them into alignment so they are behind the master
+ step(leash_pet, SOUTH, 1)
+ if(leash_pet.y < leash_master.y)
+ step(leash_pet, NORTH, 1)
+ if(leash_pet.x < leash_master.x - 2)
+ step(leash_pet, EAST, 1)
+ if(leash_pet.y > leash_master.y)
+ step(leash_pet, SOUTH, 1)
+ if(leash_pet.y < leash_master.y)
+ step(leash_pet, NORTH, 1)
+ if(leash_pet.y > leash_master.y + 2)
+ step(leash_pet, SOUTH, 1)
+ if(leash_pet.x > leash_master.x)
+ step(leash_pet, WEST, 1)
+ if(leash_pet.x < leash_master.x)
+ step(leash_pet, EAST, 1)
+ if(leash_pet.y < leash_master.y - 2)
+ step(leash_pet, NORTH, 1)
+ if(leash_pet.x > leash_master.x)
+ step(leash_pet, WEST, 1)
+ if(leash_pet.x < leash_master.x)
+ step(leash_pet, EAST, 1)
+
+ //Knock the pet over if they get further behind. Shouldn't happen too often.
+ sleep(3) //This way running normally won't just yank the pet to the ground.
+ if(leash_master == "null") //Just to stop error messages. Break the loop early if something removed the master
+ return
+ if(leash_pet == "null")
+ return
+ if(leash_pet.x > leash_master.x + 3 || leash_pet.x < leash_master.x - 3 || leash_pet.y > leash_master.y + 3 || leash_pet.y < leash_master.y - 3)
+ //var/leash_knockdown_message = "[leash_pet] got pulled to the ground by their leash!"
+ //to_chat(leash_master, "[leash_knockdown_message]")
+ //to_chat(leash_pet, "[leash_knockdown_message]")
+ leash_pet.apply_effect(20, EFFECT_KNOCKDOWN, 0)
+
+ //This code is to check if the pet has gotten too far away, and then break the leash.
+ sleep(3) //Wait to snap the leash
+ if(leash_master == "null") //Just to stop error messages
+ return
+ if(leash_pet == "null")
+ return
+ if(leash_pet.x > leash_master.x + 5 || leash_pet.x < leash_master.x - 5 || leash_pet.y > leash_master.y + 5 || leash_pet.y < leash_master.y - 5)
+ var/leash_break_message = "The leash snapped free from [leash_pet]!"
+ for(var/mob/viewing in viewers(leash_pet, null))
+ if(viewing == leash_master)
+ to_chat(leash_master, "The leash snapped free from your pet!")
+ if(viewing == leash_pet)
+ to_chat(leash_pet, "Your leash has popped from your collar!")
+ else
+ viewing.show_message("[leash_break_message]", 1)
+ leash_pet.apply_effect(20, EFFECT_KNOCKDOWN, 0)
+ leash_pet.adjustOxyLoss(5)
+ leash_pet.remove_status_effect(/datum/status_effect/leash_pet)
+ leash_pet.remove_movespeed_modifier(MOVESPEED_ID_LEASH)
+ leash_master.remove_status_effect(/datum/status_effect/leash_dom)
+ //QDEL_NULL(mobhook_leash_master)
+ UnregisterSignal(mobhook_leash_master, COMSIG_MOVABLE_MOVED)
+ //QDEL_NULL(mobhook_leash_pet)
+ UnregisterSignal(mobhook_leash_pet, COMSIG_MOVABLE_MOVED)
+ leash_pet = "null"
+ leash_master = "null"
+ leash_used = 0
+
+/obj/item/leash/proc/on_pet_move()
+ //This should only work if there is a pet and a master.
+ //This is here pretty much just to stop the console from flooding with errors
+ if(leash_master == "null")
+ return
+ if(leash_pet == "null")
+ return
+ //Make sure the pet is still a pet
+ if(!leash_pet.has_status_effect(/datum/status_effect/leash_pet))
+ //QDEL_NULL(mobhook_leash_pet) //Probably redundant, but it's nice to be safe
+ UnregisterSignal(mobhook_leash_pet, COMSIG_MOVABLE_MOVED)
+ return
+
+ //The pet has escaped. There is no DOM. GO PET RUN.
+ if(leash_pet.has_status_effect(/datum/status_effect/leash_freepet))//If the pet is free, break
+ return
+
+ //If the pet gets too far away, they get tugged back
+ sleep(3)//A small sleep so the pet kind of bounces back after they make the step
+ if(leash_master == "null")
+ return
+ if(leash_pet == "null")
+ return
+ //West tug
+ if(leash_pet.x > leash_master.x + 2)
+ step(leash_pet, WEST, 1) //"1" is the speed of movement. We want the tug to be faster than their slow current walk speed.
+ //East tug
+ if(leash_pet.x < leash_master.x - 2)
+ step(leash_pet, EAST, 1)
+ //South tug
+ if(leash_pet.y > leash_master.y + 2)
+ step(leash_pet, SOUTH, 1)
+ //North tug
+ if(leash_pet.y < leash_master.y - 2)
+ step(leash_pet, NORTH, 1)
+
+/obj/item/leash/proc/on_freepet_move()
+ //Pet is on the run. Let's drag the leash behind them.
+ if(!leash_master == "null") //If there is a master, don't do this
+ return
+ if(leash_pet == "null") //If there is no pet, don't do this
+ return
+ if(leash_pet.is_holding_item_of_type(/obj/item/leash)) //If the pet is holding the leash, don't do this
+ return
+
+ sleep(2)
+ if(leash_pet == "null")
+ return
+ //Double move to catch the leash up to the pet
+ if(src.x > leash_pet.x + 2)
+ . = step(src, WEST, 1)
+ if(src.x < leash_pet.x - 2)
+ . = step(src, EAST, 1)
+ if(src.y > leash_pet.y + 2)
+ . = step(src, SOUTH, 1)
+ if(src.y < leash_pet.y - 2)
+ . = step(src, NORTH, 1)
+ //Primary dragging code
+ if(src.x > leash_pet.x + 1)
+ . = step(src, WEST, 1) //"1" is the speed of movement. We want the tug to be faster than their slow current walk speed.
+ if(src.y > leash_pet.y)//Check the other axis, and tug them into alignment so they are behind the pet
+ . = step(src, SOUTH, 1)
+ if(src.y < leash_pet.y)
+ . = step(src, NORTH, 1)
+ if(src.x < leash_pet.x - 1)
+ . = step(src, EAST, 1)
+ if(src.y > leash_pet.y)
+ . = step(src, SOUTH, 1)
+ if(src.y < leash_pet.y)
+ . = step(src, NORTH, 1)
+ if(src.y > leash_pet.y + 1)
+ . = step(src, SOUTH, 1)
+ if(src.x > leash_pet.x)
+ . = step(src, WEST, 1)
+ if(src.x < leash_pet.x)
+ . = step(src, EAST, 1)
+ if(src.y < leash_pet.y - 1)
+ . = step(src, NORTH, 1)
+ if(src.x > leash_pet.x)
+ . = step(src, WEST, 1)
+ if(src.x < leash_pet.x)
+ . = step(src, EAST, 1)
+
+ sleep(1)
+ //Just to prevent error messages
+ if(leash_pet == "null")
+ return
+ if(src.x > leash_pet.x + 5 || src.x < leash_pet.x - 5 || src.y > leash_pet.y + 5 || src.y < leash_pet.y - 5)
+ var/leash_break_message = "The leash snapped free from [leash_pet]!"
+ for(var/mob/viewing in viewers(leash_pet, null))
+ if(viewing == leash_pet)
+ to_chat(leash_pet, "Your leash has popped from your collar!")
+ else
+ viewing.show_message("[leash_break_message]", 1)
+ leash_pet.apply_effect(20, EFFECT_KNOCKDOWN, 0)
+ leash_pet.adjustOxyLoss(5)
+ leash_pet.remove_status_effect(/datum/status_effect/leash_pet)
+ leash_pet.remove_status_effect(/datum/status_effect/leash_freepet)
+ //QDEL_NULL(mobhook_leash_pet)
+ UnregisterSignal(mobhook_leash_pet, COMSIG_MOVABLE_MOVED)
+ //QDEL_NULL(mobhook_leash_freepet)
+ UnregisterSignal(mobhook_leash_freepet, COMSIG_MOVABLE_MOVED)
+ leash_pet = "null"
+ leash_used = 0
+
+/obj/item/leash/dropped() //Drop the leash, and the leash effects stop
+ . = ..()
+ if(leash_pet == "null") //There is no pet. Stop this silliness
+ return
+ if(leash_master == "null")
+ return
+ //Dropping procs any time the leash changes slots. So, we will wait a tick and see if the leash was actually dropped
+ sleep(1)
+ if(leash_master.is_holding_item_of_type(/obj/item/leash) || istype(leash_master.get_item_by_slot(SLOT_BELT), /obj/item/leash))
+ return //Dom still has the leash as it turns out. Cancel the proc.
+ for(var/mob/viewing in viewers(leash_master, null))
+ viewing.show_message("[leash_master] has dropped the leash.", 1)
+ //DOM HAS DROPPED LEASH. PET IS FREE. SCP HAS BREACHED CONTAINMENT.
+ leash_pet.remove_movespeed_modifier(MOVESPEED_ID_LEASH)
+ //mobhook_leash_freepet = leash_pet.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src,PROC_REF(on_freepet_move))))
+ RegisterSignal(leash_pet, COMSIG_MOVABLE_MOVED,PROC_REF(on_freepet_move))
+ mobhook_leash_freepet = leash_pet
+ leash_master.remove_status_effect(/datum/status_effect/leash_dom) //No dom with no leash. We will get a new dom if the leash is picked back up.
+ leash_master = "null"
+ //QDEL_NULL(mobhook_leash_master)
+ UnregisterSignal(mobhook_leash_master, COMSIG_MOVABLE_MOVED)
+
+/obj/item/leash/equipped(mob/user)
+ . = ..()
+ if(leash_used == 0) //Don't apply statuses with a fresh leash. Keeps things clean on the backend.
+ return
+ sleep(2)
+ if(leash_pet == "null")
+ return
+ leash_master = user
+ if(leash_master.has_status_effect(/datum/status_effect/leash_freepet) || leash_master.has_status_effect(/datum/status_effect/leash_pet)) //Pet picked up their own leash.
+ leash_master = "null"
+ return
+ leash_master.apply_status_effect(/datum/status_effect/leash_dom)
+ //mobhook_leash_master = leash_master.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src,PROC_REF(on_master_move))))
+ RegisterSignal(leash_master, COMSIG_MOVABLE_MOVED,PROC_REF(on_master_move))
+ mobhook_leash_master = leash_master
+ leash_pet.remove_status_effect(/datum/status_effect/leash_freepet)
+ //QDEL_NULL(mobhook_leash_freepet)
+ UnregisterSignal(mobhook_leash_freepet, COMSIG_MOVABLE_MOVED)
+ leash_pet.add_movespeed_modifier(MOVESPEED_ID_LEASH, multiplicative_slowdown = 5)
+
+/datum/crafting_recipe/leash
+ name = "Leash"
+ result = /obj/item/leash
+ time = 40
+ reqs = list(/obj/item/stack/sheet/metal = 1,
+ /obj/item/stack/sheet/cloth = 3)
+ category = CAT_MISC
diff --git a/hyperstation/code/obj/lunaritems.dm b/hyperstation/code/obj/lunaritems.dm
new file mode 100644
index 0000000000..e3e190c772
--- /dev/null
+++ b/hyperstation/code/obj/lunaritems.dm
@@ -0,0 +1,154 @@
+/obj/structure/lunaraltar
+ name = "lunar altar"
+ desc = "Judging by the symbols, millenia ago, it seems that the creatures of this world used to offer something precious to the icy moon that orbits this hellish planet. You... Wouldn't do that, would you?"
+ icon = 'icons/obj/hand_of_god_structures.dmi' //Placeholder.
+ icon_state = "convertaltar-blue" //Placeholder.
+ anchored = TRUE
+ density = TRUE
+ light_color = LIGHT_COLOR_FIRE
+ var/used = FALSE
+
+/obj/structure/lunaraltar/Initialize(mapload)
+ . = ..()
+ set_light(2)
+
+/obj/structure/lunaraltar/attack_hand(mob/living/user)
+ . = ..()
+ if(.)
+ return
+ if(used)
+ to_chat(user, "The altar seems shattered.")
+ return
+ if(istype(user, /mob/living/carbon/human/))
+ var/part = pick("r_arm","l_arm","r_leg","l_leg")
+ var/mob/living/carbon/human/H = user
+ var/obj/item/bodypart/bodypart
+ var/paralysistype
+ switch(part)
+ if("l_leg")
+ bodypart = H.get_bodypart(BODY_ZONE_L_LEG)
+ paralysistype = TRAIT_PARALYSIS_L_LEG
+ if("r_leg")
+ bodypart = H.get_bodypart(BODY_ZONE_R_LEG)
+ paralysistype = TRAIT_PARALYSIS_R_LEG
+ if("l_arm")
+ bodypart = H.get_bodypart(BODY_ZONE_L_ARM)
+ paralysistype = TRAIT_PARALYSIS_L_ARM
+ if("r_arm")
+ bodypart = H.get_bodypart(BODY_ZONE_R_ARM)
+ paralysistype = TRAIT_PARALYSIS_R_ARM
+ if(!bodypart)
+ to_chat(user, "The altar does nothing.")
+ return
+ to_chat(user,"You begin placing your hand on the altar.")
+ playsound(src, 'sound/weapons/slice.ogg', 50, 1, 5)
+ if(do_after(user, 100, target=src))
+ if(used)
+ return
+ visible_message("[user]'s [bodypart] is momentarily enveloped by shadows before they are gruesomely twisted and dismembered!", \
+ "Your [bodypart] is momentarily enveloped by shadows before it's gruesomely twisted and dismembered!")
+ bodypart.dismember()
+ bodypart.Destroy()
+ ADD_TRAIT(H, paralysistype, "trauma_paralysis")
+ H.update_disabled_bodyparts()
+ H.bleed_rate += 5
+ H.emote("scream")
+ var/obj/item/lunar = pick(/obj/item/helfiretincture,
+ /obj/item/instrument/guitar/biggerukulele)
+ new lunar(src.loc)
+ visible_message("As you blink, cracks appear on the altar and a flash of lunar light reaches its surface. A gift?")
+ message_admins("[ADMIN_LOOKUPFLW(user)] has sacrificed their [bodypart] on the lunar altar at [AREACOORD(src)].")
+ icon_state = "sacrificealtar-blue"
+ set_light(0)
+ update_icon()
+ used = TRUE
+
+/obj/item/helfiretincture
+ name = "helfire tincture" //"Hel" not Hell. Intended.
+ icon = 'hyperstation/icons/obj/lunar.dmi'
+ icon_state = "helfire_tincture"
+ desc = "Burn everyone nearby... Including you."
+ var/cooldowntime = 45 SECONDS
+ var/used = FALSE
+
+/obj/item/helfiretincture/attack_self(mob/user)
+ if(!used)
+ used = TRUE
+ visible_message("[user] draws from the power of a Hellfire Tincture!", \
+ "You draw the power of the Hellfire Tincture!")
+ for(var/mob/living/H in spiral_range(8, user))
+ H.adjustFireLoss(10)
+ H.adjust_fire_stacks(5)
+ H.IgniteMob()
+ playsound(src.loc, 'hyperstation/sound/misc/helfire_use.ogg', 100, 1, extrarange = 8)
+ icon_state = "helfire_tincture_used"
+ update_icon()
+ addtimer(CALLBACK(src,PROC_REF(restore), user), cooldowntime)
+ else
+ to_chat(user, "It's too soon to use this again!")
+
+/obj/item/helfiretincture/proc/restore(mob/user)
+ used = FALSE
+ icon_state = "helfire_tincture"
+ update_icon()
+ to_chat(user, "The tincture vibrates with power once again.")
+
+
+/obj/item/instrument/guitar/biggerukulele
+ name = "bigger ukulele"
+ desc = "...and his music was electric."
+ icon_state = "biggerukulele"
+ item_state = "guitar"
+ instrumentExt = "ogg"
+ attack_verb = list("played a sick solo on", "electrified", "crashed", "smashed")
+ hitsound = 'sound/weapons/stringsmash.ogg'
+ instrumentId = "guitar"
+ siemens_coefficient = -1
+ hit_reaction_chance = 100
+ var/tesla_power = 25000
+ var/tesla_range = 20
+ var/tesla_flags = TESLA_MOB_DAMAGE | TESLA_OBJ_DAMAGE
+ var/legacy = FALSE
+ var/legacy_dmg = 30
+ var/reactive_cooldown_duration = 20
+ var/reactive_cooldown = 0
+ var/firstpickup = TRUE
+
+/obj/item/instrument/guitar/biggerukulele/dropped(mob/user)
+ ..()
+ if(istype(user))
+ user.flags_1 &= ~TESLA_IGNORE_1
+
+/obj/item/instrument/guitar/biggerukulele/pickup(mob/user)
+ ..()
+ if(istype(user))
+ user.flags_1 |= TESLA_IGNORE_1
+ if(firstpickup)
+ SEND_SOUND(user, sound('hyperstation/sound/misc/biggerukulelepickup.ogg', volume = 100))
+ firstpickup = FALSE
+
+/obj/item/instrument/guitar/biggerukulele/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
+ if(owner.flags_1 & TESLA_IGNORE_1)
+ //Do literally nothing. Checking for absence of flags doesn't work for whatever reason, so if !TESLA_IGNORE_1 is not an option.
+ else if (owner.flags_1)
+ owner.flags_1 |= TESLA_IGNORE_1 //Sometimes picking up the item doesn't grant the user the flag. This is a way to go around that. Thank you, DM Code.
+ if(prob(hit_reaction_chance))
+ if(world.time < reactive_cooldown)
+ var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread
+ sparks.set_up(1, 1, src)
+ sparks.start()
+ owner.visible_message("The electric strings of [owner]'s bigger ukulele! The instrument merely emits some sparks.")
+ return
+ owner.visible_message("[src] blocks [attack_text], sending out arcs of lightning!")
+ if(!legacy)
+ tesla_zap(owner, tesla_range, tesla_power, tesla_flags)
+ playsound(src.loc, 'sound/weapons/emitter2.ogg', 100, 1, extrarange = 10)
+ else
+ for(var/mob/living/M in view(7, owner))
+ if(M == owner)
+ continue
+ owner.Beam(M,icon_state="purple_lightning",icon='icons/effects/effects.dmi',time=5)
+ M.adjustFireLoss(legacy_dmg)
+ playsound(M, 'sound/machines/defib_zap.ogg', 50, 1, -1)
+ reactive_cooldown = world.time + reactive_cooldown_duration
+ return TRUE
diff --git a/hyperstation/code/obj/milking machine.dm b/hyperstation/code/obj/milking machine.dm
new file mode 100644
index 0000000000..e8f9f33ecb
--- /dev/null
+++ b/hyperstation/code/obj/milking machine.dm
@@ -0,0 +1,83 @@
+/obj/item/milking_machine
+ icon = 'hyperstation/icons/obj/milking_machine.dmi'
+ name = "milking machine"
+ icon_state = "Off"
+ item_state = "Off"
+ desc = "A pocket sized pump and tubing assembly designed to collect and store products from mammary glands."
+
+ slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_POCKET
+
+ var/on = FALSE
+ var/obj/item/reagent_containers/glass/inserted_item = null
+
+ var/transfer_rate = 0.50 // How much we transfer every 2 seconds
+ var/target_organ = "breasts" // What organ we are transfering from
+
+/obj/item/milking_machine/examine(mob/user)
+ . = ..()
+ to_chat(user, "[src] is currently [on ? "on" : "off"].")
+ if (inserted_item)
+ to_chat(user, "[inserted_item] contains [inserted_item.reagents.total_volume]/[inserted_item.reagents.maximum_volume] units")
+
+/obj/item/milking_machine/attackby(obj/item/W, mob/user, params)
+ add_fingerprint(user)
+ if(istype(W, /obj/item/reagent_containers/glass) && !inserted_item)
+ if(!user.transferItemToLoc(W, src))
+ return ..()
+ inserted_item = W
+ UpdateIcon()
+ else
+ return ..()
+
+/obj/item/milking_machine/interact(mob/user)
+ if(!isAI(user) && inserted_item)
+ add_fingerprint(user)
+ on = !on
+ if (on)
+ to_chat(user, "You turn [src] on.")
+ START_PROCESSING(SSobj, src)
+ else
+ to_chat(user, "You turn [src] off.")
+ STOP_PROCESSING(SSobj, src)
+ UpdateIcon()
+ else
+ ..()
+
+/obj/item/milking_machine/proc/UpdateIcon()
+ icon_state = "[on ? "On" : "Off"][inserted_item ? "Beaker" : ""]"
+ item_state = icon_state
+
+
+/obj/item/milking_machine/AltClick(mob/living/user)
+ add_fingerprint(user)
+ user.put_in_hands(inserted_item)
+ inserted_item = null
+ on = FALSE
+ STOP_PROCESSING(SSobj, src)
+ UpdateIcon()
+
+/obj/item/milking_machine/process()
+ var/mob/living/carbon/W = loc
+ if (W)
+ var/obj/item/organ/genital/breasts/O = W.getorganslot(target_organ)
+ if (O)
+ if (O.reagents.total_volume >= transfer_rate * 2)
+ if (inserted_item.reagents.total_volume < inserted_item.reagents.maximum_volume)
+ O.reagents.trans_to(inserted_item.reagents, amount = transfer_rate)
+ else
+ to_chat(W, "[src] stops pumping. [inserted_item] is full.")
+ on = FALSE
+ STOP_PROCESSING(SSobj, src)
+ UpdateIcon()
+
+/obj/item/milking_machine/penis
+ name = "penis milking machine"
+ icon_state = "PenisOff"
+ item_state = "PenisOff"
+ desc = "A pocket sized pump and tubing assembly designed to collect and store products from the penis."
+
+ target_organ = "testicles" // Since semen is stored in the balls
+
+/obj/item/milking_machine/penis/UpdateIcon()
+ icon_state = "Penis[on ? "On" : "Off"][inserted_item ? "Beaker" : ""]"
+ item_state = icon_state
diff --git a/hyperstation/code/obj/plushes.dm b/hyperstation/code/obj/plushes.dm
new file mode 100644
index 0000000000..74936cb956
--- /dev/null
+++ b/hyperstation/code/obj/plushes.dm
@@ -0,0 +1,79 @@
+/*/obj/item/toy/plush/mammal/winfre
+ desc = "An adorable stuffed toy of a pissed hyena. She looks unamused."
+ icon = 'hyperstation/icons/obj/plushes.dmi'
+ icon_state = "winfre"
+ item_state = "winfre"
+ attack_verb = list("cackled", "swirlie'd", "stepped on")*/
+
+/*/obj/item/toy/plush/mammal/marilyn
+ desc = "A cute stuffed fox toy. Now, about that sponge bath..."
+ icon = 'hyperstation/icons/obj/plushes.dmi'
+ icon_state = "marilyn"
+ item_state = "marilyn"*/
+
+/*/obj/item/toy/plush/mammal/winterbloo
+ name = "Will, the biggest dog"
+ desc = "A plush made to look like a thick as hell shiba, looks a little bigger than average too."
+ icon = 'hyperstation/icons/obj/plushes.dmi'
+ icon_state = "winterbloo"
+ item_state = "winterbloo"
+ attack_verb = list("hugged", "cuddled", "embraced")*/
+
+/*/obj/item/toy/plush/lizardplushie/chris
+ name = "Chris Plushie"
+ desc = "An adorable stuffed toy of an angry ashwalker. He even comes with his own little crusher!" // CHRIS CHRIS CHRIS!
+ icon = 'hyperstation/icons/obj/plushes.dmi'
+ gender = MALE
+ icon_state = "chris"
+ item_state = "chris"
+ squeak_override = list('sound/weapons/plasma_cutter.ogg' = 1)*/ // The sound the crusher uses is the same as the plasma cutter :P
+
+
+//society has progressed past the need for Tania
+
+/*/obj/item/toy/plush/mammal/grug
+ name = "Grug S. Cavemannington Plushie"
+ desc = " Everybody's favorite space faring caveman in plushie form. You feel an overwhelming urge to break things while holding it."
+ icon = 'hyperstation/icons/obj/plushes.dmi'
+ icon_state = "grug"
+ item_state = "grug"
+ attack_verb = list("thomped", "ook'd", "stoned")
+ squeak_override = list('sound/voice/gorillaplush.ogg' = 1)*/
+
+/*/obj/item/toy/plush/mammal/winterdawn
+ name = "hyena plushie"
+ desc = "An adorable stuffed toy of a mammal that seems to resemble a crew member! She's a little yeen in a big labcoat."
+ gender = FEMALE //probably a girl
+ icon = 'hyperstation/icons/obj/plushes.dmi'
+ icon_state = "lyricalpaws"
+ item_state = "lyricalpaws"
+ attack_verb = list("hugged", "cuddled", "embraced")
+ squeak_override = list(
+ 'modular_citadel/sound/voice/bark1.ogg' = 1,
+ 'modular_citadel/sound/voice/bark2.ogg' = 1
+ )*/
+
+/*/obj/item/toy/plush/mammal/chemlight
+ desc = "An adorable stuffed toy of a mammal that seems to resemble a crew member! It looks to glow and sport four arms."
+ gender = MALE //he's a boy, right?
+ icon = 'hyperstation/icons/obj/plushes.dmi'
+ icon_state = "chemlight"
+ item_state = "chemlight"
+ light_color = "#CCFF33"
+ light_power = 0.25
+ light_range = 1.4
+ attack_verb = list("bapped", "slapped", "bonked")*/
+
+//Enzo_Leon patron stuff
+/*/obj/item/toy/plush/mammal/enzo_leon
+ name = "rat plushie"
+ desc = "An stuffed toy of a mammal that seems to resemble a crew member! Iconically it has a squeaker as you'd expect."
+ gender = MALE //It's a boy!
+ icon = 'hyperstation/icons/obj/plushes.dmi'
+ icon_state = "enzor"
+ item_state = "enzor"
+ attack_verb = list("tail bapped", "whomped", "squeaked")
+ squeak_override = list('sound/effects/mousesqueek.ogg'= 1)*/
+//End
+
+//Removed all donator plushes for Hyperstation Admins/players in this file.
\ No newline at end of file
diff --git a/hyperstation/code/obj/pole.dm b/hyperstation/code/obj/pole.dm
new file mode 100644
index 0000000000..3502059484
--- /dev/null
+++ b/hyperstation/code/obj/pole.dm
@@ -0,0 +1,93 @@
+//hyperstation 13 stripper pole! about time?
+
+/obj/structure/pole
+ name = "stripper pole"
+ desc = "A pole fastened to the ceiling and floor, used to show of ones goods to company."
+ icon = 'hyperstation/icons/obj/pole.dmi'
+ icon_state = "pole"
+ density = TRUE
+ anchored = TRUE
+ var/icon_state_inuse
+ layer = 4 //make it the same layer as players.
+ pseudo_z_axis = 9 //stepping onto the pole makes you raise upwards!
+ density = 0 //easy to step up on
+
+/obj/structure/pole/attack_hand(mob/living/user)
+ . = ..()
+ if(.)
+ return
+ if(obj_flags & IN_USE)
+ to_chat(user, "It's already in use - wait a bit.")
+ return
+ else
+ obj_flags |= IN_USE
+ user.setDir(SOUTH)
+ user.Stun(100)
+ user.forceMove(src.loc)
+ user.visible_message("[user] dances on [src]!")
+ animatepole(user)
+ user.layer = layer //set them to the poles layer
+ obj_flags &= ~IN_USE
+ user.pixel_y = 0
+ user.pixel_z = pseudo_z_axis //incase we are off it when we jump on!
+ icon_state = initial(icon_state)
+
+/obj/structure/pole/proc/animatepole(mob/living/user)
+ return
+
+/obj/structure/pole/animatepole(mob/living/user)
+
+ if (user.loc != src.loc)
+ return
+ animate(user,pixel_x = -6, pixel_y = 0, time = 10)
+ sleep(20)
+ user.dir = 4
+ animate(user,pixel_x = -6,pixel_y = 24, time = 10)
+ sleep(12)
+ src.layer = 4.01 //move the pole infront for now. better to move the pole, because the character moved behind people sitting above otherwise
+ animate(user,pixel_x = 6,pixel_y = 12, time = 5)
+ user.dir = 8
+ sleep(6)
+ animate(user,pixel_x = -6,pixel_y = 4, time = 5)
+ user.dir = 4
+ src.layer = 4 // move it back.
+ sleep(6)
+ user.dir = 1
+ animate(user,pixel_x = 0, pixel_y = 0, time = 3)
+ sleep(6)
+ user.do_jitter_animation()
+ sleep(6)
+ user.dir = 2
+
+
+/obj/item/polepack
+ name = "stripper pole flatpack"
+ desc = "A wrench is required to construct."
+ icon = 'hyperstation/icons/obj/pole_small.dmi'
+ throwforce = 0
+ icon_state = "pole_base"
+ var/unwrapped = 0
+ w_class = WEIGHT_CLASS_HUGE
+
+/obj/item/polepack/attackby(obj/item/P, mob/user, params) //erecting a pole here.
+ add_fingerprint(user)
+ if(P.tool_behaviour == TOOL_WRENCH)
+ if (!(item_flags & IN_INVENTORY))
+ to_chat(user, "You start to fasten the frame to the floor and celing...")
+ if(P.use_tool(src, user, 8 SECONDS, volume=50))
+ to_chat(user, "You construct the stripper pole!")
+ var/obj/structure/pole/C = new
+ C.loc = loc
+ del(src)
+ return
+
+/obj/structure/pole/attackby(obj/item/P, mob/user, params) //un-erecting a pole. :(
+ add_fingerprint(user)
+ if(P.tool_behaviour == TOOL_WRENCH)
+ to_chat(user, "You start to unfastening the frame...")
+ if(P.use_tool(src, user, 8 SECONDS, volume=50))
+ to_chat(user, "You take down the stripper pole!")
+ var/obj/item/polepack/C = new
+ C.loc = loc
+ del(src)
+ return
diff --git a/hyperstation/code/obj/pregnancytester.dm b/hyperstation/code/obj/pregnancytester.dm
new file mode 100644
index 0000000000..08e765e5c0
--- /dev/null
+++ b/hyperstation/code/obj/pregnancytester.dm
@@ -0,0 +1,43 @@
+// /obj/item/pregnancytest
+// name = "pregnancy test"
+// desc = "A one time use small device, used to determine whether someone is pregnant or not."
+// icon = 'hyperstation/icons/obj/pregnancytest.dmi'
+// throwforce = 0
+// icon_state = "ptest"
+// var/status = 0
+// var/results = "null"
+// w_class = WEIGHT_CLASS_TINY
+
+
+// /obj/item/pregnancytest/attack_self(mob/user)
+// if(QDELETED(src))
+// return
+// if(!isliving(user))
+// return
+// if(isAI(user))
+// return
+// if(user.stat > 0)//unconscious or dead
+// return
+// if(status == 1)
+// return //Already been used once, pregnancy tests only work once.
+// test(user)
+
+// /obj/item/pregnancytest/proc/force(mob/living/user)
+// //Force it negative
+// icon_state = "negative"
+// name = "[results] pregnancy test"
+// status = 1
+// to_chat(user, "You use the pregnancy test, the display reads negative!")
+
+
+// /obj/item/pregnancytest/proc/test(mob/living/user)
+
+// var/obj/item/organ/genital/womb/W = user.getorganslot("womb")
+// if(W.pregnant == 1)
+// results = "positive"
+// icon_state = "positive"
+// name = "[results] pregnancy test"
+// status = 1
+// to_chat(user, "You use the pregnancy test, the display reads positive!")
+// else
+// force(user)
diff --git a/hyperstation/code/obj/rewards.dm b/hyperstation/code/obj/rewards.dm
new file mode 100644
index 0000000000..96bb50004f
--- /dev/null
+++ b/hyperstation/code/obj/rewards.dm
@@ -0,0 +1,200 @@
+/obj/item/pen/bluemarker
+ name = "blue marker"
+ desc = "A simple blueberry scented marker."
+ icon_state = "marker_blue"
+ colour = "blue"
+
+/obj/item/pen/bluemarker/attack_self(mob/user)
+ user.emote("sniff")
+ to_chat(user, "Ahh~ blueberries!")
+
+/obj/item/clothing/suit/napoleonic
+ name = "napoleonic uniform"
+ desc = "An heirloom passed down from the Gruber family dynasty."
+ icon_state = "napoleonic"
+ item_state = "napoleonic"
+ body_parts_covered = CHEST|GROIN|ARMS
+ cold_protection = CHEST|GROIN|ARMS
+
+/obj/item/clothing/suit/shackles
+ name = "Plastitanium Shackles"
+ desc = "A set of heavy plastitanium shackles, there are chains still attatched"
+ icon_state = "shackles"
+ item_state = "shackles"
+ icon = 'hyperstation/icons/mobs/rewards.dmi'
+ alternate_worn_icon = 'hyperstation/icons/mobs/rewards.dmi'
+ mutantrace_variation = NONE
+
+/obj/item/clothing/suit/luwethtrench
+ name = "Syndicate Commander's Coat"
+ desc = "A sinister looking black and red jacket. The gold collar and shoulders denote that this belongs to a high ranking syndicate officer. A rather strange brooch is pinned to the coat, displaying a unique range of lustrous brass cracks through the deep blacks of it’s hammered finish."
+ icon = 'hyperstation/icons/obj/clothing/rewards.dmi'
+ alternate_worn_icon = 'hyperstation/icons/mobs/rewards.dmi'
+ body_parts_covered = CHEST|GROIN|ARMS|LEGS
+ icon_state = "luwethtrench"
+ item_state = "luwethtrench"
+ mutantrace_variation = NONE
+
+/obj/item/clothing/gloves/ring/luweth
+ name = "Luweth’s Wedding Band"
+ icon = 'hyperstation/icons/obj/clothing/rewards.dmi'
+ icon_state = "luweth"
+ desc = "A seemingly natural yet rather rough brass ring, which shows off a unique range of lustrous brass cracks through the deep blacks of it’s hammered finish. 'Till death does us part.'"
+
+/obj/item/dice/d20/blue
+ name = "blue d20"
+ desc = "Clearly the dice hate you."
+ icon_state = "d20_blue"
+ sides = 20
+ unique = TRUE
+
+/obj/item/clothing/suit/hooded/wintercoat/death
+ name = "inconspicuous winter coat"
+ icon_state = "rdeath"
+ item_state = "rdeath"
+ allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/screwdriver, /obj/item/crowbar, /obj/item/wrench, /obj/item/stack/cable_coil, /obj/item/weldingtool, /obj/item/multitool)
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
+ hoodtype = /obj/item/clothing/head/hooded/winterhood/death
+
+/obj/item/clothing/head/hooded/winterhood/death
+ icon_state = "winterhood_death"
+
+/obj/item/storage/pill_bottle/heat
+ name = "heat-b-gone pill bottle"
+ desc = "a bottle of pills from a sketchy pharmaceutical corporation. at the bottom of the label is a small red S."
+
+/obj/item/reagent_containers/pill/heat
+ name = "heat-b-gone pill"
+ desc = "claims to be foolproof heat repression medication but it tastes extremely sweet."
+ icon_state = "pill8"
+ list_reagents = list(/datum/reagent/consumable/sugar = 10)
+ roundstart = 1
+
+/obj/item/storage/pill_bottle/heat/PopulateContents()
+ for(var/i in 1 to 7)
+ new /obj/item/reagent_containers/pill/heat(src)
+
+/obj/item/storage/pill_bottle/betablock
+ name = "anaphrodisiacs pill bottle"
+ desc = "a bottle of anaphrodisiacs."
+
+/obj/item/reagent_containers/pill/betablock
+ name = "anaphrodisiac pill"
+ desc = "Prescribed to races that have trouble keeping their urges in check."
+ icon_state = "pill5"
+ list_reagents = list(/datum/reagent/drug/anaphrodisiac = 10)
+ roundstart = 1
+
+/obj/item/storage/pill_bottle/betablock/PopulateContents()
+ for(var/i in 1 to 7)
+ new /obj/item/reagent_containers/pill/betablock(src)
+
+/obj/item/clothing/head/crystalline
+ name = "crystalline shards"
+ icon = 'hyperstation/icons/obj/rewards.dmi'
+ desc = "A handful of blue crystals. They look like they came from some sort of cave."
+ alternate_worn_icon = 'hyperstation/icons/mobs/rewards.dmi'
+ icon_state = "crystalline"
+
+/obj/item/clothing/mask/keaton
+ name = "keaton mask"
+ desc = "A mask made to look like the mythical Keaton."
+ icon = 'hyperstation/icons/obj/rewards.dmi'
+ alternate_worn_icon = 'hyperstation/icons/mobs/rewards.dmi'
+ icon_state = "keaton"
+ flags_inv = HIDEFACE|HIDEFACIALHAIR
+ w_class = WEIGHT_CLASS_SMALL
+
+/obj/item/clothing/suit/chloe
+ name = "Fleet Commander's Overcoat"
+ desc = "Custom tailored to warm the cold commanding hearts of the Syndicate's feared XIV'th battle group. Its armour plating has been removed."
+ icon_state = "commissar_greatcoat"
+ item_state = "commissar_greatcoat"
+ allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/screwdriver, /obj/item/crowbar, /obj/item/wrench, /obj/item/stack/cable_coil, /obj/item/weldingtool, /obj/item/multitool)
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
+ body_parts_covered = CHEST|GROIN|ARMS|LEGS
+ mutantrace_variation = MUTANTRACE_VARIATION
+ tauric = TRUE
+
+/obj/item/clothing/head/chloe
+ name = "Fleet Commander's Beret"
+ desc = "A beret bearing a worn golden symbol that stikes fear in the hearts of many. It smells faintly of plasma and gunpowder."
+ icon = 'hyperstation/icons/obj/clothing/rewards.dmi'
+ alternate_worn_icon = 'hyperstation/icons/mobs/rewards.dmi'
+ icon_state = "commissar_beret"
+ item_state = "commissar_beret"
+
+/obj/item/clothing/under/touchinfuzzy
+ name = "provocative jumpsuit"
+ desc = "A form fitting jumpsuit with a golden trim zipper! Smells faintly of succubus milk."
+ icon_state = "touchinfuzzyuni"
+ item_state = "touchinfuzzyuni"
+ body_parts_covered = CHEST|GROIN|ARMS|LEGS
+ fitted = NO_FEMALE_UNIFORM
+
+/obj/item/toy/sword/chloesabre
+ name = "Fleet Commander's Sabre"
+ desc = "An elegant weapon, similar in design to the Captain's Sabre, but with a syndicate twist."
+ icon = 'icons/obj/custom.dmi'
+ alternate_worn_icon = 'icons/mob/custom_w.dmi'
+ icon_state = "darksabre"
+ item_state = "darksabre"
+ force = 5
+ throwforce = 5
+ hitsound = 'sound/weapons/rapierhit.ogg'
+ lefthand_file = 'modular_citadel/icons/mob/inhands/stunsword_left.dmi'
+ righthand_file = 'modular_citadel/icons/mob/inhands/stunsword_right.dmi'
+ obj_flags = UNIQUE_RENAME
+ attack_verb = list("slashed", "cut")
+
+/obj/item/toy/sword/chloesabre/get_belt_overlay()
+ return mutable_appearance('icons/obj/custom.dmi', "darksheath-darksabre")
+
+/obj/item/toy/sword/chloesabre/get_worn_belt_overlay(icon_file)
+ return mutable_appearance(icon_file, "darksheath-darksabre")
+
+/obj/item/mialasscale
+ name = "Miala's Scale"
+ desc = "A bright, and familiar, cyan scale from an equally familiar snake being."
+ icon = 'hyperstation/icons/obj/rewards.dmi'
+ icon_state = "m_scale"
+ item_state = "m_scale"
+ w_class = WEIGHT_CLASS_SMALL
+
+/obj/item/bong/kiseru
+ name = "black lacquered kiseru"
+ desc = "it is a black lacquered kiseru with a ornate silver head and mouthpiece, you can feel it's old age as you hold it"
+ icon = 'hyperstation/icons/obj/rewards.dmi'
+ icon_state = null
+ item_state = null
+ w_class = WEIGHT_CLASS_NORMAL
+ light_color = "#FFCC66"
+ icon_off = "pipe"
+ icon_on = "pipe_lit"
+
+/obj/item/clothing/suit/hooded/occultrobes
+ name = "Occult Robes"
+ desc = "I didn't even know they made these in XXS..."
+ icon_state = "coatwinter"
+ item_state = "coatwinter"
+ body_parts_covered = CHEST|GROIN|ARMS
+ cold_protection = CHEST|GROIN|ARMS
+ min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0)
+ allowed = list(/obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter)
+
+/obj/item/clothing/suit/hooded/occultrobes
+ name = "occult robes"
+ desc = "I didn't even know they made these in XXS..."
+ icon_state = "occultrobes"
+ hoodtype = /obj/item/clothing/head/hooded/occultrobeshood
+ mutantrace_variation = NONE
+
+/obj/item/clothing/head/hooded/occultrobeshood
+ name = "occult hood"
+ icon_state = "occultrobeshood"
+ body_parts_covered = HEAD
+ cold_protection = HEAD
+ min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
+ flags_inv = HIDEHAIR|HIDEFACE|HIDEEARS
+ mutantrace_variation = NONE
diff --git a/hyperstation/code/obj/rope.dm b/hyperstation/code/obj/rope.dm
new file mode 100644
index 0000000000..6d31813720
--- /dev/null
+++ b/hyperstation/code/obj/rope.dm
@@ -0,0 +1,56 @@
+//Jay
+//TODO: Better sprites
+//Rope
+/obj/item/restraints/handcuffs/rope/ //Fun
+ name = "soft rope"
+ desc = "A comfortable rope that would be easy to slip out of if you needed. Kinky."
+ breakouttime = 10 //Easy to break out. It's not for gaming.
+ icon = 'hyperstation/icons/obj/rope.dmi'
+ icon_state = "rope"
+ item_state = "rope" //This sprite is in restraints.dmi until I figure out how to refrence somewhere else
+ cuffsound = 'sound/weapons/cablecuff.ogg'
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
+ price = 2
+
+/mob/living/proc/rope_add(source) //Check to see if the rope is on, and then add effects
+ var/mob/living/carbon/M = source
+ if(M.handcuffed)
+ var/rope_message = pick("The rope is tightly tied onto you!")
+ to_chat(M, "[rope_message]")
+ M.min_arousal = 33
+ M.arousal_rate += 2
+ update_stat()
+
+/mob/living/proc/rope_remove(list/sources, temp_min_arousal, temp_max_arousal, temp_arousal_rate) //Check to see it the rope is gone, and reset effects
+ var/mob/living/carbon/M = sources
+ if (!M.handcuffed)
+ var/rope_message = pick("The rope has been removed!")
+ to_chat(M, "[rope_message]")
+ M.min_arousal = temp_min_arousal
+ M.max_arousal = temp_max_arousal
+ M.arousal_rate = temp_arousal_rate
+ update_stat()
+
+/obj/item/restraints/handcuffs/rope/attack(mob/living/carbon/C, mob/living/user)
+ . = ..()
+ var/temp_min_arousal = C.min_arousal //Temp variables to hold original arousal values
+ var/temp_max_arousal = C.max_arousal
+ var/temp_arousal_rate = C.arousal_rate
+ var/datum/callback/Cback = new(user, /mob/living/proc/rope_add, C)//Put the rope on
+ addtimer(Cback, 6, TIMER_UNIQUE)//We are going to call this proc six seconds after the click. The rope tying takes 5 seconds
+ var/datum/callback/Cback2 = new(user, /mob/living/proc/rope_remove, C, temp_min_arousal, temp_max_arousal, temp_arousal_rate)
+ while(1) //Loop until break - Because I can't figure out any better way to do it.
+ var/rope_emote = pick("moan", "blush")
+ sleep(50) //5 second wait
+ addtimer(Cback2, 1, TIMER_UNIQUE) //Just keep calling this timer proc
+ if (prob(10))
+ C.emote(rope_emote)
+ if (!C.handcuffed)
+ return //Break when the rope is removed
+
+/datum/crafting_recipe/rope
+ name = "Soft Rope"
+ result = /obj/item/restraints/handcuffs/rope
+ time = 40
+ reqs = list(/obj/item/stack/sheet/cloth = 5)
+ category = CAT_MISC
diff --git a/hyperstation/code/obj/sizeitems.dm b/hyperstation/code/obj/sizeitems.dm
new file mode 100644
index 0000000000..3a8b5c0213
--- /dev/null
+++ b/hyperstation/code/obj/sizeitems.dm
@@ -0,0 +1,14 @@
+/obj/item/reagent_containers/food/drinks/prospacillin
+ name = "Prospacillin Bottle"
+ desc = "An incredibly expensive bottle used by GATO command. It has golden engravings and reeks of corporate greed."
+ w_class = WEIGHT_CLASS_TINY
+ icon_state = "prospacillin"
+ list_reagents = list(/datum/reagent/growthchem = 15)
+
+/obj/item/reagent_containers/food/drinks/diminicillin
+ name = "Diminicillin Bottle"
+ desc = "An incredibly expensive bottle used by GATO command. It has golden engravings and reeks of corporate greed."
+ w_class = WEIGHT_CLASS_TINY
+ icon_state = "diminicillin"
+ list_reagents = list(/datum/reagent/shrinkchem = 15)
+
diff --git a/hyperstation/code/obj/sounding.dm b/hyperstation/code/obj/sounding.dm
new file mode 100644
index 0000000000..08715aa38e
--- /dev/null
+++ b/hyperstation/code/obj/sounding.dm
@@ -0,0 +1,69 @@
+/obj/item/sounding
+ name = "sounding rod"
+ desc = "Dont be silly, stuff your willy!"
+ icon = 'hyperstation/icons/obj/sounding.dmi'
+ throwforce = 0
+ icon_state = "sounding_wrapped"
+ var/unwrapped = 0
+ w_class = WEIGHT_CLASS_TINY
+ price = 1
+
+/obj/item/sounding/attack_self(mob/user)
+ if(!istype(user))
+ return
+ if(isliving(user))
+ if(unwrapped == 0)
+ icon_state = "sounding_rod"
+ unwrapped = 1
+ to_chat(user, "You unwrap the rod.")
+ playsound(user, 'sound/items/poster_ripped.ogg', 50, 1, -1)
+ return
+
+/obj/item/sounding/attack(mob/living/carbon/C, mob/living/user)
+
+ if(unwrapped == 0 )
+ to_chat(user, "You must remove the rod from the package first!")
+ return
+ var/obj/item/organ/genital/penis/P = C.getorganslot("penis")
+ if(P&&P.is_exposed())
+ if(P.sounding)
+ to_chat(user, "They already have a rod inside!")
+ return
+ if(isliving(C)&&isliving(user)&&unwrapped == 1)
+ C.visible_message("[user] is trying to insert a rod inside [C]!",\
+ "[user] is trying to insert a rod inside you!")
+ if(!do_mob(user, C, 4 SECONDS))
+ return
+ var/mob/living/carbon/human/L = C
+ playsound(C, 'sound/lewd/champ_fingering.ogg', 50, 1, -1)
+ P.sounding = 1
+ if(L)
+ L.update_genitals()
+ to_chat(C, "Your penis feels stuffed and stretched!")
+ qdel(src)
+
+ return
+ to_chat(user, "You can't find anywhere to put the rod inside.")
+
+/mob/living/carbon/human/proc/removesounding()
+
+ var/obj/item/organ/genital/penis/P = src.getorganslot("penis")
+ if(P.sounding)
+ new /obj/item/sounding/used_sounding(usr.loc)
+ to_chat(src, "The rod falls off from your penis.")
+ P.sounding = 0
+ src.update_genitals()
+
+/obj/item/sounding/used_sounding
+ name = "sounding rod"
+ icon_state = "sounding_rod"
+ unwrapped = 2
+ w_class = WEIGHT_CLASS_TINY
+
+/mob/living/carbon/human/proc/soundingclimax()
+
+ var/obj/item/organ/genital/penis/P = src.getorganslot("penis")
+ if(P.sounding)
+ new /obj/item/sounding/used_sounding(usr.loc)
+ P.sounding = 0
+ src.update_genitals()
\ No newline at end of file
diff --git a/hyperstation/code/obj/stargate_clothing.dm b/hyperstation/code/obj/stargate_clothing.dm
new file mode 100644
index 0000000000..8ff520de1b
--- /dev/null
+++ b/hyperstation/code/obj/stargate_clothing.dm
@@ -0,0 +1,9 @@
+/obj/item/clothing/under/tunic
+ name = "Tunic"
+ desc = "A simples tunic."
+ icon = 'hyperstation/icons/obj/clothing/suits.dmi'
+ icon_state = "tunic"
+ alternate_worn_icon = 'hyperstation/icons/mobs/suits.dmi'
+ item_state = "r_suit"
+ can_adjust = FALSE
+ mutantrace_variation = NONE
diff --git a/hyperstation/code/obj/vibrator.dm b/hyperstation/code/obj/vibrator.dm
new file mode 100644
index 0000000000..91aeeb47e4
--- /dev/null
+++ b/hyperstation/code/obj/vibrator.dm
@@ -0,0 +1,157 @@
+//Hyperstation 13 vibrator
+//For all them subs/bottoms out there, that wanna give someone the power to make them cum remotely.
+
+/obj/item/electropack/vibrator
+ name = "remote vibrator"
+ desc = "A remote device that can deliver pleasure at a fair. It has three intensities that can be set by twisting the base."
+ icon = 'hyperstation/icons/obj/vibrator.dmi'
+ icon_state = "vibe"
+ item_state = "vibe"
+ w_class = WEIGHT_CLASS_SMALL
+ slot_flags = ITEM_SLOT_DENYPOCKET //no more pocket shockers
+ var/mode = 1
+ var/style = "long"
+ var/inside = FALSE
+ var/last = 0
+ price = 3
+
+/obj/item/electropack/vibrator/Initialize(mapload) //give the device its own code
+ . = ..()
+ code = rand(1,30)
+
+/obj/item/electropack/vibrator/small //can go anywhere
+ name = "small remote vibrator"
+ style = "small"
+ icon_state = "vibesmall"
+ item_state = "vibesmall"
+
+/obj/item/electropack/vibrator/AltClick(mob/living/user)
+ var/dat = {"
+
+Frequency/Code for vibrator:
+Frequency:
+[format_frequency(src.frequency)]
+Set
+
+Code:
+[src.code]
+Set
+"}
+ user << browse(dat, "window=radio")
+ onclose(user, "radio")
+ return
+
+/obj/item/electropack/shockcollar/security
+ name = "security shock collar"
+ desc = "A reinforced security collar. It has two electrodes that press against the neck, for disobedient pets."
+ icon_state = "shockseccollar"
+ item_state = "shockseccollar"
+
+/obj/item/electropack/vibrator/attack_self(mob/user)
+ if(!istype(user))
+ return
+ if(isliving(user))
+ playsound(user, 'sound/effects/clock_tick.ogg', 50, 1, -1)
+ switch(mode)
+ if(1)
+ mode = 2
+ to_chat(user, "You twist the bottom of [src], setting it to the medium setting.")
+ return
+ if(2)
+ mode = 3
+ to_chat(user, "You twist the bottom of [src], setting it to the high setting.")
+ return
+ if(3)
+ mode = 1
+ to_chat(user, "You twist the bottom of [src], setting it to the low setting.")
+ return
+
+/obj/item/electropack/vibrator/attack(mob/living/carbon/C, mob/living/user)
+
+ var/obj/item/organ/genital/picked_organ
+ var/mob/living/carbon/human/S = user
+ var/mob/living/carbon/human/T = C
+ picked_organ = S.target_genitals(T)
+ if(picked_organ)
+ C.visible_message("[user] is trying to attach [src] to [T]!",\
+ "[user] is trying to put [src] on you!")
+ if(!do_mob(user, C, 5 SECONDS))//warn them and have a delay of 5 seconds to apply.
+ return
+
+ if(style == "long" && !(picked_organ.name == "vagina")) //long vibrators dont fit on anything but vaginas, but small ones fit everywhere
+ to_chat(user, "[src] is too big to fit there, use a smaller version.")
+ return
+
+ if(!picked_organ.equipment)
+ if(!(style == "long"))
+ to_chat(user, "You attach [src] to [T]'s [picked_organ.name].")
+ else
+ to_chat(user, "You insert [src] into [T]'s [picked_organ.name].")
+ else
+ to_chat(user, "They already have a [picked_organ.equipment.name] there.")
+ return
+
+ if(!user.transferItemToLoc(src, picked_organ)) //check if you can put it in
+ return
+ src.inside = TRUE
+ picked_organ.equipment = src
+
+ else
+ to_chat(user, "You don't see anywhere to attach this.")
+
+
+/obj/item/electropack/vibrator/receive_signal(datum/signal/signal)
+ if(!signal || signal.data["code"] != code)
+ return
+
+ if(last > world.time)
+ return
+
+ last = world.time + 3 SECONDS //lets stop spam.
+
+ if(inside)
+ var/obj/item/organ/genital/G = src.loc
+ var/mob/living/carbon/U = G.owner
+
+ if(G)
+ switch(G.name) //just being fancy
+ if("breasts")
+ to_chat(U, "[src] vibrates against your nipples!")
+ else
+ to_chat(U, "[src] vibrates against your [G.name]!")
+
+ var/intencity = 6*mode
+ U.adjust_arousal(intencity) //give pleasure
+ playsound(U.loc, 'sound/lewd/vibrate.ogg', (intencity+5), 1, -1) //vibe intencity scaled up abit for sound
+
+
+ switch(mode)
+ if(1) //low, setting for RP, it wont force your character to do anything.
+ to_chat(U, "You feel pleasure surge through your [G.name]")
+ U.do_jitter_animation() //do animation without heartbeat
+ if(2) //med, can make you cum
+ to_chat(U, "You feel intense pleasure surge through your [G.name]")
+ U.do_jitter_animation()
+ if (U.getArousalLoss() >= 100 && ishuman(U) && U.has_dna())
+ U.mob_climax(forced_climax=TRUE)
+ if(3) //high, makes you stun
+ to_chat(U, "You feel overpowering pleasure surge through your [G.name]")
+ U.Jitter(3)
+ U.Stun(30)
+ if (U.getArousalLoss() >= 100 && ishuman(U) && U.has_dna())
+ U.mob_climax(forced_climax=TRUE)
+ if(prob(50))
+ U.emote("moan")
+
+
+
+ playsound(src, 'sound/lewd/vibrate.ogg', 40, 1, -1)
+ if(style == "long") //haha vibrator go brrrrrrr
+ icon_state = "vibing"
+
+ sleep(30)
+ icon_state = "vibe"
+ else
+ icon_state = "vibingsmall"
+ sleep(30)
+ icon_state = "vibesmall"
diff --git a/hyperstation/icons/chat/chat.dmi b/hyperstation/icons/chat/chat.dmi
new file mode 100644
index 0000000000..cf74d73796
Binary files /dev/null and b/hyperstation/icons/chat/chat.dmi differ
diff --git a/hyperstation/icons/chat/gender.dmi b/hyperstation/icons/chat/gender.dmi
new file mode 100644
index 0000000000..01cf65b92f
Binary files /dev/null and b/hyperstation/icons/chat/gender.dmi differ
diff --git a/hyperstation/icons/chat/pins.dmi b/hyperstation/icons/chat/pins.dmi
new file mode 100644
index 0000000000..6bf0a30b39
Binary files /dev/null and b/hyperstation/icons/chat/pins.dmi differ
diff --git a/hyperstation/icons/chat/reward.dmi b/hyperstation/icons/chat/reward.dmi
new file mode 100644
index 0000000000..eedb443ceb
Binary files /dev/null and b/hyperstation/icons/chat/reward.dmi differ
diff --git a/hyperstation/icons/decals/decal_new.dmi b/hyperstation/icons/decals/decal_new.dmi
new file mode 100644
index 0000000000..600ddd9f18
Binary files /dev/null and b/hyperstation/icons/decals/decal_new.dmi differ
diff --git a/hyperstation/icons/effects/cumoverlay.dmi b/hyperstation/icons/effects/cumoverlay.dmi
new file mode 100644
index 0000000000..ebed804d21
Binary files /dev/null and b/hyperstation/icons/effects/cumoverlay.dmi differ
diff --git a/hyperstation/icons/effects/lighting.dmi b/hyperstation/icons/effects/lighting.dmi
new file mode 100644
index 0000000000..3f59b47d2a
Binary files /dev/null and b/hyperstation/icons/effects/lighting.dmi differ
diff --git a/hyperstation/icons/effects/lightinglarge.dmi b/hyperstation/icons/effects/lightinglarge.dmi
new file mode 100644
index 0000000000..fb1eeebc8b
Binary files /dev/null and b/hyperstation/icons/effects/lightinglarge.dmi differ
diff --git a/hyperstation/icons/effects/lightingmed.dmi b/hyperstation/icons/effects/lightingmed.dmi
new file mode 100644
index 0000000000..0b87ce7dc1
Binary files /dev/null and b/hyperstation/icons/effects/lightingmed.dmi differ
diff --git a/hyperstation/icons/mob/animal.dmi b/hyperstation/icons/mob/animal.dmi
new file mode 100644
index 0000000000..ecce6bbf7c
Binary files /dev/null and b/hyperstation/icons/mob/animal.dmi differ
diff --git a/hyperstation/icons/mob/catmedbot.dmi b/hyperstation/icons/mob/catmedbot.dmi
new file mode 100644
index 0000000000..b276ab8c54
Binary files /dev/null and b/hyperstation/icons/mob/catmedbot.dmi differ
diff --git a/hyperstation/icons/mob/char_ears.dmi b/hyperstation/icons/mob/char_ears.dmi
new file mode 100644
index 0000000000..b4b572a05d
Binary files /dev/null and b/hyperstation/icons/mob/char_ears.dmi differ
diff --git a/hyperstation/icons/mob/char_markings.dmi b/hyperstation/icons/mob/char_markings.dmi
new file mode 100644
index 0000000000..5aff5e4d19
Binary files /dev/null and b/hyperstation/icons/mob/char_markings.dmi differ
diff --git a/hyperstation/icons/mob/char_parts.dmi b/hyperstation/icons/mob/char_parts.dmi
new file mode 100644
index 0000000000..4982376a08
Binary files /dev/null and b/hyperstation/icons/mob/char_parts.dmi differ
diff --git a/hyperstation/icons/mob/char_snouts.dmi b/hyperstation/icons/mob/char_snouts.dmi
new file mode 100644
index 0000000000..92b3163d84
Binary files /dev/null and b/hyperstation/icons/mob/char_snouts.dmi differ
diff --git a/hyperstation/icons/mob/char_tails.dmi b/hyperstation/icons/mob/char_tails.dmi
new file mode 100644
index 0000000000..0695ae639f
Binary files /dev/null and b/hyperstation/icons/mob/char_tails.dmi differ
diff --git a/hyperstation/icons/mob/char_wings.dmi b/hyperstation/icons/mob/char_wings.dmi
new file mode 100644
index 0000000000..49feac5809
Binary files /dev/null and b/hyperstation/icons/mob/char_wings.dmi differ
diff --git a/hyperstation/icons/mob/clothes/extra.dmi b/hyperstation/icons/mob/clothes/extra.dmi
new file mode 100644
index 0000000000..e5023f7fd6
Binary files /dev/null and b/hyperstation/icons/mob/clothes/extra.dmi differ
diff --git a/hyperstation/icons/mob/eyes.dmi b/hyperstation/icons/mob/eyes.dmi
new file mode 100644
index 0000000000..628bc237ea
Binary files /dev/null and b/hyperstation/icons/mob/eyes.dmi differ
diff --git a/hyperstation/icons/mob/item_lefthand.dmi b/hyperstation/icons/mob/item_lefthand.dmi
new file mode 100644
index 0000000000..8897982eba
Binary files /dev/null and b/hyperstation/icons/mob/item_lefthand.dmi differ
diff --git a/hyperstation/icons/mob/item_righthand.dmi b/hyperstation/icons/mob/item_righthand.dmi
new file mode 100644
index 0000000000..8897982eba
Binary files /dev/null and b/hyperstation/icons/mob/item_righthand.dmi differ
diff --git a/hyperstation/icons/mob/snek.dmi b/hyperstation/icons/mob/snek.dmi
new file mode 100644
index 0000000000..7bff5f1942
Binary files /dev/null and b/hyperstation/icons/mob/snek.dmi differ
diff --git a/hyperstation/icons/mob/status_indicators.dmi b/hyperstation/icons/mob/status_indicators.dmi
new file mode 100644
index 0000000000..6e2b165beb
Binary files /dev/null and b/hyperstation/icons/mob/status_indicators.dmi differ
diff --git a/hyperstation/icons/mobs/aibots.dmi b/hyperstation/icons/mobs/aibots.dmi
new file mode 100644
index 0000000000..1710d3ad5b
Binary files /dev/null and b/hyperstation/icons/mobs/aibots.dmi differ
diff --git a/hyperstation/icons/mobs/carrion.dmi b/hyperstation/icons/mobs/carrion.dmi
new file mode 100644
index 0000000000..44c17ad5e0
Binary files /dev/null and b/hyperstation/icons/mobs/carrion.dmi differ
diff --git a/hyperstation/icons/mobs/dancer/captain.dmi b/hyperstation/icons/mobs/dancer/captain.dmi
new file mode 100644
index 0000000000..9814eb93c1
Binary files /dev/null and b/hyperstation/icons/mobs/dancer/captain.dmi differ
diff --git a/hyperstation/icons/mobs/eyes.dmi b/hyperstation/icons/mobs/eyes.dmi
new file mode 100644
index 0000000000..d9ecfc2ea3
Binary files /dev/null and b/hyperstation/icons/mobs/eyes.dmi differ
diff --git a/hyperstation/icons/mobs/feet.dmi b/hyperstation/icons/mobs/feet.dmi
new file mode 100644
index 0000000000..3a0d6bd2ff
Binary files /dev/null and b/hyperstation/icons/mobs/feet.dmi differ
diff --git a/hyperstation/icons/mobs/gloves.dmi b/hyperstation/icons/mobs/gloves.dmi
new file mode 100644
index 0000000000..70fa72dc87
Binary files /dev/null and b/hyperstation/icons/mobs/gloves.dmi differ
diff --git a/hyperstation/icons/mobs/head.dmi b/hyperstation/icons/mobs/head.dmi
new file mode 100644
index 0000000000..e83719e149
Binary files /dev/null and b/hyperstation/icons/mobs/head.dmi differ
diff --git a/hyperstation/icons/mobs/mimic.dmi b/hyperstation/icons/mobs/mimic.dmi
new file mode 100644
index 0000000000..181b534fc9
Binary files /dev/null and b/hyperstation/icons/mobs/mimic.dmi differ
diff --git a/hyperstation/icons/mobs/nails.dmi b/hyperstation/icons/mobs/nails.dmi
new file mode 100644
index 0000000000..575b513200
Binary files /dev/null and b/hyperstation/icons/mobs/nails.dmi differ
diff --git a/hyperstation/icons/mobs/rewards.dmi b/hyperstation/icons/mobs/rewards.dmi
new file mode 100644
index 0000000000..7a92799b4b
Binary files /dev/null and b/hyperstation/icons/mobs/rewards.dmi differ
diff --git a/hyperstation/icons/mobs/suits.dmi b/hyperstation/icons/mobs/suits.dmi
new file mode 100644
index 0000000000..e7fd46fc51
Binary files /dev/null and b/hyperstation/icons/mobs/suits.dmi differ
diff --git a/hyperstation/icons/mobs/uniform.dmi b/hyperstation/icons/mobs/uniform.dmi
new file mode 100644
index 0000000000..b9ac335e26
Binary files /dev/null and b/hyperstation/icons/mobs/uniform.dmi differ
diff --git a/hyperstation/icons/mobs/uniforms.dmi b/hyperstation/icons/mobs/uniforms.dmi
new file mode 100644
index 0000000000..2a16fa055b
Binary files /dev/null and b/hyperstation/icons/mobs/uniforms.dmi differ
diff --git a/hyperstation/icons/mobs/wendigo.dmi b/hyperstation/icons/mobs/wendigo.dmi
new file mode 100644
index 0000000000..7c8de1d121
Binary files /dev/null and b/hyperstation/icons/mobs/wendigo.dmi differ
diff --git a/hyperstation/icons/mobs/werewolf.dmi b/hyperstation/icons/mobs/werewolf.dmi
new file mode 100644
index 0000000000..fb89c833ed
Binary files /dev/null and b/hyperstation/icons/mobs/werewolf.dmi differ
diff --git a/hyperstation/icons/obj/bench.dmi b/hyperstation/icons/obj/bench.dmi
new file mode 100644
index 0000000000..514e6ff2f2
Binary files /dev/null and b/hyperstation/icons/obj/bench.dmi differ
diff --git a/hyperstation/icons/obj/bluespace_thread.dmi b/hyperstation/icons/obj/bluespace_thread.dmi
new file mode 100644
index 0000000000..8a2a48353b
Binary files /dev/null and b/hyperstation/icons/obj/bluespace_thread.dmi differ
diff --git a/hyperstation/icons/obj/cargo/sweatshop/cloth.dmi b/hyperstation/icons/obj/cargo/sweatshop/cloth.dmi
new file mode 100644
index 0000000000..bd71472c66
Binary files /dev/null and b/hyperstation/icons/obj/cargo/sweatshop/cloth.dmi differ
diff --git a/hyperstation/icons/obj/cargo/sweatshop/metal.dmi b/hyperstation/icons/obj/cargo/sweatshop/metal.dmi
new file mode 100644
index 0000000000..e847d11c35
Binary files /dev/null and b/hyperstation/icons/obj/cargo/sweatshop/metal.dmi differ
diff --git a/hyperstation/icons/obj/cargo/sweatshop/sweatshop.dmi b/hyperstation/icons/obj/cargo/sweatshop/sweatshop.dmi
new file mode 100644
index 0000000000..27fd5fb1a6
Binary files /dev/null and b/hyperstation/icons/obj/cargo/sweatshop/sweatshop.dmi differ
diff --git a/hyperstation/icons/obj/cargo/sweatshop/wooden.dmi b/hyperstation/icons/obj/cargo/sweatshop/wooden.dmi
new file mode 100644
index 0000000000..6a190f8dec
Binary files /dev/null and b/hyperstation/icons/obj/cargo/sweatshop/wooden.dmi differ
diff --git a/hyperstation/icons/obj/clothing/glasses.dmi b/hyperstation/icons/obj/clothing/glasses.dmi
new file mode 100644
index 0000000000..7dfa817043
Binary files /dev/null and b/hyperstation/icons/obj/clothing/glasses.dmi differ
diff --git a/hyperstation/icons/obj/clothing/gloves.dmi b/hyperstation/icons/obj/clothing/gloves.dmi
new file mode 100644
index 0000000000..6f425bd06e
Binary files /dev/null and b/hyperstation/icons/obj/clothing/gloves.dmi differ
diff --git a/hyperstation/icons/obj/clothing/head.dmi b/hyperstation/icons/obj/clothing/head.dmi
new file mode 100644
index 0000000000..a7fa83039b
Binary files /dev/null and b/hyperstation/icons/obj/clothing/head.dmi differ
diff --git a/hyperstation/icons/obj/clothing/neck.dmi b/hyperstation/icons/obj/clothing/neck.dmi
new file mode 100644
index 0000000000..dadf202668
Binary files /dev/null and b/hyperstation/icons/obj/clothing/neck.dmi differ
diff --git a/hyperstation/icons/obj/clothing/rewards.dmi b/hyperstation/icons/obj/clothing/rewards.dmi
new file mode 100644
index 0000000000..ac43a627d1
Binary files /dev/null and b/hyperstation/icons/obj/clothing/rewards.dmi differ
diff --git a/hyperstation/icons/obj/clothing/shoes.dmi b/hyperstation/icons/obj/clothing/shoes.dmi
new file mode 100644
index 0000000000..af087b7333
Binary files /dev/null and b/hyperstation/icons/obj/clothing/shoes.dmi differ
diff --git a/hyperstation/icons/obj/clothing/sizeaccessories.dmi b/hyperstation/icons/obj/clothing/sizeaccessories.dmi
new file mode 100644
index 0000000000..b9079b3538
Binary files /dev/null and b/hyperstation/icons/obj/clothing/sizeaccessories.dmi differ
diff --git a/hyperstation/icons/obj/clothing/suits.dmi b/hyperstation/icons/obj/clothing/suits.dmi
new file mode 100644
index 0000000000..9d90fd06c8
Binary files /dev/null and b/hyperstation/icons/obj/clothing/suits.dmi differ
diff --git a/hyperstation/icons/obj/clothing/uniforms.dmi b/hyperstation/icons/obj/clothing/uniforms.dmi
new file mode 100644
index 0000000000..56e017c560
Binary files /dev/null and b/hyperstation/icons/obj/clothing/uniforms.dmi differ
diff --git a/hyperstation/icons/obj/condom.dmi b/hyperstation/icons/obj/condom.dmi
new file mode 100644
index 0000000000..7d8db7a6fe
Binary files /dev/null and b/hyperstation/icons/obj/condom.dmi differ
diff --git a/hyperstation/icons/obj/control_rod.dmi b/hyperstation/icons/obj/control_rod.dmi
new file mode 100644
index 0000000000..bef59ebf2e
Binary files /dev/null and b/hyperstation/icons/obj/control_rod.dmi differ
diff --git a/hyperstation/icons/obj/cosmetic.dmi b/hyperstation/icons/obj/cosmetic.dmi
new file mode 100644
index 0000000000..2c329e9c42
Binary files /dev/null and b/hyperstation/icons/obj/cosmetic.dmi differ
diff --git a/hyperstation/icons/obj/economy.dmi b/hyperstation/icons/obj/economy.dmi
new file mode 100644
index 0000000000..6e838230ba
Binary files /dev/null and b/hyperstation/icons/obj/economy.dmi differ
diff --git a/hyperstation/icons/obj/fleshlight.dmi b/hyperstation/icons/obj/fleshlight.dmi
new file mode 100644
index 0000000000..809dcaa3a4
Binary files /dev/null and b/hyperstation/icons/obj/fleshlight.dmi differ
diff --git a/hyperstation/icons/obj/genitals/belly.dmi b/hyperstation/icons/obj/genitals/belly.dmi
new file mode 100644
index 0000000000..cd6598644c
Binary files /dev/null and b/hyperstation/icons/obj/genitals/belly.dmi differ
diff --git a/hyperstation/icons/obj/genitals/butt.dmi b/hyperstation/icons/obj/genitals/butt.dmi
new file mode 100644
index 0000000000..713c0e1804
Binary files /dev/null and b/hyperstation/icons/obj/genitals/butt.dmi differ
diff --git a/hyperstation/icons/obj/inflatable.dmi b/hyperstation/icons/obj/inflatable.dmi
new file mode 100644
index 0000000000..65888be4ad
Binary files /dev/null and b/hyperstation/icons/obj/inflatable.dmi differ
diff --git a/hyperstation/icons/obj/leash.dmi b/hyperstation/icons/obj/leash.dmi
new file mode 100644
index 0000000000..08a9ae3f06
Binary files /dev/null and b/hyperstation/icons/obj/leash.dmi differ
diff --git a/hyperstation/icons/obj/lunar.dmi b/hyperstation/icons/obj/lunar.dmi
new file mode 100644
index 0000000000..8254574ea1
Binary files /dev/null and b/hyperstation/icons/obj/lunar.dmi differ
diff --git a/hyperstation/icons/obj/machinery/rbmk.dmi b/hyperstation/icons/obj/machinery/rbmk.dmi
new file mode 100644
index 0000000000..1c52ddac12
Binary files /dev/null and b/hyperstation/icons/obj/machinery/rbmk.dmi differ
diff --git a/hyperstation/icons/obj/machinery/reactor_parts.dmi b/hyperstation/icons/obj/machinery/reactor_parts.dmi
new file mode 100644
index 0000000000..7fd5cfccb0
Binary files /dev/null and b/hyperstation/icons/obj/machinery/reactor_parts.dmi differ
diff --git a/hyperstation/icons/obj/medical.dmi b/hyperstation/icons/obj/medical.dmi
new file mode 100644
index 0000000000..9b1b3f349c
Binary files /dev/null and b/hyperstation/icons/obj/medical.dmi differ
diff --git a/hyperstation/icons/obj/milking_machine.dmi b/hyperstation/icons/obj/milking_machine.dmi
new file mode 100644
index 0000000000..8e8ccd73cb
Binary files /dev/null and b/hyperstation/icons/obj/milking_machine.dmi differ
diff --git a/hyperstation/icons/obj/objects.dmi b/hyperstation/icons/obj/objects.dmi
new file mode 100644
index 0000000000..2631d9ff11
Binary files /dev/null and b/hyperstation/icons/obj/objects.dmi differ
diff --git a/hyperstation/icons/obj/plushes.dmi b/hyperstation/icons/obj/plushes.dmi
new file mode 100644
index 0000000000..0438ab7dfa
Binary files /dev/null and b/hyperstation/icons/obj/plushes.dmi differ
diff --git a/hyperstation/icons/obj/pole.dmi b/hyperstation/icons/obj/pole.dmi
new file mode 100644
index 0000000000..42c23dbef4
Binary files /dev/null and b/hyperstation/icons/obj/pole.dmi differ
diff --git a/hyperstation/icons/obj/pole_small.dmi b/hyperstation/icons/obj/pole_small.dmi
new file mode 100644
index 0000000000..ddb8f9e04c
Binary files /dev/null and b/hyperstation/icons/obj/pole_small.dmi differ
diff --git a/hyperstation/icons/obj/pregnancytest.dmi b/hyperstation/icons/obj/pregnancytest.dmi
new file mode 100644
index 0000000000..d7563cad12
Binary files /dev/null and b/hyperstation/icons/obj/pregnancytest.dmi differ
diff --git a/hyperstation/icons/obj/railings.dmi b/hyperstation/icons/obj/railings.dmi
new file mode 100644
index 0000000000..3a1f07cdbf
Binary files /dev/null and b/hyperstation/icons/obj/railings.dmi differ
diff --git a/hyperstation/icons/obj/rbmk_computer.dmi b/hyperstation/icons/obj/rbmk_computer.dmi
new file mode 100644
index 0000000000..66bbcb40ac
Binary files /dev/null and b/hyperstation/icons/obj/rbmk_computer.dmi differ
diff --git a/hyperstation/icons/obj/rewards.dmi b/hyperstation/icons/obj/rewards.dmi
new file mode 100644
index 0000000000..9c5f069fd3
Binary files /dev/null and b/hyperstation/icons/obj/rewards.dmi differ
diff --git a/hyperstation/icons/obj/rope.dmi b/hyperstation/icons/obj/rope.dmi
new file mode 100644
index 0000000000..22834cdb2d
Binary files /dev/null and b/hyperstation/icons/obj/rope.dmi differ
diff --git a/hyperstation/icons/obj/sounding.dmi b/hyperstation/icons/obj/sounding.dmi
new file mode 100644
index 0000000000..75c237e8dd
Binary files /dev/null and b/hyperstation/icons/obj/sounding.dmi differ
diff --git a/hyperstation/icons/obj/stationobjs.dmi b/hyperstation/icons/obj/stationobjs.dmi
new file mode 100644
index 0000000000..424ea8458c
Binary files /dev/null and b/hyperstation/icons/obj/stationobjs.dmi differ
diff --git a/hyperstation/icons/obj/structures/sauna_oven.dmi b/hyperstation/icons/obj/structures/sauna_oven.dmi
new file mode 100644
index 0000000000..fb0871ca91
Binary files /dev/null and b/hyperstation/icons/obj/structures/sauna_oven.dmi differ
diff --git a/hyperstation/icons/obj/vibrator.dmi b/hyperstation/icons/obj/vibrator.dmi
new file mode 100644
index 0000000000..0a52ffb04d
Binary files /dev/null and b/hyperstation/icons/obj/vibrator.dmi differ
diff --git a/hyperstation/sound/ambience/embrace.ogg b/hyperstation/sound/ambience/embrace.ogg
new file mode 100644
index 0000000000..2f150fce8e
Binary files /dev/null and b/hyperstation/sound/ambience/embrace.ogg differ
diff --git a/hyperstation/sound/creatures/mimic/mimic_attack.ogg b/hyperstation/sound/creatures/mimic/mimic_attack.ogg
new file mode 100644
index 0000000000..4d44884051
Binary files /dev/null and b/hyperstation/sound/creatures/mimic/mimic_attack.ogg differ
diff --git a/hyperstation/sound/creatures/mimic/mimic_transform.ogg b/hyperstation/sound/creatures/mimic/mimic_transform.ogg
new file mode 100644
index 0000000000..d6ecd92f29
Binary files /dev/null and b/hyperstation/sound/creatures/mimic/mimic_transform.ogg differ
diff --git a/hyperstation/sound/effects/rbmk/alarm.ogg b/hyperstation/sound/effects/rbmk/alarm.ogg
new file mode 100644
index 0000000000..8e698a69f1
Binary files /dev/null and b/hyperstation/sound/effects/rbmk/alarm.ogg differ
diff --git a/hyperstation/sound/effects/rbmk/meltdown.ogg b/hyperstation/sound/effects/rbmk/meltdown.ogg
new file mode 100644
index 0000000000..9c0a1a9917
Binary files /dev/null and b/hyperstation/sound/effects/rbmk/meltdown.ogg differ
diff --git a/hyperstation/sound/effects/rbmk/reactor_hum.ogg b/hyperstation/sound/effects/rbmk/reactor_hum.ogg
new file mode 100644
index 0000000000..adc1e867ed
Binary files /dev/null and b/hyperstation/sound/effects/rbmk/reactor_hum.ogg differ
diff --git a/hyperstation/sound/effects/rbmk/switch.ogg b/hyperstation/sound/effects/rbmk/switch.ogg
new file mode 100644
index 0000000000..668d3b0310
Binary files /dev/null and b/hyperstation/sound/effects/rbmk/switch.ogg differ
diff --git a/hyperstation/sound/effects/rbmk/switch2.ogg b/hyperstation/sound/effects/rbmk/switch2.ogg
new file mode 100644
index 0000000000..b9cd7d1540
Binary files /dev/null and b/hyperstation/sound/effects/rbmk/switch2.ogg differ
diff --git a/hyperstation/sound/effects/rbmk/switch3.ogg b/hyperstation/sound/effects/rbmk/switch3.ogg
new file mode 100644
index 0000000000..b89cbab4cc
Binary files /dev/null and b/hyperstation/sound/effects/rbmk/switch3.ogg differ
diff --git a/hyperstation/sound/effects/rbmk/turbine_end.ogg b/hyperstation/sound/effects/rbmk/turbine_end.ogg
new file mode 100644
index 0000000000..55859aff90
Binary files /dev/null and b/hyperstation/sound/effects/rbmk/turbine_end.ogg differ
diff --git a/hyperstation/sound/effects/rbmk/turbine_mid.ogg b/hyperstation/sound/effects/rbmk/turbine_mid.ogg
new file mode 100644
index 0000000000..2010370425
Binary files /dev/null and b/hyperstation/sound/effects/rbmk/turbine_mid.ogg differ
diff --git a/hyperstation/sound/effects/rbmk/turbine_start.ogg b/hyperstation/sound/effects/rbmk/turbine_start.ogg
new file mode 100644
index 0000000000..4104d0f627
Binary files /dev/null and b/hyperstation/sound/effects/rbmk/turbine_start.ogg differ
diff --git a/hyperstation/sound/effects/ship/freespace2/crane_1.wav b/hyperstation/sound/effects/ship/freespace2/crane_1.wav
new file mode 100644
index 0000000000..394bd658cb
Binary files /dev/null and b/hyperstation/sound/effects/ship/freespace2/crane_1.wav differ
diff --git a/hyperstation/sound/effects/ship/reactor/falloutwind.ogg b/hyperstation/sound/effects/ship/reactor/falloutwind.ogg
new file mode 100644
index 0000000000..4119371517
Binary files /dev/null and b/hyperstation/sound/effects/ship/reactor/falloutwind.ogg differ
diff --git a/hyperstation/sound/effects/ship/reactor/startup.ogg b/hyperstation/sound/effects/ship/reactor/startup.ogg
new file mode 100644
index 0000000000..4df6e736c5
Binary files /dev/null and b/hyperstation/sound/effects/ship/reactor/startup.ogg differ
diff --git a/hyperstation/sound/effects/ship/reactor/startup2.ogg b/hyperstation/sound/effects/ship/reactor/startup2.ogg
new file mode 100644
index 0000000000..b11e80fc82
Binary files /dev/null and b/hyperstation/sound/effects/ship/reactor/startup2.ogg differ
diff --git a/hyperstation/sound/halflife/anomalous_materials.ogg b/hyperstation/sound/halflife/anomalous_materials.ogg
new file mode 100644
index 0000000000..56e77d4916
Binary files /dev/null and b/hyperstation/sound/halflife/anomalous_materials.ogg differ
diff --git a/hyperstation/sound/halflife/beamstart5.ogg b/hyperstation/sound/halflife/beamstart5.ogg
new file mode 100644
index 0000000000..2208a63170
Binary files /dev/null and b/hyperstation/sound/halflife/beamstart5.ogg differ
diff --git a/hyperstation/sound/halflife/hevsuit_firstpickup.ogg b/hyperstation/sound/halflife/hevsuit_firstpickup.ogg
new file mode 100644
index 0000000000..872add7007
Binary files /dev/null and b/hyperstation/sound/halflife/hevsuit_firstpickup.ogg differ
diff --git a/hyperstation/sound/halflife/hevsuit_pickup.ogg b/hyperstation/sound/halflife/hevsuit_pickup.ogg
new file mode 100644
index 0000000000..20bd96685e
Binary files /dev/null and b/hyperstation/sound/halflife/hevsuit_pickup.ogg differ
diff --git a/hyperstation/sound/machines/BoltsDown.ogg b/hyperstation/sound/machines/BoltsDown.ogg
new file mode 100644
index 0000000000..263776de09
Binary files /dev/null and b/hyperstation/sound/machines/BoltsDown.ogg differ
diff --git a/hyperstation/sound/machines/BoltsUp.ogg b/hyperstation/sound/machines/BoltsUp.ogg
new file mode 100644
index 0000000000..202765c63d
Binary files /dev/null and b/hyperstation/sound/machines/BoltsUp.ogg differ
diff --git a/hyperstation/sound/machines/blastDoorClose.ogg b/hyperstation/sound/machines/blastDoorClose.ogg
new file mode 100644
index 0000000000..086c6b00eb
Binary files /dev/null and b/hyperstation/sound/machines/blastDoorClose.ogg differ
diff --git a/hyperstation/sound/machines/blastDoorOpen.ogg b/hyperstation/sound/machines/blastDoorOpen.ogg
new file mode 100644
index 0000000000..2c60517228
Binary files /dev/null and b/hyperstation/sound/machines/blastDoorOpen.ogg differ
diff --git a/hyperstation/sound/machines/shutter.ogg b/hyperstation/sound/machines/shutter.ogg
new file mode 100644
index 0000000000..e1248aec40
Binary files /dev/null and b/hyperstation/sound/machines/shutter.ogg differ
diff --git a/hyperstation/sound/machines/sm/accent/normal/1.ogg b/hyperstation/sound/machines/sm/accent/normal/1.ogg
new file mode 100644
index 0000000000..e92beed7fe
Binary files /dev/null and b/hyperstation/sound/machines/sm/accent/normal/1.ogg differ
diff --git a/hyperstation/sound/machines/sm/accent/normal/2.ogg b/hyperstation/sound/machines/sm/accent/normal/2.ogg
new file mode 100644
index 0000000000..05e3c9ff17
Binary files /dev/null and b/hyperstation/sound/machines/sm/accent/normal/2.ogg differ
diff --git a/hyperstation/sound/machines/sm/accent/normal/3.ogg b/hyperstation/sound/machines/sm/accent/normal/3.ogg
new file mode 100644
index 0000000000..38de5571a4
Binary files /dev/null and b/hyperstation/sound/machines/sm/accent/normal/3.ogg differ
diff --git a/hyperstation/sound/machines/sm/accent/normal/4.ogg b/hyperstation/sound/machines/sm/accent/normal/4.ogg
new file mode 100644
index 0000000000..2e71e976e8
Binary files /dev/null and b/hyperstation/sound/machines/sm/accent/normal/4.ogg differ
diff --git a/hyperstation/sound/machines/sm/accent/normal/5.ogg b/hyperstation/sound/machines/sm/accent/normal/5.ogg
new file mode 100644
index 0000000000..04852e10f2
Binary files /dev/null and b/hyperstation/sound/machines/sm/accent/normal/5.ogg differ
diff --git a/hyperstation/sound/machines/windowdoor.ogg b/hyperstation/sound/machines/windowdoor.ogg
new file mode 100644
index 0000000000..7bdeafd81a
Binary files /dev/null and b/hyperstation/sound/machines/windowdoor.ogg differ
diff --git a/hyperstation/sound/misc/ai_laws_update.ogg b/hyperstation/sound/misc/ai_laws_update.ogg
new file mode 100644
index 0000000000..5eaaf7cbe4
Binary files /dev/null and b/hyperstation/sound/misc/ai_laws_update.ogg differ
diff --git a/hyperstation/sound/misc/biggerukulelepickup.ogg b/hyperstation/sound/misc/biggerukulelepickup.ogg
new file mode 100644
index 0000000000..43eeca19c5
Binary files /dev/null and b/hyperstation/sound/misc/biggerukulelepickup.ogg differ
diff --git a/hyperstation/sound/misc/bonk.ogg b/hyperstation/sound/misc/bonk.ogg
new file mode 100644
index 0000000000..8f03c8ecc0
Binary files /dev/null and b/hyperstation/sound/misc/bonk.ogg differ
diff --git a/hyperstation/sound/misc/bookify.ogg b/hyperstation/sound/misc/bookify.ogg
new file mode 100644
index 0000000000..bba99ace1d
Binary files /dev/null and b/hyperstation/sound/misc/bookify.ogg differ
diff --git a/hyperstation/sound/misc/fallingthroughclouds.ogg b/hyperstation/sound/misc/fallingthroughclouds.ogg
new file mode 100644
index 0000000000..78e3c3d6a0
Binary files /dev/null and b/hyperstation/sound/misc/fallingthroughclouds.ogg differ
diff --git a/hyperstation/sound/misc/family.ogg b/hyperstation/sound/misc/family.ogg
new file mode 100644
index 0000000000..39c97e7e50
Binary files /dev/null and b/hyperstation/sound/misc/family.ogg differ
diff --git a/hyperstation/sound/misc/goofy.ogg b/hyperstation/sound/misc/goofy.ogg
new file mode 100644
index 0000000000..b1dd596b2d
Binary files /dev/null and b/hyperstation/sound/misc/goofy.ogg differ
diff --git a/hyperstation/sound/misc/helfire_use.ogg b/hyperstation/sound/misc/helfire_use.ogg
new file mode 100644
index 0000000000..d1489bcfab
Binary files /dev/null and b/hyperstation/sound/misc/helfire_use.ogg differ
diff --git a/hyperstation/sound/misc/slam.ogg b/hyperstation/sound/misc/slam.ogg
new file mode 100644
index 0000000000..de1dc83dcc
Binary files /dev/null and b/hyperstation/sound/misc/slam.ogg differ
diff --git a/hyperstation/sound/misc/slamofthenorthstar.ogg b/hyperstation/sound/misc/slamofthenorthstar.ogg
new file mode 100644
index 0000000000..4d28638b73
Binary files /dev/null and b/hyperstation/sound/misc/slamofthenorthstar.ogg differ
diff --git a/hyperstation/sound/misc/what_a_thrill.ogg b/hyperstation/sound/misc/what_a_thrill.ogg
new file mode 100644
index 0000000000..be330a2172
Binary files /dev/null and b/hyperstation/sound/misc/what_a_thrill.ogg differ
diff --git a/hyperstation/sound/misc/what_a_thrill2.ogg b/hyperstation/sound/misc/what_a_thrill2.ogg
new file mode 100644
index 0000000000..f2a235b977
Binary files /dev/null and b/hyperstation/sound/misc/what_a_thrill2.ogg differ
diff --git a/hyperstation/sound/misc/wilhelm.ogg b/hyperstation/sound/misc/wilhelm.ogg
new file mode 100644
index 0000000000..80f7e11853
Binary files /dev/null and b/hyperstation/sound/misc/wilhelm.ogg differ
diff --git a/hyperstation/sound/misc/yodadeath.ogg b/hyperstation/sound/misc/yodadeath.ogg
new file mode 100644
index 0000000000..831d406147
Binary files /dev/null and b/hyperstation/sound/misc/yodadeath.ogg differ
diff --git a/hyperstation/sound/voice/emotes/bat_c1.ogg b/hyperstation/sound/voice/emotes/bat_c1.ogg
new file mode 100644
index 0000000000..c11beedc10
Binary files /dev/null and b/hyperstation/sound/voice/emotes/bat_c1.ogg differ
diff --git a/hyperstation/sound/voice/emotes/bat_c2.ogg b/hyperstation/sound/voice/emotes/bat_c2.ogg
new file mode 100644
index 0000000000..45e13541c5
Binary files /dev/null and b/hyperstation/sound/voice/emotes/bat_c2.ogg differ
diff --git a/hyperstation/sound/voice/emotes/bat_c3.ogg b/hyperstation/sound/voice/emotes/bat_c3.ogg
new file mode 100644
index 0000000000..1e34c2cf99
Binary files /dev/null and b/hyperstation/sound/voice/emotes/bat_c3.ogg differ
diff --git a/hyperstation/sound/voice/emotes/bat_c4.ogg b/hyperstation/sound/voice/emotes/bat_c4.ogg
new file mode 100644
index 0000000000..94f43daf6f
Binary files /dev/null and b/hyperstation/sound/voice/emotes/bat_c4.ogg differ
diff --git a/hyperstation/sound/voice/emotes/bat_c5.ogg b/hyperstation/sound/voice/emotes/bat_c5.ogg
new file mode 100644
index 0000000000..17207ccb8c
Binary files /dev/null and b/hyperstation/sound/voice/emotes/bat_c5.ogg differ
diff --git a/hyperstation/sound/voice/emotes/bat_c6.ogg b/hyperstation/sound/voice/emotes/bat_c6.ogg
new file mode 100644
index 0000000000..ebb7400781
Binary files /dev/null and b/hyperstation/sound/voice/emotes/bat_c6.ogg differ
diff --git a/hyperstation/sound/voice/emotes/bat_c7.ogg b/hyperstation/sound/voice/emotes/bat_c7.ogg
new file mode 100644
index 0000000000..de58d94f1d
Binary files /dev/null and b/hyperstation/sound/voice/emotes/bat_c7.ogg differ
diff --git a/hyperstation/sound/voice/emotes/bat_c8.ogg b/hyperstation/sound/voice/emotes/bat_c8.ogg
new file mode 100644
index 0000000000..3952d54072
Binary files /dev/null and b/hyperstation/sound/voice/emotes/bat_c8.ogg differ
diff --git a/hyperstation/sound/voice/emotes/bat_c9.ogg b/hyperstation/sound/voice/emotes/bat_c9.ogg
new file mode 100644
index 0000000000..702eac32c5
Binary files /dev/null and b/hyperstation/sound/voice/emotes/bat_c9.ogg differ
diff --git a/hyperstation/sound/voice/emotes/female_cough1.ogg b/hyperstation/sound/voice/emotes/female_cough1.ogg
new file mode 100644
index 0000000000..35cea5a5c9
Binary files /dev/null and b/hyperstation/sound/voice/emotes/female_cough1.ogg differ
diff --git a/hyperstation/sound/voice/emotes/female_cough2.ogg b/hyperstation/sound/voice/emotes/female_cough2.ogg
new file mode 100644
index 0000000000..1322ed0548
Binary files /dev/null and b/hyperstation/sound/voice/emotes/female_cough2.ogg differ
diff --git a/hyperstation/sound/voice/emotes/female_cough3.ogg b/hyperstation/sound/voice/emotes/female_cough3.ogg
new file mode 100644
index 0000000000..97c2d8104d
Binary files /dev/null and b/hyperstation/sound/voice/emotes/female_cough3.ogg differ
diff --git a/hyperstation/sound/voice/emotes/female_cough4.ogg b/hyperstation/sound/voice/emotes/female_cough4.ogg
new file mode 100644
index 0000000000..bb0b32888c
Binary files /dev/null and b/hyperstation/sound/voice/emotes/female_cough4.ogg differ
diff --git a/hyperstation/sound/voice/emotes/female_cough5.ogg b/hyperstation/sound/voice/emotes/female_cough5.ogg
new file mode 100644
index 0000000000..4f95822a22
Binary files /dev/null and b/hyperstation/sound/voice/emotes/female_cough5.ogg differ
diff --git a/hyperstation/sound/voice/emotes/female_cough6.ogg b/hyperstation/sound/voice/emotes/female_cough6.ogg
new file mode 100644
index 0000000000..debd775ef7
Binary files /dev/null and b/hyperstation/sound/voice/emotes/female_cough6.ogg differ
diff --git a/hyperstation/sound/voice/emotes/male_cough1.ogg b/hyperstation/sound/voice/emotes/male_cough1.ogg
new file mode 100644
index 0000000000..142d90b214
Binary files /dev/null and b/hyperstation/sound/voice/emotes/male_cough1.ogg differ
diff --git a/hyperstation/sound/voice/emotes/male_cough2.ogg b/hyperstation/sound/voice/emotes/male_cough2.ogg
new file mode 100644
index 0000000000..7dde1e86ad
Binary files /dev/null and b/hyperstation/sound/voice/emotes/male_cough2.ogg differ
diff --git a/hyperstation/sound/voice/emotes/male_cough3.ogg b/hyperstation/sound/voice/emotes/male_cough3.ogg
new file mode 100644
index 0000000000..35cc3e7aa5
Binary files /dev/null and b/hyperstation/sound/voice/emotes/male_cough3.ogg differ
diff --git a/hyperstation/sound/voice/emotes/male_cough4.ogg b/hyperstation/sound/voice/emotes/male_cough4.ogg
new file mode 100644
index 0000000000..1687844821
Binary files /dev/null and b/hyperstation/sound/voice/emotes/male_cough4.ogg differ
diff --git a/hyperstation/sound/voice/emotes/racc_chitter_1.ogg b/hyperstation/sound/voice/emotes/racc_chitter_1.ogg
new file mode 100644
index 0000000000..35a90dff62
Binary files /dev/null and b/hyperstation/sound/voice/emotes/racc_chitter_1.ogg differ
diff --git a/hyperstation/sound/voice/emotes/racc_chitter_2.ogg b/hyperstation/sound/voice/emotes/racc_chitter_2.ogg
new file mode 100644
index 0000000000..060c442f04
Binary files /dev/null and b/hyperstation/sound/voice/emotes/racc_chitter_2.ogg differ
diff --git a/hyperstation/sound/voice/emotes/racc_chitter_3.ogg b/hyperstation/sound/voice/emotes/racc_chitter_3.ogg
new file mode 100644
index 0000000000..ad750c8a69
Binary files /dev/null and b/hyperstation/sound/voice/emotes/racc_chitter_3.ogg differ
diff --git a/hyperstation/sound/voice/emotes/racc_chitter_4.ogg b/hyperstation/sound/voice/emotes/racc_chitter_4.ogg
new file mode 100644
index 0000000000..538e765e5a
Binary files /dev/null and b/hyperstation/sound/voice/emotes/racc_chitter_4.ogg differ
diff --git a/hyperstation/sound/voice/emotes/racc_chitter_5.ogg b/hyperstation/sound/voice/emotes/racc_chitter_5.ogg
new file mode 100644
index 0000000000..478edce39f
Binary files /dev/null and b/hyperstation/sound/voice/emotes/racc_chitter_5.ogg differ
diff --git a/hyperstation/sound/voice/emotes/racc_chitter_6.ogg b/hyperstation/sound/voice/emotes/racc_chitter_6.ogg
new file mode 100644
index 0000000000..871a47c4c0
Binary files /dev/null and b/hyperstation/sound/voice/emotes/racc_chitter_6.ogg differ
diff --git a/hyperstation/sound/voice/emotes/racc_chitter_7.ogg b/hyperstation/sound/voice/emotes/racc_chitter_7.ogg
new file mode 100644
index 0000000000..0f10084b17
Binary files /dev/null and b/hyperstation/sound/voice/emotes/racc_chitter_7.ogg differ
diff --git a/hyperstation/sound/voice/emotes/racc_chitter_8.ogg b/hyperstation/sound/voice/emotes/racc_chitter_8.ogg
new file mode 100644
index 0000000000..2e141dd37b
Binary files /dev/null and b/hyperstation/sound/voice/emotes/racc_chitter_8.ogg differ
diff --git a/hyperstation/sound/voice/emotes/sniff.ogg b/hyperstation/sound/voice/emotes/sniff.ogg
new file mode 100644
index 0000000000..4e4edf8b8f
Binary files /dev/null and b/hyperstation/sound/voice/emotes/sniff.ogg differ
diff --git a/hyperstation/sound/voice/emotes/tesh_sneeze1.ogg b/hyperstation/sound/voice/emotes/tesh_sneeze1.ogg
new file mode 100644
index 0000000000..b344bbcff9
Binary files /dev/null and b/hyperstation/sound/voice/emotes/tesh_sneeze1.ogg differ
diff --git a/hyperstation/sound/voice/emotes/tesh_sneeze1b.ogg b/hyperstation/sound/voice/emotes/tesh_sneeze1b.ogg
new file mode 100644
index 0000000000..f742865464
Binary files /dev/null and b/hyperstation/sound/voice/emotes/tesh_sneeze1b.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index e0e3907547..c116221b75 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -3961,6 +3961,7 @@
#include "GainStation13\code\modules\mob\living\emote_ported.dm"
#include "GainStation13\code\modules\mob\living\fullness.dm"
#include "GainStation13\code\modules\mob\living\nutribot.dm"
+#include "GainStation13\code\modules\mob\living\species.dm"
#include "GainStation13\code\modules\mob\living\vore\eating\trasheat_lists.dm"
#include "GainStation13\code\modules\reagents\chemistry\reagents\consumable_reagents.dm"
#include "GainStation13\code\modules\reagents\chemistry\reagents\dwarverndrinks.dm"
@@ -3981,7 +3982,6 @@
#include "GainStation13\code\obj\items\circuits.dm"
#include "GainStation13\code\obj\items\holy.dm"
#include "GainStation13\code\obj\items\minor_items.dm"
-#include "GainStation13\code\obj\items\pride_pins.dm"
#include "GainStation13\code\obj\items\sensors\weight_infared.dm"
#include "GainStation13\code\obj\structure\airlock.dm"
#include "GainStation13\code\obj\structure\calorite_doors.dm"
@@ -3995,8 +3995,10 @@
#include "GainStation13\code\obj\weapons\feeder_ebow.dm"
#include "GainStation13\code\structures\trapped_items.dm"
#include "GainStation13\icons\obj\vairous_weapons.dm"
-#include "GainStation13\code\modules\mob\living\species.dm"
-
+#include "hyperstation\code\modules\resize\resize_action.dm"
+#include "hyperstation\code\modules\resize\resizing.dm"
+#include "hyperstation\code\modules\resize\sizechems.dm"
+#include "hyperstation\code\modules\resize\sizegun.dm"
#include "interface\interface.dm"
#include "interface\menu.dm"
#include "interface\stylesheet.dm"