diff --git a/aurorastation.dme b/aurorastation.dme index 3cece0f186d..e45665d8632 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -719,6 +719,7 @@ #include "code\datums\wires\disposals.dm" #include "code\datums\wires\explosive.dm" #include "code\datums\wires\fabricator.dm" +#include "code\datums\wires\hardsuit.dm" #include "code\datums\wires\iff.dm" #include "code\datums\wires\ipc_tag_scanner.dm" #include "code\datums\wires\landmine.dm" @@ -728,6 +729,7 @@ #include "code\datums\wires\robot.dm" #include "code\datums\wires\smartfridge.dm" #include "code\datums\wires\smes.dm" +#include "code\datums\wires\stasis_cage.dm" #include "code\datums\wires\suit_storage_unit.dm" #include "code\datums\wires\vending.dm" #include "code\datums\wires\wires.dm" @@ -2155,7 +2157,6 @@ #include "code\modules\clothing\spacesuits\rig\rig_construction.dm" #include "code\modules\clothing\spacesuits\rig\rig_pieces.dm" #include "code\modules\clothing\spacesuits\rig\rig_verbs.dm" -#include "code\modules\clothing\spacesuits\rig\rig_wiring.dm" #include "code\modules\clothing\spacesuits\rig\modules\combat.dm" #include "code\modules\clothing\spacesuits\rig\modules\computer.dm" #include "code\modules\clothing\spacesuits\rig\modules\modules.dm" @@ -3139,6 +3140,7 @@ #include "code\modules\modular_computers\file_system\programs\generic\ntnrc_client.dm" #include "code\modules\modular_computers\file_system\programs\generic\records.dm" #include "code\modules\modular_computers\file_system\programs\generic\scanner.dm" +#include "code\modules\modular_computers\file_system\programs\generic\signaler.dm" #include "code\modules\modular_computers\file_system\programs\medical\suit_sensors.dm" #include "code\modules\modular_computers\file_system\programs\pai\access_lock.dm" #include "code\modules\modular_computers\file_system\programs\pai\directives.dm" diff --git a/code/__DEFINES/wires.dm b/code/__DEFINES/wires.dm index c111a952f4d..39fea6220db 100644 --- a/code/__DEFINES/wires.dm +++ b/code/__DEFINES/wires.dm @@ -61,9 +61,6 @@ #define WIRE_TIMING "Timing" #define WIRE_TX "Transmit" #define WIRE_UNBOLT "Unbolt" -#define WIRE_ZAP "High Voltage Circuit" -#define WIRE_ZAP1 "High Voltage Circuit 1" -#define WIRE_ZAP2 "High Voltage Circuit 2" #define WIRE_OVERCLOCK "Overclock" #define WIRE_FOCUS "Focus" #define WIRE_FLUSH "Flush" diff --git a/code/datums/skills/occupational/engineering.dm b/code/datums/skills/occupational/engineering.dm index 5b71002fa55..9cb68dc6dad 100644 --- a/code/datums/skills/occupational/engineering.dm +++ b/code/datums/skills/occupational/engineering.dm @@ -10,10 +10,13 @@ skill_level_descriptions = alist( SKILL_LEVEL_UNFAMILIAR = "You have little to no experience working with electrical systems.", SKILL_LEVEL_FAMILIAR = "You have some experience working with electrical systems, though are not formally trained.
" \ - + " - You can use a power cell to attempt emergency power bypasses on piloting consoles when they're unpowered.", - SKILL_LEVEL_TRAINED = "You have formal training in electrical engineering concepts, equivalent to a Bachelor's Degree.", + + " - You can use a power cell to attempt emergency power bypasses on piloting consoles when they're unpowered.
" \ + + " - You can recognize what a few wires do in electrical devices by sight.", + SKILL_LEVEL_TRAINED = "You have formal training in electrical engineering concepts, equivalent to a Bachelor's Degree.
" \ + + " - You can use a power cell to attempt emergency power bypasses on piloting consoles when they're unpowered.
" \ + + " - You can recognize what several wires do in electrical devices by sight.", SKILL_LEVEL_PROFESSIONAL = "You have many years of training in electrical related Engineering concepts, equivalent to a Master's Degree or better.
" \ - + " - You can tell what most machinery and airlock wires do just by looking at them." \ + + " - You can recognize what many wires do in electrical devices by sight." \ ) /singleton/skill/mechanical_engineering @@ -35,14 +38,17 @@ SKILL_LEVEL_FAMILIAR = "You have some experience working with physical machinery, though are not formally trained.
" \ + " - Items you craft will be of slightly lower than average quality.
" \ + " - You take 50% longer to craft anything.
" \ - + " - You can interact with some Engineering-related machinery.", + + " - You can interact with some Engineering-related machinery.
" \ + + " - You can recognize what a few wires do in mechanical devices by sight.", SKILL_LEVEL_TRAINED = "You have formal training in general Engineering concepts, equivalent to a Bachelor's Degree.
" \ + " - You can craft items at the standard speed.
" \ - + " - Items you craft are generally at average quality.
" \ - + " - You can interact with most if not all Engineering equipment.", + + " - You can craft a wider variety of items.
" \ + + " - You can interact with most if not all Engineering equipment.
" \ + + " - You can recognize what several wires do in mechanical devices by sight.", SKILL_LEVEL_PROFESSIONAL = "You have many years of training in general Engineering concepts, equivalent to a Master's Degree or better.
" \ + " - You can craft items 50% faster.
" \ - + " - Items you craft are of much higher quality on average.
" \ + + " - You can craft a much wider variety of items.
" \ + + " - You can recognize what many wires do in mechanical devices by sight.", ) /singleton/skill/atmospherics_systems @@ -54,6 +60,16 @@ subcategory = SKILL_SUBCATEGORY_ENGINEERING required = TRUE component_type = ATMOSPHERICS_SYSTEMS_SKILL_COMPONENT + skill_level_descriptions = alist( + SKILL_LEVEL_UNFAMILIAR = "You have little to no experience working with atmospherics systems.
" \ + + " - No mechanical changes implemented at this skill level.", + SKILL_LEVEL_FAMILIAR = "You have some experience working with atmospherics systems, though are not formally trained.
" \ + + " - You can recognize what a few wires do in atmospherics devices by sight.", + SKILL_LEVEL_TRAINED = "You have formal training in general Engineering concepts, equivalent to a Bachelor's Degree.
" \ + + " - You can recognize what some wires do in atmospherics devices by sight.", + SKILL_LEVEL_PROFESSIONAL = "You have many years of training in general Engineering concepts, equivalent to a Master's Degree or better.
" \ + + " - You can recognize what many wires do in atmospherics devices by sight." \ + ) /singleton/skill/reactor_systems name = "Reactor Systems" @@ -66,9 +82,9 @@ required = TRUE component_type = REACTOR_SYSTEMS_SKILL_COMPONENT skill_level_descriptions = alist( - SKILL_LEVEL_UNFAMILIAR = "You have little to no experience working with Reactor systems.
" \ + SKILL_LEVEL_UNFAMILIAR = "You have little to no experience working with reactor systems.
" \ + " - You cannot interact with Reactor computers such as the INDRA controllers.", - SKILL_LEVEL_FAMILIAR = "You have some experience working with physical machinery, though are not formally trained.
" \ + SKILL_LEVEL_FAMILIAR = "You have some experience working with reactor systems, though are not formally trained.
" \ + " - You cannot interact with Reactor computers such as the INDRA controller.", SKILL_LEVEL_TRAINED = "You have formal training in general Engineering concepts, equivalent to a Bachelor's Degree.
" \ + " - This is the minimum skill level required to operate the INDRA.", diff --git a/code/datums/skills/occupational/operations.dm b/code/datums/skills/occupational/operations.dm index 87502d3855e..10a6edd4a8b 100644 --- a/code/datums/skills/occupational/operations.dm +++ b/code/datums/skills/occupational/operations.dm @@ -15,12 +15,14 @@ SKILL_LEVEL_UNFAMILIAR = "You have zero training or experience with synthetics.
" \ + " - You suffer a large penalty to the chances of successfully performing any surgery on synthetics.", SKILL_LEVEL_FAMILIAR = "You have minimal training on the basics of synthetic repair and maintenance. This could be the level of a hobbyist, or someone currently pursuing a degree in robotics.
" \ + + " - You can recognize what a few wires do in mechatronic devices by sight.
" \ + "You can perform the following procedures without penalties:
" \ + " - Opening or closing external maintenance panels to make superficial repairs.
" \ + " - Repairing basic damage with a welder or cables.
" \ + " - Repairing external damage to mechanical limbs.
" \ + " - Cutting someone out of a hardsuit.", SKILL_LEVEL_TRAINED = "You have years of formal training or experience on repairing and maintaining synthetics equivalent to a Bachelor's degree in Robotics.
" \ + + " - You can recognize what several wires do in mechatronic devices by sight.
" \ + "You can perform the following procedures without penalties:
" \ + " - Opening or closing external maintenance panels to make superficial repairs.
" \ + " - Repairing basic damage with a welder or cables.
" \ @@ -33,7 +35,8 @@ + " - Re-attach (organic) limbs. Robotic limbs require the Robotics skill instead.
" \ + " - Perform all forms of internal repairs to IPCs.
" \ + " - Prepare an MMI for cyborgification.", - SKILL_LEVEL_PROFESSIONAL = "Not currently implemented, functions exactly as per Trained.
" \ + SKILL_LEVEL_PROFESSIONAL = "You have years of formal training or experience on repairing and maintaining synthetics equivalent to a Master's degree in Robotics or better.
" \ + + " - You can recognize what many wires do in mechatronic devices by sight.
" \ + "You can perform the following procedures without penalties:
" \ + " - Opening or closing external maintenance panels to make superficial repairs.
" \ + " - Repairing basic damage with a welder or cables.
" \ diff --git a/code/datums/skills/occupational/science.dm b/code/datums/skills/occupational/science.dm index 11ba41e11ac..9adf9ffec53 100644 --- a/code/datums/skills/occupational/science.dm +++ b/code/datums/skills/occupational/science.dm @@ -39,16 +39,19 @@ skill_level_descriptions = alist( SKILL_LEVEL_UNFAMILIAR = "You have little to no knowledge of Xenobiology", SKILL_LEVEL_FAMILIAR = "You have a passing experience with Xenobiology.
" \ + + " - You can recognize what a few wires do in xenobiological sciences hardware by sight.
" \ + " - Your melee attacks made against Xenofauna such as Space Carp deal 10% more damage.
" \ + " - You harvest one additional part when butchering Xenofauna.
" \ + " - You are +2.5% more likely to succeed when performing surgery on a patient that is a different species than your own.
" \ + " - You can perform surgeries 5% faster on a patient that is a different species than your own.", SKILL_LEVEL_TRAINED = "You have a moderate experience with Xenobiology.
" \ + + " - You can recognize what several wires do in xenobiological sciences hardware by sight.
" \ + " - Your melee attacks made against Xenofauna such as Space Carp deal 15% more damage.
" \ + " - You harvest two additional parts when butchering Xenofauna.
" \ + " - You are +5% more likely to succeed when performing surgery on a patient that is a different species than your own.
" \ + " - You can perform surgeries 10% faster on a patient that is a different species than your own.", SKILL_LEVEL_PROFESSIONAL = "You have extensive experience with Xenobiology.
" \ + + " - You can recognize what many wires do in xenobiological sciences hardware by sight.
" \ + " - Your melee attacks made against Xenofauna such as Space Carp deal 20% more damage.
" \ + " - You harvest three additional parts when butchering Xenofauna.
" \ + " - You are +7.5% more likely to succeed when performing surgery on a patient that is a different species than your own.
" \ diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm index 1bd05d1fd9d..0ebda75935a 100644 --- a/code/datums/wires/airlock.dm +++ b/code/datums/wires/airlock.dm @@ -3,6 +3,7 @@ /datum/wires/airlock proper_name = "Airlock" holder_type = /obj/structure/machinery/door/airlock + associated_skill = MECHANICAL_ENGINEERING_SKILL_COMPONENT /datum/wires/airlock/New(atom/holder) wires = list( @@ -200,6 +201,6 @@ continue dat += "[capitalize(color)]: [get_wire(color)]
" - var/datum/browser/wire_win = new(user, "airlockwires", "Airlock Wires", 450, 500) + var/datum/browser/wire_win = new(user, "airlockwires", "Airlock Wires", 450, 720) wire_win.set_content(dat) wire_win.open() diff --git a/code/datums/wires/alarm.dm b/code/datums/wires/alarm.dm index ac75f7c0057..edfe7fbcdaa 100644 --- a/code/datums/wires/alarm.dm +++ b/code/datums/wires/alarm.dm @@ -1,8 +1,9 @@ /datum/wires/alarm proper_name = "Air Alarm" holder_type = /obj/structure/machinery/alarm + associated_skill = ATMOSPHERICS_SYSTEMS_SKILL_COMPONENT -/datum/wires/airalarm/New(atom/holder) +/datum/wires/alarm/New(atom/holder) wires = list( WIRE_POWER, WIRE_IDSCAN, WIRE_AI, @@ -12,7 +13,7 @@ add_duds(3) ..() -/datum/wires/airalarm/interactable(mob/user) +/datum/wires/alarm/interactable(mob/user) if(!..()) return FALSE var/obj/structure/machinery/alarm/A = holder @@ -23,7 +24,7 @@ var/obj/structure/machinery/alarm/A = holder . = ..() . += A.locked ? "The air alarm is locked." : "The air alarm is unlocked." - . += (A.shorted || (A.stat & (NOPOWER|BROKEN))) ? "The Air Alarm is offline." : "The Air Alarm is working properly!" + . += (A.shorted || (A.stat & (NOPOWER|BROKEN))) ? "The air alarm is offline." : "The air alarm is working properly!" . += A.aidisabled ? "The 'AI control allowed' light is off." : "The 'AI control allowed' light is on." . += A.rcon_setting ? "The remote control link is enabled." : "The remote control link is disabled." diff --git a/code/datums/wires/apc.dm b/code/datums/wires/apc.dm index 7350c4f762b..bcf7441b395 100644 --- a/code/datums/wires/apc.dm +++ b/code/datums/wires/apc.dm @@ -5,7 +5,8 @@ /datum/wires/apc/New(atom/holder) wires = list( WIRE_POWER1, WIRE_POWER2, - WIRE_IDSCAN, WIRE_AI + WIRE_IDSCAN, WIRE_AI, + WIRE_POWER_LIMIT, WIRE_LIGHT ) add_duds(6) ..() @@ -14,8 +15,9 @@ var/obj/structure/machinery/power/apc/A = holder . = ..() . += A.locked ? "The APC is locked." : "The APC is unlocked." - . += A.shorted ? "The APCs power has been shorted." : "The APC is working properly!" + . += A.shorted ? "The APCs power has been shorted!" : "The APC is working properly." . += A.aidisabled ? "The 'AI control allowed' light is off." : "The 'AI control allowed' light is on." + . += A.light_explosion_safety ? "The low-voltage regulator telltale is on." : "The low-voltage regulator telltale is off." /datum/wires/apc/interactable(mob/user) if(!..()) @@ -24,23 +26,21 @@ return A?.panel_open /datum/wires/apc/on_pulse(wire) - var/obj/structure/machinery/power/apc/A = holder switch(wire) - if(WIRE_IDSCAN) set_locked(A, FALSE) addtimer(CALLBACK(src, PROC_REF(set_locked), A, TRUE), 30 SECONDS) - - if (WIRE_POWER1, WIRE_POWER2) + if(WIRE_POWER1, WIRE_POWER2) set_short_out(A, TRUE) addtimer(CALLBACK(src, PROC_REF(set_short_out), A, FALSE), 120 SECONDS) - - if (WIRE_AI) + if(WIRE_AI) set_ai_control(A, TRUE) addtimer(CALLBACK(src, PROC_REF(set_ai_control), A, FALSE), 1 SECONDS) - + if(WIRE_LIGHT) + if(!A.light_explosion_safety) + A.overload_lighting() /datum/wires/apc/proc/set_locked(var/obj/structure/machinery/power/apc/A, var/setting) if(A) @@ -74,3 +74,6 @@ if(WIRE_AI) A.aidisabled = !mend + + if(WIRE_POWER_LIMIT) + A.light_explosion_safety = mend diff --git a/code/datums/wires/disposals.dm b/code/datums/wires/disposals.dm index 1e7ff3b104a..8cd44742d36 100644 --- a/code/datums/wires/disposals.dm +++ b/code/datums/wires/disposals.dm @@ -1,7 +1,7 @@ /datum/wires/disposal proper_name = "Disposals" - random = TRUE holder_type = /obj/structure/machinery/disposal + associated_skill = MECHANICAL_ENGINEERING_SKILL_COMPONENT /datum/wires/disposal/New(atom/holder) wires = list( diff --git a/code/datums/wires/explosive.dm b/code/datums/wires/explosive.dm index db992864312..cc4c41f762c 100644 --- a/code/datums/wires/explosive.dm +++ b/code/datums/wires/explosive.dm @@ -1,3 +1,7 @@ +/datum/wires/explosive + // If we ever get an explosives or similar skill... + associated_skill = null + /datum/wires/explosive/New() wires = list( WIRE_EXPLODE diff --git a/code/datums/wires/fabricator.dm b/code/datums/wires/fabricator.dm index 84679d06562..a6f9d5563a1 100644 --- a/code/datums/wires/fabricator.dm +++ b/code/datums/wires/fabricator.dm @@ -1,6 +1,7 @@ /datum/wires/fabricator proper_name = "Fabricator" holder_type = /obj/structure/machinery/fabricator + associated_skill = MECHANICAL_ENGINEERING_SKILL_COMPONENT /datum/wires/fabricator/New(atom/holder) wires = list( diff --git a/code/modules/clothing/spacesuits/rig/rig_wiring.dm b/code/datums/wires/hardsuit.dm similarity index 98% rename from code/modules/clothing/spacesuits/rig/rig_wiring.dm rename to code/datums/wires/hardsuit.dm index b545e7cb9b1..396c80110ba 100644 --- a/code/modules/clothing/spacesuits/rig/rig_wiring.dm +++ b/code/datums/wires/hardsuit.dm @@ -11,8 +11,8 @@ #define RIG_INTERFACE_SHOCK BITFLAG(5) /datum/wires/rig - random = 1 holder_type = /obj/item/rig + associated_skill = ROBOTICS_SKILL_COMPONENT /datum/wires/rig/New(atom/holder) wires = list( diff --git a/code/datums/wires/ipc_tag_scanner.dm b/code/datums/wires/ipc_tag_scanner.dm index a812d9674e0..9e0703fbeb6 100644 --- a/code/datums/wires/ipc_tag_scanner.dm +++ b/code/datums/wires/ipc_tag_scanner.dm @@ -1,7 +1,7 @@ /datum/wires/tag_scanner proper_name = "IPC Tag Scanner" - random = TRUE holder_type = /obj/item/ipc_tag_scanner + associated_skill = ROBOTICS_SKILL_COMPONENT /datum/wires/tag_scanner/New() wires = list( diff --git a/code/datums/wires/landmine.dm b/code/datums/wires/landmine.dm index 51a4dde9f65..3142af09866 100644 --- a/code/datums/wires/landmine.dm +++ b/code/datums/wires/landmine.dm @@ -1,3 +1,7 @@ +/datum/wires/landmine + // If we ever get an explosives or similar skill... + associated_skill = null + /datum/wires/landmine/New() wires = list( WIRE_EXPLODE diff --git a/code/datums/wires/nuclearbomb.dm b/code/datums/wires/nuclearbomb.dm index f9fc2ffe8d5..2d7d1a3358c 100644 --- a/code/datums/wires/nuclearbomb.dm +++ b/code/datums/wires/nuclearbomb.dm @@ -2,6 +2,8 @@ proper_name = "Nuclear Fission Device" holder_type = /obj/structure/machinery/nuclearbomb random = 1 + // If we ever get an explosives or similar skill... well, probably not even then. + associated_skill = null /datum/wires/nuclearbomb/New() wires = list( diff --git a/code/datums/wires/particle_accelerator.dm b/code/datums/wires/particle_accelerator.dm index bdcd4e32a9e..c3f42171144 100644 --- a/code/datums/wires/particle_accelerator.dm +++ b/code/datums/wires/particle_accelerator.dm @@ -1,6 +1,7 @@ /datum/wires/particle_acc/control_box proper_name = "Particle Accelerator" holder_type = /obj/structure/machinery/particle_accelerator/control_box + associated_skill = REACTOR_SYSTEMS_SKILL_COMPONENT /datum/wires/particle_acc/control_box/New() wires = list( diff --git a/code/datums/wires/robot.dm b/code/datums/wires/robot.dm index 3a9043cdf86..0c8682102c7 100644 --- a/code/datums/wires/robot.dm +++ b/code/datums/wires/robot.dm @@ -1,7 +1,7 @@ /datum/wires/robot proper_name = "Synthetic" - random = TRUE holder_type = /mob/living/silicon/robot + associated_skill = ROBOTICS_SKILL_COMPONENT /datum/wires/robot/New() wires = list( diff --git a/code/datums/wires/stasis_cage.dm b/code/datums/wires/stasis_cage.dm new file mode 100644 index 00000000000..8f7f10263e3 --- /dev/null +++ b/code/datums/wires/stasis_cage.dm @@ -0,0 +1,56 @@ +/datum/wires/stasis_cage + proper_name = "Stasis Cage" + holder_type = /obj/structure/machinery/stasis_cage + associated_skill = XENOBIOLOGY_SKILL_COMPONENT + +/datum/wires/stasis_cage/New(atom/holder) + wires = list( + WIRE_SAFETY, + WIRE_RELEASE, + WIRE_LOCK + ) + add_duds(3) + ..() + +/datum/wires/stasis_cage/interactable(mob/user) + SHOULD_CALL_PARENT(FALSE) + var/obj/structure/machinery/stasis_cage/stasis_cage = holder + if (stasis_cage.panel_open) + return TRUE + return FALSE + +/datum/wires/stasis_cage/get_status() + var/obj/structure/machinery/stasis_cage/stasis_cage = holder + . = ..() + if(!stasis_cage.use_power) + . += "The panel is unpowered." + else + . += "The panel is powered." + . += "The biometric safety sensors are [(WIRE_SAFETY in cut_wires) ? "connected" : "disconnected"]." + . += "The cage's emergency auto-release mechanism is [(WIRE_RELEASE in cut_wires) ? "disabled" : "enabled"]." + . += "The cage lid motors are [(WIRE_LOCK in cut_wires) ? "overriden" : "nominal"]." + +/datum/wires/stasis_cage/on_cut(wire, mend, source) + var/obj/structure/machinery/stasis_cage/stasis_cage = holder + switch (wire) + if (WIRE_SAFETY) + stasis_cage.safety = !stasis_cage.safety + if (WIRE_RELEASE) + if (stasis_cage.contained && !mend) + stasis_cage.release() + holder.update_icon() + if (WIRE_LOCK) + if (!mend) + playsound(stasis_cage.loc, 'sound/machines/BoltsDown.ogg', 60) + holder.visible_message(SPAN_WARNING("The cage's lid bolts down destructively, denting itself!"), SPAN_NOTICE("You notice the cage lid override flag blink hastily.")) + stasis_cage.broken = TRUE + holder.update_icon() + +/datum/wires/stasis_cage/on_pulse(wire) + var/obj/structure/machinery/stasis_cage/stasis_cage = holder + switch (wire) + if (WIRE_SAFETY) + if (stasis_cage.contained) + if (prob(20)) + holder.visible_message(SPAN_WARNING("The cage hastily flicks open its lid!"), SPAN_NOTICE("You notice the biometric sensor flag blink fervently.")) + stasis_cage.release() diff --git a/code/datums/wires/suit_storage_unit.dm b/code/datums/wires/suit_storage_unit.dm index b462f83d544..d7e03a12d45 100644 --- a/code/datums/wires/suit_storage_unit.dm +++ b/code/datums/wires/suit_storage_unit.dm @@ -1,6 +1,7 @@ /datum/wires/suit_storage_unit proper_name = "Suit Storage Unit" holder_type = /obj/structure/machinery/suit_cycler + associated_skill = ROBOTICS_SKILL_COMPONENT /datum/wires/suit_storage_unit/New() wires = list( diff --git a/code/datums/wires/vending.dm b/code/datums/wires/vending.dm index eb2d327d234..bd1eb532482 100644 --- a/code/datums/wires/vending.dm +++ b/code/datums/wires/vending.dm @@ -1,6 +1,7 @@ /datum/wires/vending proper_name = "Vending Machine" holder_type = /obj/structure/machinery/vending + associated_skill = MECHANICAL_ENGINEERING_SKILL_COMPONENT /datum/wires/vending/New() wires = list( @@ -78,6 +79,6 @@ continue dat += "[capitalize(color)]: [get_wire(color)]
" - var/datum/browser/wire_win = new(user, "vendingwires", "Vending Wires", 450, 500) + var/datum/browser/wire_win = new(user, "vendingwires", "Vending Wires", 450, 720) wire_win.set_content(dat) wire_win.open() diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm index c01ca21d33c..8bc46caa09d 100644 --- a/code/datums/wires/wires.dm +++ b/code/datums/wires/wires.dm @@ -2,6 +2,7 @@ GLOBAL_LIST_INIT(wire_color_directory, list()) GLOBAL_LIST_INIT(wire_name_directory, list()) +GLOBAL_LIST_INIT(wire_skill_reveal_directory, list()) /datum/wires /// The holder (atom that contains these wires). @@ -27,6 +28,10 @@ GLOBAL_LIST_INIT(wire_name_directory, list()) var/random = FALSE /// Wire manufacturer for the UI skin. var/manufacturer = "hephaestus" + /// Skill component path used for partial wire reveals. Null disables skill-based reveals. + var/associated_skill = ELECTRICAL_ENGINEERING_SKILL_COMPONENT + /// Key used for round-stable partial skill-based reveals + var/skill_reveal_key /datum/wires/New(atom/holder) ..() @@ -40,6 +45,7 @@ GLOBAL_LIST_INIT(wire_name_directory, list()) // If there is a dictionary key set, we'll want to use that. Otherwise, use the holder type. var/key = dictionary_key ? dictionary_key : holder_type + skill_reveal_key = key RegisterSignal(holder, COMSIG_QDELETING, PROC_REF(on_holder_qdel)) if(random) @@ -54,6 +60,7 @@ GLOBAL_LIST_INIT(wire_name_directory, list()) /datum/wires/Destroy() holder = null + skill_reveal_key = null //properly clear refs to avoid harddels & other problems for(var/color in assemblies) var/obj/item/assembly/assembly = assemblies[color] @@ -102,6 +109,8 @@ GLOBAL_LIST_INIT(wire_name_directory, list()) /datum/wires/proc/shuffle_wires() colors.Cut() + if(skill_reveal_key) + GLOB.wire_skill_reveal_directory -= skill_reveal_key randomize() /datum/wires/proc/repair() @@ -241,8 +250,8 @@ GLOBAL_LIST_INIT(wire_name_directory, list()) /** * Checks whether wire assignments should be revealed. * - * Returns TRUE if the wires should be revealed, FALSE otherwise. - * Currently checks for admin ghost AI, abductor multitool and blueprints. + * Returns TRUE if every wire assignment in this panel should be revealed, FALSE otherwise. + * Currently checks for admin ghosts and blueprints. * Arguments: * * user - The mob to check when deciding whether to reveal wires. */ @@ -259,10 +268,6 @@ GLOBAL_LIST_INIT(wire_name_directory, list()) if(istype(user.get_active_hand(), /obj/item/blueprints)) return TRUE - // Max electrical engineering skill grants this as a perk - if (GET_SKILL_LEVEL(user, ELECTRICAL_ENGINEERING_SKILL_COMPONENT) >= SKILL_LEVEL_PROFESSIONAL) - return TRUE - return FALSE /** @@ -276,6 +281,57 @@ GLOBAL_LIST_INIT(wire_name_directory, list()) /datum/wires/proc/always_reveal_wire(color) return FALSE +/** + * Gets how many wires this user's associated skill reveals. + * + * Familiar: 15%; Trained: 35%; Professional: 65%. + * Arguments: + * * user - The mob to check when deciding how many wires to reveal. + */ +/datum/wires/proc/get_skill_revealed_wire_count(mob/user) + if(random || !associated_skill || !LAZYLEN(colors) || !user) + return 0 + + var/skill_level = GET_SKILL_LEVEL(user, associated_skill) + if(isnull(skill_level)) + skill_level = SKILL_LEVEL_UNFAMILIAR + + // If you're trained in various electromechanical biz, you can probably suss out at least some basics. + var/has_electromech_proficiency = (GET_SKILL_LEVEL(user, ELECTRICAL_ENGINEERING_SKILL_COMPONENT) >= SKILL_LEVEL_TRAINED && GET_SKILL_LEVEL(user, MECHANICAL_ENGINEERING_SKILL_COMPONENT) >= SKILL_LEVEL_TRAINED) + var/reveal_percent = 0 + if(skill_level >= SKILL_LEVEL_PROFESSIONAL) + reveal_percent = 75 + else if(skill_level >= SKILL_LEVEL_TRAINED) + reveal_percent = 50 + else if(skill_level >= SKILL_LEVEL_FAMILIAR || has_electromech_proficiency) + reveal_percent = 30 + else + return 0 + + return clamp(round(LAZYLEN(colors) * reveal_percent / 100), 0, LAZYLEN(colors)) + +/datum/wires/proc/get_skill_revealed_colors(var/reveal_count) + if(reveal_count <= 0 || random || !LAZYLEN(colors)) + return null + + var/reveal_key = skill_reveal_key ? skill_reveal_key : (dictionary_key ? dictionary_key : holder_type) + var/list/skill_reveal_order = GLOB.wire_skill_reveal_directory[reveal_key] + var/rebuild_order = !skill_reveal_order || length(skill_reveal_order) != LAZYLEN(colors) + if(!rebuild_order) + for(var/color in skill_reveal_order) + if(!(color in colors)) + rebuild_order = TRUE + break + + if(rebuild_order) + skill_reveal_order = list() + for(var/color in colors) + skill_reveal_order += color + skill_reveal_order = shuffle(skill_reveal_order) + GLOB.wire_skill_reveal_directory[reveal_key] = skill_reveal_order + + return skill_reveal_order.Copy(1, reveal_count + 1) + /datum/wires/ui_host() return holder @@ -297,11 +353,14 @@ GLOBAL_LIST_INIT(wire_name_directory, list()) /datum/wires/ui_data(mob/user) var/list/data = list() var/list/payload = list() - var/reveal_wires = can_reveal_wires(user) + var/reveal_all = can_reveal_wires(user) + var/list/skill_revealed_colors + if(!reveal_all) + skill_revealed_colors = get_skill_revealed_colors(get_skill_revealed_wire_count(user)) for(var/color in colors) var/wire_label = null - if (reveal_wires || always_reveal_wire(color)) + if(reveal_all || always_reveal_wire(color) || (skill_revealed_colors && (color in skill_revealed_colors))) wire_label = is_dud_color(color) ? "None" : get_wire(color) payload.Add(list(list( diff --git a/code/game/objects/items/devices/PDA/radio.dm b/code/game/objects/items/devices/PDA/radio.dm index 40b5049fbd7..7c7b2f76dd7 100644 --- a/code/game/objects/items/devices/PDA/radio.dm +++ b/code/game/objects/items/devices/PDA/radio.dm @@ -44,8 +44,12 @@ var/frequency = 1457 var/code = 30.0 var/last_transmission + var/listening = TRUE var/datum/radio_frequency/radio_connection +/obj/item/integrated_signaler/signal/network_card + listening = FALSE + /obj/item/integrated_signaler/signal/Initialize() . = ..() if(!SSradio) @@ -57,26 +61,83 @@ set_frequency(frequency) /obj/item/integrated_signaler/signal/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency) + if(!SSradio || !new_frequency) + return FALSE + var/sanitized_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + if(sanitized_frequency > RADIO_HIGH_FREQ) + sanitized_frequency = RADIO_HIGH_FREQ - 1 + if(radio_connection && frequency == sanitized_frequency) + return TRUE + if(radio_connection) + SSradio.remove_object(src, frequency) + frequency = sanitized_frequency + if(listening) + radio_connection = SSradio.add_object(src, frequency) + else + radio_connection = null + return TRUE -/obj/item/integrated_signaler/signal/proc/send_signal(message="ACTIVATE") +/obj/item/integrated_signaler/signal/proc/set_listening(var/new_listening) + new_listening = !!new_listening + if(listening == new_listening) + return TRUE + + listening = new_listening + if(listening) + return set_frequency(frequency) + + if(radio_connection) + SSradio.remove_object(src, frequency) + radio_connection = null + return TRUE + +/obj/item/integrated_signaler/signal/proc/send_signal(message="ACTIVATE", var/mob/user) + if(!SSradio) + return FALSE + var/datum/radio_frequency/transmit_connection = radio_connection + if(!transmit_connection) + transmit_connection = SSradio.return_frequency(frequency) + if(!transmit_connection) + return FALSE + if(within_jamming_range(src)) + return FALSE if(last_transmission && world.time < (last_transmission + 5)) - return + return FALSE last_transmission = world.time var/time = time2text(world.realtime,"hh:mm:ss") var/turf/T = get_turf(src) - GLOB.lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]") + var/user_key = user ? user.key : usr?.key + GLOB.lastsignalers.Add("[time] : [user_key ? user_key : "userless"] used [src] @ location ([T ? T.x : 0],[T ? T.y : 0],[T ? T.z : 0]) : [format_frequency(frequency)]/[code]") var/datum/signal/signal = new signal.source = src signal.encryption = code signal.data["message"] = message + if(user) + signal.data["user"] = WEAKREF(user) - radio_connection.post_signal(src, signal) + transmit_connection.post_signal(src, signal) + return TRUE + +/obj/item/integrated_signaler/signal/receive_signal(var/datum/signal/signal) + if(!signal) + return FALSE + if(signal.source == src) + return FALSE + if(signal.data["message"] != "ACTIVATE") + return FALSE + if(isnull(signal.encryption)) + return FALSE + if(within_jamming_range(src)) + return FALSE + + var/obj/item/computer_hardware/network_card/network_card = loc + if(istype(network_card)) + return network_card.receive_signaler_signal(src, signal) + return FALSE /obj/item/integrated_signaler/signal/Destroy() - SSradio.remove_object(src, frequency) + if(SSradio) + SSradio.remove_object(src, frequency) return ..() diff --git a/code/game/objects/structures/machinery/atmoalter/canister.dm b/code/game/objects/structures/machinery/atmoalter/canister.dm index 2a2093a19f3..71207747d2a 100644 --- a/code/game/objects/structures/machinery/atmoalter/canister.dm +++ b/code/game/objects/structures/machinery/atmoalter/canister.dm @@ -567,7 +567,23 @@ update_flag /obj/structure/machinery/portable_atmospherics/canister/do_signaler() valve_open = !valve_open if(valve_open) + if(holding) + release_log += "Valve was opened by a signaler, starting the transfer into [holding]
" + else + release_log += "Valve was opened by a signaler, starting the transfer into the air
" log_open_userless("a signaler") + if(!(processing_flags & MACHINERY_PROCESS_SELF)) + START_PROCESSING_MACHINE(src, MACHINERY_PROCESS_SELF) + process() + else + if(holding) + release_log += "Valve was closed by a signaler, stopping the transfer into [holding]
" + else + release_log += "Valve was closed by a signaler, stopping the transfer into the air
" + + update_connected_network() + update_icon() + SStgui.update_uis(src) /** * Dirty way to fill room with gas. However it is a bit easier to do than creating some floor/engine/n2o -rastaf0 diff --git a/code/game/objects/structures/machinery/stasis_cage.dm b/code/game/objects/structures/machinery/stasis_cage.dm index 49051c6a385..4f1a18347bb 100644 --- a/code/game/objects/structures/machinery/stasis_cage.dm +++ b/code/game/objects/structures/machinery/stasis_cage.dm @@ -252,65 +252,3 @@ if(target.buckled_to) target.buckled_to.unbuckle() contain(user, target) - - -/datum/wires/stasis_cage - proper_name = "Stasis Cage" - holder_type = /obj/structure/machinery/stasis_cage - - -/datum/wires/stasis_cage/New(atom/holder) - wires = list( - WIRE_SAFETY, - WIRE_RELEASE, - WIRE_LOCK - ) - add_duds(3) - ..() - -/datum/wires/stasis_cage/interactable(mob/user) - SHOULD_CALL_PARENT(FALSE) - var/obj/structure/machinery/stasis_cage/stasis_cage = holder - if (stasis_cage.panel_open) - return TRUE - return FALSE - - -/datum/wires/stasis_cage/get_status() - var/obj/structure/machinery/stasis_cage/stasis_cage = holder - . = ..() - if(!stasis_cage.use_power) - . += "The panel is unpowered." - else - . += "The panel is powered." - . += "The biometric safety sensors are [(WIRE_SAFETY in cut_wires) ? "connected" : "disconnected"]." - . += "The cage's emergency auto-release mechanism is [(WIRE_RELEASE in cut_wires) ? "disabled" : "enabled"]." - . += "The cage lid motors are [(WIRE_LOCK in cut_wires) ? "overriden" : "nominal"]." - - -/datum/wires/stasis_cage/on_cut(wire, mend, source) - var/obj/structure/machinery/stasis_cage/stasis_cage = holder - switch (wire) - if (WIRE_SAFETY) - stasis_cage.safety = !stasis_cage.safety - if (WIRE_RELEASE) - if (stasis_cage.contained && !mend) - stasis_cage.release() - holder.update_icon() - if (WIRE_LOCK) - if (!mend) - playsound(stasis_cage.loc, 'sound/machines/BoltsDown.ogg', 60) - holder.visible_message(SPAN_WARNING("The cage's lid bolts down destructively, denting itself!"), SPAN_NOTICE("You notice the cage lid override flag blink hastily.")) - stasis_cage.broken = TRUE - holder.update_icon() - - -/datum/wires/stasis_cage/on_pulse(wire) - var/obj/structure/machinery/stasis_cage/stasis_cage = holder - switch (wire) - if (WIRE_SAFETY) - if (stasis_cage.contained) - if (prob(20)) - holder.visible_message(SPAN_WARNING("The cage hastily flicks open its lid!"), SPAN_NOTICE("You notice the biometric sensor flag blink fervently.")) - stasis_cage.release() - diff --git a/code/game/objects/structures/machinery/vending/engitech.dm b/code/game/objects/structures/machinery/vending/engitech.dm index 2d5435f058a..899f3bb0123 100644 --- a/code/game/objects/structures/machinery/vending/engitech.dm +++ b/code/game/objects/structures/machinery/vending/engitech.dm @@ -22,7 +22,7 @@ /obj/item/flash/synthetic = 4, /obj/item/cell/high = 12, /obj/item/assembly/prox_sensor = 8, - /obj/item/assembly/signaler = 8, + /obj/item/assembly/signaler = 20, /obj/item/healthanalyzer = 3, /obj/item/surgery/scalpel = 2, /obj/item/surgery/circular_saw = 2, @@ -62,6 +62,7 @@ /obj/item/multitool = 4, /obj/item/taperoll/engineering = 4, /obj/item/clothing/glasses/safety/goggles = 4, + /obj/item/assembly/signaler = 20, /obj/item/airlock_electronics = 20, /obj/item/module/power_control = 10, /obj/item/airalarm_electronics = 10, diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index 3e44f46c0dc..6537b9a1492 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -4,7 +4,7 @@ icon = 'icons/obj/assemblies/new_assemblies.dmi' icon_state = "" obj_flags = OBJ_FLAG_CONDUCTABLE - w_class = WEIGHT_CLASS_SMALL + w_class = WEIGHT_CLASS_TINY matter = list(MATERIAL_STEEL = 100) recyclable = TRUE throwforce = 2 diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index ddfef2325e8..a01d976c17b 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -189,13 +189,22 @@ return FALSE if(!secured) audible_message("[icon2html(src, viewers(get_turf(src)))] *beep* *beep*", "*beep* *beep*") + return FALSE if(normal && a_right && a_left) - if(a_right != D) - a_right.pulsed(FALSE) - if(a_left != D) - a_left.pulsed(FALSE) + var/obj/item/assembly/right = a_right + if(right && right != D && right.holder == src) + right.pulsed(FALSE) + if(QDELETED(src)) + return TRUE + var/obj/item/assembly/left = a_left + if(left && left != D && left.holder == src) + left.pulsed(FALSE) + if(QDELETED(src)) + return TRUE if(master) master.receive_signal() + else + audible_message("[icon2html(src, viewers(get_turf(src)))] *beep* *beep*", "*beep* *beep*") return TRUE /obj/item/assembly_holder/hear_talk(mob/living/M, msg, verb, datum/language/speaking) diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm index 85f4f0e2346..4b41b5f98e7 100644 --- a/code/modules/assembly/signaler.dm +++ b/code/modules/assembly/signaler.dm @@ -72,8 +72,9 @@ switch(action) if("freq") var/new_frequency = text2num(params["freq"]) * 10 - if(new_frequency < RADIO_LOW_FREQ || new_frequency > RADIO_HIGH_FREQ) - new_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + new_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + if(new_frequency > RADIO_HIGH_FREQ) + new_frequency = RADIO_HIGH_FREQ - 1 set_frequency(new_frequency) if("code") diff --git a/code/modules/modular_computers/computers/subtypes/preset_handheld.dm b/code/modules/modular_computers/computers/subtypes/preset_handheld.dm index 38e95f30650..dfe6cf16df0 100644 --- a/code/modules/modular_computers/computers/subtypes/preset_handheld.dm +++ b/code/modules/modular_computers/computers/subtypes/preset_handheld.dm @@ -60,6 +60,10 @@ . = ..() card_slot.stored_item = new /obj/item/pen/silver +/obj/item/modular_computer/handheld/preset/engineering/install_default_hardware() + ..() + network_card = new /obj/item/computer_hardware/network_card/signaler(src) + /obj/item/modular_computer/handheld/preset/engineering/atmos _app_preset_type = /datum/modular_computer_app_presets/engineering/atmos @@ -86,6 +90,10 @@ /obj/item/modular_computer/handheld/preset/supply/machinist _app_preset_type = /datum/modular_computer_app_presets/supply/machinist +/obj/item/modular_computer/handheld/preset/supply/machinist/install_default_hardware() + ..() + network_card = new /obj/item/computer_hardware/network_card/signaler(src) + // Cargo Delivery /obj/item/modular_computer/handheld/preset/supply/cargo_delivery _app_preset_type = /datum/modular_computer_app_presets/cargo_delivery @@ -125,6 +133,10 @@ . = ..() card_slot.stored_item = new /obj/item/pen/white +/obj/item/modular_computer/handheld/preset/research/install_default_hardware() + ..() + network_card = new /obj/item/computer_hardware/network_card/signaler(src) + /obj/item/modular_computer/handheld/preset/research/rd _app_preset_type = /datum/modular_computer_app_presets/research/rd diff --git a/code/modules/modular_computers/computers/subtypes/preset_laptop.dm b/code/modules/modular_computers/computers/subtypes/preset_laptop.dm index 3e0bef3bb46..c86a6cf2c0f 100644 --- a/code/modules/modular_computers/computers/subtypes/preset_laptop.dm +++ b/code/modules/modular_computers/computers/subtypes/preset_laptop.dm @@ -31,6 +31,11 @@ _app_preset_type = /datum/modular_computer_app_presets/engineering enrolled = DEVICE_COMPANY +/obj/item/modular_computer/laptop/preset/engineering/install_default_hardware() + ..() + QDEL_NULL(network_card) + network_card = new /obj/item/computer_hardware/network_card/signaler(src) + /obj/item/modular_computer/laptop/preset/engineering/ce name = "chief engineer's laptop" desc = "A portable computer belonging to the chief engineer." @@ -58,6 +63,8 @@ /obj/item/modular_computer/laptop/preset/research/install_default_hardware() ..() ai_slot = new /obj/item/computer_hardware/ai_slot(src) + QDEL_NULL(network_card) + network_card = new /obj/item/computer_hardware/network_card/signaler(src) /obj/item/modular_computer/laptop/preset/research/rd name = "research director's laptop" @@ -130,6 +137,8 @@ /obj/item/modular_computer/laptop/preset/supply/robotics/install_default_hardware() ..() access_cable_dongle = new /obj/item/computer_hardware/access_cable_dongle(src) + QDEL_NULL(network_card) + network_card = new /obj/item/computer_hardware/network_card/signaler(src) // Representative /obj/item/modular_computer/laptop/preset/representative diff --git a/code/modules/modular_computers/computers/subtypes/preset_pda.dm b/code/modules/modular_computers/computers/subtypes/preset_pda.dm index 667add4c92a..70373c8452d 100644 --- a/code/modules/modular_computers/computers/subtypes/preset_pda.dm +++ b/code/modules/modular_computers/computers/subtypes/preset_pda.dm @@ -60,6 +60,10 @@ . = ..() card_slot.stored_item = new /obj/item/pen/silver +/obj/item/modular_computer/handheld/pda/engineering/install_default_hardware() + ..() + network_card = new /obj/item/computer_hardware/network_card/signaler(src) + /obj/item/modular_computer/handheld/pda/engineering/atmos _app_preset_type = /datum/modular_computer_app_presets/engineering/atmos @@ -82,6 +86,10 @@ /obj/item/modular_computer/handheld/pda/supply/machinist _app_preset_type = /datum/modular_computer_app_presets/supply/machinist +/obj/item/modular_computer/handheld/pda/supply/machinist/install_default_hardware() + ..() + network_card = new /obj/item/computer_hardware/network_card/signaler(src) + /obj/item/modular_computer/handheld/pda/supply/om icon_add = "q" @@ -117,6 +125,10 @@ . = ..() card_slot.stored_item = new /obj/item/pen/white +/obj/item/modular_computer/handheld/pda/research/install_default_hardware() + ..() + network_card = new /obj/item/computer_hardware/network_card/signaler(src) + /obj/item/modular_computer/handheld/pda/research/rd _app_preset_type = /datum/modular_computer_app_presets/research/rd icon_add = "rd" diff --git a/code/modules/modular_computers/computers/subtypes/preset_wristbound.dm b/code/modules/modular_computers/computers/subtypes/preset_wristbound.dm index 201c56d25df..f359db41de0 100644 --- a/code/modules/modular_computers/computers/subtypes/preset_wristbound.dm +++ b/code/modules/modular_computers/computers/subtypes/preset_wristbound.dm @@ -154,6 +154,10 @@ . = ..() card_slot.stored_item = new /obj/item/pen/silver +/obj/item/modular_computer/handheld/wristbound/preset/pda/engineering/install_default_hardware() + ..() + network_card = new /obj/item/computer_hardware/network_card/signaler(src) + /obj/item/modular_computer/handheld/wristbound/preset/pda/engineering/atmos _app_preset_type = /datum/modular_computer_app_presets/engineering/atmos @@ -183,6 +187,10 @@ /obj/item/modular_computer/handheld/wristbound/preset/pda/supply/machinist _app_preset_type = /datum/modular_computer_app_presets/supply/machinist +/obj/item/modular_computer/handheld/wristbound/preset/pda/supply/machinist/install_default_hardware() + ..() + network_card = new /obj/item/computer_hardware/network_card/signaler(src) + // Medical /obj/item/modular_computer/handheld/wristbound/preset/pda/medical @@ -211,6 +219,10 @@ . = ..() card_slot.stored_item = new /obj/item/pen/white +/obj/item/modular_computer/handheld/wristbound/preset/pda/research/install_default_hardware() + ..() + network_card = new /obj/item/computer_hardware/network_card/signaler(src) + /obj/item/modular_computer/handheld/wristbound/preset/pda/research/rd _app_preset_type = /datum/modular_computer_app_presets/research/rd icon_add = "rd" diff --git a/code/modules/modular_computers/file_system/programs/app_presets_.dm b/code/modules/modular_computers/file_system/programs/app_presets_.dm index bc158b9cb32..ed3c1d6c222 100644 --- a/code/modules/modular_computers/file_system/programs/app_presets_.dm +++ b/code/modules/modular_computers/file_system/programs/app_presets_.dm @@ -55,6 +55,7 @@ ABSTRACT_TYPE(/datum/modular_computer_app_presets) /datum/computer_file/program/camera_monitor,\ /datum/computer_file/program/lighting_control,\ /datum/computer_file/program/fusion_codex,\ + /datum/computer_file/program/signaler,\ ) #define COMPUTER_APP_PRESET_HORIZON_MEDICAL list(\ @@ -71,6 +72,7 @@ ABSTRACT_TYPE(/datum/modular_computer_app_presets) /datum/computer_file/program/scanner/science,\ /datum/computer_file/program/scanner/gas,\ /datum/computer_file/program/away_manifest,\ + /datum/computer_file/program/signaler,\ ) #define COMPUTER_APP_PRESET_HORIZON_SECURITY list(\ diff --git a/code/modules/modular_computers/file_system/programs/app_presets_crew.dm b/code/modules/modular_computers/file_system/programs/app_presets_crew.dm index 1243f2f0a4d..c1c089ead41 100644 --- a/code/modules/modular_computers/file_system/programs/app_presets_crew.dm +++ b/code/modules/modular_computers/file_system/programs/app_presets_crew.dm @@ -294,7 +294,7 @@ /datum/modular_computer_app_presets/supply/machinist/New() . = ..() - program_list += list(/datum/computer_file/program/records/medical, /datum/computer_file/program/scanner/science, /datum/computer_file/program/robotics) + program_list += list(/datum/computer_file/program/records/medical, /datum/computer_file/program/scanner/science, /datum/computer_file/program/robotics, /datum/computer_file/program/signaler) //Machinist is the bastard child of supply/operation, it doesn't have access to shit essentially program_list -= list(/datum/computer_file/program/civilian/cargocontrol, /datum/computer_file/program/civilian/cargodelivery, /datum/computer_file/program/away_manifest) diff --git a/code/modules/modular_computers/file_system/programs/engineering/fusion_codex.dm b/code/modules/modular_computers/file_system/programs/engineering/fusion_codex.dm index f987b424c0c..505814de9c9 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/fusion_codex.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/fusion_codex.dm @@ -4,7 +4,7 @@ program_icon_state = "medcomp" program_key_icon_state = "teal_key" extended_desc = "Useful program to reference reaction chains for nuclear fusion reactors." - size = 14 + size = 4 requires_access_to_run = PROGRAM_ACCESS_LIST_ONE required_access_run = list(ACCESS_ENGINE) required_access_download = list(ACCESS_ENGINE) diff --git a/code/modules/modular_computers/file_system/programs/generic/signaler.dm b/code/modules/modular_computers/file_system/programs/generic/signaler.dm new file mode 100644 index 00000000000..322c332ed44 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/generic/signaler.dm @@ -0,0 +1,249 @@ +#define NTOS_SIGNALER_CHANNEL_COUNT 8 +#define NTOS_SIGNALER_RECEIVE_CHANNELS 2 +#define NTOS_SIGNALER_DEFAULT_FREQUENCY 1457 +#define NTOS_SIGNALER_DEFAULT_CODE 30 +#define NTOS_SIGNALER_MAX_LABEL_LENGTH 24 + +/datum/computer_file/program/signaler + filename = "signaler" + filedesc = "Signal Manager" + extended_desc = "This program configures signaler-capable network cards for receiving and sending signals on custom frequencies." + program_icon_state = "generic" + program_key_icon_state = "green_key" + color = LIGHT_COLOR_GREEN + size = 1 + requires_ntnet = FALSE + available_on_ntnet = TRUE + usage_flags = PROGRAM_ALL_REGULAR + tgui_id = "NTOSSignaler" + + var/list/signal_configurations = list() + +/datum/computer_file/program/signaler/New(obj/item/modular_computer/comp) + . = ..() + ensure_signal_configurations() + +/datum/computer_file/program/signaler/clone(var/rename = FALSE, var/computer) + ensure_signal_configurations() + var/datum/computer_file/program/signaler/signaler_clone = ..(rename, computer) + signaler_clone.signal_configurations = list() + for(var/list/configuration in signal_configurations) + signaler_clone.signal_configurations += list(configuration.Copy()) + return signaler_clone + +/datum/computer_file/program/signaler/run_program(var/mob/user) + if(!..()) + return FALSE + sync_receive_radios() + return TRUE + +/datum/computer_file/program/signaler/ui_data(mob/user) + ensure_signal_configurations() + sync_receive_radios() + + var/list/data = initial_data() + data["hardware_ready"] = !!get_signaler_card() + data["hardware_status"] = get_hardware_status() + data["min_frequency"] = RADIO_LOW_FREQ + data["max_frequency"] = RADIO_HIGH_FREQ + data["receive_channels"] = NTOS_SIGNALER_RECEIVE_CHANNELS + data["silent"] = silent + data["channels"] = list() + + for(var/i = 1 to NTOS_SIGNALER_CHANNEL_COUNT) + var/list/configuration = signal_configurations[i] + data["channels"] += list(list( + "index" = i, + "label" = configuration["label"], + "frequency" = configuration["frequency"], + "code" = configuration["code"], + "receiving" = i <= NTOS_SIGNALER_RECEIVE_CHANNELS, + "receiving_enabled" = configuration["receiving_enabled"], + "last_received_code" = configuration["last_received_code"], + "last_received_time" = configuration["last_received_time"] + )) + + return data + +/datum/computer_file/program/signaler/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + if(..()) + return TRUE + + if(action == "toggle_silent") + silent = !silent + . = TRUE + return + + ensure_signal_configurations() + var/channel_index = clamp(round(text2num(params["index"])), 1, NTOS_SIGNALER_CHANNEL_COUNT) + var/list/configuration = signal_configurations[channel_index] + if(!configuration) + return TRUE + + switch(action) + if("rename") + var/new_label = tgui_input_text(usr, "Enter a new label for this [channel_index <= NTOS_SIGNALER_RECEIVE_CHANNELS ? "receiver" : "signaler"].", "Rename Signal", configuration["label"], NTOS_SIGNALER_MAX_LABEL_LENGTH) + if(isnull(new_label)) + return TRUE + new_label = sanitize(new_label, NTOS_SIGNALER_MAX_LABEL_LENGTH) + configuration["label"] = length(new_label) ? new_label : default_label(channel_index) + . = TRUE + if("frequency") + configuration["frequency"] = clean_frequency(text2num(params["frequency"])) + if(channel_index <= NTOS_SIGNALER_RECEIVE_CHANNELS) + sync_receive_radios() + . = TRUE + if("toggle_receive") + if(channel_index <= NTOS_SIGNALER_RECEIVE_CHANNELS) + configuration["receiving_enabled"] = !configuration["receiving_enabled"] + if(!configuration["receiving_enabled"]) + configuration["last_received_code"] = null + configuration["last_received_time"] = null + sync_receive_radios() + . = TRUE + if("code") + if(channel_index > NTOS_SIGNALER_RECEIVE_CHANNELS) + configuration["code"] = clamp(round(text2num(params["code"])), 1, 100) + . = TRUE + if("reset") + configuration["frequency"] = NTOS_SIGNALER_DEFAULT_FREQUENCY + configuration["code"] = NTOS_SIGNALER_DEFAULT_CODE + if(channel_index <= NTOS_SIGNALER_RECEIVE_CHANNELS) + configuration["receiving_enabled"] = FALSE + configuration["last_received_code"] = null + configuration["last_received_time"] = null + sync_receive_radios() + . = TRUE + if("send") + if(channel_index > NTOS_SIGNALER_RECEIVE_CHANNELS) + send_signal(channel_index, usr) + . = TRUE + +/datum/computer_file/program/signaler/proc/ensure_signal_configurations() + for(var/i = 1 to min(length(signal_configurations), NTOS_SIGNALER_CHANNEL_COUNT)) + var/list/configuration = signal_configurations[i] + if(!configuration) + continue + if(isnull(configuration["receiving_enabled"])) + configuration["receiving_enabled"] = FALSE + + for(var/i = length(signal_configurations) + 1 to NTOS_SIGNALER_CHANNEL_COUNT) + signal_configurations += list(list( + "label" = default_label(i), + "frequency" = NTOS_SIGNALER_DEFAULT_FREQUENCY, + "code" = NTOS_SIGNALER_DEFAULT_CODE, + "receiving_enabled" = FALSE, + "last_received_code" = null, + "last_received_time" = null + )) + +/datum/computer_file/program/signaler/proc/default_label(var/channel_index) + if(channel_index <= NTOS_SIGNALER_RECEIVE_CHANNELS) + return "Receiver [channel_index]" + return "Signal [channel_index - NTOS_SIGNALER_RECEIVE_CHANNELS]" + +/datum/computer_file/program/signaler/proc/clean_frequency(var/frequency) + if(!isnum(frequency)) + frequency = NTOS_SIGNALER_DEFAULT_FREQUENCY + frequency = sanitize_frequency(frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + if(frequency > RADIO_HIGH_FREQ) + frequency = RADIO_HIGH_FREQ - 1 + return frequency + +/datum/computer_file/program/signaler/proc/get_signaler_card() + if(!computer?.network_card) + return null + var/obj/item/computer_hardware/network_card/network_card = computer.network_card + if(!network_card.enabled || !network_card.check_functionality() || !network_card.sradio) + return null + return network_card + +/datum/computer_file/program/signaler/proc/get_hardware_status() + if(!computer?.network_card) + return "No network card detected." + var/obj/item/computer_hardware/network_card/network_card = computer.network_card + if(!network_card.sradio) + return "Installed network card does not support signaling." + if(!network_card.enabled) + return "Signaler network card is disabled." + if(!network_card.check_functionality()) + return "Signaler network card is damaged." + return "Ready." + +/datum/computer_file/program/signaler/proc/sync_receive_radios() + var/obj/item/computer_hardware/network_card/network_card = get_signaler_card() + if(!network_card) + return FALSE + + for(var/i = 1 to NTOS_SIGNALER_RECEIVE_CHANNELS) + var/obj/item/integrated_signaler/signal/radio = network_card.get_signaler_radio(i) + var/list/configuration = signal_configurations[i] + if(radio && configuration) + if(configuration["receiving_enabled"]) + radio.set_frequency(configuration["frequency"]) + radio.set_listening(TRUE) + else + radio.set_listening(FALSE) + + return TRUE + +/datum/computer_file/program/signaler/proc/send_signal(var/channel_index, var/mob/user) + var/obj/item/computer_hardware/network_card/network_card = get_signaler_card() + if(!network_card) + computer?.output_error(FONT_SMALL(SPAN_WARNING("Hardware Error - Signaler network card unavailable."))) + return FALSE + + var/list/configuration = signal_configurations[channel_index] + if(!configuration) + return FALSE + + var/obj/item/integrated_signaler/signal/radio = network_card.get_signaler_radio(0) + if(!radio) + return FALSE + + var/old_frequency = radio.frequency + var/old_code = radio.code + + radio.set_frequency(configuration["frequency"]) + radio.code = configuration["code"] + var/sent = radio.send_signal("ACTIVATE", user) + radio.code = old_code + radio.set_frequency(old_frequency) + + if(sent) + computer?.output_notice("Signal sent on [format_frequency(configuration["frequency"])]/[configuration["code"]].", 0) + else + computer?.output_error(FONT_SMALL(SPAN_WARNING("Unable to send signal. The transmitter may be cooling down or jammed."))) + + return sent + +/datum/computer_file/program/signaler/proc/receive_signal(var/channel_index, var/datum/signal/signal) + if(channel_index < 1 || channel_index > NTOS_SIGNALER_RECEIVE_CHANNELS) + return FALSE + + ensure_signal_configurations() + var/list/configuration = signal_configurations[channel_index] + if(!configuration) + return FALSE + if(!configuration["receiving_enabled"]) + return FALSE + + configuration["last_received_code"] = signal.encryption + configuration["last_received_time"] = worldtime2text() + + if(computer) + var/received_message = "Signal received on [configuration["label"]]: [isnull(signal.encryption) ? "no code" : signal.encryption]." + if(silent) + computer.output_notice(received_message, 0) + else + playsound(get_turf(computer), 'sound/machines/twobeep.ogg', 20, 1) + computer.audible_message("[icon2html(computer, viewers(1, get_turf(computer)))] [computer] beeps, \"[received_message]\"") + + SStgui.update_uis(src) + return TRUE + +#undef NTOS_SIGNALER_CHANNEL_COUNT +#undef NTOS_SIGNALER_RECEIVE_CHANNELS +#undef NTOS_SIGNALER_DEFAULT_FREQUENCY +#undef NTOS_SIGNALER_DEFAULT_CODE +#undef NTOS_SIGNALER_MAX_LABEL_LENGTH diff --git a/code/modules/modular_computers/hardware/network_card.dm b/code/modules/modular_computers/hardware/network_card.dm index 182fa048843..cad9523c77b 100644 --- a/code/modules/modular_computers/hardware/network_card.dm +++ b/code/modules/modular_computers/hardware/network_card.dm @@ -13,6 +13,8 @@ GLOBAL_VAR_INIT(ntnet_card_uid, 1) var/long_range = FALSE var/ethernet = FALSE // Hard-wired, therefore always on, ignores NTNet wireless checks. var/obj/item/integrated_signaler/signal/sradio = FALSE // integrated signaler - not present on basic model. + var/obj/item/integrated_signaler/signal/secondary_sradio = FALSE // second receiver for NTOS signaler software. + var/obj/item/integrated_signaler/signal/transmit_sradio = FALSE // transmitter for NTOS signaler software. malfunction_probability = 1 /obj/item/computer_hardware/network_card/diagnostics(mob/user) @@ -41,7 +43,7 @@ GLOBAL_VAR_INIT(ntnet_card_uid, 1) /obj/item/computer_hardware/network_card/signaler/Initialize() . = ..() - sradio = new /obj/item/integrated_signaler/signal(src) + initialize_signaler_radios() /obj/item/computer_hardware/network_card/advanced name = "advanced NTNet network card" @@ -54,7 +56,7 @@ GLOBAL_VAR_INIT(ntnet_card_uid, 1) /obj/item/computer_hardware/network_card/advanced/Initialize() . = ..() - sradio = new /obj/item/integrated_signaler/signal(src) + initialize_signaler_radios() /obj/item/computer_hardware/network_card/wired name = "wired NTNet network card" @@ -67,7 +69,46 @@ GLOBAL_VAR_INIT(ntnet_card_uid, 1) /obj/item/computer_hardware/network_card/wired/Initialize() . = ..() - sradio = new /obj/item/integrated_signaler/signal(src) + initialize_signaler_radios() + +/obj/item/computer_hardware/network_card/proc/initialize_signaler_radios() + if(!sradio) + sradio = new /obj/item/integrated_signaler/signal/network_card(src) + sradio.set_listening(FALSE) + if(!secondary_sradio) + secondary_sradio = new /obj/item/integrated_signaler/signal/network_card(src) + secondary_sradio.set_listening(FALSE) + if(!transmit_sradio) + transmit_sradio = new /obj/item/integrated_signaler/signal/network_card(src) + transmit_sradio.set_listening(FALSE) + +/obj/item/computer_hardware/network_card/proc/get_signaler_radio(var/channel = 1) + if(channel == 0) + return transmit_sradio + if(channel == 2) + return secondary_sradio + return sradio + +/obj/item/computer_hardware/network_card/proc/receive_signaler_signal(var/obj/item/integrated_signaler/signal/receiver, var/datum/signal/signal) + if(!parent_computer || !parent_computer.enabled || !enabled || !check_functionality()) + return FALSE + + var/channel + if(receiver == sradio) + channel = 1 + else if(receiver == secondary_sradio) + channel = 2 + else + return FALSE + + if(istype(parent_computer.active_program, /datum/computer_file/program/signaler)) + var/datum/computer_file/program/signaler/active_signaler = parent_computer.active_program + active_signaler.receive_signal(channel, signal) + + for(var/datum/computer_file/program/signaler/signaler_program in parent_computer.idle_threads) + signaler_program.receive_signal(channel, signal) + + return TRUE // Returns a string identifier of this network card /obj/item/computer_hardware/network_card/proc/get_network_tag() @@ -106,6 +147,10 @@ GLOBAL_VAR_INIT(ntnet_card_uid, 1) /obj/item/computer_hardware/network_card/Destroy() if(sradio) QDEL_NULL(sradio) + if(secondary_sradio) + QDEL_NULL(secondary_sradio) + if(transmit_sradio) + QDEL_NULL(transmit_sradio) if(parent_computer?.network_card == src) parent_computer.network_card = null parent_computer = null diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 1b1bd57fc3b..fd774f97be5 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -175,6 +175,8 @@ ABSTRACT_TYPE(/obj/structure/machinery/power/apc) /// If we're actually able to charge var/charge_mode = CHARGE_MODE_CHARGE var/last_time = 1 + /// For doing silly games with blowing out lights + var/light_explosion_safety = TRUE /obj/structure/machinery/power/apc/mechanics_hints(mob/user, distance, is_adjacent) . += ..() diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index 20beaa5d926..81be560f2f4 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -159,6 +159,7 @@ SPAN_NOTICE("You detach [rig] from \the [src]")) rig.forceMove(get_turf(user)) + rig.detached() rig = null overlays = new/list() @@ -176,12 +177,15 @@ if (rig) to_chat(user, SPAN_WARNING("There is another device in the way.")) return ..() + var/obj/item/assembly_holder/H = attacking_item + if(!H.secured) + to_chat(user, SPAN_WARNING("The assembly must be secured with a screwdriver.")) + return TRUE user.visible_message("[user] begins rigging [attacking_item] to \the [src].", "You begin rigging [attacking_item] to \the [src]") if(do_after(user, 20)) user.visible_message(SPAN_NOTICE("[user] rigs [attacking_item] to \the [src]."), SPAN_NOTICE("You rig [attacking_item] to \the [src]")) - var/obj/item/assembly_holder/H = attacking_item if (istype(H.a_left,/obj/item/assembly/igniter) || istype(H.a_right,/obj/item/assembly/igniter)) message_admins("[key_name_admin(user)] rigged fueltank at [loc.loc.name] ([loc.x],[loc.y],[loc.z]) for explosion. (JMP)") log_game("[key_name(user)] rigged fueltank at [loc.loc.name] ([loc.x],[loc.y],[loc.z]) for explosion.") diff --git a/html/changelogs/Bat-SignalsWires.yml b/html/changelogs/Bat-SignalsWires.yml new file mode 100644 index 00000000000..fac848d5aff --- /dev/null +++ b/html/changelogs/Bat-SignalsWires.yml @@ -0,0 +1,13 @@ +author: Batrachophrenoboocosmomachia +delete-after: True +changes: + - qol: "Eligible non-random wire panels now use relevant domain skills where applicable: engineering skills, robotics, and xenobiology implemented, instead of only Electrical Engineering 4." + - rscadd: "Adds new wire to APCs that blows out all the lights if you pulse it with the safeties off." + - rscadd: "Adds the Signal Manager NTOS app for signaler-capable network cards. Pre-installed in Engineering, Research, and Machinist app presets." + - rscadd: "Engineering, Research, and Machinist character modular computers now come with signaler-capable network cards." + - qol: "Increases remote signaler availability in EngiTech vendors and made assembly items tiny." + - bugfix: "Fixes air alarm wiring to use the correct wire datum path." + - bugfix: "Fixes canister-attached signaler regression." + - bugfix: "Fixes assembly holders failing silently when unsecured to fuel tanks; fuel tanks now reject unsecured assembly holders." + - bugfix: "Fixes runtime when an activation deletes or detaches one of the paired assemblies." + - qol: "Reduces the Fusion Codex program size." diff --git a/tgui/packages/tgui/interfaces/NTOSSignaler.tsx b/tgui/packages/tgui/interfaces/NTOSSignaler.tsx new file mode 100644 index 00000000000..b5ffdddb985 --- /dev/null +++ b/tgui/packages/tgui/interfaces/NTOSSignaler.tsx @@ -0,0 +1,187 @@ +import { + Box, + Button, + LabeledList, + NumberInput, + Section, + Table, +} from 'tgui-core/components'; +import { toFixed } from 'tgui-core/math'; + +import { useBackend } from '../backend'; +import { NtosWindow } from '../layouts'; + +type SignalerChannel = { + index: number; + label: string; + frequency: number; + code: number; + receiving: boolean; + receiving_enabled: boolean; + last_received_code: number | null; + last_received_time: string | null; +}; + +type Data = { + hardware_ready: boolean; + hardware_status: string; + min_frequency: number; + max_frequency: number; + receive_channels: number; + silent: boolean; + channels: SignalerChannel[]; +}; + +export const NTOSSignaler = () => { + const { act, data } = useBackend(); + const { channels = [] } = data; + const rows: SignalerChannel[][] = []; + + for (let i = 0; i < channels.length; i += 2) { + rows.push(channels.slice(i, i + 2)); + } + + return ( + + +
act('toggle_silent')} + /> + } + > + + {data.hardware_status} + +
+ + {rows.map((row) => ( + + {row.map((channel) => ( + + + + ))} + + ))} +
+
+
+ ); +}; + +const SignalSlot = (props: { channel: SignalerChannel; data: Data }) => { + const { act } = useBackend(); + const { channel, data } = props; + const channelNumber = channel.receiving + ? channel.index + : channel.index - data.receive_channels; + + return ( +
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/Signaler.tsx b/tgui/packages/tgui/interfaces/Signaler.tsx index 5882bc3d29c..db31351e4f8 100644 --- a/tgui/packages/tgui/interfaces/Signaler.tsx +++ b/tgui/packages/tgui/interfaces/Signaler.tsx @@ -32,15 +32,15 @@ export const Signaler = (props) => { toFixed(value, 2)} + format={(value) => toFixed(value, 1)} width="80px" - onChange={(value) => act('freq', { freq: value * 10 })} + onChange={(value) => act('freq', { freq: value })} />