diff --git a/code/__defines/shock.dm b/code/__defines/shock.dm new file mode 100644 index 0000000000..d877cc3ef7 --- /dev/null +++ b/code/__defines/shock.dm @@ -0,0 +1,4 @@ +// KEEP THIS UP TO DATE WITH tgui/packages/tgui/interfaces/ShockConfigurator.tsx +#define SHOCKFLAG_BURNDAMAGE 0x1 +#define SHOCKFLAG_DIGESTION 0x2 +#define SHOCKFLAG_TEST "test" diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index ed440457cb..aa09565613 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -256,6 +256,7 @@ // Instantiate tgui panel tgui_say = new(src, "tgui_say") + tgui_shocker = new(src, "tgui_shock") initialize_commandbar_spy() tgui_panel = new(src, "browseroutput") @@ -304,6 +305,7 @@ // Initialize tgui panel tgui_say.initialize() + tgui_shocker.initialize() connection_time = world.time connection_realtime = world.realtime diff --git a/code/modules/client/shock.dm b/code/modules/client/shock.dm new file mode 100644 index 0000000000..21152d10f4 --- /dev/null +++ b/code/modules/client/shock.dm @@ -0,0 +1,149 @@ +/client/var/datum/tgui_shock/tgui_shocker + +/client/verb/configure_shocker() + set name = "Configure MultiShock Integration" + set category = "OOC.Game Settings" + + if(tgui_shocker) + tgui_shocker.tgui_interact(mob) + +/mob/proc/attempt_multishock(flag) + client?.attempt_multishock(flag) + +/client/proc/attempt_multishock(flag) + tgui_shocker?.shock(flag) + +// NOTE: This datum controls TWO UIs, `window` is hidden and provides all of the WebSocket shit, `tgui_interact` is a +// normal configuration UI! +/datum/tgui_shock + /// The user who opened the window + var/client/client + /// The modal window + var/datum/tgui_window/window + + var/port = 8765 + var/enabled_flags = 0 + var/intensity = 15 + var/duration = 1 + + var/connected = FALSE + var/selected_device = -1 + var/list/available_devices + +////////////////////////////////////////// +// SHOCK.JS UI // +////////////////////////////////////////// +/datum/tgui_shock/New(client/client, id) + src.client = client + window = new(client, id) + window.subscribe(src, PROC_REF(on_message)) + window.is_browser = TRUE + +/datum/tgui_shock/proc/initialize() + set waitfor = FALSE + window.initialize( + inline_js = file2text('html/shock.js') + ) + +/datum/tgui_shock/proc/connect() + window.send_message("connect", list( + "port" = port, + )) + +/datum/tgui_shock/proc/request_devices() + if(!connected) + return + window.send_message("enumerateShockers") + +/datum/tgui_shock/proc/shock(flag) + if(!connected || !selected_device) + return + + if(flag != SHOCKFLAG_TEST) + if(!(enabled_flags & flag)) + return + + window.send_message("shock", list( + "intensity" = intensity, + "duration" = duration, + "shocker_ids" = list( + selected_device + ), + "warning" = FALSE, + )) + +/datum/tgui_shock/proc/estop() + window.send_message("estop") + +/datum/tgui_shock/proc/on_message(type, payload, href_list) + if(type == "connected") + connected = TRUE + else if(type == "disconnected") + connected = FALSE + else if(type == "error") + connected = FALSE + log_debug("WebSocket Error [json_encode(payload)]") + else if(type == "incomingMessage") + if(payload["lastCall"] == "get_devices") + available_devices = json_decode(payload["data"]) + +////////////////////////////////////////// +// TGUI // +////////////////////////////////////////// +/datum/tgui_shock/tgui_state(mob/user) + return GLOB.tgui_always_state + +/datum/tgui_shock/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui, custom_state) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ShockConfigurator", "Shock Configurator") + ui.open() + +/datum/tgui_shock/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + data["port"] = port + data["connected"] = connected + data["intensity"] = intensity + data["duration"] = duration + data["selectedDevice"] = selected_device + data["availableDevices"] = available_devices + data["enabledFlags"] = enabled_flags + + return data + +/datum/tgui_shock/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = ..() + + switch(action) + if("connect") + if(connected) + estop() + else + // TODO: preferences + connect() + . = TRUE + if("request_devices") + request_devices() + . = TRUE + if("estop") + estop() + . = TRUE + if("setSelectedDevice") + selected_device = text2num(params["device"]) + . = TRUE + if("test") + shock(SHOCKFLAG_TEST) + . = TRUE + if("set_flag") + enabled_flags ^= text2num(params["flag"]) + . = TRUE + if("port") + port = text2num(params["port"]) + . = TRUE + if("intensity") + intensity = text2num(params["intensity"]) + . = TRUE + if("duration") + duration = text2num(params["duration"]) + . = TRUE diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm index 1922f97392..50e1dd16c7 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/damage_procs.dm @@ -76,6 +76,7 @@ if(COLD_RESISTANCE in mutations) damage = 0 adjustFireLoss(damage * blocked) + attempt_multishock(SHOCKFLAG_BURNDAMAGE) if(SEARING) apply_damage(round(damage / 3), BURN, def_zone, initial_blocked, soaked, sharp, edge, used_weapon) apply_damage(round(damage / 3 * 2), BRUTE, def_zone, initial_blocked, soaked, sharp, edge, used_weapon) diff --git a/code/modules/vore/eating/bellymodes_datum_vr.dm b/code/modules/vore/eating/bellymodes_datum_vr.dm index 828b70ea54..78a046a446 100644 --- a/code/modules/vore/eating/bellymodes_datum_vr.dm +++ b/code/modules/vore/eating/bellymodes_datum_vr.dm @@ -71,6 +71,7 @@ GLOBAL_LIST_INIT(digest_modes, list()) L.adjustOxyLoss(B.digest_oxy) L.adjustToxLoss(B.digest_tox) L.adjustCloneLoss(B.digest_clone) + L.attempt_multishock(SHOCKFLAG_DIGESTION) // Send a message when a prey-thing enters hard crit. if(iscarbon(L) && old_health > 0 && L.health <= 0) to_chat(B.owner, span_notice("You feel [L] go still within your [lowertext(B.name)].")) diff --git a/html/shock.js b/html/shock.js new file mode 100644 index 0000000000..68ad8b61a4 --- /dev/null +++ b/html/shock.js @@ -0,0 +1,79 @@ +let webSocket; +let authKey; +let lastCall; + +const reactRoot = document.getElementById("react-root"); +if (reactRoot) { + reactRoot.innerHTML = "