diff --git a/_maps/map_files/LayeniaStation/LayeniaStation.dmm b/_maps/map_files/LayeniaStation/LayeniaStation.dmm index e5feee0bf..bc5414030 100644 --- a/_maps/map_files/LayeniaStation/LayeniaStation.dmm +++ b/_maps/map_files/LayeniaStation/LayeniaStation.dmm @@ -7019,6 +7019,12 @@ /obj/effect/landmark/nuclear_waste_spawner, /turf/open/floor/plasteel, /area/maintenance/disposal/incinerator) +"aRt" = ( +/obj/machinery/power/grounding_rod{ + anchored = 1 + }, +/turf/open/floor/plasteel/dark, +/area/engine/tether) "aRw" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 4 @@ -10931,6 +10937,12 @@ dir = 1; pixel_y = 16 }, +/obj/machinery/power/grounding_rod{ + anchored = 1 + }, +/obj/machinery/power/grounding_rod{ + anchored = 1 + }, /turf/open/floor/plasteel/dark, /area/engine/tether) "bxV" = ( @@ -12621,6 +12633,9 @@ icon_state = "sandyfloor_red"; name = "crimson sand" }, +/obj/structure/cable{ + icon_state = "0-2" + }, /turf/open/floor/plasteel/airless/solarpanel{ initial_gas_mix = "o2=22;n2=82;TEMP=180"; planetary_atmos = 1 @@ -42973,6 +42988,10 @@ name = "parquet" }, /area/chapel/main) +"gQR" = ( +/obj/machinery/safety_tether, +/turf/open/floor/engine, +/area/engine/tether) "gQT" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, @@ -62489,6 +62508,7 @@ dir = 10; name = "air supply pipe" }, +/obj/item/paper/fluff/safety_tether, /turf/open/floor/plasteel/dark{ dir = 1; icon_state = "darkfull_plate" @@ -100318,6 +100338,10 @@ }, /turf/open/floor/plating/layenia, /area/layenia/cloudlayer) +"pkn" = ( +/obj/item/paper/fluff/safety_tether, +/turf/open/floor/plasteel/dark, +/area/engine/tether) "pko" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden/layer3{ dir = 8; @@ -131201,6 +131225,16 @@ /obj/effect/turf_decal/stripes/corner, /turf/open/floor/plasteel/dark, /area/quartermaster/miningdock) +"txg" = ( +/obj/machinery/light{ + dir = 1; + pixel_y = 16 + }, +/obj/machinery/power/grounding_rod{ + anchored = 1 + }, +/turf/open/floor/plasteel/dark, +/area/engine/tether) "txl" = ( /obj/structure/closet/secure_closet/personal, /obj/machinery/power/apc{ @@ -206870,7 +206904,7 @@ yip woc bxU pLi -pLi +aRt lgU qTO woc @@ -207128,7 +207162,7 @@ woc jgm jgm jgm -pLi +pkn cZp woc tgZ @@ -207383,7 +207417,7 @@ ykt wWR woc jgm -jgm +gQR jgm pLi pIP @@ -207896,9 +207930,9 @@ xdO ykt wWR woc -bxU -pLi +txg pLi +aRt uPI xqo woc diff --git a/code/__HELPERS/_hyper_helpers.dm b/code/__HELPERS/_hyper_helpers.dm index 5e23e5857..a898715b3 100644 --- a/code/__HELPERS/_hyper_helpers.dm +++ b/code/__HELPERS/_hyper_helpers.dm @@ -3,6 +3,9 @@ GLOBAL_LIST_EMPTY(cosmetic_chests) GLOBAL_LIST_EMPTY(cosmetic_arms) GLOBAL_LIST_EMPTY(cosmetic_legs) +//A list of safety tether machines for cloud chasms to refer to for emergency teleportation of fallen mobs +GLOBAL_LIST_EMPTY(safety_tethers_list) + /proc/init_cosmetic_parts(datum/cosmetic_part/type, list/global_list) for(var/subtype in subtypesof(type)) var/datum/cosmetic_part/part = new subtype() diff --git a/code/datums/components/chasm.dm b/code/datums/components/chasm.dm index 4a64e5f8c..f64f60494 100644 --- a/code/datums/components/chasm.dm +++ b/code/datums/components/chasm.dm @@ -1,6 +1,7 @@ // Used by /turf/open/chasm and subtypes to implement the "dropping" mechanic /datum/component/chasm var/turf/target_turf + var/turf/linked_turf //turf tile this chasm tile is linked to. Used to get type of turf tile to determine if safety tether applies var/fall_message = "GAH! Ah... where are you?" var/oblivion_message = "You stumble and stare into the abyss before you. It stares back, and you fall into the enveloping dark." @@ -30,9 +31,10 @@ /obj/effect/baseturf_helper )) -/datum/component/chasm/Initialize(turf/target) +/datum/component/chasm/Initialize(turf/target, turf/linked) RegisterSignal(parent, list(COMSIG_MOVABLE_CROSSED, COMSIG_ATOM_ENTERED), .proc/Entered) target_turf = target + linked_turf = linked START_PROCESSING(SSobj, src) // process on create, in case stuff is still there /datum/component/chasm/proc/Entered(datum/source, atom/movable/AM) @@ -94,6 +96,7 @@ return TRUE /datum/component/chasm/proc/drop(atom/movable/AM) + //Make sure the item is still there after our sleep if(!AM || QDELETED(AM)) return @@ -112,44 +115,79 @@ falling_atoms -= AM else + // send to oblivion AM.visible_message("[AM] falls into [parent]!", "[oblivion_message]") + if (isliving(AM)) var/mob/living/L = AM L.notransform = TRUE L.Stun(200) - L.resting = TRUE + if(!issilicon(AM)) + L.resting = TRUE if(L.client && check_rights_for(L.client, R_FUN)) playsound(AM, pick('hyperstation/sound/misc/yodadeath.ogg', 'hyperstation/sound/misc/fallingthroughclouds.ogg', 'hyperstation/sound/misc/goofy.ogg', 'hyperstation/sound/misc/wilhelm.ogg'), 100, 0) else if(prob(5)) playsound(AM, pick('hyperstation/sound/misc/yodadeath.ogg', 'hyperstation/sound/misc/fallingthroughclouds.ogg', 'hyperstation/sound/misc/goofy.ogg', 'hyperstation/sound/misc/wilhelm.ogg'), 100, 0) - var/oldtransform = AM.transform - var/oldcolor = AM.color - var/oldalpha = AM.alpha - animate(AM, transform = matrix() - matrix(), alpha = 0, color = rgb(0, 0, 0), time = 10) - for(var/i in 1 to 5) - //Make sure the item is still there after our sleep - if(!AM || QDELETED(AM)) - return - AM.pixel_y-- - sleep(2) + if(linked_turf.name == "clouds" && (iscyborg(AM) || iscarbon(AM))) + var/mob/living/victim = AM + + var/tether_number = GLOB.safety_tethers_list.len + + //If safety tethers are present, get one from the global list to teleport the body to if operational + if(tether_number > 0) + if(tether_number == 1) + + // If teleportation fails + if(!GLOB.safety_tethers_list[1].bungee_teleport(victim)) + finishdrop(AM) + else + + //Just in case multiple safety tethers are present + if(!GLOB.safety_tethers_list[rand(1,GLOB.safety_tethers_list.len)].bungee_teleport(victim)) + finishdrop(AM) + if(isliving(AM)) + var/mob/living/L = AM + L.notransform = FALSE + else + finishdrop(AM) + else + finishdrop(AM) + + +/datum/component/chasm/proc/finishdrop(atom/movable/AM) + + var/oldalpha = AM.alpha + var/oldcolor = AM.color + var/oldtransform = AM.transform + + //Animate our falling items, then delete them. + animate(AM, transform = matrix() - matrix(), alpha = 0, color = rgb(0, 0, 0), time = 10) + + for(var/i in 1 to 5) //Make sure the item is still there after our sleep if(!AM || QDELETED(AM)) return + AM.pixel_y-- + sleep(2) - if(iscyborg(AM)) - var/mob/living/silicon/robot/S = AM - qdel(S.mmi) + //Make sure the item is still there after our sleep + if(!AM || QDELETED(AM)) + return - falling_atoms -= AM - qdel(AM) - if(AM && !QDELETED(AM)) //It's indestructible - var/atom/parent = src.parent - parent.visible_message("[parent] spits out [AM]!") - AM.alpha = oldalpha - AM.color = oldcolor - AM.transform = oldtransform - AM.throw_at(get_edge_target_turf(parent,pick(GLOB.alldirs)),rand(1, 10),rand(1, 10)) + if(iscyborg(AM)) + var/mob/living/silicon/robot/S = AM + qdel(S.mmi) + + falling_atoms -= AM + qdel(AM) + if(AM && !QDELETED(AM)) //It's indestructible + var/atom/parent = src.parent + parent.visible_message("[parent] spits out [AM]!") + AM.alpha = oldalpha + AM.color = oldcolor + AM.transform = oldtransform + AM.throw_at(get_edge_target_turf(parent,pick(GLOB.alldirs)),rand(1, 10),rand(1, 10)) \ No newline at end of file diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index 114492543..cd18282d5 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -157,8 +157,13 @@ Class Procs: /obj/machinery/proc/locate_machinery() return -/obj/machinery/process()//If you dont use process or power why are you here - return PROCESS_KILL +/obj/machinery/process() + + //Here because if the machine doesn't have a process code attached to it or if PROCESS_KILL is returned it will no longer draw power. + if(is_operational()) + return 1 + else + return 0 /obj/machinery/proc/process_atmos()//If you dont use process why are you here return PROCESS_KILL diff --git a/code/game/turfs/simulated/chasm.dm b/code/game/turfs/simulated/chasm.dm index 88211a9fb..8cdd7be14 100644 --- a/code/game/turfs/simulated/chasm.dm +++ b/code/game/turfs/simulated/chasm.dm @@ -12,16 +12,20 @@ /turf/open/chasm/Initialize() . = ..() - AddComponent(/datum/component/chasm, SSmapping.get_turf_below(src)) + AddComponent(/datum/component/chasm, SSmapping.get_turf_below(src), src) /turf/open/chasm/proc/set_target(turf/target) var/datum/component/chasm/chasm_component = GetComponent(/datum/component/chasm) chasm_component.target_turf = target +// Only runs in the case of things like pride's mirror who are just using the turf as an easy reference + /turf/open/chasm/proc/drop(atom/movable/AM) + var/datum/component/chasm/chasm_component = GetComponent(/datum/component/chasm) chasm_component.drop(AM) + /turf/open/chasm/MakeSlippery(wet_setting, min_wet_time, wet_time_to_add, max_wet_time, permanent) return @@ -137,4 +141,4 @@ /turf/open/chasm/cloud/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir) underlay_appearance.icon = 'icons/turf/space.dmi' underlay_appearance.plane = PLANE_SPACE - return TRUE + return TRUE \ No newline at end of file diff --git a/hyperstation/code/game/machinery/safety_tether.dm b/hyperstation/code/game/machinery/safety_tether.dm new file mode 100644 index 000000000..1955d7599 --- /dev/null +++ b/hyperstation/code/game/machinery/safety_tether.dm @@ -0,0 +1,254 @@ +//Teleports players who fall off cloud chasm tiles to itself, drawing power in the process. + +//bugs; power draw not working + +//Maybe if separating into multiple objects like with the grav gen, have it arc tesla lightning to each corner upon teleport? + + +// Have it pull back all player mobs, and all borgs if possible +// have chasm tiles, when something falls in send a signal that the safety tether catches, containing the chasm turf and the mob +// then the tether can either report it's inactive to the tile so that it then deletes the entity, or teeleport the player + +// create global list of safety tethers in the world to then process + +//Easy definitions to have the machine talk on the appropriate frequencies. +#define SPEAKCOMMON(message) radio.talk_into(src, message, null) +#define SPEAKMEDICAL(message) radio.talk_into(src, message, RADIO_CHANNEL_MEDICAL) +#define SPEAKSCIENCE(message) radio.talk_into(src, message, RADIO_CHANNEL_SCIENCE) + +/obj/machinery/safety_tether + name = "safety tether" + desc = "A gargantuan machine that performs emergency teleportations on those unlucky or clumsy enough to slip off the edge." + density = FALSE + icon = 'hyperstation/icons/obj/machinery/safety_tether.dmi' + icon_state = "safety_tether" + verb_say = "states" + + //Shifts the starting location so as to center the machine + pixel_x = -32 + pixel_y = -32 + + //Set width and height in case we make this dense + bound_width = 96 + bound_height = 96 + + //We don't want this getting destroyed since it cannot be remade. + move_resist = INFINITY + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + + //var/obj/machinery/computer/tether_computer/connected = null //So we remember the connected clone machine. + + //Here for easy balancing and possibly changing it between different tether objects or if upgrades occur. + + var/dismember_prob = 50 //Probability of dismembering 2 limbs rather than 1 + + //Max and min amounts of clone damage for organics + var/cloneloss_min = 45 + var/cloneloss_max = 70 + + //Amount of subject's blood to remove on successful teleport + var/bleed_ratio = 0.25 + + //Max and min amounts of burn damage for silicates. + /* + Cyborg health guide + 100; max health + <50; module 3 offline + <0; module 2 offline + <-50; module 3 offline + -100+; death + */ + var/silicon_burn_min = 120 + var/silicon_burn_max = 140 + + var/brightness_on = 4 //Range of light when on + light_power = 1.5 //Strength of the light when on + light_color = LIGHT_COLOR_CYAN + + //Defines the center point around which lighting is located + var/atom/light_source + + use_power = IDLE_POWER_USE + power_channel = EQUIP + idle_power_usage = 1000 + var/teleport_power_draw = 900000 //Uses about a fourth of the upgraded power cell plus default in APCs + var/tesla_power_ratio = 0.75 //ratio of its actual power draw to the tesla it creates + + critical_machine = TRUE //If this machine is critical to station operation and should have the area be excempted from power failures. + + var/internal_radio = TRUE + var/obj/item/radio/radio + + //Give it permission to talk on both medical and science frequencies, as with geneticists + var/radio_key = /obj/item/encryptionkey/headset_medsci + +/obj/machinery/safety_tether/Initialize() + . = ..() + + //Adds this to the global list of safety tethers in the world to pull from when chasms attempt to drop mobs + GLOB.safety_tethers_list += src + + if(internal_radio) + radio = new(src) + radio.keyslot = new radio_key + radio.subspace_transmission = TRUE + radio.canhear_range = 0 + radio.recalculateChannels() + + //ensures light is properly centered around the tether. Removes lighting system's pixel approximation that breaks it. + light_source = get_turf(src) + update_icon() + power_change() //Here to start lights on ititialization. + +/obj/machinery/safety_tether/Destroy() + QDEL_NULL(radio) + GLOB.safety_tethers_list -= src + light_source.set_light(0) + . = ..() + +/obj/machinery/safety_tether/doMove(atom/destination) + . = ..() + //ensures light is properly centered around the tether. Removes lighting system's pixel approximation that breaks it. + light_source.set_light(0) + light_source = get_turf(src) + return . + +/obj/machinery/safety_tether/update_icon() + cut_overlays() + if(is_operational()) + add_overlay("operational_overlay") + + +/obj/machinery/safety_tether/examine(mob/user) + . = ..() + if(is_operational()) + . += "The safety tether's currently protecting the station." + else + . += "The safety tether's offline." + + +/obj/machinery/safety_tether/attack_ai(mob/user) + return examine(user) + +//Returns true if teleport is successful, false otherwise +/obj/machinery/safety_tether/proc/bungee_teleport(mob/living/M) + + if(ismovableatom(M) && is_operational() != 0 && do_teleport(M, get_turf(src), channel = TELEPORT_CHANNEL_BLUESPACE)) + use_power(teleport_power_draw) + + //Style points + playsound(src, 'sound/magic/lightningbolt.ogg', 100, 1, extrarange = 5) + // TESLA_MOB_DAMAGE | TESLA_OBJ_DAMAGE | TESLA_MOB_STUN | TESLA_ALLOW_DUPLICATES + tesla_zap(src, 3, teleport_power_draw * tesla_power_ratio, TESLA_MOB_DAMAGE | FALSE | TESLA_MOB_STUN | FALSE) //Set to zap harmlessly but in a way that looks cool + + M.spawn_gibs() + M.emote("scream") + + log_admin("[M] ([key_name(M)]) was saved by the Safety Tether.") + + if(iscarbon(M)) + + var/mob/living/carbon/Carbon = M + + to_chat(Carbon, "Buzzing static snaps taut on your chest....") + Carbon.adjustCloneLoss(rand(cloneloss_min, cloneloss_max)) + + //Random limb removal + var/dismember_num = 1 + + //Dismember two limbs + if(prob(dismember_prob)) + dismember_num = 2 + + var/dismembered_arm = FALSE + var/dismembered_leg = FALSE + + for(var/obj/item/bodypart/BP in Carbon.bodyparts) + if(BP.body_part != CHEST && BP.body_part != HEAD) //I am not ready to find out what happens if your chest is missing + var/zone = BP.body_zone + //Checks to ensure that only one arm and one leg each are dismembered, to prevent severe disabling like 2 arms being removed. + if(zone == BODY_ZONE_L_ARM || zone == BODY_ZONE_R_ARM) + if(!dismembered_arm) + dismembered_arm = TRUE + BP.dismember() + + if(zone == BODY_ZONE_L_LEG || zone == BODY_ZONE_R_LEG) + if(!dismembered_leg) + dismembered_leg = TRUE + BP.dismember() + + dismember_num -= 1 + + //We've dismembered enough limbs. + if(dismember_num <= 0) + break + + //Bleed our pal a little + M.blood_volume -= BLOOD_VOLUME_NORMAL * M.blood_ratio * bleed_ratio + + src.visible_message("[src] spits out [M] and viscera!") + if(internal_radio) + + //Area name gotten just in case the locale of it's moved from engineering when mapmaking. + var/area/A = get_area(get_turf(src)) + var/area_name = A.name + SPEAKMEDICAL("The safety tether's caught the would-be crater [M] at the [area_name].") + + //All this ended up causing issues by canting the player 90 degrees due to the player having enforced rest at the time of teleportation + //animate(M, transform = oldtransform, alpha = oldalpha, color = oldcolor, time = 10) + + //M.transform = oldtransform + //M.alpha = oldalpha + //M.color = oldcolor + else + if(issilicon(M)) + var/mob/living/silicon/S = M + to_chat(S, "Your circuits spark, slag, and pop as overwhelming white noise crackles and YANKS...") + S.apply_damage_type(damage = rand(silicon_burn_min, silicon_burn_max), damagetype = BURN) + + src.visible_message("[src] spits out [M] and oily, smoking circuits!") + if(internal_radio) + + //Area name gotten just in case the locale of it's moved from engineering when mapmaking. + var/area/A = get_area(get_turf(src)) + var/area_name = A.name + SPEAKSCIENCE("The safety tether's caught the would-be crater [M] at the [area_name].") + + return TRUE + + else + log_admin("[M] ([key_name(M)]) was failed by the Safety Tether and fell into the clouds.") + return FALSE + +//Updates machine icon and lighting every time power in the area changes +/obj/machinery/safety_tether/power_change() + . = ..() + if(light_source) + if(stat & NOPOWER) + SPEAKCOMMON("The Safety Tether's shut down from a lack of power.") + light_source.set_light(0) + else + SPEAKCOMMON("The Safety Tether is back online.") + light_source.set_light(brightness_on, light_power, light_color) + update_icon() + +/* + * Manual -- A big ol' manual. + */ + +/obj/item/paper/fluff/safety_tether + name = "paper - 'Safety Tether Introduction" + info = {"

Kinaris IVA Safety Tether

+ Hello, if you’re reading this note, that means you’ve been selected to work in our Retrograde Tether Program. Few portions of Andromeda have such outdated engrams as Layenia does, so getting a proper Tether raised quite a lot of pushback. However, working in a gas giant has been deemed hazardous enough to warrant the allowance of a Safety Tether.

+ For those unfamiliar with the technology of a Safety Tether; + First and foremost; Understand that it needs to be powered at all times. Failure to supply constant energy to the tether can and will prevent its function, and doom personnel to the clouds beneath if any are so unlucky as to fall in the interim.
+ Secondly; Speaking of clouds. when powered, the tether will rescue any humanoid beings or borgs who would slip into them.
+ Thirdly; Large amounts of electricity are siphoned upon any and every use of the Safety Tether, enough so to arc through the air and hopefully into the grounding rods supplied nearby. The transit of matter instantaneously and unplanned to one location from anywhere on the planet is, understandably, enough to drain a fourth of the APCs energy or so when activated.

+ + However, the old engram Layenia works under is faulty at best, resulting in blood loss, loss of limbs, and painful cellular damages or burns to personnel and cyborgs respectively when falling into the clouds. Because of this, caution is still highly advised when working on the exterior of Layenia. A radio has been retrofitted to the device to call appropriate medical or scientific services onscene as an added precaution.

+ Should any more information be needed on the tether, please contact your local sector executive."} + +//#undef SPEAK +#undef SPEAKCOMMON +#undef SPEAKMEDICAL +#undef SPEAKSCIENCE \ No newline at end of file diff --git a/hyperstation/icons/obj/machinery/safety_tether.dmi b/hyperstation/icons/obj/machinery/safety_tether.dmi new file mode 100644 index 000000000..fcf4b788b Binary files /dev/null and b/hyperstation/icons/obj/machinery/safety_tether.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 8d5177b7f..6236e1025 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3116,6 +3116,7 @@ #include "hyperstation\code\game\area\Space_Station_13_areas.dm" #include "hyperstation\code\game\machinery\announcement_system.dm" #include "hyperstation\code\game\machinery\lore_terminal.dm" +#include "hyperstation\code\game\machinery\safety_tether.dm" #include "hyperstation\code\game\machinery\computer\timeclock.dm" #include "hyperstation\code\game\objects\effects\landmarks.dm" #include "hyperstation\code\game\objects\effects\temporary_visuals\miscellaneous.dm"