diff --git a/code/__defines/obj.dm b/code/__defines/obj.dm index a2b6ac18afa..698102a16f7 100644 --- a/code/__defines/obj.dm +++ b/code/__defines/obj.dm @@ -1,5 +1,6 @@ #define OBJ_FLAG_ROTATABLE (1<<1) //Can this object be rotated? #define OBJ_FLAG_ROTATABLE_ANCHORED (1<<2) // This object can be rotated even while anchored +#define OBJ_FLAG_SIGNALER (1<<3) // Can this take a signaler? only in use for machinery /obj/proc/iswrench() return FALSE diff --git a/code/_helpers/logging.dm b/code/_helpers/logging.dm index 55a08f5e634..6bae7006b25 100644 --- a/code/_helpers/logging.dm +++ b/code/_helpers/logging.dm @@ -50,6 +50,14 @@ game_log("ADMIN", text) send_gelf_log(short_message=text, long_message="[time_stamp()]: [text]",level=level,category="ADMIN",additional_data=list("_ckey"=html_encode(ckey),"_admin_key"=html_encode(admin_key),"_ckey_target"=html_encode(ckey_target))) +/proc/log_signal(var/text) + if(length(signal_log) >= 100) + signal_log.Cut(1, 2) + signal_log.Add("|[time_stamp()]| [text]") + if(config.log_signaler) + game_log("SIGNALER", text) + send_gelf_log(short_message=text, long_message="[time_stamp()]: [text]",level=SEVERITY_NOTICE,category="SIGNALER") + /proc/log_debug(text,level = SEVERITY_DEBUG) if (config.log_debug) game_log("DEBUG", text) diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 91714717f02..38029204416 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -8,6 +8,7 @@ var/list/gamemode_cache = list() var/log_access = 0 // log login/logout var/log_say = 0 // log client say var/log_admin = 0 // log admin actions + var/log_signaler = 0 // log signaler actions var/log_debug = 1 // log debug output var/log_game = 0 // log game events var/log_vote = 0 // log voting @@ -405,6 +406,9 @@ var/list/gamemode_cache = list() if ("log_admin") config.log_admin = 1 + if ("log_signaler") + config.log_signaler = 1 + if ("log_debug") config.log_debug = text2num(value) diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index a00b498ce03..0f53287bbac 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -11,6 +11,7 @@ density = 1 var/health = 100.0 flags = CONDUCT + obj_flags = OBJ_FLAG_SIGNALER w_class = ITEMSIZE_HUGE var/valve_open = 0 @@ -131,6 +132,9 @@ else update_flag |= 32 + if(signaler) + update_flag |= 64 + if(update_flag == old_flag) return 1 else @@ -162,6 +166,9 @@ update_flag cut_overlays() set_light(FALSE) + if(signaler) + add_overlay("signaler") + if(update_flag & 1) add_overlay("can-open") if(update_flag & 2) @@ -182,7 +189,6 @@ update_flag var/mutable_appearance/indicator_overlay = mutable_appearance(icon, "can-o3", EFFECTS_ABOVE_LIGHTING_LAYER) add_overlay(indicator_overlay) set_light(1.4, 1, COLOR_BRIGHT_GREEN) - return /obj/machinery/portable_atmospherics/canister/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) if(exposed_temperature > temperature_resistance) @@ -191,21 +197,25 @@ update_flag /obj/machinery/portable_atmospherics/canister/proc/healthcheck() if(destroyed) - return 1 + return TRUE if (src.health <= 10) var/atom/location = src.loc location.assume_air(air_contents) - src.destroyed = 1 + destroyed = TRUE + obj_flags &= ~OBJ_FLAG_SIGNALER playsound(src.loc, 'sound/effects/spray.ogg', 10, 1, -3) - src.density = 0 - update_icon() + density = FALSE if (src.holding) src.holding.forceMove(src.loc) src.holding = null + detach_signaler() + + update_icon() + return 1 else return 1 @@ -285,12 +295,17 @@ update_flag valve_open = !valve_open /obj/machinery/portable_atmospherics/canister/attackby(var/obj/item/W as obj, var/mob/user as mob) - if(!W.iswrench() && !istype(W, /obj/item/tank) && !istype(W, /obj/item/device/analyzer) && !istype(W, /obj/item/modular_computer)) - visible_message("\The [user] hits \the [src] with \a [W]!") + if(!W.iswrench() && !istype(W, /obj/item/tank) && !istype(W, /obj/item/device/analyzer) && !istype(W, /obj/item/modular_computer) && !issignaler(W) && !(W.iswirecutter() && signaler)) + if(W.flags & NOBLUDGEON) + return + visible_message(SPAN_WARNING("\The [user] hits \the [src] with \the [W]!"), SPAN_NOTICE("You hit \the [src] with \the [W].")) + user.do_attack_animation(src, W) + playsound(src, 'sound/weapons/smash.ogg', 60, 1) src.health -= W.force if(!istype(W, /obj/item/forensics)) src.add_fingerprint(user) healthcheck() + return if(istype(user, /mob/living/silicon/robot) && istype(W, /obj/item/tank/jetpack)) var/datum/gas_mixture/thejetpack = W:air_contents @@ -307,6 +322,7 @@ update_flag ..() + update_icon() SSnanoui.update_uis(src) // Update all NanoUIs attached to src /obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user as mob) @@ -415,6 +431,11 @@ update_flag return 1 +/obj/machinery/portable_atmospherics/canister/do_signaler() + valve_open = !valve_open + if(valve_open) + log_open_userless("a signaler") + /obj/machinery/portable_atmospherics/canister/phoron/Initialize() . = ..() diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm index 286f07dfade..58357409a9c 100644 --- a/code/game/machinery/atmoalter/portable_atmospherics.dm +++ b/code/game/machinery/atmoalter/portable_atmospherics.dm @@ -133,8 +133,7 @@ A.analyze_gases(src, user) return - return - + return ..() /obj/machinery/portable_atmospherics/powered @@ -195,3 +194,9 @@ log_admin("[user] ([user.ckey]) opened '[src.name]' containing [gases].", ckey=key_name(user)) message_admins("[key_name_admin(user)] opened '[src.name]' containing [gases]. (JMP)") + +/obj/machinery/portable_atmospherics/proc/log_open_userless(var/cause) + if(air_contents.gas.len == 0) + return + + message_admins("'[src.name]' was opened[cause ? " by [cause]" : ""], containing [english_list(air_contents.gas)]. (JMP)") \ No newline at end of file diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index 19161455e55..d454dcbe550 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -164,6 +164,11 @@ Class Procs: return ..() +/obj/machinery/examine(mob/user) + . = ..() + if(signaler && Adjacent(user)) + to_chat(user, SPAN_WARNING("\The [src] has a hidden signaler attached to it.")) + /obj/machinery/proc/machinery_process() //If you dont use process or power why are you here if(!(use_power || idle_power_usage || active_power_usage)) return PROCESS_KILL @@ -280,6 +285,44 @@ Class Procs: return ..() +/obj/machinery/attackby(obj/item/W, mob/user) + if(obj_flags & OBJ_FLAG_SIGNALER) + if(issignaler(W)) + if(signaler) + to_chat(user, SPAN_WARNING("\The [src] already has a signaler attached.")) + return + var/obj/item/device/assembly/signaler/S = W + user.drop_from_inventory(W, src) + signaler = S + S.machine = src + user.visible_message("[user] attaches \the [S] to \the [src].", SPAN_NOTICE("You attach \the [S] to \the [src]."), range = 3) + log_and_message_admins("has attached a signaler to \the [src].", user, get_turf(src)) + return + else if(W.iswirecutter() && signaler) + user.visible_message("[user] removes \the [signaler] from \the [src].", SPAN_NOTICE("You remove \the [signaler] from \the [src]."), range = 3) + user.put_in_hands(detach_signaler()) + return + + return ..() + +/obj/machinery/proc/detach_signaler(var/turf/detach_turf) + if(!signaler) + return + + if(!detach_turf) + detach_turf = get_turf(src) + if(!detach_turf) + log_debug("[src] tried to drop a signaler, but it had no turf ([src.x]-[src.y]-[src.z])") + return + + var/obj/item/device/assembly/signaler/S = signaler + + signaler.forceMove(detach_turf) + signaler.machine = null + signaler = null + + return S + /obj/machinery/proc/RefreshParts() /obj/machinery/proc/assign_uid() diff --git a/code/global.dm b/code/global.dm index ec92e970fed..a442f7ebfb3 100644 --- a/code/global.dm +++ b/code/global.dm @@ -40,6 +40,7 @@ var/host = null //only here until check @ code\modules\ghosttrap\trap.dm:112 is var/list/jobMax = list() var/list/bombers = list() var/list/admin_log = list() +var/list/signal_log = list() var/list/lastsignalers = list() // Keeps last 100 signals here in format: "[src] used \ref[src] @ location [src.loc]: [freq]/[code]" var/list/lawchanges = list() // Stores who uploaded laws to which silicon-based lifeform, and what the law was. var/list/reg_dna = list() diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index e6f82a4c239..c381f73e6cb 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -30,6 +30,7 @@ var/list/admin_verbs_admin = list( /datum/admins/proc/access_news_network, /*allows access of newscasters*/ /client/proc/giveruntimelog, /*allows us to give access to runtime logs to somebody*/ /client/proc/getserverlog, /*allows us to fetch server logs (diary) for other days*/ + /client/proc/view_signal_log, /*allows admins to check the log of signaler uses*/ /client/proc/jumptocoord, /*we ghost and jump to a coordinate*/ /client/proc/Getmob, /*teleports a mob to our location*/ /client/proc/Getkey, /*teleports a mob with a certain ckey to our location*/ diff --git a/code/modules/admin/verbs/getlogs.dm b/code/modules/admin/verbs/getlogs.dm index 731fa95b71b..0e6af92039a 100644 --- a/code/modules/admin/verbs/getlogs.dm +++ b/code/modules/admin/verbs/getlogs.dm @@ -78,3 +78,16 @@ //Other log stuff put here for the sake of organisation + +/client/proc/view_signal_log() + set name = "View Signaler Log" + set desc = "Use this to view who sent signaler signals to things." + set category = "Admin" + + var/text_signal_log = "" + for(var/log in signal_log) + text_signal_log += "[log]
" + + var/datum/browser/signal_win = new(usr, "signallog", "Signal Log", 550, 500) + signal_win.set_content(text_signal_log) + signal_win.open() \ No newline at end of file diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm index 7e90e173a30..1f56ed070e8 100644 --- a/code/modules/assembly/signaler.dm +++ b/code/modules/assembly/signaler.dm @@ -67,12 +67,12 @@ if(href_list["send"]) spawn( 0 ) - signal() + signal(usr) var/datum/vueui/ui = SSvueui.get_open_ui(usr, src) ui.check_for_change() -/obj/item/device/assembly/signaler/proc/signal() +/obj/item/device/assembly/signaler/proc/signal(var/mob/user) if(!radio_connection) return if(within_jamming_range(src)) @@ -81,6 +81,8 @@ signal.source = src signal.encryption = code signal.data["message"] = "ACTIVATE" + if(user) + signal.data["user"] = WEAKREF(user) radio_connection.post_signal(src, signal) return @@ -111,6 +113,15 @@ pulse(TRUE) if(machine) + var/found_user = FALSE + if(signal.data["user"]) + var/datum/weakref/user_ref = signal.data["user"] + var/mob/user = user_ref.resolve() + if(user) + found_user = TRUE + log_signal("[key_name(user)] has sent a signal to [machine.name] ([machine.x]-[machine.y]-[machine.z])") + if(!found_user) + log_signal("A userless signaler has sent a signal to [machine.name] ([machine.x]-[machine.y]-[machine.z])") machine.audible_message("[icon2html(machine, viewers(get_turf(machine)))] [capitalize_first_letters(src.name)] beeps, \"Beep beep!\"") else if(!holder) audible_message("[icon2html(src, viewers(get_turf(src)))] [capitalize_first_letters(src.name)] beeps, \"Beep beep!\"") @@ -154,7 +165,7 @@ if(use_check_and_message(user)) return to_chat(user, SPAN_NOTICE("You click \the [src]'s signal button.")) - signal() + signal(user) /obj/item/device/assembly/signaler/proc/deadman_trigger(var/mob/user) if(deadman) //If its not activated, there is no point in triggering it diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index d53ba8fc0cf..05307743f43 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -11,7 +11,7 @@ anchored = FALSE density = TRUE req_access = list(access_engine_equip) - obj_flags = OBJ_FLAG_ROTATABLE + obj_flags = OBJ_FLAG_ROTATABLE | OBJ_FLAG_SIGNALER var/id use_power = 0 //uses powernet power, not APC power @@ -47,8 +47,6 @@ to_chat(user, SPAN_WARNING("\The [src] is bolted and welded to the floor, and ready to fire.")) if(Adjacent(user)) to_chat(user, SPAN_NOTICE("The shot counter display reads: [shot_counter]")) - if(signaler) - to_chat(user, SPAN_WARNING("\The [src] has a hidden signaler attached to it.")) /obj/machinery/power/emitter/Destroy() if(special_emitter) @@ -163,21 +161,6 @@ shot_counter++ /obj/machinery/power/emitter/attackby(obj/item/W, mob/user) - if(istype(W, /obj/item/device/assembly/signaler)) - var/obj/item/device/assembly/signaler/S = W - user.drop_from_inventory(W, src) - signaler = S - S.machine = src - user.visible_message(SPAN_WARNING("\The [user] attaches \the [S] to \the [src]."), - SPAN_NOTICE("You attach \the [S] to \the [src].")) - return - if(W.iswirecutter() && signaler) - signaler.forceMove(get_turf(user)) - signaler.machine = null - user.visible_message(SPAN_WARNING("\The [user] removes \the [signaler] from \the [src]."), - SPAN_NOTICE("You remove \the [signaler] to \the [src].")) - signaler = null - return if(W.iswrench()) if(active) to_chat(user, SPAN_WARNING("You cannot unbolt \the [src] while it's active.")) diff --git a/config/example/config.txt b/config/example/config.txt index 2d0bf2de89e..47d71d6088c 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -54,6 +54,9 @@ LOG_SAY ## log admin actions LOG_ADMIN +## log signaler actions +LOG_SIGNALER + ## log client access (logon/logoff) LOG_ACCESS diff --git a/html/changelogs/geeves-remote_canisters.yml b/html/changelogs/geeves-remote_canisters.yml new file mode 100644 index 00000000000..390e01bd9db --- /dev/null +++ b/html/changelogs/geeves-remote_canisters.yml @@ -0,0 +1,7 @@ +author: Geeves + +delete-after: True + +changes: + - rscadd: "You can now attach signalers to canisters. When activated, the signaler will toggle the valve open or shut." + - tweak: "Attacking a canister now has a sound and an animation." \ No newline at end of file diff --git a/icons/obj/atmos.dmi b/icons/obj/atmos.dmi index 5e8d5a28a07..7ddebb5e747 100644 Binary files a/icons/obj/atmos.dmi and b/icons/obj/atmos.dmi differ