From 255f2ce4493c3b46ce7b41a07fb26b0958c84628 Mon Sep 17 00:00:00 2001 From: Artur Date: Wed, 22 Jan 2020 20:17:42 +0100 Subject: [PATCH] Testmerge Conflict fix? --- code/__DEFINES/machines.dm | 5 +- code/__HELPERS/radio.dm | 2 +- code/datums/browser.dm | 12 +- code/datums/spawners_menu.dm | 114 +-- code/game/atoms.dm | 7 + .../machinery/computer/launchpad_control.dm | 197 ++-- .../computer/prisoner/gulag_teleporter.dm | 30 +- code/game/machinery/computer/teleporter.dm | 107 +-- code/game/machinery/launch_pad.dm | 132 ++- code/game/machinery/suit_storage_unit.dm | 906 +++++++++--------- code/game/objects/items/eightball.dm | 113 ++- code/modules/NTNet/network.dm | 5 + code/modules/NTNet/relays.dm | 9 +- code/modules/assembly/signaler.dm | 91 +- .../mob/dead/observer/notificationprefs.dm | 2 +- .../mob/living/simple_animal/bot/mulebot.dm | 133 +-- .../NTNet/NTNRC/conversation.dm | 10 +- .../modular_computers/file_system/program.dm | 30 +- .../file_system/programs/configurator.dm | 2 + .../file_system/programs/ntnrc_client.dm | 248 ++--- .../modular_computers/laptop_vendor.dm | 36 +- code/modules/shuttle/emergency.dm | 2 +- code/modules/tgui/tgui.dm | 728 +++++++------- node_modules/.yarn-integrity | 10 + tgui-next/README.md | 51 +- tgui-next/packages/tgui/interfaces/Cargo.js | 76 -- tgui-next/packages/tgui/public/tgui.bundle.js | 4 +- tgui-next/packages/tgui/routes.js | 92 +- tgui/assets/tgui.js | 2 +- tools/dmitool/.project | 17 - 30 files changed, 1585 insertions(+), 1588 deletions(-) create mode 100644 node_modules/.yarn-integrity delete mode 100644 tools/dmitool/.project diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index 8d3f07b5a6..eee0676dec 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -108,7 +108,10 @@ #define NUKE_ON_TIMING 2 #define NUKE_ON_EXPLODING 3 +#define MACHINE_NOT_ELECTRIFIED 0 +#define MACHINE_ELECTRIFIED_PERMANENT -1 +#define MACHINE_DEFAULT_ELECTRIFY_TIME 30 //these flags are used to tell the DNA modifier if a plant gene cannot be extracted or modified. #define PLANT_GENE_REMOVABLE (1<<0) -#define PLANT_GENE_EXTRACTABLE (1<<1) \ No newline at end of file +#define PLANT_GENE_EXTRACTABLE (1<<1) diff --git a/code/__HELPERS/radio.dm b/code/__HELPERS/radio.dm index 7ab21d0402..5fe87bdf5b 100644 --- a/code/__HELPERS/radio.dm +++ b/code/__HELPERS/radio.dm @@ -1,6 +1,6 @@ // Ensure the frequency is within bounds of what it should be sending/receiving at /proc/sanitize_frequency(frequency, free = FALSE) - . = round(frequency) + frequency = round(frequency) if(free) . = CLAMP(frequency, MIN_FREE_FREQ, MAX_FREE_FREQ) else diff --git a/code/datums/browser.dm b/code/datums/browser.dm index b226d85112..779391c4de 100644 --- a/code/datums/browser.dm +++ b/code/datums/browser.dm @@ -39,12 +39,12 @@ //title_image = ntitle_image /datum/browser/proc/add_stylesheet(name, file) - stylesheets["[ckey(name)].css"] = file - register_asset("[ckey(name)].css", file) - -/datum/browser/proc/add_script(name, file) - scripts["[ckey(name)].js"] = file - register_asset("[ckey(name)].js", file) + if(istype(name, /datum/asset/spritesheet)) + var/datum/asset/spritesheet/sheet = name + stylesheets["spritesheet_[sheet.name].css"] = "data/spritesheets/[sheet.name]" + else + stylesheets["[ckey(name)].css"] = file + register_asset("[ckey(name)].css", file) /datum/browser/proc/set_content(ncontent) content = ncontent diff --git a/code/datums/spawners_menu.dm b/code/datums/spawners_menu.dm index 95ea4b1b93..1adcb6fde6 100644 --- a/code/datums/spawners_menu.dm +++ b/code/datums/spawners_menu.dm @@ -1,58 +1,58 @@ -/datum/spawners_menu - var/mob/dead/observer/owner - -/datum/spawners_menu/New(mob/dead/observer/new_owner) - if(!istype(new_owner)) - qdel(src) - owner = new_owner - -/datum/spawners_menu/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.observer_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "spawners_menu", "Spawners Menu", 700, 600, master_ui, state) - ui.open() - -/datum/spawners_menu/ui_data(mob/user) - var/list/data = list() - data["spawners"] = list() - for(var/spawner in GLOB.mob_spawners) - var/list/this = list() - this["name"] = spawner - this["short_desc"] = "" - this["flavor_text"] = "" - this["important_warning"] = "" - this["refs"] = list() - for(var/spawner_obj in GLOB.mob_spawners[spawner]) - this["refs"] += "[REF(spawner_obj)]" - if(!this["desc"]) - if(istype(spawner_obj, /obj/effect/mob_spawn)) - var/obj/effect/mob_spawn/MS = spawner_obj - this["short_desc"] = MS.short_desc - this["flavor_text"] = MS.flavour_text - this["important_info"] = MS.important_info - else - var/obj/O = spawner_obj - this["desc"] = O.desc - this["amount_left"] = LAZYLEN(GLOB.mob_spawners[spawner]) - data["spawners"] += list(this) - - return data - -/datum/spawners_menu/ui_act(action, params) - if(..()) - return - - var/spawner_ref = pick(GLOB.mob_spawners[params["name"]]) - var/obj/effect/mob_spawn/MS = locate(spawner_ref) in GLOB.poi_list - if(!MS) - return - - switch(action) - if("jump") - if(MS) - owner.forceMove(get_turf(MS)) - . = TRUE - if("spawn") - if(MS) - MS.attack_ghost(owner) +/datum/spawners_menu + var/mob/dead/observer/owner + +/datum/spawners_menu/New(mob/dead/observer/new_owner) + if(!istype(new_owner)) + qdel(src) + owner = new_owner + +/datum/spawners_menu/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.observer_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "spawners_menu", "Spawners Menu", 700, 600, master_ui, state) + ui.open() + +/datum/spawners_menu/ui_data(mob/user) + var/list/data = list() + data["spawners"] = list() + for(var/spawner in GLOB.mob_spawners) + var/list/this = list() + this["name"] = spawner + this["short_desc"] = "" + this["flavor_text"] = "" + this["important_warning"] = "" + this["refs"] = list() + for(var/spawner_obj in GLOB.mob_spawners[spawner]) + this["refs"] += "[REF(spawner_obj)]" + if(!this["desc"]) + if(istype(spawner_obj, /obj/effect/mob_spawn)) + var/obj/effect/mob_spawn/MS = spawner_obj + this["short_desc"] = MS.short_desc + this["flavor_text"] = MS.flavour_text + this["important_info"] = MS.important_info + else + var/obj/O = spawner_obj + this["desc"] = O.desc + this["amount_left"] = LAZYLEN(GLOB.mob_spawners[spawner]) + data["spawners"] += list(this) + + return data + +/datum/spawners_menu/ui_act(action, params) + if(..()) + return + + var/spawner_ref = pick(GLOB.mob_spawners[params["name"]]) + var/obj/effect/mob_spawn/MS = locate(spawner_ref) in GLOB.poi_list + if(!MS) + return + + switch(action) + if("jump") + if(MS) + owner.forceMove(get_turf(MS)) + . = TRUE + if("spawn") + if(MS) + MS.attack_ghost(owner) . = TRUE \ No newline at end of file diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 1f62be8a24..85a89178bb 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -728,6 +728,13 @@ /atom/proc/multitool_act(mob/living/user, obj/item/I) return +/atom/proc/multitool_check_buffer(user, obj/item/I, silent = FALSE) + if(!istype(I, /obj/item/multitool)) + if(user && !silent) + to_chat(user, "[I] has no data buffer!") + return FALSE + return TRUE + /atom/proc/screwdriver_act(mob/living/user, obj/item/I) SEND_SIGNAL(src, COMSIG_ATOM_SCREWDRIVER_ACT, user, I) diff --git a/code/game/machinery/computer/launchpad_control.dm b/code/game/machinery/computer/launchpad_control.dm index b5d393bce0..1924cd9f23 100644 --- a/code/game/machinery/computer/launchpad_control.dm +++ b/code/game/machinery/computer/launchpad_control.dm @@ -1,11 +1,13 @@ /obj/machinery/computer/launchpad - name = "\improper launchpad control console" + name = "launchpad control console" desc = "Used to teleport objects to and from a launchpad." icon_screen = "teleport" icon_keyboard = "teleport_key" circuit = /obj/item/circuitboard/computer/launchpad_console - var/sending = TRUE - var/current_pad //current pad viewed on the screen + ui_x = 475 + ui_y = 260 + + var/selected_id var/list/obj/machinery/launchpad/launchpads var/maximum_pads = 4 @@ -18,7 +20,9 @@ return /obj/machinery/computer/launchpad/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/multitool)) + if(W.tool_behaviour == TOOL_MULTITOOL) + if(!multitool_check_buffer(user, W)) + return var/obj/item/multitool/M = W if(M.buffer && istype(M.buffer, /obj/machinery/launchpad)) if(LAZYLEN(launchpads) < maximum_pads) @@ -36,55 +40,7 @@ return FALSE return TRUE -/obj/machinery/computer/launchpad/proc/get_pad(number) - var/obj/machinery/launchpad/pad = launchpads[number] - return pad - -/obj/machinery/computer/launchpad/ui_interact(mob/user) - . = ..() - var/list/t = list() - if(!LAZYLEN(launchpads)) - obj_flags &= ~IN_USE //Yeah so if you deconstruct teleporter while its in the process of shooting it wont disable the console - t += "
No launchpad located.

" - else - for(var/i in 1 to LAZYLEN(launchpads)) - if(pad_exists(i)) - var/obj/machinery/launchpad/pad = get_pad(i) - if(pad.stat & NOPOWER) - t+= "[pad.display_name]" - else - t+= "[pad.display_name]" - else - launchpads -= get_pad(i) - t += "
" - - if(current_pad) - var/obj/machinery/launchpad/pad = get_pad(current_pad) - t += "
[pad.display_name]
" - t += "Rename" - t += "Remove

" - t += "O" //up-left - t += "^" //up - t += "O
" //up-right - t += "<"//left - t += "R"//reset to 0 - t += ">
"//right - t += "O"//down-left - t += "v"//down - t += "O
"//down-right - t += "
" - t += "
Current offset:

" - t += "
[abs(pad.y_offset)] [pad.y_offset > 0 ? "N":"S"] \[SET\]

" - t += "
[abs(pad.x_offset)] [pad.x_offset > 0 ? "E":"W"] \[SET\]

" - - t += "
Launch" - t += " Pull" - - var/datum/browser/popup = new(user, "launchpad", name, 300, 500) - popup.set_content(t.Join()) - popup.open() - -/obj/machinery/computer/launchpad/proc/teleport(mob/user, obj/machinery/launchpad/pad) +/obj/machinery/computer/launchpad/proc/teleport(mob/user, obj/machinery/launchpad/pad, sending) if(QDELETED(pad)) to_chat(user, "ERROR: Launchpad not responding. Check launchpad integrity.") return @@ -93,66 +49,83 @@ return pad.doteleport(user, sending) -/obj/machinery/computer/launchpad/Topic(href, href_list) - var/obj/machinery/launchpad/pad - if(href_list["pad"]) - pad = get_pad(text2num(href_list["pad"])) +/obj/machinery/computer/launchpad/proc/get_pad(number) + var/obj/machinery/launchpad/pad = launchpads[number] + return pad +/obj/machinery/computer/launchpad/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "launchpad_console", name, ui_x, ui_y, master_ui, state) + ui.open() + +/obj/machinery/computer/launchpad/ui_data(mob/user) + var/list/data = list() + var/list/pad_list = list() + for(var/i in 1 to LAZYLEN(launchpads)) + if(pad_exists(i)) + var/obj/machinery/launchpad/pad = get_pad(i) + var/list/this_pad = list() + this_pad["name"] = pad.display_name + this_pad["id"] = i + if(pad.stat & NOPOWER) + this_pad["inactive"] = TRUE + pad_list += list(this_pad) + else + launchpads -= get_pad(i) + data["launchpads"] = pad_list + data["selected_id"] = selected_id + if(selected_id) + var/obj/machinery/launchpad/current_pad = launchpads[selected_id] + data["x"] = current_pad.x_offset + data["y"] = current_pad.y_offset + data["pad_name"] = current_pad.display_name + data["range"] = current_pad.range + data["selected_pad"] = current_pad + if(QDELETED(current_pad) || (current_pad.stat & NOPOWER)) + data["pad_active"] = FALSE + return data + data["pad_active"] = TRUE + + return data + +/obj/machinery/computer/launchpad/ui_act(action, params) if(..()) return - if(!LAZYLEN(launchpads)) - updateDialog() - return + var/obj/machinery/launchpad/current_pad = launchpads[selected_id] + switch(action) + if("select_pad") + selected_id = text2num(params["id"]) + . = TRUE + if("set_pos") + var/new_x = text2num(params["x"]) + var/new_y = text2num(params["y"]) + current_pad.set_offset(new_x, new_y) + . = TRUE + if("move_pos") + var/plus_x = text2num(params["x"]) + var/plus_y = text2num(params["y"]) + current_pad.set_offset( + x = current_pad.x_offset + plus_x, + y = current_pad.y_offset + plus_y + ) + . = TRUE + if("rename") + . = TRUE + var/new_name = params["name"] + if(!new_name) + return + current_pad.display_name = new_name + if("remove") + if(usr && alert(usr, "Are you sure?", "Unlink Launchpad", "I'm Sure", "Abort") != "Abort") + launchpads -= current_pad + selected_id = null + . = TRUE + if("launch") + teleport(usr, current_pad, TRUE) + . = TRUE - if(href_list["choose_pad"]) - current_pad = text2num(href_list["pad"]) - - if(href_list["raisex"]) - if(pad.x_offset < pad.range) - pad.x_offset++ - - if(href_list["lowerx"]) - if(pad.x_offset > (pad.range * -1)) - pad.x_offset-- - - if(href_list["raisey"]) - if(pad.y_offset < pad.range) - pad.y_offset++ - - if(href_list["lowery"]) - if(pad.y_offset > (pad.range * -1)) - pad.y_offset-- - - if(href_list["reset"]) - pad.y_offset = 0 - pad.x_offset = 0 - - if(href_list["change_name"]) - var/new_name = stripped_input(usr, "What do you wish to name the launchpad?", "Launchpad", pad.display_name, 15) - if(!new_name) - return - pad.display_name = new_name - - if(href_list["setx"]) - var/newx = input(usr, "Input new x offset", pad.display_name, pad.x_offset) as null|num - if(!isnull(newx)) - pad.x_offset = CLAMP(newx, -pad.range, pad.range) - - if(href_list["sety"]) - var/newy = input(usr, "Input new y offset", pad.display_name, pad.y_offset) as null|num - if(!isnull(newy)) - pad.y_offset = CLAMP(newy, -pad.range, pad.range) - - if(href_list["remove"]) - if(usr && alert(usr, "Are you sure?", "Remove Launchpad", "I'm Sure", "Abort") != "Abort") - launchpads -= pad - - if(href_list["launch"]) - sending = TRUE - teleport(usr, pad) - - if(href_list["pull"]) - sending = FALSE - teleport(usr, pad) - - updateDialog() + if("pull") + teleport(usr, current_pad, FALSE) + . = TRUE + . = TRUE \ No newline at end of file diff --git a/code/game/machinery/computer/prisoner/gulag_teleporter.dm b/code/game/machinery/computer/prisoner/gulag_teleporter.dm index f6e164efe9..e4a6a0b2d4 100644 --- a/code/game/machinery/computer/prisoner/gulag_teleporter.dm +++ b/code/game/machinery/computer/prisoner/gulag_teleporter.dm @@ -6,6 +6,9 @@ icon_keyboard = "security_key" req_access = list(ACCESS_ARMORY) circuit = /obj/item/circuitboard/computer/gulag_teleporter_console + ui_x = 350 + ui_y = 295 + var/default_goal = 200 var/obj/machinery/gulag_teleporter/teleporter = null var/obj/structure/gulag_beacon/beacon = null @@ -22,7 +25,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "gulag_console", name, 455, 440, master_ui, state) + ui = new(user, src, ui_key, "gulag_console", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/computer/prisoner/gulag_teleporter_computer/ui_data(mob/user) @@ -50,13 +53,19 @@ data["teleporter_location"] = "([teleporter.x], [teleporter.y], [teleporter.z])" data["teleporter_lock"] = teleporter.locked data["teleporter_state_open"] = teleporter.state_open + else + data["teleporter"] = null if(beacon) data["beacon"] = beacon data["beacon_location"] = "([beacon.x], [beacon.y], [beacon.z])" + else + data["beacon"] = null if(contained_id) data["id"] = contained_id data["id_name"] = contained_id.registered_name data["goal"] = contained_id.goal + else + data["id"] = null data["can_teleport"] = can_teleport return data @@ -72,36 +81,41 @@ switch(action) if("scan_teleporter") teleporter = findteleporter() + return TRUE if("scan_beacon") beacon = findbeacon() + return TRUE if("handle_id") if(contained_id) id_eject(usr) else id_insert(usr) + return TRUE if("set_goal") - var/new_goal = input("Set the amount of points:", "Points", contained_id.goal) as num|null + var/new_goal = text2num(params["value"]) if(!isnum(new_goal)) return if(!new_goal) new_goal = default_goal - if (new_goal > 1000) - to_chat(usr, "The entered amount of points is too large. Points have instead been set to the maximum allowed amount.") contained_id.goal = CLAMP(new_goal, 0, 1000) //maximum 1000 points + return TRUE if("toggle_open") if(teleporter.locked) - to_chat(usr, "The teleporter is locked") + to_chat(usr, "The teleporter must be unlocked first.") return teleporter.toggle_open() + return TRUE if("teleporter_lock") if(teleporter.state_open) - to_chat(usr, "Close the teleporter before locking!") + to_chat(usr, "The teleporter must be closed first.") return teleporter.locked = !teleporter.locked + return TRUE if("teleport") if(!teleporter || !beacon) return addtimer(CALLBACK(src, .proc/teleport, usr), 5) + return TRUE /obj/machinery/computer/prisoner/gulag_teleporter_computer/proc/scan_machinery() teleporter = findteleporter() @@ -129,12 +143,12 @@ say("[contained_id]'s ID card goal defaulting to [contained_id.goal] points.") log_game("[key_name(user)] teleported [key_name(prisoner)] to the Labor Camp [COORD(beacon)] for [id_goal_not_set ? "default goal of ":""][contained_id.goal] points.") teleporter.handle_prisoner(contained_id, temporary_record) - playsound(src, 'sound/weapons/emitter.ogg', 50, 1) + playsound(src, 'sound/weapons/emitter.ogg', 50, TRUE) prisoner.forceMove(get_turf(beacon)) prisoner.Stun(40) // small travel dizziness to_chat(prisoner, "The teleportation makes you a little dizzy.") new /obj/effect/particle_effect/sparks(get_turf(prisoner)) - playsound(src, "sparks", 50, 1) + playsound(src, "sparks", 50, TRUE) if(teleporter.locked) teleporter.locked = FALSE teleporter.toggle_open() diff --git a/code/game/machinery/computer/teleporter.dm b/code/game/machinery/computer/teleporter.dm index c52cf29660..518f38fc35 100644 --- a/code/game/machinery/computer/teleporter.dm +++ b/code/game/machinery/computer/teleporter.dm @@ -5,6 +5,8 @@ icon_keyboard = "teleport_key" light_color = LIGHT_COLOR_BLUE circuit = /obj/item/circuitboard/computer/teleporter + ui_x = 475 + ui_y = 130 var/regime_set = "Teleporter" var/id var/obj/machinery/teleport/station/power_station @@ -32,34 +34,30 @@ break return power_station -/obj/machinery/computer/teleporter/ui_interact(mob/user) - . = ..() - var/data = "

Teleporter Status

" - if(!power_station) - data += "
No power station linked.
" - else if(!power_station.teleporter_hub) - data += "
No hub linked.
" +obj/machinery/computer/teleporter/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ + datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "teleporter", name, ui_x, ui_y, master_ui, state) + ui.open() + +/obj/machinery/computer/teleporter/ui_data(mob/user) + var/list/data = list() + data["power_station"] = power_station ? TRUE : FALSE + data["teleporter_hub"] = power_station?.teleporter_hub ? TRUE : FALSE + data["regime_set"] = regime_set + data["target"] = !target ? "None" : "[get_area(target)] [(regime_set != "Gate") ? "" : "Teleporter"]" + data["calibrating"] = calibrating + + if(power_station?.teleporter_hub?.calibrated || power_station?.teleporter_hub?.accuracy >= 3) + data["calibrated"] = TRUE else - data += "
Current regime: [regime_set]
" - data += "Current target: [(!target) ? "None" : "[get_area(target)] [(regime_set != "Gate") ? "" : "Teleporter"]"]
" - if(calibrating) - data += "Calibration: In Progress" - else if(power_station.teleporter_hub.calibrated || power_station.efficiency >= 3) - data += "Calibration: Optimal" - else - data += "Calibration: Sub-Optimal" - data += "

" + data["calibrated"] = FALSE - data += "Change regime
" - data += "Set target
" + return data - data += "
Calibrate Hub" - var/datum/browser/popup = new(user, "teleporter", name, 400, 400) - popup.set_content(data) - popup.open() - -/obj/machinery/computer/teleporter/Topic(href, href_list) +/obj/machinery/computer/teleporter/ui_act(action, params) if(..()) return @@ -70,38 +68,39 @@ say("Error: Calibration in progress. Stand by.") return - if(href_list["regimeset"]) - power_station.engaged = 0 - power_station.teleporter_hub.update_icon() - power_station.teleporter_hub.calibrated = 0 - reset_regime() - if(href_list["settarget"]) - power_station.engaged = 0 - power_station.teleporter_hub.update_icon() - power_station.teleporter_hub.calibrated = 0 - set_target(usr) - if(href_list["calibrate"]) - if(!target) - say("Error: No target set to calibrate to.") - return - if(power_station.teleporter_hub.calibrated || power_station.efficiency >= 3) - say("Hub is already calibrated!") - return - say("Processing hub calibration to target...") + switch(action) + if("regimeset") + power_station.engaged = FALSE + power_station.teleporter_hub.update_icon() + power_station.teleporter_hub.calibrated = FALSE + reset_regime() + . = TRUE + if("settarget") + power_station.engaged = FALSE + power_station.teleporter_hub.update_icon() + power_station.teleporter_hub.calibrated = FALSE + set_target(usr) + . = TRUE + if("calibrate") + if(!target) + say("Error: No target set to calibrate to.") + return + if(power_station.teleporter_hub.calibrated || power_station.teleporter_hub.accuracy >= 3) + say("Hub is already calibrated!") + return - calibrating = 1 - power_station.update_icon() - spawn(50 * (3 - power_station.efficiency)) //Better parts mean faster calibration - calibrating = 0 - if(check_hub_connection()) - power_station.teleporter_hub.calibrated = 1 - say("Calibration complete.") - else - say("Error: Unable to detect hub.") + say("Processing hub calibration to target...") + calibrating = TRUE power_station.update_icon() - updateDialog() - - updateDialog() + spawn(50 * (3 - power_station.teleporter_hub.accuracy)) //Better parts mean faster calibration + calibrating = FALSE + if(check_hub_connection()) + power_station.teleporter_hub.calibrated = TRUE + say("Calibration complete.") + else + say("Error: Unable to detect hub.") + power_station.update_icon() + . = TRUE /obj/machinery/computer/teleporter/proc/check_hub_connection() if(!power_station) diff --git a/code/game/machinery/launch_pad.dm b/code/game/machinery/launch_pad.dm index b4a9584368..0f5e51215d 100644 --- a/code/game/machinery/launch_pad.dm +++ b/code/game/machinery/launch_pad.dm @@ -16,6 +16,7 @@ var/power_efficiency = 1 var/x_offset = 0 var/y_offset = 0 + var/indicator_icon = "launchpad_target" /obj/machinery/launchpad/RefreshParts() var/E = 0 @@ -34,17 +35,28 @@ return if(panel_open) - if(istype(I, /obj/item/multitool)) + if(I.tool_behaviour == TOOL_MULTITOOL) + if(!multitool_check_buffer(user, I)) + return var/obj/item/multitool/M = I M.buffer = src to_chat(user, "You save the data in the [I.name]'s buffer.") - return 1 + return TRUE if(default_deconstruction_crowbar(I)) return return ..() +/obj/machinery/launchpad/attack_ghost(mob/dead/observer/ghost) + . = ..() + if(.) + return + var/target_x = x + x_offset + var/target_y = y + y_offset + var/turf/target = locate(target_x, target_y, z) + ghost.forceMove(target) + /obj/machinery/launchpad/proc/isAvailable() if(stat & NOPOWER) return FALSE @@ -52,6 +64,14 @@ return FALSE return TRUE +/obj/machinery/launchpad/proc/set_offset(x, y) + if(teleporting) + return + if(!isnull(x)) + x_offset = CLAMP(x, -range, range) + if(!isnull(y)) + y_offset = CLAMP(y, -range, range) + /obj/machinery/launchpad/proc/doteleport(mob/user, sending) if(teleporting) to_chat(user, "ERROR: Launchpad busy.") @@ -69,12 +89,22 @@ var/area/A = get_area(target) flick(icon_teleport, src) - playsound(get_turf(src), 'sound/weapons/flash.ogg', 25, 1) + + //Change the indicator's icon to show that we're teleporting + if(sending) + indicator_icon = "launchpad_launch" + else + indicator_icon = "launchpad_pull" + + playsound(get_turf(src), 'sound/weapons/flash.ogg', 25, TRUE) teleporting = TRUE sleep(teleport_speed) + //Set the indicator icon back to normal + indicator_icon = "launchpad_target" + if(QDELETED(src) || !isAvailable()) return @@ -91,25 +121,25 @@ source = dest dest = target - playsound(get_turf(src), 'sound/weapons/emitter2.ogg', 25, 1) + playsound(get_turf(src), 'sound/weapons/emitter2.ogg', 25, TRUE) var/first = TRUE for(var/atom/movable/ROI in source) if(ROI == src) continue - // if it's anchored, don't teleport + if(!istype(ROI) || isdead(ROI) || iscameramob(ROI) || istype(ROI, /obj/effect/dummy/phased_mob)) + continue//don't teleport these var/on_chair = "" - if(ROI.anchored) + if(ROI.anchored)// if it's anchored, don't teleport if(isliving(ROI)) var/mob/living/L = ROI if(L.buckled) // TP people on office chairs if(L.buckled.anchored) continue - on_chair = " (on a chair)" else continue - else if(!isobserver(ROI)) + else continue if(!first) log_msg += ", " @@ -158,11 +188,11 @@ var/obj/item/storage/briefcase/launchpad/briefcase /obj/machinery/launchpad/briefcase/Initialize(mapload, briefcase) - . = ..() - if(!briefcase) - log_game("[src] has been spawned without a briefcase.") - return INITIALIZE_HINT_QDEL - src.briefcase = briefcase + . = ..() + if(!briefcase) + log_game("[src] has been spawned without a briefcase.") + return INITIALIZE_HINT_QDEL + src.briefcase = briefcase /obj/machinery/launchpad/briefcase/Destroy() QDEL_NULL(briefcase) @@ -255,7 +285,7 @@ /obj/item/launchpad_remote/ui_interact(mob/user, ui_key = "launchpad_remote", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "launchpad_remote", "Briefcase Launchpad Remote", 550, 400, master_ui, state) //width, height + ui = new(user, src, ui_key, "launchpad_remote", "Briefcase Launchpad Remote", 300, 240, master_ui, state) //width, height ui.set_style("syndicate") ui.open() @@ -270,10 +300,9 @@ return data data["pad_name"] = pad.display_name - data["abs_x"] = abs(pad.x_offset) - data["abs_y"] = abs(pad.y_offset) - data["north_south"] = pad.y_offset > 0 ? "N":"S" - data["east_west"] = pad.x_offset > 0 ? "E":"W" + data["range"] = pad.range + data["x"] = pad.x_offset + data["y"] = pad.y_offset return data /obj/item/launchpad_remote/proc/teleport(mob/user, obj/machinery/launchpad/pad) @@ -289,76 +318,33 @@ if(..()) return switch(action) - if("right") - if(pad.x_offset < pad.range) - pad.x_offset++ + if("set_pos") + var/new_x = text2num(params["x"]) + var/new_y = text2num(params["y"]) + pad.set_offset(new_x, new_y) . = TRUE - - if("left") - if(pad.x_offset > (pad.range * -1)) - pad.x_offset-- + if("move_pos") + var/plus_x = text2num(params["x"]) + var/plus_y = text2num(params["y"]) + pad.set_offset( + x = pad.x_offset + plus_x, + y = pad.y_offset + plus_y + ) . = TRUE - - if("up") - if(pad.y_offset < pad.range) - pad.y_offset++ - . = TRUE - - if("down") - if(pad.y_offset > (pad.range * -1)) - pad.y_offset-- - . = TRUE - - if("up-right") - if(pad.y_offset < pad.range) - pad.y_offset++ - if(pad.x_offset < pad.range) - pad.x_offset++ - . = TRUE - - if("up-left") - if(pad.y_offset < pad.range) - pad.y_offset++ - if(pad.x_offset > (pad.range * -1)) - pad.x_offset-- - . = TRUE - - if("down-right") - if(pad.y_offset > (pad.range * -1)) - pad.y_offset-- - if(pad.x_offset < pad.range) - pad.x_offset++ - . = TRUE - - if("down-left") - if(pad.y_offset > (pad.range * -1)) - pad.y_offset-- - if(pad.x_offset > (pad.range * -1)) - pad.x_offset-- - . = TRUE - - if("reset") - pad.y_offset = 0 - pad.x_offset = 0 - . = TRUE - if("rename") . = TRUE - var/new_name = stripped_input(usr, "How do you want to rename the launchpad?", "Launchpad", pad.display_name, 15) + var/new_name = params["name"] if(!new_name) return pad.display_name = new_name - if("remove") . = TRUE if(usr && alert(usr, "Are you sure?", "Unlink Launchpad", "I'm Sure", "Abort") != "Abort") pad = null - if("launch") sending = TRUE teleport(usr, pad) . = TRUE - if("pull") sending = FALSE teleport(usr, pad) diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index ac25b5081e..89f70efc38 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -1,453 +1,453 @@ -// SUIT STORAGE UNIT ///////////////// -/obj/machinery/suit_storage_unit - name = "suit storage unit" - desc = "An industrial unit made to hold and decontaminate irradiated equipment. It comes with a built-in UV cauterization mechanism. A small warning label advises that organic matter should not be placed into the unit." - icon = 'icons/obj/machines/suit_storage.dmi' - icon_state = "close" - density = TRUE - max_integrity = 250 - - var/obj/item/clothing/suit/space/suit = null - var/obj/item/clothing/head/helmet/space/helmet = null - var/obj/item/clothing/mask/mask = null - var/obj/item/storage = null - - var/suit_type = null - var/helmet_type = null - var/mask_type = null - var/storage_type = null - - state_open = FALSE - var/locked = FALSE - panel_open = FALSE - var/safeties = TRUE - - var/uv = FALSE - var/uv_super = FALSE - var/uv_cycles = 6 - var/message_cooldown - var/breakout_time = 300 - -/obj/machinery/suit_storage_unit/standard_unit - suit_type = /obj/item/clothing/suit/space/eva - helmet_type = /obj/item/clothing/head/helmet/space/eva - mask_type = /obj/item/clothing/mask/breath - -/obj/machinery/suit_storage_unit/captain - suit_type = /obj/item/clothing/suit/space/hardsuit/captain - mask_type = /obj/item/clothing/mask/gas/sechailer - storage_type = /obj/item/tank/jetpack/oxygen/captain - -/obj/machinery/suit_storage_unit/engine - suit_type = /obj/item/clothing/suit/space/hardsuit/engine - mask_type = /obj/item/clothing/mask/breath - storage_type= /obj/item/clothing/shoes/magboots - -/obj/machinery/suit_storage_unit/ce - suit_type = /obj/item/clothing/suit/space/hardsuit/engine/elite - mask_type = /obj/item/clothing/mask/breath - storage_type= /obj/item/clothing/shoes/magboots/advance - -/obj/machinery/suit_storage_unit/security - suit_type = /obj/item/clothing/suit/space/hardsuit/security - mask_type = /obj/item/clothing/mask/gas/sechailer - storage_type = /obj/item/tank/jetpack/oxygen/security - -/obj/machinery/suit_storage_unit/hos - suit_type = /obj/item/clothing/suit/space/hardsuit/security/hos - mask_type = /obj/item/clothing/mask/gas/sechailer - storage_type = /obj/item/tank/internals/oxygen - -/obj/machinery/suit_storage_unit/atmos - suit_type = /obj/item/clothing/suit/space/hardsuit/engine/atmos - mask_type = /obj/item/clothing/mask/gas - storage_type = /obj/item/watertank/atmos - -/obj/machinery/suit_storage_unit/mining - suit_type = /obj/item/clothing/suit/hooded/explorer/standard - mask_type = /obj/item/clothing/mask/gas/explorer - -/obj/machinery/suit_storage_unit/mining/eva - suit_type = /obj/item/clothing/suit/space/hardsuit/mining - mask_type = /obj/item/clothing/mask/breath - -/obj/machinery/suit_storage_unit/cmo - suit_type = /obj/item/clothing/suit/space/hardsuit/medical - mask_type = /obj/item/clothing/mask/breath - -/obj/machinery/suit_storage_unit/rd - suit_type = /obj/item/clothing/suit/space/hardsuit/rd - mask_type = /obj/item/clothing/mask/breath - -/obj/machinery/suit_storage_unit/syndicate - suit_type = /obj/item/clothing/suit/space/hardsuit/syndi - mask_type = /obj/item/clothing/mask/gas/syndicate - storage_type = /obj/item/tank/jetpack/oxygen/harness - -/obj/machinery/suit_storage_unit/ert/command - suit_type = /obj/item/clothing/suit/space/hardsuit/ert - mask_type = /obj/item/clothing/mask/breath - storage_type = /obj/item/tank/internals/emergency_oxygen/double - -/obj/machinery/suit_storage_unit/ert/security - suit_type = /obj/item/clothing/suit/space/hardsuit/ert/sec - mask_type = /obj/item/clothing/mask/breath - storage_type = /obj/item/tank/internals/emergency_oxygen/double - -/obj/machinery/suit_storage_unit/ert/engineer - suit_type = /obj/item/clothing/suit/space/hardsuit/ert/engi - mask_type = /obj/item/clothing/mask/breath - storage_type = /obj/item/tank/internals/emergency_oxygen/double - -/obj/machinery/suit_storage_unit/ert/medical - suit_type = /obj/item/clothing/suit/space/hardsuit/ert/med - mask_type = /obj/item/clothing/mask/breath - storage_type = /obj/item/tank/internals/emergency_oxygen/double - -/obj/machinery/suit_storage_unit/radsuit - name = "radiation suit storage unit" - suit_type = /obj/item/clothing/suit/radiation - helmet_type = /obj/item/clothing/head/radiation - storage_type = /obj/item/geiger_counter - -/obj/machinery/suit_storage_unit/open - state_open = TRUE - density = FALSE - -/obj/machinery/suit_storage_unit/Initialize() - . = ..() - wires = new /datum/wires/suit_storage_unit(src) - if(suit_type) - suit = new suit_type(src) - if(helmet_type) - helmet = new helmet_type(src) - if(mask_type) - mask = new mask_type(src) - if(storage_type) - storage = new storage_type(src) - update_icon() - -/obj/machinery/suit_storage_unit/Destroy() - QDEL_NULL(suit) - QDEL_NULL(helmet) - QDEL_NULL(mask) - QDEL_NULL(storage) - return ..() - -/obj/machinery/suit_storage_unit/update_icon() - cut_overlays() - - if(uv) - if(uv_super) - add_overlay("super") - else if(occupant) - add_overlay("uvhuman") - else - add_overlay("uv") - else if(state_open) - if(stat & BROKEN) - add_overlay("broken") - else - add_overlay("open") - if(suit) - add_overlay("suit") - if(helmet) - add_overlay("helm") - if(storage) - add_overlay("storage") - else if(occupant) - add_overlay("human") - -/obj/machinery/suit_storage_unit/power_change() - ..() - if(!is_operational() && state_open) - open_machine() - dump_contents() - update_icon() - -/obj/machinery/suit_storage_unit/proc/dump_contents() - dropContents() - helmet = null - suit = null - mask = null - storage = null - occupant = null - -/obj/machinery/suit_storage_unit/deconstruct(disassembled = TRUE) - if(!(flags_1 & NODECONSTRUCT_1)) - open_machine() - dump_contents() - new /obj/item/stack/sheet/metal (loc, 2) - qdel(src) - -/obj/machinery/suit_storage_unit/MouseDrop_T(atom/A, mob/user) - if(user.stat || user.lying || !Adjacent(user) || !Adjacent(A) || !isliving(A)) - return - var/mob/living/target = A - if(!state_open) - to_chat(user, "The unit's doors are shut!") - return - if(!is_operational()) - to_chat(user, "The unit is not operational!") - return - if(occupant || helmet || suit || storage) - to_chat(user, "It's too cluttered inside to fit in!") - return - - if(target == user) - user.visible_message("[user] starts squeezing into [src]!", "You start working your way into [src]...") - else - target.visible_message("[user] starts shoving [target] into [src]!", "[user] starts shoving you into [src]!") - - if(do_mob(user, target, 30)) - if(occupant || helmet || suit || storage) - return - if(target == user) - user.visible_message("[user] slips into [src] and closes the door behind [user.p_them()]!", "You slip into [src]'s cramped space and shut its door.") - else - target.visible_message("[user] pushes [target] into [src] and shuts its door!", "[user] shoves you into [src] and shuts the door!") - close_machine(target) - add_fingerprint(user) - -/obj/machinery/suit_storage_unit/proc/cook() - var/mob/living/mob_occupant = occupant - if(uv_cycles) - uv_cycles-- - uv = TRUE - locked = TRUE - update_icon() - if(occupant) - if(uv_super) - mob_occupant.adjustFireLoss(rand(20, 36)) - else - mob_occupant.adjustFireLoss(rand(10, 16)) - mob_occupant.emote("scream") - addtimer(CALLBACK(src, .proc/cook), 50) - else - uv_cycles = initial(uv_cycles) - uv = FALSE - locked = FALSE - if(uv_super) - visible_message("[src]'s door creaks open with a loud whining noise. A cloud of foul black smoke escapes from its chamber.") - playsound(src, 'sound/machines/airlock_alien_prying.ogg', 50, 1) - helmet = null - qdel(helmet) - suit = null - qdel(suit) // Delete everything but the occupant. - mask = null - qdel(mask) - storage = null - qdel(storage) - // The wires get damaged too. - wires.cut_all() - else - if(!occupant) - visible_message("[src]'s door slides open. The glowing yellow lights dim to a gentle green.") - else - visible_message("[src]'s door slides open, barraging you with the nauseating smell of charred flesh.") - mob_occupant.radiation = 0 - playsound(src, 'sound/machines/airlockclose.ogg', 25, 1) - var/list/things_to_clear = list() //Done this way since using GetAllContents on the SSU itself would include circuitry and such. - if(suit) - things_to_clear += suit - things_to_clear += suit.GetAllContents() - if(helmet) - things_to_clear += helmet - things_to_clear += helmet.GetAllContents() - if(mask) - things_to_clear += mask - things_to_clear += mask.GetAllContents() - if(storage) - things_to_clear += storage - things_to_clear += storage.GetAllContents() - if(occupant) - things_to_clear += occupant - things_to_clear += occupant.GetAllContents() - for(var/atom/movable/AM in things_to_clear) //Scorches away blood and forensic evidence, although the SSU itself is unaffected - SEND_SIGNAL(AM, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRONG) - AM.clean_blood() - AM.fingerprints = list() - var/datum/component/radioactive/contamination = AM.GetComponent(/datum/component/radioactive) - if(contamination) - qdel(contamination) - open_machine(FALSE) - if(occupant) - dump_contents() - -/obj/machinery/suit_storage_unit/proc/shock(mob/user, prb) - if(!prob(prb)) - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(5, 1, src) - s.start() - if(electrocute_mob(user, src, src, 1, TRUE)) - return 1 - -/obj/machinery/suit_storage_unit/relaymove(mob/user) - if(locked) - if(message_cooldown <= world.time) - message_cooldown = world.time + 50 - to_chat(user, "[src]'s door won't budge!") - return - open_machine() - dump_contents() - -/obj/machinery/suit_storage_unit/container_resist(mob/living/user) - if(!locked) - open_machine() - dump_contents() - return - user.changeNext_move(CLICK_CD_BREAKOUT) - user.last_special = world.time + CLICK_CD_BREAKOUT - user.visible_message("You see [user] kicking against the doors of [src]!", \ - "You start kicking against the doors... (this will take about [DisplayTimeText(breakout_time)].)", \ - "You hear a thump from [src].") - if(do_after(user,(breakout_time), target = src)) - if(!user || user.stat != CONSCIOUS || user.loc != src ) - return - user.visible_message("[user] successfully broke out of [src]!", \ - "You successfully break out of [src]!") - open_machine() - dump_contents() - - add_fingerprint(user) - if(locked) - visible_message("You see [user] kicking against the doors of [src]!", \ - "You start kicking against the doors...") - addtimer(CALLBACK(src, .proc/resist_open, user), 300) - else - open_machine() - dump_contents() - -/obj/machinery/suit_storage_unit/proc/resist_open(mob/user) - if(!state_open && occupant && (user in src) && user.stat == 0) // Check they're still here. - visible_message("You see [user] burst out of [src]!", \ - "You escape the cramped confines of [src]!") - open_machine() - -/obj/machinery/suit_storage_unit/attackby(obj/item/I, mob/user, params) - if(state_open && is_operational()) - if(istype(I, /obj/item/clothing/head/mob_holder)) - to_chat(user, "You can't quite fit that in while you hold it!") - return - if(istype(I, /obj/item/clothing/suit)) - if(suit) - to_chat(user, "The unit already contains a suit!.") - return - if(!user.transferItemToLoc(I, src)) - return - suit = I - else if(istype(I, /obj/item/clothing/head)) - if(helmet) - to_chat(user, "The unit already contains a helmet!") - return - if(!user.transferItemToLoc(I, src)) - return - helmet = I - else if(istype(I, /obj/item/clothing/mask)) - if(mask) - to_chat(user, "The unit already contains a mask!") - return - if(!user.transferItemToLoc(I, src)) - return - mask = I - else - if(storage) - to_chat(user, "The auxiliary storage compartment is full!") - return - if(!user.transferItemToLoc(I, src)) - return - storage = I - - visible_message("[user] inserts [I] into [src]", "You load [I] into [src].") - update_icon() - return - - if(panel_open && is_wire_tool(I)) - wires.interact(user) - return - if(!state_open) - if(default_deconstruction_screwdriver(user, "panel", "close", I)) - return - if(default_pry_open(I)) - dump_contents() - return - - return ..() - -/obj/machinery/suit_storage_unit/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "suit_storage_unit", name, 400, 305, master_ui, state) - ui.open() - -/obj/machinery/suit_storage_unit/ui_data() - var/list/data = list() - data["locked"] = locked - data["open"] = state_open - data["safeties"] = safeties - data["uv_active"] = uv - data["uv_super"] = uv_super - if(helmet) - data["helmet"] = helmet.name - else - data["helmet"] = null - if(suit) - data["suit"] = suit.name - else - data["suit"] = null - if(mask) - data["mask"] = mask.name - else - data["mask"] = null - if(storage) - data["storage"] = storage.name - else - data["storage"] = null - if(occupant) - data["occupied"] = TRUE - else - data["occupied"] = FALSE - return data - -/obj/machinery/suit_storage_unit/ui_act(action, params) - if(..() || uv) - return - switch(action) - if("door") - if(state_open) - close_machine() - else - open_machine(0) - if(occupant) - dump_contents() // Dump out contents if someone is in there. - . = TRUE - if("lock") - if(state_open) - return - locked = !locked - . = TRUE - if("uv") - if(occupant && safeties) - return - else if(!helmet && !mask && !suit && !storage && !occupant) - return - else - if(occupant) - var/mob/living/mob_occupant = occupant - to_chat(mob_occupant, "[src]'s confines grow warm, then hot, then scorching. You're being burned [!mob_occupant.stat ? "alive" : "away"]!") - cook() - . = TRUE - if("dispense") - if(!state_open) - return - - var/static/list/valid_items = list("helmet", "suit", "mask", "storage") - var/item_name = params["item"] - if(item_name in valid_items) - var/obj/item/I = vars[item_name] - vars[item_name] = null - if(I) - I.forceMove(loc) - . = TRUE - update_icon() +// SUIT STORAGE UNIT ///////////////// +/obj/machinery/suit_storage_unit + name = "suit storage unit" + desc = "An industrial unit made to hold and decontaminate irradiated equipment. It comes with a built-in UV cauterization mechanism. A small warning label advises that organic matter should not be placed into the unit." + icon = 'icons/obj/machines/suit_storage.dmi' + icon_state = "close" + density = TRUE + max_integrity = 250 + + var/obj/item/clothing/suit/space/suit = null + var/obj/item/clothing/head/helmet/space/helmet = null + var/obj/item/clothing/mask/mask = null + var/obj/item/storage = null + + var/suit_type = null + var/helmet_type = null + var/mask_type = null + var/storage_type = null + + state_open = FALSE + var/locked = FALSE + panel_open = FALSE + var/safeties = TRUE + + var/uv = FALSE + var/uv_super = FALSE + var/uv_cycles = 6 + var/message_cooldown + var/breakout_time = 300 + +/obj/machinery/suit_storage_unit/standard_unit + suit_type = /obj/item/clothing/suit/space/eva + helmet_type = /obj/item/clothing/head/helmet/space/eva + mask_type = /obj/item/clothing/mask/breath + +/obj/machinery/suit_storage_unit/captain + suit_type = /obj/item/clothing/suit/space/hardsuit/captain + mask_type = /obj/item/clothing/mask/gas/sechailer + storage_type = /obj/item/tank/jetpack/oxygen/captain + +/obj/machinery/suit_storage_unit/engine + suit_type = /obj/item/clothing/suit/space/hardsuit/engine + mask_type = /obj/item/clothing/mask/breath + storage_type= /obj/item/clothing/shoes/magboots + +/obj/machinery/suit_storage_unit/ce + suit_type = /obj/item/clothing/suit/space/hardsuit/engine/elite + mask_type = /obj/item/clothing/mask/breath + storage_type= /obj/item/clothing/shoes/magboots/advance + +/obj/machinery/suit_storage_unit/security + suit_type = /obj/item/clothing/suit/space/hardsuit/security + mask_type = /obj/item/clothing/mask/gas/sechailer + storage_type = /obj/item/tank/jetpack/oxygen/security + +/obj/machinery/suit_storage_unit/hos + suit_type = /obj/item/clothing/suit/space/hardsuit/security/hos + mask_type = /obj/item/clothing/mask/gas/sechailer + storage_type = /obj/item/tank/internals/oxygen + +/obj/machinery/suit_storage_unit/atmos + suit_type = /obj/item/clothing/suit/space/hardsuit/engine/atmos + mask_type = /obj/item/clothing/mask/gas + storage_type = /obj/item/watertank/atmos + +/obj/machinery/suit_storage_unit/mining + suit_type = /obj/item/clothing/suit/hooded/explorer/standard + mask_type = /obj/item/clothing/mask/gas/explorer + +/obj/machinery/suit_storage_unit/mining/eva + suit_type = /obj/item/clothing/suit/space/hardsuit/mining + mask_type = /obj/item/clothing/mask/breath + +/obj/machinery/suit_storage_unit/cmo + suit_type = /obj/item/clothing/suit/space/hardsuit/medical + mask_type = /obj/item/clothing/mask/breath + +/obj/machinery/suit_storage_unit/rd + suit_type = /obj/item/clothing/suit/space/hardsuit/rd + mask_type = /obj/item/clothing/mask/breath + +/obj/machinery/suit_storage_unit/syndicate + suit_type = /obj/item/clothing/suit/space/hardsuit/syndi + mask_type = /obj/item/clothing/mask/gas/syndicate + storage_type = /obj/item/tank/jetpack/oxygen/harness + +/obj/machinery/suit_storage_unit/ert/command + suit_type = /obj/item/clothing/suit/space/hardsuit/ert + mask_type = /obj/item/clothing/mask/breath + storage_type = /obj/item/tank/internals/emergency_oxygen/double + +/obj/machinery/suit_storage_unit/ert/security + suit_type = /obj/item/clothing/suit/space/hardsuit/ert/sec + mask_type = /obj/item/clothing/mask/breath + storage_type = /obj/item/tank/internals/emergency_oxygen/double + +/obj/machinery/suit_storage_unit/ert/engineer + suit_type = /obj/item/clothing/suit/space/hardsuit/ert/engi + mask_type = /obj/item/clothing/mask/breath + storage_type = /obj/item/tank/internals/emergency_oxygen/double + +/obj/machinery/suit_storage_unit/ert/medical + suit_type = /obj/item/clothing/suit/space/hardsuit/ert/med + mask_type = /obj/item/clothing/mask/breath + storage_type = /obj/item/tank/internals/emergency_oxygen/double + +/obj/machinery/suit_storage_unit/radsuit + name = "radiation suit storage unit" + suit_type = /obj/item/clothing/suit/radiation + helmet_type = /obj/item/clothing/head/radiation + storage_type = /obj/item/geiger_counter + +/obj/machinery/suit_storage_unit/open + state_open = TRUE + density = FALSE + +/obj/machinery/suit_storage_unit/Initialize() + . = ..() + wires = new /datum/wires/suit_storage_unit(src) + if(suit_type) + suit = new suit_type(src) + if(helmet_type) + helmet = new helmet_type(src) + if(mask_type) + mask = new mask_type(src) + if(storage_type) + storage = new storage_type(src) + update_icon() + +/obj/machinery/suit_storage_unit/Destroy() + QDEL_NULL(suit) + QDEL_NULL(helmet) + QDEL_NULL(mask) + QDEL_NULL(storage) + return ..() + +/obj/machinery/suit_storage_unit/update_icon() + cut_overlays() + + if(uv) + if(uv_super) + add_overlay("super") + else if(occupant) + add_overlay("uvhuman") + else + add_overlay("uv") + else if(state_open) + if(stat & BROKEN) + add_overlay("broken") + else + add_overlay("open") + if(suit) + add_overlay("suit") + if(helmet) + add_overlay("helm") + if(storage) + add_overlay("storage") + else if(occupant) + add_overlay("human") + +/obj/machinery/suit_storage_unit/power_change() + ..() + if(!is_operational() && state_open) + open_machine() + dump_contents() + update_icon() + +/obj/machinery/suit_storage_unit/proc/dump_contents() + dropContents() + helmet = null + suit = null + mask = null + storage = null + occupant = null + +/obj/machinery/suit_storage_unit/deconstruct(disassembled = TRUE) + if(!(flags_1 & NODECONSTRUCT_1)) + open_machine() + dump_contents() + new /obj/item/stack/sheet/metal (loc, 2) + qdel(src) + +/obj/machinery/suit_storage_unit/MouseDrop_T(atom/A, mob/user) + if(user.stat || user.lying || !Adjacent(user) || !Adjacent(A) || !isliving(A)) + return + var/mob/living/target = A + if(!state_open) + to_chat(user, "The unit's doors are shut!") + return + if(!is_operational()) + to_chat(user, "The unit is not operational!") + return + if(occupant || helmet || suit || storage) + to_chat(user, "It's too cluttered inside to fit in!") + return + + if(target == user) + user.visible_message("[user] starts squeezing into [src]!", "You start working your way into [src]...") + else + target.visible_message("[user] starts shoving [target] into [src]!", "[user] starts shoving you into [src]!") + + if(do_mob(user, target, 30)) + if(occupant || helmet || suit || storage) + return + if(target == user) + user.visible_message("[user] slips into [src] and closes the door behind [user.p_them()]!", "You slip into [src]'s cramped space and shut its door.") + else + target.visible_message("[user] pushes [target] into [src] and shuts its door!", "[user] shoves you into [src] and shuts the door!") + close_machine(target) + add_fingerprint(user) + +/obj/machinery/suit_storage_unit/proc/cook() + var/mob/living/mob_occupant = occupant + if(uv_cycles) + uv_cycles-- + uv = TRUE + locked = TRUE + update_icon() + if(occupant) + if(uv_super) + mob_occupant.adjustFireLoss(rand(20, 36)) + else + mob_occupant.adjustFireLoss(rand(10, 16)) + mob_occupant.emote("scream") + addtimer(CALLBACK(src, .proc/cook), 50) + else + uv_cycles = initial(uv_cycles) + uv = FALSE + locked = FALSE + if(uv_super) + visible_message("[src]'s door creaks open with a loud whining noise. A cloud of foul black smoke escapes from its chamber.") + playsound(src, 'sound/machines/airlock_alien_prying.ogg', 50, 1) + helmet = null + qdel(helmet) + suit = null + qdel(suit) // Delete everything but the occupant. + mask = null + qdel(mask) + storage = null + qdel(storage) + // The wires get damaged too. + wires.cut_all() + else + if(!occupant) + visible_message("[src]'s door slides open. The glowing yellow lights dim to a gentle green.") + else + visible_message("[src]'s door slides open, barraging you with the nauseating smell of charred flesh.") + mob_occupant.radiation = 0 + playsound(src, 'sound/machines/airlockclose.ogg', 25, 1) + var/list/things_to_clear = list() //Done this way since using GetAllContents on the SSU itself would include circuitry and such. + if(suit) + things_to_clear += suit + things_to_clear += suit.GetAllContents() + if(helmet) + things_to_clear += helmet + things_to_clear += helmet.GetAllContents() + if(mask) + things_to_clear += mask + things_to_clear += mask.GetAllContents() + if(storage) + things_to_clear += storage + things_to_clear += storage.GetAllContents() + if(occupant) + things_to_clear += occupant + things_to_clear += occupant.GetAllContents() + for(var/atom/movable/AM in things_to_clear) //Scorches away blood and forensic evidence, although the SSU itself is unaffected + SEND_SIGNAL(AM, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRONG) + AM.clean_blood() + AM.fingerprints = list() + var/datum/component/radioactive/contamination = AM.GetComponent(/datum/component/radioactive) + if(contamination) + qdel(contamination) + open_machine(FALSE) + if(occupant) + dump_contents() + +/obj/machinery/suit_storage_unit/proc/shock(mob/user, prb) + if(!prob(prb)) + var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(5, 1, src) + s.start() + if(electrocute_mob(user, src, src, 1, TRUE)) + return 1 + +/obj/machinery/suit_storage_unit/relaymove(mob/user) + if(locked) + if(message_cooldown <= world.time) + message_cooldown = world.time + 50 + to_chat(user, "[src]'s door won't budge!") + return + open_machine() + dump_contents() + +/obj/machinery/suit_storage_unit/container_resist(mob/living/user) + if(!locked) + open_machine() + dump_contents() + return + user.changeNext_move(CLICK_CD_BREAKOUT) + user.last_special = world.time + CLICK_CD_BREAKOUT + user.visible_message("You see [user] kicking against the doors of [src]!", \ + "You start kicking against the doors... (this will take about [DisplayTimeText(breakout_time)].)", \ + "You hear a thump from [src].") + if(do_after(user,(breakout_time), target = src)) + if(!user || user.stat != CONSCIOUS || user.loc != src ) + return + user.visible_message("[user] successfully broke out of [src]!", \ + "You successfully break out of [src]!") + open_machine() + dump_contents() + + add_fingerprint(user) + if(locked) + visible_message("You see [user] kicking against the doors of [src]!", \ + "You start kicking against the doors...") + addtimer(CALLBACK(src, .proc/resist_open, user), 300) + else + open_machine() + dump_contents() + +/obj/machinery/suit_storage_unit/proc/resist_open(mob/user) + if(!state_open && occupant && (user in src) && user.stat == 0) // Check they're still here. + visible_message("You see [user] burst out of [src]!", \ + "You escape the cramped confines of [src]!") + open_machine() + +/obj/machinery/suit_storage_unit/attackby(obj/item/I, mob/user, params) + if(state_open && is_operational()) + if(istype(I, /obj/item/clothing/head/mob_holder)) + to_chat(user, "You can't quite fit that in while you hold it!") + return + if(istype(I, /obj/item/clothing/suit)) + if(suit) + to_chat(user, "The unit already contains a suit!.") + return + if(!user.transferItemToLoc(I, src)) + return + suit = I + else if(istype(I, /obj/item/clothing/head)) + if(helmet) + to_chat(user, "The unit already contains a helmet!") + return + if(!user.transferItemToLoc(I, src)) + return + helmet = I + else if(istype(I, /obj/item/clothing/mask)) + if(mask) + to_chat(user, "The unit already contains a mask!") + return + if(!user.transferItemToLoc(I, src)) + return + mask = I + else + if(storage) + to_chat(user, "The auxiliary storage compartment is full!") + return + if(!user.transferItemToLoc(I, src)) + return + storage = I + + visible_message("[user] inserts [I] into [src]", "You load [I] into [src].") + update_icon() + return + + if(panel_open && is_wire_tool(I)) + wires.interact(user) + return + if(!state_open) + if(default_deconstruction_screwdriver(user, "panel", "close", I)) + return + if(default_pry_open(I)) + dump_contents() + return + + return ..() + +/obj/machinery/suit_storage_unit/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ + datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "suit_storage_unit", name, 400, 305, master_ui, state) + ui.open() + +/obj/machinery/suit_storage_unit/ui_data() + var/list/data = list() + data["locked"] = locked + data["open"] = state_open + data["safeties"] = safeties + data["uv_active"] = uv + data["uv_super"] = uv_super + if(helmet) + data["helmet"] = helmet.name + else + data["helmet"] = null + if(suit) + data["suit"] = suit.name + else + data["suit"] = null + if(mask) + data["mask"] = mask.name + else + data["mask"] = null + if(storage) + data["storage"] = storage.name + else + data["storage"] = null + if(occupant) + data["occupied"] = TRUE + else + data["occupied"] = FALSE + return data + +/obj/machinery/suit_storage_unit/ui_act(action, params) + if(..() || uv) + return + switch(action) + if("door") + if(state_open) + close_machine() + else + open_machine(0) + if(occupant) + dump_contents() // Dump out contents if someone is in there. + . = TRUE + if("lock") + if(state_open) + return + locked = !locked + . = TRUE + if("uv") + if(occupant && safeties) + return + else if(!helmet && !mask && !suit && !storage && !occupant) + return + else + if(occupant) + var/mob/living/mob_occupant = occupant + to_chat(mob_occupant, "[src]'s confines grow warm, then hot, then scorching. You're being burned [!mob_occupant.stat ? "alive" : "away"]!") + cook() + . = TRUE + if("dispense") + if(!state_open) + return + + var/static/list/valid_items = list("helmet", "suit", "mask", "storage") + var/item_name = params["item"] + if(item_name in valid_items) + var/obj/item/I = vars[item_name] + vars[item_name] = null + if(I) + I.forceMove(loc) + . = TRUE + update_icon() diff --git a/code/game/objects/items/eightball.dm b/code/game/objects/items/eightball.dm index b37c147357..c4a15a1871 100644 --- a/code/game/objects/items/eightball.dm +++ b/code/game/objects/items/eightball.dm @@ -54,7 +54,7 @@ to_chat(user, "[src] was shaken recently, it needs time to settle.") return - user.visible_message("[user] starts shaking [src].", "You start shaking [src].", "You hear shaking and sloshing.") + user.visible_message("[user] starts shaking [src].", "You start shaking [src].", "You hear shaking and sloshing.") shaking = TRUE @@ -95,16 +95,47 @@ // except it actually ASKS THE DEAD (wooooo) /obj/item/toy/eightball/haunted - shake_time = 150 - cooldown_time = 1800 + shake_time = 30 SECONDS + cooldown_time = 3 MINUTES flags_1 = HEAR_1 var/last_message var/selected_message - var/list/votes + //these kind of store the same thing but one is easier to work with. + var/list/votes = list() + var/list/voted = list() + var/static/list/haunted_answers = list( + "yes" = list( + "It is certain", + "It is decidedly so", + "Without a doubt", + "Yes definitely", + "You may rely on it", + "As I see it, yes", + "Most likely", + "Outlook good", + "Yes", + "Signs point to yes" + ), + "maybe" = list( + "Reply hazy try again", + "Ask again later", + "Better not tell you now", + "Cannot predict now", + "Concentrate and ask again" + ), + "no" = list( + "Don't count on it", + "My reply is no", + "My sources say no", + "Outlook not so good", + "Very doubtful" + ) + ) /obj/item/toy/eightball/haunted/Initialize(mapload) . = ..() - votes = list() + for (var/answer in haunted_answers) + votes[answer] = 0 GLOB.poi_list |= src /obj/item/toy/eightball/haunted/Destroy() @@ -122,7 +153,7 @@ interact(user) return ..() -/obj/item/toy/eightball/haunted/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode, atom/movable/source) +/obj/item/toy/eightball/haunted/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode) . = ..() last_message = raw_message @@ -137,38 +168,31 @@ if(isobserver(usr)) interact(usr) -/obj/item/toy/eightball/haunted/proc/get_vote_tallies() - var/list/answers = list() - for(var/ckey in votes) - var/selected = votes[ckey] - if(selected in answers) - answers[selected]++ - else - answers[selected] = 1 - - return answers - - /obj/item/toy/eightball/haunted/get_answer() - if(!votes.len) - return pick(possible_answers) + var/top_amount = 0 + var/top_vote - var/list/tallied_votes = get_vote_tallies() + for(var/vote in votes) + var/amount_of_votes = length(votes[vote]) + if(amount_of_votes > top_amount) + top_vote = vote + top_amount = amount_of_votes + //If one option actually has votes and there's a tie, pick between them 50/50 + else if(top_amount && amount_of_votes == top_amount && prob(50)) + top_vote = vote + top_amount = amount_of_votes - // I miss python sorting, then I wouldn't have to muck about with - // all this - var/most_popular_answer - var/most_amount = 0 - // yes, if there is a tie, there is an arbitary decision - // but we never said the spirit world was fair - for(var/A in tallied_votes) - var/amount = tallied_votes[A] - if(amount > most_amount) - most_popular_answer = A + if(isnull(top_vote)) + top_vote = pick(votes) - return most_popular_answer + for(var/vote in votes) + votes[vote] = 0 -/obj/item/toy/eightball/haunted/ui_interact(mob/user, ui_key="main", datum/tgui/ui=null, force_open=0, datum/tgui/master_ui=null, datum/ui_state/state = GLOB.observer_state) + voted.Cut() + + return top_vote + +/obj/item/toy/eightball/haunted/ui_interact(mob/user, ui_key="main", datum/tgui/ui=null, force_open=0, datum/tgui/master_ui=null, datum/ui_state/state = GLOB.always_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) @@ -179,21 +203,13 @@ var/list/data = list() data["shaking"] = shaking data["question"] = selected_message - var/list/tallied_votes = get_vote_tallies() data["answers"] = list() - - for(var/pa in possible_answers) + for(var/pa in haunted_answers) var/list/L = list() L["answer"] = pa - var/amount = 0 - if(pa in tallied_votes) - amount = tallied_votes[pa] - L["amount"] = amount - var/selected = FALSE - if(votes[user.ckey] == pa) - selected = TRUE - L["selected"] = selected + L["amount"] = votes[pa] + L["selected"] = voted[user.ckey] data["answers"] += list(L) return data @@ -206,8 +222,11 @@ switch(action) if("vote") var/selected_answer = params["answer"] - if(!(selected_answer in possible_answers)) + if(!(selected_answer in haunted_answers)) + return + if(user.ckey in voted) return else - votes[user.ckey] = selected_answer - . = TRUE + votes[selected_answer] += 1 + voted[user.ckey] = selected_answer + . = TRUE \ No newline at end of file diff --git a/code/modules/NTNet/network.dm b/code/modules/NTNet/network.dm index 645f05ac90..a7607fb746 100644 --- a/code/modules/NTNet/network.dm +++ b/code/modules/NTNet/network.dm @@ -202,6 +202,11 @@ if(filename == P.filename) return P +/datum/ntnet/proc/get_chat_channel_by_id(id) + for(var/datum/ntnet_conversation/chan in chat_channels) + if(chan.id == id) + return chan + // Resets the IDS alarm /datum/ntnet/proc/resetIDS() intrusion_detection_alarm = FALSE diff --git a/code/modules/NTNet/relays.dm b/code/modules/NTNet/relays.dm index eaf2aa466b..373f6451b9 100644 --- a/code/modules/NTNet/relays.dm +++ b/code/modules/NTNet/relays.dm @@ -9,6 +9,9 @@ icon_state = "bus" density = TRUE circuit = /obj/item/circuitboard/machine/ntnet_relay + ui_x = 400 + ui_y = 300 + var/datum/ntnet/NTNet = null // This is mostly for backwards reference and to allow varedit modifications from ingame. var/enabled = 1 // Set to 0 if the relay was turned off var/dos_failure = 0 // Set to 1 if the relay failed due to (D)DoS attack @@ -66,7 +69,7 @@ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "ntnet_relay", "NTNet Quantum Relay", 500, 300, master_ui, state) + ui = new(user, src, ui_key, "ntnet_relay", "NTNet Quantum Relay", ui_x, ui_y, master_ui, state) ui.open() @@ -88,10 +91,12 @@ dos_failure = 0 update_icon() SSnetworks.station_network.add_log("Quantum relay manually restarted from overload recovery mode to normal operation mode.") + return TRUE if("toggle") enabled = !enabled SSnetworks.station_network.add_log("Quantum relay manually [enabled ? "enabled" : "disabled"].") update_icon() + return TRUE /obj/machinery/ntnet_relay/Initialize() uid = gl_uid++ @@ -113,4 +118,4 @@ D.target = null D.error = "Connection to quantum relay severed" - return ..() + return ..() \ No newline at end of file diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm index 4fba44a3f2..53cbb8ea2a 100644 --- a/code/modules/assembly/signaler.dm +++ b/code/modules/assembly/signaler.dm @@ -11,7 +11,6 @@ var/code = DEFAULT_SIGNALER_CODE var/frequency = FREQ_SIGNALER - var/delay = 0 var/datum/radio_frequency/radio_connection var/suicider = null var/hearing_range = 1 @@ -48,64 +47,50 @@ holder.update_icon() return -/obj/item/assembly/signaler/ui_interact(mob/user, flag1) - . = ..() - if(is_secured(user)) - var/t1 = "-------" - var/dat = {" - - -Send Signal
-Frequency/Code for signaler:
-Frequency: -[format_frequency(src.frequency)] -Set
- -Code: -[src.code] -Set
-[t1] -
"} - user << browse(dat, "window=radio") - onclose(user, "radio") +/obj/item/assembly/signaler/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) + if(!is_secured(user)) return + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + var/ui_width = 280 + var/ui_height = 132 + ui = new(user, src, ui_key, "signaler", name, ui_width, ui_height, master_ui, state) + ui.open() +/obj/item/assembly/signaler/ui_data(mob/user) + var/list/data = list() + data["frequency"] = frequency + data["code"] = code + data["minFrequency"] = MIN_FREE_FREQ + data["maxFrequency"] = MAX_FREE_FREQ -/obj/item/assembly/signaler/Topic(href, href_list) - ..() + return data - if(!usr.canUseTopic(src, BE_CLOSE)) - usr << browse(null, "window=radio") - onclose(usr, "radio") +/obj/item/assembly/signaler/ui_act(action, params) + if(..()) return + switch(action) + if("signal") + INVOKE_ASYNC(src, .proc/signal) + . = TRUE + if("freq") + frequency = unformat_frequency(params["freq"]) + frequency = sanitize_frequency(frequency, TRUE) + set_frequency(frequency) + . = TRUE + if("code") + code = text2num(params["code"]) + code = round(code) + . = TRUE + if("reset") + if(params["reset"] == "freq") + frequency = initial(frequency) + else + code = initial(code) + . = TRUE - if (href_list["set"]) - - if(href_list["set"] == "freq") - var/new_freq = input(usr, "Input a new signalling frequency", "Remote Signaller Frequency", format_frequency(frequency)) as num|null - if(!usr.canUseTopic(src, BE_CLOSE)) - return - new_freq = unformat_frequency(new_freq) - new_freq = sanitize_frequency(new_freq, TRUE) - set_frequency(new_freq) - - if(href_list["set"] == "code") - var/new_code = input(usr, "Input a new signalling code", "Remote Signaller Code", code) as num|null - if(!usr.canUseTopic(src, BE_CLOSE)) - return - new_code = round(new_code) - new_code = CLAMP(new_code, 1, 100) - code = new_code - - if(href_list["send"]) - spawn( 0 ) - signal() - - if(usr) - attack_self(usr) - - return - + update_icon() + /obj/item/assembly/signaler/attackby(obj/item/W, mob/user, params) if(issignaler(W)) var/obj/item/assembly/signaler/signaler2 = W diff --git a/code/modules/mob/dead/observer/notificationprefs.dm b/code/modules/mob/dead/observer/notificationprefs.dm index 9eef167469..6c1d76eaf3 100644 --- a/code/modules/mob/dead/observer/notificationprefs.dm +++ b/code/modules/mob/dead/observer/notificationprefs.dm @@ -24,7 +24,7 @@ /datum/notificationpanel/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.observer_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "notificationpanel", "Notification Preferences", 700, 700, master_ui, state) + ui = new(user, src, ui_key, "notificationpanel", "Notification Preferences", 270, 360, master_ui, state) ui.open() /datum/notificationpanel/ui_data(mob/user) diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm index 48cf93210e..0d909f5642 100644 --- a/code/modules/mob/living/simple_animal/bot/mulebot.dm +++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm @@ -14,7 +14,7 @@ icon_state = "mulebot0" density = TRUE move_resist = MOVE_FORCE_STRONG - animate_movement = 1 + animate_movement = FORWARD_STEPS health = 50 maxHealth = 50 damage_coeff = list(BRUTE = 0.5, BURN = 0.7, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) @@ -29,10 +29,14 @@ model = "MULE" bot_core_type = /obj/machinery/bot_core/mulebot + var/ui_x = 350 + var/ui_y = 425 + var/id path_image_color = "#7F5200" + var/base_icon = "mulebot" var/atom/movable/load = null var/mob/living/passenger = null var/turf/target // this is turf to navigate to (location of beacon) @@ -74,16 +78,16 @@ /mob/living/simple_animal/bot/mulebot/proc/set_id(new_id) id = new_id if(paicard) - bot_name = "\improper MULEbot ([new_id])" + bot_name = "[initial(name)] ([new_id])" else - name = "\improper MULEbot ([new_id])" + name = "[initial(name)] ([new_id])" /mob/living/simple_animal/bot/mulebot/bot_reset() ..() reached_target = 0 /mob/living/simple_animal/bot/mulebot/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/screwdriver)) + if(I.tool_behaviour == TOOL_SCREWDRIVER) ..() if(open) on = FALSE @@ -91,13 +95,13 @@ if(!user.transferItemToLoc(I, src)) return cell = I - visible_message("[user] inserts a cell into [src].", + visible_message("[user] inserts a cell into [src].", "You insert the new cell into [src].") - else if(istype(I, /obj/item/crowbar) && open && cell) + else if(I.tool_behaviour == TOOL_CROWBAR && open && cell) cell.add_fingerprint(usr) cell.forceMove(loc) cell = null - visible_message("[user] crowbars out the power cell from [src].", + visible_message("[user] crowbars out the power cell from [src].", "You pry the powercell out of [src].") else if(is_wire_tool(I) && open) return attack_hand(user) @@ -115,21 +119,19 @@ return /mob/living/simple_animal/bot/mulebot/emag_act(mob/user) - . = SEND_SIGNAL(src, COMSIG_ATOM_EMAG_ACT) if(emagged < 1) emagged = TRUE if(!open) locked = !locked to_chat(user, "You [locked ? "lock" : "unlock"] [src]'s controls!") flick("mulebot-emagged", src) - playsound(src, "sparks", 100, 0) - return TRUE + playsound(src, "sparks", 100, FALSE) /mob/living/simple_animal/bot/mulebot/update_icon() if(open) - icon_state="mulebot-hatch" + icon_state="[base_icon]-hatch" else - icon_state = "mulebot[wires.is_cut(WIRE_AVOIDANCE)]" + icon_state = "[base_icon][wires.is_cut(WIRE_AVOIDANCE)]" cut_overlays() if(load && !ismob(load))//buckling handles the mob offsets load.pixel_y = initial(load.pixel_y) + 9 @@ -152,7 +154,7 @@ /mob/living/simple_animal/bot/mulebot/bullet_act(obj/item/projectile/Proj) . = ..() - if(. == BULLET_ACT_HIT && !QDELETED(src)) //Got hit and not blown up yet.) + if(. && !QDELETED(src)) //Got hit and not blown up yet. if(prob(50) && !isnull(load)) unload(0) if(prob(25)) @@ -171,7 +173,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "mulebot", name, 600, 375, master_ui, state) + ui = new(user, src, ui_key, "mulebot", name, ui_x, ui_y, master_ui, state) ui.open() /mob/living/simple_animal/bot/mulebot/ui_data(mob/user) @@ -191,12 +193,15 @@ else data["load"] = load ? load.name : null data["destination"] = destination ? destination : null + data["home"] = home_destination + data["destinations"] = GLOB.deliverybeacontags data["cell"] = cell ? TRUE : FALSE data["cellPercent"] = cell ? cell.percent() : null data["autoReturn"] = auto_return data["autoPickup"] = auto_pickup data["reportDelivery"] = report_delivery data["haspai"] = paicard ? TRUE : FALSE + data["id"] = id return data /mob/living/simple_animal/bot/mulebot/ui_act(action, params) @@ -216,10 +221,10 @@ return . = TRUE else - bot_control(action, usr) // Kill this later. + bot_control(action, usr, params) // Kill this later. . = TRUE -/mob/living/simple_animal/bot/mulebot/bot_control(command, mob/user, pda = FALSE) +/mob/living/simple_animal/bot/mulebot/bot_control(command, mob/user, list/params = list(), pda = FALSE) if(pda && wires.is_cut(WIRE_RX)) // MULE wireless is controlled by wires. return @@ -234,15 +239,27 @@ if(mode == BOT_IDLE || mode == BOT_DELIVER) start_home() if("destination") - var/new_dest = input(user, "Enter Destination:", name, destination) as null|anything in GLOB.deliverybeacontags + var/new_dest + if(pda) + new_dest = input(user, "Enter Destination:", name, destination) as null|anything in GLOB.deliverybeacontags + else + new_dest = params["value"] if(new_dest) set_destination(new_dest) if("setid") - var/new_id = stripped_input(user, "Enter ID:", name, id, MAX_NAME_LEN) + var/new_id + if(pda) + new_id = stripped_input(user, "Enter ID:", name, id, MAX_NAME_LEN) + else + new_id = params["value"] if(new_id) set_id(new_id) if("sethome") - var/new_home = input(user, "Enter Home:", name, home_destination) as null|anything in GLOB.deliverybeacontags + var/new_home + if(pda) + new_home = input(user, "Enter Home:", name, home_destination) as null|anything in GLOB.deliverybeacontags + else + new_home = params["value"] if(new_home) home_destination = new_home if("unload") @@ -317,26 +334,28 @@ /mob/living/simple_animal/bot/mulebot/proc/buzz(type) switch(type) if(SIGH) - audible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.") - playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) + audible_message("[src] makes a sighing buzz.") + playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, FALSE) if(ANNOYED) - audible_message("[src] makes an annoyed buzzing sound.", "You hear an electronic buzzing sound.") - playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0) + audible_message("[src] makes an annoyed buzzing sound.") + playsound(loc, 'sound/machines/buzz-two.ogg', 50, FALSE) if(DELIGHT) - audible_message("[src] makes a delighted ping!", "You hear a ping.") - playsound(loc, 'sound/machines/ping.ogg', 50, 0) + audible_message("[src] makes a delighted ping!") + playsound(loc, 'sound/machines/ping.ogg', 50, FALSE) // mousedrop a crate to load the bot // can load anything if hacked /mob/living/simple_animal/bot/mulebot/MouseDrop_T(atom/movable/AM, mob/user) - if(user.incapacitated() || user.lying) return if(!istype(AM)) return + if(!istype(AM) || isdead(AM) || iscameramob(AM) || istype(AM, /obj/effect/dummy/phased_mob)) + return + load(AM) // called to load a crate @@ -449,10 +468,8 @@ process_bot() num_steps-- if(mode != BOT_IDLE) - spawn(0) - for(var/i=num_steps,i>0,i--) - sleep(2) - process_bot() + var/process_timer = addtimer(CALLBACK(src, .proc/process_bot), 2, TIMER_LOOP|TIMER_STOPPABLE) + addtimer(CALLBACK(GLOBAL_PROC, /proc/deltimer, process_timer), (num_steps*2) + 1) /mob/living/simple_animal/bot/mulebot/proc/process_bot() if(!on || client) @@ -491,6 +508,7 @@ B.setDir(newdir) bloodiness-- + var/oldloc = loc var/moved = step_towards(src, next) // attempt to move if(cell) @@ -516,11 +534,7 @@ buzz(SIGH) mode = BOT_WAIT_FOR_NAV blockcount = 0 - spawn(20) - calc_path(avoid=next) - if(path.len > 0) - buzz(DELIGHT) - mode = BOT_BLOCKED + addtimer(CALLBACK(src, .proc/process_blocked, next), 2 SECONDS) return return else @@ -533,18 +547,26 @@ if(BOT_NAV) // calculate new path mode = BOT_WAIT_FOR_NAV - spawn(0) - calc_path() + INVOKE_ASYNC(src, .proc/process_nav) - if(path.len > 0) - blockcount = 0 - mode = BOT_BLOCKED - buzz(DELIGHT) +/mob/living/simple_animal/bot/mulebot/proc/process_blocked(turf/next) + calc_path(avoid=next) + if(path.len > 0) + buzz(DELIGHT) + mode = BOT_BLOCKED - else - buzz(SIGH) +/mob/living/simple_animal/bot/mulebot/proc/process_nav() + calc_path() - mode = BOT_NO_ROUTE + if(path.len > 0) + blockcount = 0 + mode = BOT_BLOCKED + buzz(DELIGHT) + + else + buzz(SIGH) + + mode = BOT_NO_ROUTE // calculates a path to the current destination // given an optional turf to avoid @@ -574,26 +596,28 @@ /mob/living/simple_animal/bot/mulebot/proc/start_home() if(!on) return - spawn(0) - set_destination(home_destination) - mode = BOT_BLOCKED + INVOKE_ASYNC(src, .proc/do_start_home) update_icon() +/mob/living/simple_animal/bot/mulebot/proc/do_start_home() + set_destination(home_destination) + mode = BOT_BLOCKED + // called when bot reaches current target /mob/living/simple_animal/bot/mulebot/proc/at_target() if(!reached_target) - radio_channel = "Supply" //Supply channel - audible_message("[src] makes a chiming sound!", "You hear a chime.") - playsound(loc, 'sound/machines/chime.ogg', 50, 0) + radio_channel = RADIO_CHANNEL_SUPPLY //Supply channel + audible_message("[src] makes a chiming sound!") + playsound(loc, 'sound/machines/chime.ogg', 50, FALSE) reached_target = 1 if(pathset) //The AI called us here, so notify it of our arrival. loaddir = dir //The MULE will attempt to load a crate in whatever direction the MULE is "facing". if(calling_ai) to_chat(calling_ai, "[icon2html(src, calling_ai)] [src] wirelessly plays a chiming sound!") - playsound(calling_ai, 'sound/machines/chime.ogg',40, 0) + playsound(calling_ai, 'sound/machines/chime.ogg',40, FALSE) calling_ai = null - radio_channel = "AI Private" //Report on AI Private instead if the AI is controlling us. + radio_channel = RADIO_CHANNEL_AI_PRIVATE //Report on AI Private instead if the AI is controlling us. if(load) // if loaded, unload at target if(report_delivery) @@ -645,7 +669,7 @@ log_combat(src, H, "run over", null, "(DAMTYPE: [uppertext(BRUTE)])") H.visible_message("[src] drives over [H]!", \ "[src] drives over you!") - playsound(loc, 'sound/effects/splat.ogg', 50, 1) + playsound(loc, 'sound/effects/splat.ogg', 50, TRUE) var/damage = rand(5,15) H.apply_damage(2*damage, BRUTE, BODY_ZONE_HEAD, run_armor_check(BODY_ZONE_HEAD, "melee")) @@ -734,7 +758,7 @@ /mob/living/simple_animal/bot/mulebot/insertpai(mob/user, obj/item/paicard/card) if(..()) - visible_message("[src] safeties are locked on.") + visible_message("[src] safeties are locked on.") #undef SIGH #undef ANNOYED @@ -742,3 +766,4 @@ /obj/machinery/bot_core/mulebot req_access = list(ACCESS_CARGO) + \ No newline at end of file diff --git a/code/modules/modular_computers/NTNet/NTNRC/conversation.dm b/code/modules/modular_computers/NTNet/NTNRC/conversation.dm index 7c4058ef39..4f916359be 100644 --- a/code/modules/modular_computers/NTNet/NTNRC/conversation.dm +++ b/code/modules/modular_computers/NTNet/NTNRC/conversation.dm @@ -1,3 +1,5 @@ +#define MAX_CHANNELS 1000 + /datum/ntnet_conversation var/id = null var/title = "Untitled Conversation" @@ -8,7 +10,11 @@ var/static/ntnrc_uid = 0 /datum/ntnet_conversation/New() - id = ntnrc_uid++ + id = ntnrc_uid + 1 + if(id > MAX_CHANNELS) + qdel(src) + return + ntnrc_uid = id if(SSnetworks.station_network) SSnetworks.station_network.chat_channels.Add(src) ..() @@ -66,3 +72,5 @@ add_status_message("[client.username] has changed channel title from [title] to [newtitle]") title = newtitle + +#undef MAX_CHANNELS \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index 1a4869215b..f661f0ec10 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -71,7 +71,7 @@ // Check if the user can run program. Only humans can operate computer. Automatically called in run_program() // User has to wear their ID for ID Scan to work. // Can also be called manually, with optional parameter being access_to_check to scan the user's ID -/datum/computer_file/program/proc/can_run(mob/user, loud = 0, access_to_check, transfer = 0) +/datum/computer_file/program/proc/can_run(mob/user, loud = FALSE, access_to_check, transfer = FALSE) // Defaults to required_access if(!access_to_check) if(transfer && transfer_access) @@ -79,16 +79,16 @@ else access_to_check = required_access if(!access_to_check) // No required_access, allow it. - return 1 + return TRUE if(!transfer && computer && (computer.obj_flags & EMAGGED)) //emags can bypass the execution locks but not the download ones. - return 1 + return TRUE if(IsAdminGhost(user)) - return 1 + return TRUE if(issilicon(user)) - return 1 + return TRUE if(ishuman(user)) var/obj/item/card/id/D @@ -101,17 +101,17 @@ if(!I && !D) if(loud) to_chat(user, "\The [computer] flashes an \"RFID Error - Unable to scan ID\" warning.") - return 0 + return FALSE if(I) if(access_to_check in I.GetAccess()) - return 1 + return TRUE else if(D) if(access_to_check in D.GetAccess()) - return 1 + return TRUE if(loud) to_chat(user, "\The [computer] flashes an \"Access Denied\" warning.") - return 0 + return FALSE // This attempts to retrieve header data for UIs. If implementing completely new device of different type than existing ones // always include the device here in this proc. This proc basically relays the request to whatever is running the program. @@ -127,15 +127,15 @@ if(requires_ntnet && network_destination) generate_network_log("Connection opened to [network_destination].") program_state = PROGRAM_STATE_ACTIVE - return 1 - return 0 + return TRUE + return FALSE // Use this proc to kill the program. Designed to be implemented by each program if it requires on-quit logic, such as the NTNRC client. /datum/computer_file/program/proc/kill_program(forced = FALSE) program_state = PROGRAM_STATE_KILLED if(network_destination) generate_network_log("Connection to [network_destination] closed.") - return 1 + return TRUE /datum/computer_file/program/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) @@ -158,17 +158,17 @@ // ALWAYS INCLUDE PARENT CALL ..() OR DIE IN FIRE. /datum/computer_file/program/ui_act(action,params,datum/tgui/ui) if(..()) - return 1 + return TRUE if(computer) switch(action) if("PC_exit") computer.kill_program() ui.close() - return 1 + return TRUE if("PC_shutdown") computer.shutdown_computer() ui.close() - return 1 + return TRUE if("PC_minimize") var/mob/user = usr if(!computer.active_program || !computer.all_components[MC_CPU]) diff --git a/code/modules/modular_computers/file_system/programs/configurator.dm b/code/modules/modular_computers/file_system/programs/configurator.dm index 8ceb1a4dec..2d60323d10 100644 --- a/code/modules/modular_computers/file_system/programs/configurator.dm +++ b/code/modules/modular_computers/file_system/programs/configurator.dm @@ -10,6 +10,8 @@ unsendable = 1 undeletable = 1 size = 4 + ui_x = 420 + ui_y = 630 available_on_ntnet = 0 requires_ntnet = 0 tgui_id = "ntos_configuration" diff --git a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm index 29a1df9ebe..d8b3f96f42 100644 --- a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm +++ b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm @@ -10,215 +10,225 @@ ui_header = "ntnrc_idle.gif" available_on_ntnet = 1 tgui_id = "ntos_net_chat" + ui_x = 900 + ui_y = 675 - var/last_message = null // Used to generate the toolbar icon + var/last_message // Used to generate the toolbar icon var/username - var/datum/ntnet_conversation/channel = null - var/operator_mode = 0 // Channel operator mode - var/netadmin_mode = 0 // Administrator mode (invisible to other users + bypasses passwords) + var/active_channel + var/list/channel_history = list() + var/operator_mode = FALSE // Channel operator mode + var/netadmin_mode = FALSE // Administrator mode (invisible to other users + bypasses passwords) /datum/computer_file/program/chatclient/New() username = "DefaultUser[rand(100, 999)]" /datum/computer_file/program/chatclient/ui_act(action, params) if(..()) - return 1 + return + var/datum/ntnet_conversation/channel = SSnetworks.station_network.get_chat_channel_by_id(active_channel) + var/authed = FALSE + if(channel && ((channel.operator == src) || netadmin_mode)) + authed = TRUE switch(action) if("PRG_speak") - . = 1 - if(!channel) - return 1 - var/mob/living/user = usr - var/message = reject_bad_text(input(user, "Enter message or leave blank to cancel: ")) - if(!message || !channel) + if(!channel || isnull(active_channel)) return + var/message = reject_bad_text(params["message"]) + if(!message) + return + if(channel.password && !(src in channel.clients)) + if(channel.password == message) + channel.add_client(src) + return TRUE + channel.add_message(message, username) + var/mob/living/user = usr user.log_talk(message, LOG_CHAT, tag="as [username] to channel [channel.title]") - + return TRUE if("PRG_joinchannel") - . = 1 - var/datum/ntnet_conversation/C - for(var/datum/ntnet_conversation/chan in SSnetworks.station_network.chat_channels) - if(chan.id == text2num(params["id"])) - C = chan - break - - if(!C) - return 1 + var/new_target = text2num(params["id"]) + if(isnull(new_target) || new_target == active_channel) + return if(netadmin_mode) - channel = C // Bypasses normal leave/join and passwords. Technically makes the user invisible to others. - return 1 + active_channel = new_target // Bypasses normal leave/join and passwords. Technically makes the user invisible to others. + return TRUE - if(C.password) - var/mob/living/user = usr - var/password = reject_bad_text(input(user,"Access Denied. Enter password:")) - if(C && (password == C.password)) - C.add_client(src) - channel = C - return 1 - C.add_client(src) - channel = C + active_channel = new_target + channel = SSnetworks.station_network.get_chat_channel_by_id(new_target) + if(!(src in channel.clients) && !channel.password) + channel.add_client(src) + return TRUE if("PRG_leavechannel") - . = 1 if(channel) channel.remove_client(src) - channel = null + active_channel = null + return TRUE if("PRG_newchannel") - . = 1 - var/mob/living/user = usr - var/channel_title = reject_bad_text(input(user,"Enter channel name or leave blank to cancel:")) + var/channel_title = reject_bad_text(params["new_channel_name"]) if(!channel_title) return - var/datum/ntnet_conversation/C = new/datum/ntnet_conversation() + var/datum/ntnet_conversation/C = new /datum/ntnet_conversation() C.add_client(src) C.operator = src - channel = C C.title = channel_title + active_channel = C.id + return TRUE if("PRG_toggleadmin") - . = 1 if(netadmin_mode) - netadmin_mode = 0 + netadmin_mode = FALSE if(channel) channel.remove_client(src) // We shouldn't be in channel's user list, but just in case... - channel = null - return 1 + return TRUE var/mob/living/user = usr - if(can_run(usr, 1, ACCESS_NETWORK)) - if(channel) - var/response = alert(user, "Really engage admin-mode? You will be disconnected from your current channel!", "NTNRC Admin mode", "Yes", "No") - if(response == "Yes") - if(channel) - channel.remove_client(src) - channel = null - else - return - netadmin_mode = 1 + if(can_run(user, TRUE, ACCESS_NETWORK)) + for(var/C in SSnetworks.station_network.chat_channels) + var/datum/ntnet_conversation/chan = C + chan.remove_client(src) + netadmin_mode = TRUE + return TRUE if("PRG_changename") - . = 1 - var/mob/living/user = usr - var/newname = sanitize(input(user,"Enter new nickname or leave blank to cancel:")) + var/newname = sanitize(params["new_name"]) if(!newname) - return 1 - if(channel) - channel.add_status_message("[username] is now known as [newname].") + return + for(var/C in SSnetworks.station_network.chat_channels) + var/datum/ntnet_conversation/chan = C + if(src in chan.clients) + chan.add_status_message("[username] is now known as [newname].") username = newname - + return TRUE if("PRG_savelog") - . = 1 if(!channel) return - var/mob/living/user = usr - var/logname = stripped_input(user,"Enter desired logfile name (.log) or leave blank to cancel:") - if(!logname || !channel) - return 1 - var/datum/computer_file/data/logfile = new/datum/computer_file/data/logfile() + var/logname = stripped_input(params["log_name"]) + if(!logname) + return + var/datum/computer_file/data/logfile = new /datum/computer_file/data/logfile() // Now we will generate HTML-compliant file that can actually be viewed/printed. logfile.filename = logname logfile.stored_data = "\[b\]Logfile dump from NTNRC channel [channel.title]\[/b\]\[BR\]" for(var/logstring in channel.messages) - logfile.stored_data += "[logstring]\[BR\]" - logfile.stored_data += "\[b\]Logfile dump completed.\[/b\]" + logfile.stored_data = "[logfile.stored_data][logstring]\[BR\]" + logfile.stored_data = "[logfile.stored_data]\[b\]Logfile dump completed.\[/b\]" logfile.calculate_size() var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] if(!computer || !hard_drive || !hard_drive.store_file(logfile)) if(!computer) // This program shouldn't even be runnable without computer. CRASH("Var computer is null!") - return 1 if(!hard_drive) - computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive connection error\" warning.") + computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive connection error\" warning.") else // In 99.9% cases this will mean our HDD is full - computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive may be full. Please free some space and try again. Required space: [logfile.size]GQ\" warning.") + computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive may be full. Please free some space and try again. Required space: [logfile.size]GQ\" warning.") + return TRUE if("PRG_renamechannel") - . = 1 - if(!operator_mode || !channel) - return 1 - var/mob/living/user = usr - var/newname = reject_bad_text(input(user, "Enter new channel name or leave blank to cancel:")) + if(!authed) + return + var/newname = reject_bad_text(params["new_name"]) if(!newname || !channel) return channel.add_status_message("Channel renamed from [channel.title] to [newname] by operator.") channel.title = newname + return TRUE if("PRG_deletechannel") - . = 1 - if(channel && ((channel.operator == src) || netadmin_mode)) + if(authed) qdel(channel) - channel = null + active_channel = null + return TRUE if("PRG_setpassword") - . = 1 - if(!channel || ((channel.operator != src) && !netadmin_mode)) - return 1 + if(!authed) + return - var/mob/living/user = usr - var/newpassword = sanitize(input(user, "Enter new password for this channel. Leave blank to cancel, enter 'nopassword' to remove password completely:")) - if(!channel || !newpassword || ((channel.operator != src) && !netadmin_mode)) - return 1 + var/new_password = sanitize(params["new_password"]) + if(!authed) + return - if(newpassword == "nopassword") - channel.password = "" - else - channel.password = newpassword + channel.password = new_password + return TRUE /datum/computer_file/program/chatclient/process_tick() - ..() + . = ..() + var/datum/ntnet_conversation/channel = SSnetworks.station_network.get_chat_channel_by_id(active_channel) if(program_state != PROGRAM_STATE_KILLED) ui_header = "ntnrc_idle.gif" if(channel) // Remember the last message. If there is no message in the channel remember null. - last_message = channel.messages.len ? channel.messages[channel.messages.len - 1] : null + last_message = length(channel.messages) ? channel.messages[length(channel.messages)] : null else last_message = null - return 1 - if(channel && channel.messages && channel.messages.len) - ui_header = last_message == channel.messages[channel.messages.len - 1] ? "ntnrc_idle.gif" : "ntnrc_new.gif" + return TRUE + if(channel?.messages?.len) + ui_header = last_message == channel.messages[length(channel.messages)] ? "ntnrc_idle.gif" : "ntnrc_new.gif" else ui_header = "ntnrc_idle.gif" /datum/computer_file/program/chatclient/kill_program(forced = FALSE) - if(channel) + for(var/C in SSnetworks.station_network.chat_channels) + var/datum/ntnet_conversation/channel = C channel.remove_client(src) - channel = null ..() +/datum/computer_file/program/chatclient/ui_static_data(mob/user) + var/list/data = list() + data["can_admin"] = can_run(user, FALSE, ACCESS_NETWORK) + return data + /datum/computer_file/program/chatclient/ui_data(mob/user) if(!SSnetworks.station_network || !SSnetworks.station_network.chat_channels) - return + return list() var/list/data = list() data = get_header_data() + var/list/all_channels = list() + for(var/C in SSnetworks.station_network.chat_channels) + var/datum/ntnet_conversation/conv = C + if(conv && conv.title) + all_channels.Add(list(list( + "chan" = conv.title, + "id" = conv.id + ))) + data["all_channels"] = all_channels + data["active_channel"] = active_channel + data["username"] = username data["adminmode"] = netadmin_mode + var/datum/ntnet_conversation/channel = SSnetworks.station_network.get_chat_channel_by_id(active_channel) if(channel) data["title"] = channel.title - var/list/messages[0] - for(var/M in channel.messages) - messages.Add(list(list( - "msg" = M - ))) - data["messages"] = messages - var/list/clients[0] + var/authed = FALSE + if(!channel.password) + authed = TRUE + if(netadmin_mode) + authed = TRUE + var/list/clients = list() for(var/C in channel.clients) + if(C == src) + authed = TRUE var/datum/computer_file/program/chatclient/cl = C clients.Add(list(list( "name" = cl.username ))) - data["clients"] = clients - operator_mode = (channel.operator == src) ? 1 : 0 - data["is_operator"] = operator_mode || netadmin_mode - - else // Channel selection screen - var/list/all_channels[0] - for(var/C in SSnetworks.station_network.chat_channels) - var/datum/ntnet_conversation/conv = C - if(conv && conv.title) - all_channels.Add(list(list( - "chan" = conv.title, - "id" = conv.id + data["authed"] = authed + //no fishing for ui data allowed + if(authed) + data["clients"] = clients + var/list/messages = list() + for(var/M in channel.messages) + messages.Add(list(list( + "msg" = M ))) - data["all_channels"] = all_channels + data["messages"] = messages + data["is_operator"] = (channel.operator == src) || netadmin_mode + else + data["clients"] = list() + data["messages"] = list() + else + data["clients"] = list() + data["authed"] = FALSE + data["messages"] = list() - return data \ No newline at end of file + return data diff --git a/code/modules/modular_computers/laptop_vendor.dm b/code/modules/modular_computers/laptop_vendor.dm index 9078830da0..2bed456c41 100644 --- a/code/modules/modular_computers/laptop_vendor.dm +++ b/code/modules/modular_computers/laptop_vendor.dm @@ -27,6 +27,9 @@ var/dev_printer = 0 // 0: None, 1: Standard var/dev_card = 0 // 0: None, 1: Standard + ui_x = 500 + ui_y = 400 + // Removes all traces of old order and allows you to begin configuration from scratch. /obj/machinery/lapvend/proc/reset_order() state = 0 @@ -173,7 +176,7 @@ switch(action) if("pick_device") if(state) // We've already picked a device type - return TRUE + return FALSE devtype = text2num(params["pick"]) state = 1 fabricate_and_recalc_price(FALSE) @@ -185,7 +188,7 @@ try_purchase() return TRUE if((state != 1) && devtype) // Following IFs should only be usable when in the Select Loadout mode - return TRUE + return FALSE switch(action) if("confirm_order") state = 2 // Wait for ID swipe for payment processing @@ -219,36 +222,53 @@ dev_card = text2num(params["card"]) fabricate_and_recalc_price(FALSE) return TRUE - return TRUE + return FALSE /obj/machinery/lapvend/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) if(stat & (BROKEN | NOPOWER | MAINT)) if(ui) ui.close() - return TRUE + return FALSE ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if (!ui) - ui = new(user, src, ui_key, "computer_fabricator", "Personal Computer Vendor", 500, 400, state = state) + ui = new(user, src, ui_key, "computer_fabricator", "Personal Computer Vendor", ui_x, ui_y, state = state) ui.open() /obj/machinery/lapvend/attackby(obj/item/I, mob/user) if(istype(I, /obj/item/stack/spacecash)) var/obj/item/stack/spacecash/c = I - if(!user.temporarilyRemoveItemFromInventory(c)) return credits += c.value visible_message("[user] inserts [c.value] credits into [src].") qdel(c) return + /*else if(istype(I, /obj/item/holochip)) + var/obj/item/holochip/HC = I + credits += HC.credits + visible_message("[user] inserts a $[HC.credits] holocredit chip into [src].") + qdel(HC) + return + else if(istype(I, /obj/item/card/id)) + if(state != 2) + return + var/obj/item/card/id/ID = I + var/datum/bank_account/account = ID.registered_account + var/target_credits = total_price - credits + if(!account.adjust_money(-target_credits)) + say("Insufficient money on card to purchase!") + return + credits += target_credits + say("$[target_credits] has been desposited from your account.") + return */ //Goonconomy when return ..() // Simplified payment processing, returns 1 on success. /obj/machinery/lapvend/proc/process_payment() if(total_price > credits) say("Insufficient credits.") - return TRUE + return FALSE else return TRUE @@ -288,4 +308,4 @@ state = 3 addtimer(CALLBACK(src, .proc/reset_order), 100) return TRUE - return TRUE \ No newline at end of file + return FALSE \ No newline at end of file diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm index c863e0e44a..9229781c0f 100644 --- a/code/modules/shuttle/emergency.dm +++ b/code/modules/shuttle/emergency.dm @@ -21,7 +21,7 @@ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) ui = new(user, src, ui_key, "emergency_shuttle_console", name, - 400, 400, master_ui, state) + 400, 350, master_ui, state) ui.open() /obj/machinery/computer/emergency_shuttle/ui_data() diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm index 411d299813..55fe8f0bb5 100644 --- a/code/modules/tgui/tgui.dm +++ b/code/modules/tgui/tgui.dm @@ -1,364 +1,364 @@ - /** - * tgui - * - * /tg/station user interface library - **/ - - /** - * tgui datum (represents a UI). - **/ -/datum/tgui - /// The mob who opened/is using the UI. - var/mob/user - /// The object which owns the UI. - var/datum/src_object - /// The title of te UI. - var/title - /// The ui_key of the UI. This allows multiple UIs for one src_object. - var/ui_key - /// The window_id for browse() and onclose(). - var/window_id - /// The window width. - var/width = 0 - /// The window height - var/height = 0 - /// The style to be used for this UI. - var/style = "nanotrasen" - /// The interface (template) to be used for this UI. - var/interface - /// Update the UI every MC tick. - var/autoupdate = TRUE - /// If the UI has been initialized yet. - var/initialized = FALSE - /// The data (and datastructure) used to initialize the UI. - var/list/initial_data - /// The static data used to initialize the UI. - var/list/initial_static_data - /// The status/visibility of the UI. - var/status = UI_INTERACTIVE - /// Topic state used to determine status/interactability. - var/datum/ui_state/state = null - /// The parent UI. - var/datum/tgui/master_ui - /// Children of this UI. - var/list/datum/tgui/children = list() - var/custom_browser_id = FALSE - var/ui_screen = "home" - - /** - * public - * - * Create a new UI. - * - * required user mob The mob who opened/is using the UI. - * required src_object datum The object or datum which owns the UI. - * required ui_key string The ui_key of the UI. - * required interface string The interface used to render the UI. - * optional title string The title of the UI. - * optional width int The window width. - * optional height int The window height. - * optional master_ui datum/tgui The parent UI. - * optional state datum/ui_state The state used to determine status. - * - * return datum/tgui The requested UI. - **/ -/datum/tgui/New(mob/user, datum/src_object, ui_key, interface, title, width = 0, height = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state, browser_id = null) - src.user = user - src.src_object = src_object - src.ui_key = ui_key - src.window_id = browser_id ? browser_id : "[REF(src_object)]-[ui_key]" // DO NOT replace with \ref here. src_object could potentially be tagged - src.custom_browser_id = browser_id ? TRUE : FALSE - - set_interface(interface) - - if(title) - src.title = sanitize(title) - if(width) - src.width = width - if(height) - src.height = height - - src.master_ui = master_ui - if(master_ui) - master_ui.children += src - src.state = state - - var/datum/asset/assets = get_asset_datum(/datum/asset/group/tgui) - assets.send(user) - - /** - * public - * - * Open this UI (and initialize it with data). - **/ -/datum/tgui/proc/open() - if(!user.client) - return // Bail if there is no client. - - update_status(push = FALSE) // Update the window status. - if(status < UI_UPDATE) - return // Bail if we're not supposed to open. - - var/window_size - if(width && height) // If we have a width and height, use them. - window_size = "size=[width]x[height];" - else - window_size = "" - - // Remove titlebar and resize handles for a fancy window - var/have_title_bar - if(user.client.prefs.tgui_fancy) - have_title_bar = "titlebar=0;can_resize=0;" - else - have_title_bar = "titlebar=1;can_resize=1;" - - // Generate page html - var/html - html = SStgui.basehtml - // Allow the src object to override the html if needed - html = src_object.ui_base_html(html) - // Replace template tokens with important UI data - // NOTE: Intentional \ref usage; tgui datums can't/shouldn't - // be tagged, so this is an effective unwrap - html = replacetextEx(html, "\[ref]", "\ref[src]") - html = replacetextEx(html, "\[style]", style) - - // Open the window. - user << browse(html, "window=[window_id];can_minimize=0;auto_format=0;[window_size][have_title_bar]") - if (!custom_browser_id) - // Instruct the client to signal UI when the window is closed. - // NOTE: Intentional \ref usage; tgui datums can't/shouldn't - // be tagged, so this is an effective unwrap - winset(user, window_id, "on-close=\"uiclose \ref[src]\"") - - if(!initial_data) - initial_data = src_object.ui_data(user) - if(!initial_static_data) - initial_static_data = src_object.ui_static_data(user) - - SStgui.on_open(src) - - /** - * public - * - * Reinitialize the UI. - * (Possibly with a new interface and/or data). - * - * optional template string The name of the new interface. - * optional data list The new initial data. - **/ -/datum/tgui/proc/reinitialize(interface, list/data, list/static_data) - if(interface) - set_interface(interface) // Set a new interface. - if(data) - initial_data = data - if(static_data) - initial_static_data = static_data - open() - - /** - * public - * - * Close the UI, and all its children. - **/ -/datum/tgui/proc/close() - user << browse(null, "window=[window_id]") // Close the window. - src_object.ui_close() - SStgui.on_close(src) - for(var/datum/tgui/child in children) // Loop through and close all children. - child.close() - children.Cut() - state = null - master_ui = null - qdel(src) - - /** - * public - * - * Set the style for this UI. - * - * required style string The new UI style. - **/ -/datum/tgui/proc/set_style(style) - src.style = lowertext(style) - - /** - * public - * - * Set the interface (template) for this UI. - * - * required interface string The new UI interface. - **/ -/datum/tgui/proc/set_interface(interface) - src.interface = lowertext(interface) - - /** - * public - * - * Enable/disable auto-updating of the UI. - * - * required state bool Enable/disable auto-updating. - **/ -/datum/tgui/proc/set_autoupdate(state = TRUE) - autoupdate = state - - /** - * private - * - * Package the data to send to the UI, as JSON. - * This includes the UI data and config_data. - * - * return string The packaged JSON. - **/ -/datum/tgui/proc/get_json(list/data, list/static_data) - var/list/json_data = list() - - json_data["config"] = list( - "title" = title, - "status" = status, - "screen" = ui_screen, - "style" = style, - "interface" = interface, - "fancy" = user.client.prefs.tgui_fancy, - "locked" = user.client.prefs.tgui_lock && !custom_browser_id, - "observer" = isobserver(user), - "window" = window_id, - // NOTE: Intentional \ref usage; tgui datums can't/shouldn't - // be tagged, so this is an effective unwrap - "ref" = "\ref[src]" - ) - - if(!isnull(data)) - json_data["data"] = data - if(!isnull(static_data)) - json_data["static_data"] = static_data - - // Generate the JSON. - var/json = json_encode(json_data) - // Strip #255/improper. - json = replacetext(json, "\proper", "") - json = replacetext(json, "\improper", "") - return json - - /** - * private - * - * Handle clicks from the UI. - * Call the src_object's ui_act() if status is UI_INTERACTIVE. - * If the src_object's ui_act() returns 1, update all UIs attacked to it. - **/ -/datum/tgui/Topic(href, href_list) - if(user != usr) - return // Something is not right here. - - var/action = href_list["action"] - var/params = href_list; params -= "action" - - switch(action) - if("tgui:initialize") - user << output(url_encode(get_json(initial_data, initial_static_data)), "[custom_browser_id ? window_id : "[window_id].browser"]:initialize") - initialized = TRUE - if("tgui:view") - if(params["screen"]) - ui_screen = params["screen"] - SStgui.update_uis(src_object) - if("tgui:log") - // Force window to show frills on fatal errors - if(params["fatal"]) - winset(user, window_id, "titlebar=1;can-resize=1;size=600x600") - if("tgui:link") - user << link(params["url"]) - if("tgui:fancy") - user.client.prefs.tgui_fancy = TRUE - if("tgui:nofrills") - user.client.prefs.tgui_fancy = FALSE - else - update_status(push = FALSE) // Update the window state. - if(src_object.ui_act(action, params, src, state)) // Call ui_act() on the src_object. - SStgui.update_uis(src_object) // Update if the object requested it. - - /** - * private - * - * Update the UI. - * Only updates the data if update is true, otherwise only updates the status. - * - * optional force bool If the UI should be forced to update. - **/ -/datum/tgui/process(force = FALSE) - var/datum/host = src_object.ui_host(user) - if(!src_object || !host || !user) // If the object or user died (or something else), abort. - close() - return - - if(status && (force || autoupdate)) - update() // Update the UI if the status and update settings allow it. - else - update_status(push = TRUE) // Otherwise only update status. - - /** - * private - * - * Push data to an already open UI. - * - * required data list The data to send. - * optional force bool If the update should be sent regardless of state. - **/ -/datum/tgui/proc/push_data(data, static_data, force = FALSE) - update_status(push = FALSE) // Update the window state. - if(!initialized) - return // Cannot update UI if it is not set up yet. - if(status <= UI_DISABLED && !force) - return // Cannot update UI, we have no visibility. - - // Send the new JSON to the update() Javascript function. - user << output(url_encode(get_json(data, static_data)), "[custom_browser_id ? window_id : "[window_id].browser"]:update") - - /** - * private - * - * Updates the UI by interacting with the src_object again, which will hopefully - * call try_ui_update on it. - * - * optional force_open bool If force_open should be passed to ui_interact. - **/ -/datum/tgui/proc/update(force_open = FALSE) - src_object.ui_interact(user, ui_key, src, force_open, master_ui, state) - - /** - * private - * - * Update the status/visibility of the UI for its user. - * - * optional push bool Push an update to the UI (an update is always sent for UI_DISABLED). - **/ -/datum/tgui/proc/update_status(push = FALSE) - var/status = src_object.ui_status(user, state) - if(master_ui) - status = min(status, master_ui.status) - set_status(status, push) - if(status == UI_CLOSE) - close() - - /** - * private - * - * Set the status/visibility of the UI. - * - * required status int The status to set (UI_CLOSE/UI_DISABLED/UI_UPDATE/UI_INTERACTIVE). - * optional push bool Push an update to the UI (an update is always sent for UI_DISABLED). - **/ -/datum/tgui/proc/set_status(status, push = FALSE) - if(src.status != status) // Only update if status has changed. - if(src.status == UI_DISABLED) - src.status = status - if(push) - update() - else - src.status = status - if(status == UI_DISABLED || push) // Update if the UI just because disabled, or a push is requested. - push_data(null, force = TRUE) - -/datum/tgui/proc/log_message(message) - log_tgui("[user] ([user.ckey]) using \"[title]\":\n[message]") - + /** + * tgui + * + * /tg/station user interface library + **/ + + /** + * tgui datum (represents a UI). + **/ +/datum/tgui + /// The mob who opened/is using the UI. + var/mob/user + /// The object which owns the UI. + var/datum/src_object + /// The title of te UI. + var/title + /// The ui_key of the UI. This allows multiple UIs for one src_object. + var/ui_key + /// The window_id for browse() and onclose(). + var/window_id + /// The window width. + var/width = 0 + /// The window height + var/height = 0 + /// The style to be used for this UI. + var/style = "nanotrasen" + /// The interface (template) to be used for this UI. + var/interface + /// Update the UI every MC tick. + var/autoupdate = TRUE + /// If the UI has been initialized yet. + var/initialized = FALSE + /// The data (and datastructure) used to initialize the UI. + var/list/initial_data + /// The static data used to initialize the UI. + var/list/initial_static_data + /// The status/visibility of the UI. + var/status = UI_INTERACTIVE + /// Topic state used to determine status/interactability. + var/datum/ui_state/state = null + /// The parent UI. + var/datum/tgui/master_ui + /// Children of this UI. + var/list/datum/tgui/children = list() + var/custom_browser_id = FALSE + var/ui_screen = "home" + + /** + * public + * + * Create a new UI. + * + * required user mob The mob who opened/is using the UI. + * required src_object datum The object or datum which owns the UI. + * required ui_key string The ui_key of the UI. + * required interface string The interface used to render the UI. + * optional title string The title of the UI. + * optional width int The window width. + * optional height int The window height. + * optional master_ui datum/tgui The parent UI. + * optional state datum/ui_state The state used to determine status. + * + * return datum/tgui The requested UI. + **/ +/datum/tgui/New(mob/user, datum/src_object, ui_key, interface, title, width = 0, height = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state, browser_id = null) + src.user = user + src.src_object = src_object + src.ui_key = ui_key + src.window_id = browser_id ? browser_id : "[REF(src_object)]-[ui_key]" // DO NOT replace with \ref here. src_object could potentially be tagged + src.custom_browser_id = browser_id ? TRUE : FALSE + + set_interface(interface) + + if(title) + src.title = sanitize(title) + if(width) + src.width = width + if(height) + src.height = height + + src.master_ui = master_ui + if(master_ui) + master_ui.children += src + src.state = state + + var/datum/asset/assets = get_asset_datum(/datum/asset/group/tgui) + assets.send(user) + + /** + * public + * + * Open this UI (and initialize it with data). + **/ +/datum/tgui/proc/open() + if(!user.client) + return // Bail if there is no client. + + update_status(push = FALSE) // Update the window status. + if(status < UI_UPDATE) + return // Bail if we're not supposed to open. + + var/window_size + if(width && height) // If we have a width and height, use them. + window_size = "size=[width]x[height];" + else + window_size = "" + + // Remove titlebar and resize handles for a fancy window + var/have_title_bar + if(user.client.prefs.tgui_fancy) + have_title_bar = "titlebar=0;can_resize=0;" + else + have_title_bar = "titlebar=1;can_resize=1;" + + // Generate page html + var/html + html = SStgui.basehtml + // Allow the src object to override the html if needed + html = src_object.ui_base_html(html) + // Replace template tokens with important UI data + // NOTE: Intentional \ref usage; tgui datums can't/shouldn't + // be tagged, so this is an effective unwrap + html = replacetextEx(html, "\[ref]", "\ref[src]") + html = replacetextEx(html, "\[style]", style) + + // Open the window. + user << browse(html, "window=[window_id];can_minimize=0;auto_format=0;[window_size][have_title_bar]") + if (!custom_browser_id) + // Instruct the client to signal UI when the window is closed. + // NOTE: Intentional \ref usage; tgui datums can't/shouldn't + // be tagged, so this is an effective unwrap + winset(user, window_id, "on-close=\"uiclose \ref[src]\"") + + if(!initial_data) + initial_data = src_object.ui_data(user) + if(!initial_static_data) + initial_static_data = src_object.ui_static_data(user) + + SStgui.on_open(src) + + /** + * public + * + * Reinitialize the UI. + * (Possibly with a new interface and/or data). + * + * optional template string The name of the new interface. + * optional data list The new initial data. + **/ +/datum/tgui/proc/reinitialize(interface, list/data, list/static_data) + if(interface) + set_interface(interface) // Set a new interface. + if(data) + initial_data = data + if(static_data) + initial_static_data = static_data + open() + + /** + * public + * + * Close the UI, and all its children. + **/ +/datum/tgui/proc/close() + user << browse(null, "window=[window_id]") // Close the window. + src_object.ui_close() + SStgui.on_close(src) + for(var/datum/tgui/child in children) // Loop through and close all children. + child.close() + children.Cut() + state = null + master_ui = null + qdel(src) + + /** + * public + * + * Set the style for this UI. + * + * required style string The new UI style. + **/ +/datum/tgui/proc/set_style(style) + src.style = lowertext(style) + + /** + * public + * + * Set the interface (template) for this UI. + * + * required interface string The new UI interface. + **/ +/datum/tgui/proc/set_interface(interface) + src.interface = lowertext(interface) + + /** + * public + * + * Enable/disable auto-updating of the UI. + * + * required state bool Enable/disable auto-updating. + **/ +/datum/tgui/proc/set_autoupdate(state = TRUE) + autoupdate = state + + /** + * private + * + * Package the data to send to the UI, as JSON. + * This includes the UI data and config_data. + * + * return string The packaged JSON. + **/ +/datum/tgui/proc/get_json(list/data, list/static_data) + var/list/json_data = list() + + json_data["config"] = list( + "title" = title, + "status" = status, + "screen" = ui_screen, + "style" = style, + "interface" = interface, + "fancy" = user.client.prefs.tgui_fancy, + "locked" = user.client.prefs.tgui_lock && !custom_browser_id, + "observer" = isobserver(user), + "window" = window_id, + // NOTE: Intentional \ref usage; tgui datums can't/shouldn't + // be tagged, so this is an effective unwrap + "ref" = "\ref[src]" + ) + + if(!isnull(data)) + json_data["data"] = data + if(!isnull(static_data)) + json_data["static_data"] = static_data + + // Generate the JSON. + var/json = json_encode(json_data) + // Strip #255/improper. + json = replacetext(json, "\proper", "") + json = replacetext(json, "\improper", "") + return json + + /** + * private + * + * Handle clicks from the UI. + * Call the src_object's ui_act() if status is UI_INTERACTIVE. + * If the src_object's ui_act() returns 1, update all UIs attacked to it. + **/ +/datum/tgui/Topic(href, href_list) + if(user != usr) + return // Something is not right here. + + var/action = href_list["action"] + var/params = href_list; params -= "action" + + switch(action) + if("tgui:initialize") + user << output(url_encode(get_json(initial_data, initial_static_data)), "[custom_browser_id ? window_id : "[window_id].browser"]:initialize") + initialized = TRUE + if("tgui:view") + if(params["screen"]) + ui_screen = params["screen"] + SStgui.update_uis(src_object) + if("tgui:log") + // Force window to show frills on fatal errors + if(params["fatal"]) + winset(user, window_id, "titlebar=1;can-resize=1;size=600x600") + if("tgui:link") + user << link(params["url"]) + if("tgui:fancy") + user.client.prefs.tgui_fancy = TRUE + if("tgui:nofrills") + user.client.prefs.tgui_fancy = FALSE + else + update_status(push = FALSE) // Update the window state. + if(src_object.ui_act(action, params, src, state)) // Call ui_act() on the src_object. + SStgui.update_uis(src_object) // Update if the object requested it. + + /** + * private + * + * Update the UI. + * Only updates the data if update is true, otherwise only updates the status. + * + * optional force bool If the UI should be forced to update. + **/ +/datum/tgui/process(force = FALSE) + var/datum/host = src_object.ui_host(user) + if(!src_object || !host || !user) // If the object or user died (or something else), abort. + close() + return + + if(status && (force || autoupdate)) + update() // Update the UI if the status and update settings allow it. + else + update_status(push = TRUE) // Otherwise only update status. + + /** + * private + * + * Push data to an already open UI. + * + * required data list The data to send. + * optional force bool If the update should be sent regardless of state. + **/ +/datum/tgui/proc/push_data(data, static_data, force = FALSE) + update_status(push = FALSE) // Update the window state. + if(!initialized) + return // Cannot update UI if it is not set up yet. + if(status <= UI_DISABLED && !force) + return // Cannot update UI, we have no visibility. + + // Send the new JSON to the update() Javascript function. + user << output(url_encode(get_json(data, static_data)), "[custom_browser_id ? window_id : "[window_id].browser"]:update") + + /** + * private + * + * Updates the UI by interacting with the src_object again, which will hopefully + * call try_ui_update on it. + * + * optional force_open bool If force_open should be passed to ui_interact. + **/ +/datum/tgui/proc/update(force_open = FALSE) + src_object.ui_interact(user, ui_key, src, force_open, master_ui, state) + + /** + * private + * + * Update the status/visibility of the UI for its user. + * + * optional push bool Push an update to the UI (an update is always sent for UI_DISABLED). + **/ +/datum/tgui/proc/update_status(push = FALSE) + var/status = src_object.ui_status(user, state) + if(master_ui) + status = min(status, master_ui.status) + set_status(status, push) + if(status == UI_CLOSE) + close() + + /** + * private + * + * Set the status/visibility of the UI. + * + * required status int The status to set (UI_CLOSE/UI_DISABLED/UI_UPDATE/UI_INTERACTIVE). + * optional push bool Push an update to the UI (an update is always sent for UI_DISABLED). + **/ +/datum/tgui/proc/set_status(status, push = FALSE) + if(src.status != status) // Only update if status has changed. + if(src.status == UI_DISABLED) + src.status = status + if(push) + update() + else + src.status = status + if(status == UI_DISABLED || push) // Update if the UI just because disabled, or a push is requested. + push_data(null, force = TRUE) + +/datum/tgui/proc/log_message(message) + log_tgui("[user] ([user.ckey]) using \"[title]\":\n[message]") + diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity new file mode 100644 index 0000000000..1a3aded610 --- /dev/null +++ b/node_modules/.yarn-integrity @@ -0,0 +1,10 @@ +{ + "systemParams": "win32-x64-72", + "modulesFolders": [], + "flags": [], + "linkedModules": [], + "topLevelPatterns": [], + "lockfileEntries": {}, + "files": [], + "artifacts": {} +} \ No newline at end of file diff --git a/tgui-next/README.md b/tgui-next/README.md index b8520392e0..df801684c6 100644 --- a/tgui-next/README.md +++ b/tgui-next/README.md @@ -220,6 +220,7 @@ This way, `Button` can pull out the `className` generated by the `Box`. ``` `Box` units, like width, height and margins can be defined in two ways: + - By plain numbers (1 unit equals `0.5em`); - In absolute measures, by providing a full unit string (e.g. `100px`). @@ -238,8 +239,6 @@ Props: - `height: number` - Box height. - `minHeight: number` - Box minimum height. - `maxHeight: number` - Box maximum height. -- `fontSize: number` - Font size. -- `fontFamily: string` - Font family. - `lineHeight: number` - Directly affects the height of text lines. Useful for adjusting button height. - `inline: boolean` - Forces the `Box` to appear as an `inline-block`, @@ -255,7 +254,6 @@ all available horizontal space. - `opacity: number` - Opacity, from 0 to 1. - `bold: boolean` - Make text bold. - `italic: boolean` - Make text italic. -- `nowrap: boolean` - Stops text from wrapping. - `textAlign: string` - Align text inside the box. - `left` (default) - `center` @@ -295,34 +293,11 @@ over the button. - `bottom` (default) - Show tooltip below the button. - `left` - Show tooltip on the left of the button. - `right` - Show tooltip on the right of the button. -- `ellipsis: boolean` - If button width is constrained, button text will -be truncated with an ellipsis. Be careful however, because this prop breaks -the baseline alignment. - `title: string` - A native browser tooltip, which appears when hovering over the button. - `content/children: any` - Content to render inside the button. - `onClick: function` - Called when element is clicked. -### `Button.Checkbox` - -A ghetto checkbox, made entirely using existing Button API. - -Props: - -- See inherited props: [Button](#button) -- `checked: boolean` - Boolean value, which marks the checkbox as checked. - -### `Collapsible` - -Displays contents when open, acts as a fluid button when closed. Click to toggle, closed by default. - -Props: - - See inherited props: [Box](#box) - - `children: any` - What is collapsed when closed - - `title: string` - Text to display on the button for collapsing - - `color: string` - Color of the button; see [Button](#button) - - `buttons: any` - Buttons or other content to render inline with the button - ### `ColorBox` Displays a 1-character wide colored square. Can be used as a status indicator, @@ -344,21 +319,6 @@ Props: - See inherited props: [Box](#box) -### `Dropdown` - -A simple dropdown box component. Lets the user select from a list of options and displays selected entry. - -Props: - - - See inherited props: [Box](#box) - - `options: string[]` - An array of strings which will be displayed in the dropdown when open - - `selected: string` - Currently selected entry - - `width: number` - Width of dropdown button and resulting menu - - `over: boolean` - dropdown renders over instead of below - - `color: string` - color of dropdown button - - `onClick: (e) => void` - Called when dropdown button is clicked - - `onSet: (e, value) => void` - Called when a value is picked from the list, `value` is the value that was picked - ### `Flex` Quickly manage the layout, alignment, and sizing of grid columns, navigation, components, and more with a full suite of responsive flexbox utilities. @@ -390,10 +350,6 @@ two flex items as far as possible from each other. Props: - See inherited props: [Box](#box) -- `spacing: number` - Spacing between flex items, in integer units -(1 unit - 0.5em). Does not directly relate to a flex css property -(adds a modifier class under the hood), and only integer numbers are -supported. - `direction: string` - This establishes the main-axis, thus defining the direction flex items are placed in the flex container. - `row` (default) - left to right. @@ -450,7 +406,6 @@ remaining space is distributed. It can be a length (e.g. `20%`, `5rem`, etc.), an `auto` or `content` keyword. - `align: string` - This allows the default alignment (or the one specified by align-items) to be overridden for individual flex items. See: [Flex](#flex). - ### `Grid` Helps you to divide horizontal space into two or more equal sections. @@ -477,7 +432,7 @@ Props: Props: -- See inherited props: [Table.Cell](#tablecell) +- See inherited props: [Table.Cell](#table-cell) - `size: number` (default: 1) - Size of the column relative to other columns. ### `Icon` @@ -490,6 +445,7 @@ Renders one of the FontAwesome icons of your choice. To smoothen the transition from v4 to v5, we have added a v4 semantic to transform names with `-o` suffixes to FA Regular icons. For example: + - `square` will get transformed to `fas square` - `square-o` will get transformed to `far square` @@ -514,7 +470,6 @@ Props: - See inherited props: [Box](#box) - `value: string` - Value of an input. -- `placeholder: string` - Text placed into Input box when value is otherwise nothing. Clears automatically when focused. - `fluid: boolean` - Fill all available horizontal space. - `onChange: (e, value) => void` - An event, which fires when you commit the text by either unfocusing the input box, or by pressing the Enter key. diff --git a/tgui-next/packages/tgui/interfaces/Cargo.js b/tgui-next/packages/tgui/interfaces/Cargo.js index 6df2c13955..b7a1a0f385 100644 --- a/tgui-next/packages/tgui/interfaces/Cargo.js +++ b/tgui-next/packages/tgui/interfaces/Cargo.js @@ -10,27 +10,6 @@ export const Cargo = props => { const { ref } = config; const supplies = data.supplies || {}; const requests = data.requests || []; - const cart = data.cart || []; - - const cartTotalAmount = cart - .reduce((total, entry) => total + entry.cost, 0); - - const cartButtons = !data.requestonly && ( - - - {cart.length === 0 && 'Cart is empty'} - {cart.length === 1 && '1 item'} - {cart.length >= 2 && cart.length + ' items'} - {' '} - {cartTotalAmount > 0 && `(${cartTotalAmount} cr)`} - -