diff --git a/code/__defines/dcs/signals.dm b/code/__defines/dcs/signals.dm index c4290a9456..873e6776ed 100644 --- a/code/__defines/dcs/signals.dm +++ b/code/__defines/dcs/signals.dm @@ -857,6 +857,9 @@ ///from /datum/species/xenochimera/handle_environment_special() #define COMSIG_XENOCHIMERA_COMPONENT "xenochimera_component" +// Hose Connector Component +#define COMSIG_HOSE_FORCEPUMP "hose_force_pump" + //Unittest data update #ifdef UNIT_TEST #define COMSIG_UNITTEST_DATA "unittest_send_data" diff --git a/code/__defines/hoses.dm b/code/__defines/hoses.dm index 056949fac8..d416a44766 100644 --- a/code/__defines/hoses.dm +++ b/code/__defines/hoses.dm @@ -1,3 +1,5 @@ #define HOSE_INPUT "Input"// Only pull liquid. #define HOSE_OUTPUT "Output"// Only push liquid. #define HOSE_NEUTRAL "Neutral"// Equalize liquids, not super efficient, can waste reagents due to rounding. + +#define HOSE_MAX_DISTANCE 10 diff --git a/code/datums/components/reagent_hose/connector.dm b/code/datums/components/reagent_hose/connector.dm index 2db8950d68..d909da9c0c 100644 --- a/code/datums/components/reagent_hose/connector.dm +++ b/code/datums/components/reagent_hose/connector.dm @@ -1,5 +1,6 @@ /datum/component/hose_connector var/name = "" + dupe_mode = COMPONENT_DUPE_ALLOWED VAR_PROTECTED/obj/carrier = null VAR_PROTECTED/flow_direction = HOSE_NEUTRAL VAR_PROTECTED/datum/hose/my_hose = null @@ -17,6 +18,7 @@ connector_number = CL.len + 1 RegisterSignal(carrier, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine)) RegisterSignal(carrier, COMSIG_MOVABLE_MOVED, PROC_REF(move_react)) + RegisterSignal(carrier, COMSIG_HOSE_FORCEPUMP, PROC_REF(force_pump)) carrier.verbs |= /atom/proc/disconnect_hose START_PROCESSING(SSobj, src) @@ -25,6 +27,7 @@ STOP_PROCESSING(SSobj, src) UnregisterSignal(carrier, COMSIG_PARENT_EXAMINE) UnregisterSignal(carrier, COMSIG_MOVABLE_MOVED) + UnregisterSignal(carrier, COMSIG_HOSE_FORCEPUMP) carrier.verbs -= /atom/proc/disconnect_hose carrier = null if(my_hose) @@ -43,10 +46,35 @@ /datum/component/hose_connector/proc/get_flow_direction() return flow_direction -/datum/component/hose_connector/output/process() - return +/datum/component/hose_connector/proc/get_id() + return "[name] #[connector_number]" + +/datum/component/hose_connector/proc/connected_reagents() + return carrier.reagents + +/datum/component/hose_connector/process() + // Return reagents to source if no hose, lossy to avoid exploits + if(!my_hose) + if(!reagents.total_volume) + reagents.trans_to_holder(connected_reagents(), reagents.maximum_volume) + reagents.clear_reagents() // Wipe it to avoid exploits + return + var/datum/reagents/connected_to = connected_reagents() + if(!connected_to) // Emergency. the vorebelly was deleted or something. Lets just hard lock that out from maintaining state by disconnecting the tube. + reagents.clear_reagents() + my_hose.disconnect() + return + handle_pump(connected_to) + +/datum/component/hose_connector/proc/handle_pump(var/datum/reagents/connected_to) + PROTECTED_PROC(TRUE) + ASSERT(connected_to) + // Drain our connector back into tank, and then fill it randomly. The hose handles swapping. + reagents.trans_to_holder(connected_to, reagents.maximum_volume) + connected_to.trans_to_holder(reagents, rand(1,reagents.maximum_volume)) /datum/component/hose_connector/proc/force_pump() + SIGNAL_HANDLER process() if(prob(5)) carrier.visible_message(span_infoplain(span_bold("\The [carrier]") + " gurgles as it pumps fluid.")) @@ -55,6 +83,8 @@ if(istype(C)) if(C.my_hose) return FALSE + if(C.flow_direction == HOSE_NEUTRAL || flow_direction == HOSE_NEUTRAL) // Always allowed + return TRUE if(C.flow_direction in (list(HOSE_INPUT, HOSE_OUTPUT) - flow_direction)) return TRUE return FALSE @@ -68,10 +98,47 @@ /datum/component/hose_connector/proc/connect(var/datum/hose/H = null) my_hose = H -/datum/component/hose_connector/proc/setup_hoses(var/datum/component/hose_connector/target, var/distancetonode) - if(target) - var/datum/hose/H = new() - H.set_hose(src, target, distancetonode) +/datum/component/hose_connector/proc/setup_hoses(var/datum/component/hose_connector/target, var/distancetonode, var/mob/user) + if(!target || QDELETED(target)) + to_chat(user,span_danger("What you were connecting to has stopped existing! Ohno!")) + return FALSE + + // Logic for handling two mobs at once would be a mess of option selections and prefs... + if(istype(src,/datum/component/hose_connector/inflation) && istype(target,/datum/component/hose_connector/inflation)) + to_chat(user,span_notice("Nothing would flow between \the [get_carrier()] and \the [target.get_carrier()] without anything to pump it!")) + return FALSE + + // Check for vore inflation connectors. + if(istype(src,/datum/component/hose_connector/inflation) || istype(target,/datum/component/hose_connector/inflation)) + // Handle the connection target once we setup the hose. Needs to be done like this as either ends can be the inflation connector + // Also has to be done on finalize, as players would be able to click one then the other, then potentially drop or do other stuff with the hose! + var/datum/component/hose_connector/inflation/I = src + if(istype(I)) + if(!I.inflation_setup(user,target)) + return FALSE + else + I = target + if(istype(I)) + if(!I.inflation_setup(user,src)) + return FALSE + else // Good going, you broke it + to_chat(user,span_notice("You're not sure what happened, but you couldn't connect the hose...")) + return FALSE + else + to_chat(user, span_notice("You connect the [src] to \the [target].")) + + // Handle invalid vorebellies, has to be done after inflation_setup() + if(!src.connected_reagents()) + to_chat(user,span_warning("\The [get_carrier()] doesn't seem ready to connect yet.")) + return FALSE + if(!target.connected_reagents()) + to_chat(user,span_warning("\The [target.get_carrier()] doesn't seem ready to connect yet.")) + return FALSE + + // Hose prepared! + var/datum/hose/H = new() + H.set_hose(src, target, distancetonode, user) + return TRUE /datum/component/hose_connector/proc/get_pairing() RETURN_TYPE(/datum/component/hose_connector) @@ -85,7 +152,9 @@ /datum/component/hose_connector/proc/on_examine(datum/source, mob/user, list/examine_texts) SIGNAL_HANDLER var/datum/component/hose_connector/hose_pair = my_hose?.get_pairing(src) - if(hose_pair) + if(istype(hose_pair,/datum/component/hose_connector/inflation)) + hose_pair = "\the [hose_pair.name]" // Slightly different, so it shows the belly attached + else if(hose_pair) hose_pair = "\the [hose_pair.get_carrier()]" else hose_pair = "nothing" @@ -101,30 +170,6 @@ // Handle distance check if too far my_hose.update_beam() -/* - * Subtypes - */ - -/// Pumps reagents out of carrier -/datum/component/hose_connector/input - name = "hose input" - flow_direction = HOSE_INPUT - -/datum/component/hose_connector/input/process() - if(carrier) - reagents.trans_to_obj(carrier, reagents.maximum_volume) - -/// Pumps reagents into carrier -/datum/component/hose_connector/output - name = "hose output" - flow_direction = HOSE_OUTPUT - -/datum/component/hose_connector/output/process() - if(carrier && my_hose) - carrier.reagents.trans_to_holder(reagents, reagents.maximum_volume) - else if(reagents.total_volume && carrier && !my_hose) - reagents.trans_to_obj(carrier, reagents.maximum_volume) - /* * Support procs/verbs */ @@ -138,15 +183,39 @@ var/list/available_sockets = list() for(var/datum/component/hose_connector/HC in GetComponents(/datum/component/hose_connector)) if(HC.get_hose()) - available_sockets |= HC + available_sockets[HC.get_id()] = HC if(!LAZYLEN(available_sockets)) return if(available_sockets.len == 1) - var/datum/component/hose_connector/AC = available_sockets[1] + var/key = available_sockets[1] + var/datum/component/hose_connector/AC = available_sockets[key] AC.disconnect_action(usr) else var/choice = tgui_input_list(usr, "Select a target hose connector.", "Socket Disconnect", available_sockets) if(choice) - var/datum/component/hose_connector/AC = choice + var/datum/component/hose_connector/AC = available_sockets[choice] AC.disconnect_action(usr) + + +/* + * Standard subtypes + */ + +/// Pumps reagents out of carrier +/datum/component/hose_connector/input + name = "hose input" + flow_direction = HOSE_INPUT + +/datum/component/hose_connector/input/handle_pump(var/datum/reagents/connected_to) + ASSERT(connected_to) + reagents.trans_to_holder(connected_to, reagents.maximum_volume) + +/// Pumps reagents into carrier +/datum/component/hose_connector/output + name = "hose output" + flow_direction = HOSE_OUTPUT + +/datum/component/hose_connector/output/handle_pump(var/datum/reagents/connected_to) + ASSERT(connected_to) + connected_to.trans_to_holder(reagents, reagents.maximum_volume) diff --git a/code/datums/components/reagent_hose/datum.dm b/code/datums/components/reagent_hose/datum.dm index 12741f3ea6..35b2887604 100644 --- a/code/datums/components/reagent_hose/datum.dm +++ b/code/datums/components/reagent_hose/datum.dm @@ -10,7 +10,7 @@ VAR_PRIVATE/hose_color = "#ffffff" - VAR_PRIVATE/initial_distance = 7 + VAR_PRIVATE/initial_distance = HOSE_MAX_DISTANCE VAR_PRIVATE/datum/beam/current_beam = null /datum/hose/proc/get_pairing(var/datum/component/hose_connector/target) @@ -104,7 +104,7 @@ new_col = reagent_node2.get_color() // We are in the beam! - qdel_swap(current_beam, A.Beam(B, icon_state = "hose", beam_color = new_col, maxdistance = world.view, beam_type = /obj/effect/ebeam/hose)) + qdel_swap(current_beam, A.Beam(B, icon_state = "hose", beam_color = new_col, maxdistance = (HOSE_MAX_DISTANCE + 1), beam_type = /obj/effect/ebeam/hose)) return TRUE diff --git a/code/datums/components/reagent_hose/inflation.dm b/code/datums/components/reagent_hose/inflation.dm new file mode 100644 index 0000000000..8006a6e23b --- /dev/null +++ b/code/datums/components/reagent_hose/inflation.dm @@ -0,0 +1,201 @@ +/* + * Inflation subtype, Big and round! + */ +/datum/component/hose_connector/inflation + flow_direction = HOSE_NEUTRAL + var/connection_mode = CHEM_INGEST + var/mob/living/carbon/human/human_owner + +/datum/component/hose_connector/inflation/Initialize() + if(!ishuman(parent)) + return COMPONENT_INCOMPATIBLE + . = ..() + human_owner = parent + remove_verb(human_owner,/atom/proc/disconnect_hose) + +/datum/component/hose_connector/inflation/Destroy() + human_owner = null + . = ..() + +/datum/component/hose_connector/inflation/on_examine(datum/source, mob/user, list/examine_texts) + return + +/datum/component/hose_connector/inflation/proc/get_destination_name() + switch(connection_mode) + if(CHEM_INGEST) + return "mouth" + if(CHEM_VORE) + return human_owner?.vore_selected?.name ? sanitize(human_owner.vore_selected.name) : "belly" + if(CHEM_BLOOD) + return "bloodstream" + return "something" + +/datum/component/hose_connector/inflation/get_id() + return "\The [human_owner]'s [get_destination_name()]" + +// Adding and removing the verb is more complex on humans... This code also expects only ONE hose connector +/datum/component/hose_connector/inflation/connect(datum/hose/H) + . = ..() + add_verb(human_owner,/atom/proc/disconnect_hose) + +/datum/component/hose_connector/inflation/remove_hose() + remove_verb(human_owner,/atom/proc/disconnect_hose) + . = ..() + +// Succ command center +/datum/component/hose_connector/inflation/proc/inflation_setup(var/mob/user,var/datum/component/hose_connector/other) + if(!other || QDELETED(other)) + to_chat(user,span_danger("You couldn't connect the hose, as the connection stopped existing! Ohno!")) + return FALSE + + // Check for destinations + var/list/options = list("Mouth") + if(human_owner?.vore_selected) + options.Add("Belly ([sanitize(human_owner.vore_selected.name)])") + if(!human_owner.isSynthetic()) // Results in a lot of bad behaviors... + options.Add("Bloodstream") + + // Choose destination + var/choice = tgui_alert(user, "Select where this hose connects.", "Hose Connection", options) + if(!user.Adjacent(human_owner) || !choice) + to_chat(user,span_notice("You decide not to connect \the [human_owner] to the hose.")) + return FALSE + + // Setup the connection to mouth, vore, or blood + var/feedback = "" + switch(choice) + if("Mouth") + connection_mode = CHEM_INGEST + feedback = "mouth" + if("Bloodstream") + connection_mode = CHEM_BLOOD + feedback = span_danger("bloodstream") + else + connection_mode = CHEM_VORE // Anything else is a vore belly name + if(human_owner.vore_selected) + feedback = sanitize(human_owner.vore_selected.name) + + // Display action + name = "[human_owner]'s [feedback]" + user.visible_message("\The [user] starts to connect the hose to \the [human_owner]'s [feedback]...") + if(!do_after(user,7 SECONDS,human_owner)) + to_chat(user,span_warning("You couldn't connect the hose!")) + return FALSE + if(other.get_hose() || get_hose()) // SHouldn't be connected to anything yet! + to_chat(user,span_warning("You couldn't connect the hose, another hose is already connected!")) + return FALSE + if(connection_mode == CHEM_BLOOD) //OWCH! + human_owner.adjustBruteLossByPart(10,BP_TORSO) + if(human_owner.can_pain_emote) // Doing this probably doesn't feel too good + human_owner.emote("pain") + to_chat(user, span_notice("You connect the hose to \the [human_owner]'s [feedback]...")) + return TRUE + +/datum/component/hose_connector/inflation/connected_reagents() + switch(connection_mode) + if(CHEM_INGEST) + return human_owner.ingested + if(CHEM_VORE) + return human_owner?.vore_selected?.reagents + if(CHEM_BLOOD) + // Inflating + var/datum/component/hose_connector/other = get_pairing() + if(!other || other.flow_direction == HOSE_OUTPUT) + return human_owner.bloodstr // Pump into blood reagents + // Draining + if(prob(30) && my_hose) // NEVER put normal reagents into the vessel... + return human_owner.vessel // Suck blood + return human_owner.bloodstr // Suck reagents from blood + +/datum/component/hose_connector/inflation/handle_pump(var/datum/reagents/connected_to) + ASSERT(connected_to) + var/datum/component/hose_connector/other = get_pairing() + var/rate = reagents.maximum_volume * 0.5 + if(connection_mode == CHEM_BLOOD) + rate = 10 // SLOW here + else + if(other.flow_direction == HOSE_OUTPUT || other.flow_direction == HOSE_NEUTRAL) // If filling mouth check prefs for belly fluid consumption. + if(connection_mode == CHEM_INGEST) + if(!human_owner.consume_liquid_belly) + for(var/datum/reagent/R in reagents.reagent_list) + if(R.from_belly) + to_chat(human_owner, span_warning("You can't consume that, it contains something produced from a belly!")) + my_hose.disconnect() // Pop! + return + if(connection_mode == CHEM_VORE) + if(!human_owner.receive_reagents) + to_chat(human_owner, span_warning("You can't transfer reagents into your [sanitize(human_owner.vore_selected.name)], your prefs dont allow it!")) + my_hose.disconnect() // Pop! + return + if(other.flow_direction == HOSE_INPUT || other.flow_direction == HOSE_NEUTRAL) // If filling mouth check prefs for belly fluid consumption. + if(connection_mode == CHEM_VORE) + if(!human_owner.give_reagents) + to_chat(human_owner, span_warning("You can't transfer reagents from your [sanitize(human_owner.vore_selected.name)], your prefs dont allow it!")) + my_hose.disconnect() // Pop! + return + + // Inflation station + switch(other.flow_direction) + if(HOSE_OUTPUT) + // inflating us + if(reagents.total_volume > 0) + reagents.vore_trans_to_mob(human_owner, rate, connection_mode, 1, 0, human_owner.vore_selected) + if(HOSE_INPUT) + // draining us + if(connected_to.total_volume > 0) + connected_to.trans_to_holder(reagents,rate) + if(HOSE_NEUTRAL) + // Sharing with us + reagents.trans_to_holder(connected_to, reagents.maximum_volume) // Load our current reagents back into tank, it's mixed! + connected_to.trans_to_holder(reagents, rand(1,reagents.maximum_volume) ) // Fill back up to a random amount + + if(connection_mode == CHEM_VORE && human_owner.vore_selected.count_liquid_for_sprite) + human_owner.handle_belly_update() + + if(prob(5) && (reagents.total_volume > 0 || connected_to.total_volume > 0)) + var/atom/pumper = other.get_carrier() + pumper.visible_message(span_infoplain(span_bold("\The [pumper]") + " gurgles.")) + +/* + * Inflation subtype, Borg edition. Geewiz janihound how come the AI lets you have two? + */ + +/// Pumps reagents out of carrier +/datum/component/hose_connector/input/borg + VAR_PRIVATE/mob/living/silicon/robot/borg_owner + +/datum/component/hose_connector/input/borg/Initialize() + if(!isrobot(parent)) + return COMPONENT_INCOMPATIBLE + . = ..() + borg_owner = parent + +/datum/component/hose_connector/input/borg/Destroy() + borg_owner = null + . = ..() + +/datum/component/hose_connector/input/borg/connected_reagents() + return borg_owner?.vore_selected?.reagents + +/datum/component/hose_connector/input/borg/on_examine(datum/source, mob/user, list/examine_texts) + return + +/// Pumps reagents into carrier +/datum/component/hose_connector/output/borg + VAR_PRIVATE/mob/living/silicon/robot/borg_owner + +/datum/component/hose_connector/output/borg/Initialize() + if(!isrobot(parent)) + return COMPONENT_INCOMPATIBLE + . = ..() + borg_owner = parent + +/datum/component/hose_connector/output/borg/Destroy() + borg_owner = null + . = ..() + +/datum/component/hose_connector/output/borg/connected_reagents() + return borg_owner?.vore_selected?.reagents + +/datum/component/hose_connector/output/borg/on_examine(datum/source, mob/user, list/examine_texts) + return diff --git a/code/datums/components/reagent_hose/item.dm b/code/datums/components/reagent_hose/item.dm index e50de303c7..b250a07717 100644 --- a/code/datums/components/reagent_hose/item.dm +++ b/code/datums/components/reagent_hose/item.dm @@ -13,7 +13,7 @@ icon_state = "hose" origin_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 1) amount = 1 - max_amount = 10 + max_amount = HOSE_MAX_DISTANCE w_class = ITEMSIZE_SMALL no_variants = TRUE stacktype = /obj/item/stack/hose @@ -40,28 +40,29 @@ if(!HC.get_hose()) if(REMB) if(HC.get_flow_direction() == HOSE_NEUTRAL || HC.get_flow_direction() != REMB.get_flow_direction()) - available_sockets |= HC + available_sockets[HC.get_id()] = HC else - available_sockets |= HC + available_sockets[HC.get_id()] = HC if(LAZYLEN(available_sockets)) if(available_sockets.len == 1) - var/datum/component/hose_connector/AC = available_sockets[1] + var/key = available_sockets[1] + var/datum/component/hose_connector/AC = available_sockets[key] if(REMB && REMB.get_carrier() == AC.get_carrier()) to_chat(user, span_notice("Connecting \the [REMB.get_carrier()] to itself seems like a bad idea. You wind \the [src] back up.")) remembered = null // Unintuitive if it does not reset state else if(REMB && REMB.valid_connection(AC)) var/distancetonode = get_dist(REMB.get_carrier(),AC.get_carrier()) - if(distancetonode > world.view) + if(distancetonode > HOSE_MAX_DISTANCE) to_chat(user, span_notice("\The [src] would probably burst if it were this long. You wind \the [src] back up.")) remembered = null // Unintuitive if it does not reset state else if(distancetonode <= amount) - to_chat(user, span_notice("You join \the [REMB] to \the [AC].")) - REMB.setup_hoses(AC,distancetonode) - use(distancetonode) + if(REMB.setup_hoses(AC,distancetonode,user)) + use(distancetonode) remembered = null + else to_chat(user, span_notice("You do not have enough tubing to connect the sockets. You wind \the [src] back up.")) remembered = null // Unintuitive if it does not reset state @@ -74,7 +75,7 @@ var/choice = tgui_input_list(user, "Select a target hose connector.", "Socket Selection", available_sockets) if(choice) - var/datum/component/hose_connector/CC = choice + var/datum/component/hose_connector/CC = available_sockets[choice] if(REMB) if(REMB.get_carrier() == CC.get_carrier()) to_chat(user, span_notice("Connecting \the [REMB.get_carrier()] to itself seems like a bad idea. You wind \the [src] back up.")) @@ -82,15 +83,13 @@ else if(REMB.valid_connection(CC)) var/distancetonode = get_dist(REMB.get_carrier(), CC.get_carrier()) - if(distancetonode > world.view) + if(distancetonode > HOSE_MAX_DISTANCE) to_chat(user, span_notice("\The [src] would probably burst if it were this long. You wind \the [src] back up.")) remembered = null // Unintuitive if it does not reset state else if(distancetonode <= amount) - to_chat(user, span_notice("You join \the [REMB] to \the [CC]")) - - REMB.setup_hoses(CC,distancetonode) - use(distancetonode) + if(REMB.setup_hoses(CC,distancetonode,user)) + use(distancetonode) remembered = null else diff --git a/code/game/objects/structures/fence.dm b/code/game/objects/structures/fence.dm index 5a20120040..8180e59dbd 100644 --- a/code/game/objects/structures/fence.dm +++ b/code/game/objects/structures/fence.dm @@ -22,6 +22,7 @@ var/cuttable = TRUE var/hole_size= NO_HOLE var/invulnerable = FALSE + var/electric = FALSE /obj/structure/fence/Initialize(mapload) update_cut_status() @@ -68,8 +69,17 @@ return TRUE return ..() +/obj/structure/fence/attack_hand(mob/user) + if(electric && isliving(user) && !user.is_incorporeal()) + if(electrocute(user)) + return + . = ..() + /obj/structure/fence/attackby(obj/item/W, mob/user) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + if(electric && isliving(user) && !user.is_incorporeal() && !(W.flags & NOCONDUCT)) + if(electrocute(user)) + return if(W.has_tool_quality(TOOL_WIRECUTTER)) if(!cuttable) to_chat(user, span_warning("This section of the fence can't be cut.")) @@ -100,6 +110,13 @@ update_cut_status() return TRUE +/obj/structure/fence/Bumped(AM) + . = ..() + if(electric && isliving(AM)) + var/mob/living/L = AM + if(!L.is_incorporeal()) + electrocute(L) + /obj/structure/fence/proc/update_cut_status() if(!cuttable) return diff --git a/code/game/objects/structures/fence_electric.dm b/code/game/objects/structures/fence_electric.dm new file mode 100644 index 0000000000..3d27f1c072 --- /dev/null +++ b/code/game/objects/structures/fence_electric.dm @@ -0,0 +1,66 @@ +#define NAME_ELECFENCE "electric fence" +#define DESC_ELECFENCE "A chain link fence attached to a nearby area power controller. Zap zap!" +#define ICON_ELECFENCE 'icons/obj/fence_electric.dmi' + +// Required to be in an area with a powered APC to electrocute! Intended for POIs. +/obj/structure/fence/electric + name = NAME_ELECFENCE + desc = DESC_ELECFENCE + icon = ICON_ELECFENCE + electric = TRUE + +/obj/structure/fence/electric_sign + name = NAME_ELECFENCE + desc = DESC_ELECFENCE + icon = ICON_ELECFENCE + electric = TRUE + icon_state = "straight_sign" + +/obj/structure/fence/door/electric + name = NAME_ELECFENCE + desc = DESC_ELECFENCE + icon = ICON_ELECFENCE + electric = TRUE + +/obj/structure/fence/end/electric + name = NAME_ELECFENCE + desc = DESC_ELECFENCE + icon = ICON_ELECFENCE + electric = TRUE + +/obj/structure/fence/corner/electric + name = NAME_ELECFENCE + desc = DESC_ELECFENCE + icon = ICON_ELECFENCE + electric = TRUE + +/obj/structure/fence/post/electric + name = NAME_ELECFENCE + desc = DESC_ELECFENCE + icon = ICON_ELECFENCE + electric = TRUE + +/obj/structure/fence/cut/medium/electric + name = NAME_ELECFENCE + desc = DESC_ELECFENCE + icon = ICON_ELECFENCE + electric = TRUE + +/obj/structure/fence/cut/large/electric + name = NAME_ELECFENCE + desc = DESC_ELECFENCE + icon = ICON_ELECFENCE + electric = TRUE + +/obj/structure/fence/proc/electrocute(mob/living/M) + if(electrocute_mob(M, get_area(src), src, 0.7)) + visible_message("\The [src] zaps [M]!") + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, src) + s.start() + return TRUE + return FALSE + +#undef NAME_ELECFENCE +#undef DESC_ELECFENCE +#undef ICON_ELECFENCE diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 1ccc48350e..1a0c0d49ce 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -69,6 +69,7 @@ regenerate_icons() AddComponent(/datum/component/personal_crafting) + AddComponent(/datum/component/hose_connector/inflation) // Comment out to disable all human mob inflation mechanics // Chicken Stuff var/animal = pick("cow","chicken_brown", "chicken_black", "chicken_white", "chick", "mouse_brown", "mouse_gray", "mouse_white", "lizard", "cat2", "goose", "penguin") diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 0ea9776094..982b6a5cec 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -205,6 +205,8 @@ riding_datum = new /datum/riding/dogborg(src) + AddComponent(/datum/component/hose_connector/input/borg) + AddComponent(/datum/component/hose_connector/output/borg) /mob/living/silicon/robot/LateInitialize() pick_module() diff --git a/code/modules/power/fusion/core/_core.dm b/code/modules/power/fusion/core/_core.dm index 5e4f046b8a..5ca45bdcb6 100644 --- a/code/modules/power/fusion/core/_core.dm +++ b/code/modules/power/fusion/core/_core.dm @@ -63,8 +63,7 @@ GLOBAL_LIST_EMPTY(fusion_cores) if((stat & BROKEN) || !powernet || !owned_field) Shutdown() - var/datum/component/hose_connector/HC = GetComponent(/datum/component/hose_connector) - HC.force_pump() + SEND_SIGNAL(src, COMSIG_HOSE_FORCEPUMP) if(owned_field) diff --git a/code/modules/reagents/holder/holder.dm b/code/modules/reagents/holder/holder.dm index 56b6ac11ef..206a50c27c 100644 --- a/code/modules/reagents/holder/holder.dm +++ b/code/modules/reagents/holder/holder.dm @@ -264,9 +264,11 @@ var/part = amount / total_volume + var/target_is_belly = isbelly(target.my_atom) // Sending reagents into bellies turns them into belly reagents + for(var/datum/reagent/current in reagent_list) var/amount_to_transfer = current.volume * part - target.add_reagent(current.id, amount_to_transfer * multiplier, current.get_data(), safety = 1, was_from_belly = current.from_belly) // We don't react until everything is in place + target.add_reagent(current.id, amount_to_transfer * multiplier, current.get_data(), safety = 1, was_from_belly = (current.from_belly || target_is_belly)) // We don't react until everything is in place if(!copy) remove_reagent(current.id, amount_to_transfer, 1) diff --git a/code/modules/reagents/machinery/pump.dm b/code/modules/reagents/machinery/pump.dm index 8fae5b7268..e9e77f5548 100644 --- a/code/modules/reagents/machinery/pump.dm +++ b/code/modules/reagents/machinery/pump.dm @@ -33,6 +33,10 @@ RefreshParts() update_icon() +/obj/machinery/pump/Destroy() + QDEL_NULL(cell) + . = ..() + /obj/machinery/pump/RefreshParts() var/obj/item/stock_parts/manipulator/SM = locate() in component_parts active_power_usage = initial(active_power_usage) / SM.rating @@ -88,8 +92,7 @@ T.pump_reagents(reagents, reagents_per_cycle) update_icon() - var/datum/component/hose_connector/HC = GetComponent(/datum/component/hose_connector) - HC.force_pump() + SEND_SIGNAL(src, COMSIG_HOSE_FORCEPUMP) // Sets the power state, if possible. // Returns TRUE/FALSE on power state changing diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index fe52cf22e0..3470f9af4e 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -240,8 +240,8 @@ /obj/item/reagent_containers/spray/chemsprayer/hosed/proc/update_hose(atom/source, atom/oldloc, direction, forced, list/old_locs, momentum_change) SIGNAL_HANDLER - var/datum/component/hose_connector/HC = GetComponent(/datum/component/hose_connector) - HC.update_hose_beam() + for(var/datum/component/hose_connector/HC in GetComponents(/datum/component/hose_connector)) + HC.update_hose_beam() /obj/item/reagent_containers/spray/chemsprayer/hosed/update_icon() ..() @@ -251,9 +251,10 @@ if(!hose_overlay) hose_overlay = new/icon(icon, "[icon_state]+hose") - var/datum/component/hose_connector/HC = GetComponent(/datum/component/hose_connector) - if(HC.get_pairing()) - add_overlay(hose_overlay) + for(var/datum/component/hose_connector/HC in GetComponents(/datum/component/hose_connector)) + if(HC.get_pairing()) + add_overlay(hose_overlay) + break /obj/item/reagent_containers/spray/chemsprayer/hosed/AltClick(mob/living/carbon/user) if(++spray_particles > 3) spray_particles = 1 diff --git a/code/modules/vore/eating/living_vr.dm b/code/modules/vore/eating/living_vr.dm index a04dbe404f..bed9ad6d01 100644 --- a/code/modules/vore/eating/living_vr.dm +++ b/code/modules/vore/eating/living_vr.dm @@ -1411,7 +1411,12 @@ if(!RTB) return FALSE - to_chat(src, span_vnotice("[RTB] has [RTB.reagents.total_volume] units of liquid.")) + var/total_report = span_vnotice("[RTB] has [RTB.reagents.total_volume] units of liquid.") + if(RTB.reagents.total_volume > 0) + for(var/datum/reagent/R in RTB.reagents.reagent_list) + total_report += "
" + total_report += span_info(" -[R.name]: [R.volume]u") + to_chat(src, total_report) /mob/living/proc/vore_transfer_reagents() set name = "Transfer Liquid (Vore)" diff --git a/icons/obj/fence_electric.dmi b/icons/obj/fence_electric.dmi new file mode 100644 index 0000000000..369eb209ec Binary files /dev/null and b/icons/obj/fence_electric.dmi differ diff --git a/vorestation.dme b/vorestation.dme index 59e5935d28..2e16e50d44 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -557,6 +557,7 @@ #include "code\datums\components\disabilities\tourettes.dm" #include "code\datums\components\reagent_hose\connector.dm" #include "code\datums\components\reagent_hose\datum.dm" +#include "code\datums\components\reagent_hose\inflation.dm" #include "code\datums\components\reagent_hose\item.dm" #include "code\datums\components\species\shadekin.dm" #include "code\datums\components\species\xenochimera.dm" @@ -1798,6 +1799,7 @@ #include "code\game\objects\structures\electricchair.dm" #include "code\game\objects\structures\extinguisher.dm" #include "code\game\objects\structures\fence.dm" +#include "code\game\objects\structures\fence_electric.dm" #include "code\game\objects\structures\fireaxe.dm" #include "code\game\objects\structures\fitness.dm" #include "code\game\objects\structures\fitness_vr.dm"