diff --git a/code/WorkInProgress/Cael_Aislinn/Jungle/jungle_turfs.dm b/code/WorkInProgress/Cael_Aislinn/Jungle/jungle_turfs.dm index e839a5ed..86840da3 100644 --- a/code/WorkInProgress/Cael_Aislinn/Jungle/jungle_turfs.dm +++ b/code/WorkInProgress/Cael_Aislinn/Jungle/jungle_turfs.dm @@ -119,6 +119,7 @@ icon = 'icons/misc/beach.dmi' icon_state = "water" icon_spawn_state = "water" + footstep_sound = "waterstep" /turf/unsimulated/jungle/water/New() ..() diff --git a/code/ZAS/Fire.dm b/code/ZAS/Fire.dm index 56ba7196..60866007 100644 --- a/code/ZAS/Fire.dm +++ b/code/ZAS/Fire.dm @@ -128,6 +128,13 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh) for(var/atom/A in loc) A.fire_act(air_contents, air_contents.temperature, air_contents.volume) + //Recheck for turf as it could change since the last check + if(!istype(my_tile) || !my_tile.zone) + if(my_tile.fire == src) + my_tile.fire = null + RemoveFire() + return 1 + //spread for(var/direction in cardinal) var/turf/simulated/enemy_tile = get_step(my_tile, direction) diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index ec17215b..23807505 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -9,6 +9,12 @@ src:Topic(href, href_list) return null +/proc/get_area_master(const/O) + var/area/A = get_area(O) + + if (isarea(A)) + return A.master + /proc/get_area(O) var/atom/location = O var/i @@ -46,6 +52,21 @@ return heard +/proc/isStationLevel(var/level) + return level in config.station_levels + +///proc/isNotStationLevel(var/level) //Wait how dumb are you people +// return !isStationLevel(level) + +/proc/isPlayerLevel(var/level) + return level in config.player_levels + +/proc/isAdminLevel(var/level) + return level in config.admin_levels + +///proc/isNotAdminLevel(var/level) //Wait how dumb are you people +// return !isAdminLevel(level) + //Magic constants obtained by using linear regression on right-angled triangles of sides 0[text]\nType vote to place your votes.\nYou have [config.vote_period/10] seconds to vote." - switch(vote_type) - if("crew_transfer") - world << sound('sound/ambience/alarm4.ogg') - if("gamemode") - world << sound('sound/ambience/alarm4.ogg') - if("custom") - world << sound('sound/ambience/alarm4.ogg') + for(var/client/C in clients) + if(C.prefs.asfx_togs & ASFX_VOTE) //Personal mute + C << sound('sound/effects/vote.ogg') if(mode == "gamemode" && going) going = 0 world << "Round start has been delayed." diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm new file mode 100644 index 00000000..007917de --- /dev/null +++ b/code/datums/wires/airlock.dm @@ -0,0 +1,174 @@ +// Wires for airlocks + +/datum/wires/airlock/secure + random = 1 + +/datum/wires/airlock + holder_type = /obj/machinery/door/airlock + wire_count = 12 + window_y = 570 + +var/const/AIRLOCK_WIRE_IDSCAN = 1 +var/const/AIRLOCK_WIRE_MAIN_POWER1 = 2 +var/const/AIRLOCK_WIRE_MAIN_POWER2 = 4 +var/const/AIRLOCK_WIRE_DOOR_BOLTS = 8 +var/const/AIRLOCK_WIRE_BACKUP_POWER1 = 16 +var/const/AIRLOCK_WIRE_BACKUP_POWER2 = 32 +var/const/AIRLOCK_WIRE_OPEN_DOOR = 64 +var/const/AIRLOCK_WIRE_AI_CONTROL = 128 +var/const/AIRLOCK_WIRE_ELECTRIFY = 256 +var/const/AIRLOCK_WIRE_SAFETY = 512 +var/const/AIRLOCK_WIRE_SPEED = 1024 +var/const/AIRLOCK_WIRE_LIGHT = 2048 + +/datum/wires/airlock/CanUse(var/mob/living/L) + var/obj/machinery/door/airlock/A = holder + if(!istype(L, /mob/living/silicon)) + if(A.isElectrified()) + if(A.shock(L, 100)) + return 0 + if(A.p_open) + return 1 + return 0 + +/datum/wires/airlock/GetInteractWindow() + var/obj/machinery/door/airlock/A = holder + var/haspower = A.arePowerSystemsOn() + . += ..() + . += text("
\n[]
\n[]
\n[]
\n[]
\n[]
\n[]
\n[]", + (A.locked ? "The door bolts have fallen!" : "The door bolts look up."), + ((A.lights && haspower) ? "The door bolt lights are on." : "The door bolt lights are off!"), + ((haspower) ? "The test light is on." : "The test light is off!"), + ((A.aiControlDisabled==0 && !A.emagged && haspower) ? "The 'AI control allowed' light is on." : "The 'AI control allowed' light is off."), + ((A.safe==0 && haspower) ? "The 'Check Wiring' light is on." : "The 'Check Wiring' light is off."), + ((A.normalspeed==0 && haspower) ? "The 'Check Timing Mechanism' light is on." : "The 'Check Timing Mechanism' light is off."), + ((A.emergency && haspower) ? "The emergency lights are on." : "The emergency lights are off.")) + +/datum/wires/airlock/UpdateCut(var/index, var/mended) + + var/obj/machinery/door/airlock/A = holder + switch(index) + if(AIRLOCK_WIRE_IDSCAN) + A.aiDisabledIdScanner = !mended + if(AIRLOCK_WIRE_MAIN_POWER1, AIRLOCK_WIRE_MAIN_POWER2) + + if(!mended) + //Cutting either one disables the main door power, but unless backup power is also cut, the backup power re-powers the door in 10 seconds. While unpowered, the door may be crowbarred open, but bolts-raising will not work. Cutting these wires may electocute the user. + A.loseMainPower() + A.shock(usr, 50) + else + A.regainMainPower() + A.shock(usr, 50) + + if(AIRLOCK_WIRE_BACKUP_POWER1, AIRLOCK_WIRE_BACKUP_POWER2) + + if(!mended) + //Cutting either one disables the backup door power (allowing it to be crowbarred open, but disabling bolts-raising), but may electocute the user. + A.loseBackupPower() + A.shock(usr, 50) + else + A.regainBackupPower() + A.shock(usr, 50) + + if(AIRLOCK_WIRE_DOOR_BOLTS) + + if(!mended) + //Cutting this wire also drops the door bolts, and mending it does not raise them. (This is what happens now, except there are a lot more wires going to door bolts at present) + A.lock(1) + A.update_icon() + + if(AIRLOCK_WIRE_AI_CONTROL) + + if(!mended) + //one wire for AI control. Cutting this prevents the AI from controlling the door unless it has hacked the door through the power connection (which takes about a minute). If both main and backup power are cut, as well as this wire, then the AI cannot operate or hack the door at all. + //aiControlDisabled: If 1, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in. + if(A.aiControlDisabled == 0) + A.aiControlDisabled = 1 + else if(A.aiControlDisabled == -1) + A.aiControlDisabled = 2 + else + if(A.aiControlDisabled == 1) + A.aiControlDisabled = 0 + else if(A.aiControlDisabled == 2) + A.aiControlDisabled = -1 + + if(AIRLOCK_WIRE_ELECTRIFY) + if(!mended) + //Cutting this wire electrifies the door, so that the next person to touch the door without insulated gloves gets electrocuted. + A.electrify(-1) + else + A.electrify(0) + return // Don't update the dialog. + + if (AIRLOCK_WIRE_SAFETY) + A.safe = mended + + if(AIRLOCK_WIRE_SPEED) + A.autoclose = mended + if(mended) + if(!A.density) + A.close() + + if(AIRLOCK_WIRE_LIGHT) + A.lights = mended + A.update_icon() + +/datum/wires/airlock/UpdatePulsed(var/index) + + var/obj/machinery/door/airlock/A = holder + switch(index) + if(AIRLOCK_WIRE_IDSCAN) + //Sending a pulse through flashes the red light on the door (if the door has power). + if(A.arePowerSystemsOn() && A.density) + A.do_animate("deny") + if(A.emergency) + A.emergency = 0 + A.update_icon() + if(AIRLOCK_WIRE_MAIN_POWER1 || AIRLOCK_WIRE_MAIN_POWER2) + //Sending a pulse through either one causes a breaker to trip, disabling the door for 10 seconds if backup power is connected, or 1 minute if not (or until backup power comes back on, whichever is shorter). + A.loseMainPower() + if(AIRLOCK_WIRE_DOOR_BOLTS) + //one wire for door bolts. Sending a pulse through this drops door bolts if they're not down (whether power's on or not), + //raises them if they are down (only if power's on) + if(!A.locked) + A.lock() + else + A.unlock() + + if(AIRLOCK_WIRE_BACKUP_POWER1 || AIRLOCK_WIRE_BACKUP_POWER2) + //two wires for backup power. Sending a pulse through either one causes a breaker to trip, but this does not disable it unless main power is down too (in which case it is disabled for 1 minute or however long it takes main power to come back, whichever is shorter). + A.loseBackupPower() + if(AIRLOCK_WIRE_AI_CONTROL) + if(A.aiControlDisabled == 0) + A.aiControlDisabled = 1 + else if(A.aiControlDisabled == -1) + A.aiControlDisabled = 2 + + spawn(10) + if(A) + if(A.aiControlDisabled == 1) + A.aiControlDisabled = 0 + else if(A.aiControlDisabled == 2) + A.aiControlDisabled = -1 + + if(AIRLOCK_WIRE_ELECTRIFY) + //one wire for electrifying the door. Sending a pulse through this electrifies the door for 30 seconds. + A.electrify(30) + if(AIRLOCK_WIRE_OPEN_DOOR) + //tries to open the door without ID + //will succeed only if the ID wire is cut or the door requires no access and it's not emagged + if(A.emagged) return + if(!A.requiresID() || A.check_access(null)) + if(A.density) A.open() + else A.close() + if(AIRLOCK_WIRE_SAFETY) + A.safe = !A.safe + if(!A.density) + A.close() + + if(AIRLOCK_WIRE_SPEED) + A.normalspeed = !A.normalspeed + + if(AIRLOCK_WIRE_LIGHT) + A.lights = !A.lights + A.update_icon() \ No newline at end of file diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm new file mode 100644 index 00000000..d62558de --- /dev/null +++ b/code/datums/wires/wires.dm @@ -0,0 +1,297 @@ +// Wire datums. Created by Giacomand. +// Was created to replace a horrible case of copy and pasted code with no care for maintability. +// Goodbye Door wires, Cyborg wires, Vending Machine wires, Autolathe wires +// Protolathe wires, APC wires and Camera wires! + +#define MAX_FLAG 65535 + +var/list/same_wires = list() +// 12 colours, if you're adding more than 12 wires then add more colours here +var/list/wireColours = list("red", "blue", "green", "black", "orange", "brown", "gold", "gray", "cyan", "navy", "purple", "pink") + +/datum/wires + + var/random = 0 // Will the wires be different for every single instance. + var/atom/holder = null // The holder + var/holder_type = null // The holder type; used to make sure that the holder is the correct type. + var/wire_count = 0 // Max is 16 + var/wires_status = 0 // BITFLAG OF WIRES + + var/list/wires = list() + var/list/signallers = list() + + var/table_options = " align='center'" + var/row_options1 = " width='80px'" + var/row_options2 = " width='260px'" + var/window_x = 370 + var/window_y = 470 + +/datum/wires/New(var/atom/holder) + ..() + src.holder = holder + if(!istype(holder, holder_type)) + CRASH("Our holder is null/the wrong type!") + return + + // Generate new wires + if(random) + GenerateWires() + // Get the same wires + else + // We don't have any wires to copy yet, generate some and then copy it. + if(!same_wires[holder_type]) + GenerateWires() + same_wires[holder_type] = src.wires.Copy() + else + var/list/wires = same_wires[holder_type] + src.wires = wires // Reference the wires list. + +/datum/wires/proc/GenerateWires() + var/list/colours_to_pick = wireColours.Copy() // Get a copy, not a reference. + var/list/indexes_to_pick = list() + //Generate our indexes + for(var/i = 1; i < MAX_FLAG && i < (1 << wire_count); i += i) + indexes_to_pick += i + colours_to_pick.len = wire_count // Downsize it to our specifications. + + while(colours_to_pick.len && indexes_to_pick.len) + // Pick and remove a colour + var/colour = pick_n_take(colours_to_pick) + + // Pick and remove an index + var/index = pick_n_take(indexes_to_pick) + + src.wires[colour] = index + //wires = shuffle(wires) + + +/datum/wires/proc/Interact(var/mob/living/user) + + var/html = null + if(holder && CanUse(user)) + html = GetInteractWindow() + if(html) + user.set_machine(holder) + else + user.unset_machine() + // No content means no window. + user << browse(null, "window=wires") + return + var/datum/browser/popup = new(user, "wires", holder.name, window_x, window_y) + popup.set_content(html) + popup.set_title_image(user.browse_rsc_icon(holder.icon, holder.icon_state)) + popup.open() + +/datum/wires/proc/GetInteractWindow() + var/html = \ + {" +
+

Exposed Wires

+ "} + + for(var/colour in wires) + html += "" + html += "[capitalize(colour)]" + html += "" + html += "[IsColourCut(colour) ? "Mend" : "Cut"]" + html += " Pulse" + html += " [IsAttached(colour) ? "Detach" : "Attach"] Signaller" + html += "" + html += "
" + + return html + +/datum/wires/Topic(href, href_list) + ..() + if(in_range(holder, usr) && isliving(usr)) + + var/mob/living/L = usr + if(CanUse(L) && href_list["action"]) + var/obj/item/I = L.get_active_hand() + holder.add_hiddenprint(L) + if(href_list["cut"]) // Toggles the cut/mend status + if(istype(I, /obj/item/weapon/wirecutters)) + var/colour = href_list["cut"] + CutWireColour(colour) + else + L << "You need wirecutters!" + + else if(href_list["pulse"]) + if(istype(I, /obj/item/device/multitool)) + var/colour = href_list["pulse"] + PulseColour(colour) + else + L << "You need a multitool!" + + else if(href_list["attach"]) + var/colour = href_list["attach"] + // Detach + if(IsAttached(colour)) + var/obj/item/O = Detach(colour) + if(O) + L.put_in_hands(O) + + // Attach + else + if(istype(I, /obj/item/device/assembly/signaler)) + L.drop_item() + Attach(colour, I) + else + L << "You need a remote signaller!" + + + + + // Update Window + Interact(usr) + + if(href_list["close"]) + usr << browse(null, "window=wires") + usr.unset_machine(holder) + +// +// Overridable Procs +// + +// Called when wires cut/mended. +/datum/wires/proc/UpdateCut(var/index, var/mended) + return + +// Called when wire pulsed. Add code here. +/datum/wires/proc/UpdatePulsed(var/index) + return + +/datum/wires/proc/CanUse(var/mob/living/L) + return 1 + +// Example of use: +/* +var/const/BOLTED= 1 +var/const/SHOCKED = 2 +var/const/SAFETY = 4 +var/const/POWER = 8 + +/datum/wires/door/UpdateCut(var/index, var/mended) + var/obj/machinery/door/airlock/A = holder + switch(index) + if(BOLTED) + if(!mended) + A.bolt() + if(SHOCKED) + A.shock() + if(SAFETY ) + A.safety() +*/ + + +// +// Helper Procs +// + +/datum/wires/proc/PulseColour(var/colour) + PulseIndex(GetIndex(colour)) + +/datum/wires/proc/PulseIndex(var/index) + if(IsIndexCut(index)) + return + UpdatePulsed(index) + +/datum/wires/proc/GetIndex(var/colour) + if(wires[colour]) + var/index = wires[colour] + return index + else + CRASH("[colour] is not a key in wires.") + +// +// Is Index/Colour Cut procs +// + +/datum/wires/proc/IsColourCut(var/colour) + var/index = GetIndex(colour) + return IsIndexCut(index) + +/datum/wires/proc/IsIndexCut(var/index) + return (index & wires_status) + +// +// Signaller Procs +// + +/datum/wires/proc/IsAttached(var/colour) + if(signallers[colour]) + return 1 + return 0 + +/datum/wires/proc/GetAttached(var/colour) + if(signallers[colour]) + return signallers[colour] + return null + +/datum/wires/proc/Attach(var/colour, var/obj/item/device/assembly/signaler/S) + if(colour && S) + if(!IsAttached(colour)) + signallers[colour] = S + S.loc = holder + S.connected = src + return S + +/datum/wires/proc/Detach(var/colour) + if(colour) + var/obj/item/device/assembly/signaler/S = GetAttached(colour) + if(S) + signallers -= colour + S.connected = null + S.loc = holder.loc + return S + + +/datum/wires/proc/Pulse(var/obj/item/device/assembly/signaler/S) + + for(var/colour in signallers) + if(S == signallers[colour]) + PulseColour(colour) + break + + +// +// Cut Wire Colour/Index procs +// + +/datum/wires/proc/CutWireColour(var/colour) + var/index = GetIndex(colour) + CutWireIndex(index) + +/datum/wires/proc/CutWireIndex(var/index) + if(IsIndexCut(index)) + wires_status &= ~index + UpdateCut(index, 1) + else + wires_status |= index + UpdateCut(index, 0) + +/datum/wires/proc/RandomCut() + var/r = rand(1, wires.len) + CutWireIndex(r) + +/datum/wires/proc/CutAll() + for(var/i = 1; i < MAX_FLAG && i < (1 << wire_count); i += i) + CutWireIndex(i) + +/datum/wires/proc/IsAllCut() + if(wires_status == (1 << wire_count) - 1) + return 1 + return 0 + +// +//Shuffle and Mend +// + +/datum/wires/proc/Shuffle() + wires_status = 0 + GenerateWires() \ No newline at end of file diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 49ce3970..ef201cd0 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -315,7 +315,7 @@ L.lastarea = newarea // Ambience goes down here -- make sure to list each area seperately for ease of adding things in later, thanks! Note: areas adjacent to each other should have the same sounds to prevent cutoff when possible.- LastyScratch - if(!(L && L.client && (L.client.prefs.toggles & SOUND_AMBIENCE))) return + if(!(L && L.client && (L.client.prefs.asfx_togs & ASFX_AMBIENCE))) return if(!L.client.ambience_playing) L.client.ambience_playing = 1 diff --git a/code/game/gamemodes/factions.dm b/code/game/gamemodes/factions.dm index 4f778dea..78c953d1 100644 --- a/code/game/gamemodes/factions.dm +++ b/code/game/gamemodes/factions.dm @@ -143,6 +143,7 @@ Stealth and Camouflage Items; /obj/item/device/chameleon:4:Chameleon-Projector; Whitespace:Seperator; Devices and Tools; +/obj/item/shoe_cover:1:Silent Step Fabric; /obj/item/weapon/card/emag:3:Cryptographic Sequencer; /obj/item/weapon/storage/toolbox/syndicate:1:Fully Loaded Toolbox; /obj/item/weapon/storage/box/syndie_kit/space:3:Space Suit; diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 72e8aa0b..fd586d33 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -58,6 +58,7 @@ Stealth and Camouflage Items; /obj/item/device/chameleon:4:Chameleon-Projector; Whitespace:Seperator; Devices and Tools; +/obj/item/shoe_cover:1:Silent Step Fabric; /obj/item/weapon/card/emag:3:Cryptographic Sequencer; /obj/item/weapon/storage/toolbox/syndicate:1:Fully Loaded Toolbox; /obj/item/weapon/storage/box/syndie_kit/space:3:Space Suit; diff --git a/code/game/machinery/computer/HolodeckControl.dm b/code/game/machinery/computer/HolodeckControl.dm index 8f35e3b9..5d6cae86 100644 --- a/code/game/machinery/computer/HolodeckControl.dm +++ b/code/game/machinery/computer/HolodeckControl.dm @@ -386,6 +386,7 @@ name = "Lush Grass" icon_state = "grass1" floor_tile = new/obj/item/stack/tile/grass + footstep_sound = "grassstep" New() floor_tile.New() //I guess New() isn't run on objects spawned without the definition of a turf to house them, ah well. @@ -406,6 +407,7 @@ name = "desert sand" desc = "Uncomfortably gritty for a hologram." icon_state = "asteroid" + footstep_sound = "gravelstep" /turf/simulated/floor/holofloor/desert/New() ..() diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index 33d53089..44782855 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -6,11 +6,10 @@ idle_power_usage = 250 active_power_usage = 500 circuit = "/obj/item/weapon/circuitboard/crew" - var/list/tracked = list( ) - + var/obj/nano_module/crew_monitor/crew_monitor /obj/machinery/computer/crew/New() - tracked = list() + crew_monitor = new(src) ..() @@ -25,6 +24,8 @@ return ui_interact(user) +/obj/machinery/computer/crew/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + crew_monitor.ui_interact(user, ui_key, ui, force_open) /obj/machinery/computer/crew/update_icon() @@ -38,97 +39,5 @@ icon_state = initial(icon_state) stat &= ~NOPOWER - -/obj/machinery/computer/crew/Topic(href, href_list) - if(..()) return - if (src.z > 6) - usr << "\red Unable to establish a connection: \black You're too far away from the station!" - return 0 - if( href_list["close"] ) - var/mob/user = usr - var/datum/nanoui/ui = nanomanager.get_open_ui(user, src, "main") - usr.unset_machine() - ui.close() - return 0 - if(href_list["update"]) - src.updateDialog() - return 1 - /obj/machinery/computer/crew/interact(mob/user) - ui_interact(user) - -/obj/machinery/computer/crew/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(stat & (BROKEN|NOPOWER)) - return - user.set_machine(src) - src.scan() - - var/data[0] - var/list/crewmembers = list() - - for(var/obj/item/clothing/under/C in src.tracked) - - - var/turf/pos = get_turf(C) - - if((C) && (C.has_sensor) && (pos) && (pos.z == src.z) && C.sensor_mode) - if(istype(C.loc, /mob/living/carbon/human)) - - var/mob/living/carbon/human/H = C.loc - - var/list/crewmemberData = list() - - crewmemberData["sensor_type"] = C.sensor_mode - crewmemberData["dead"] = H.stat > 1 - crewmemberData["oxy"] = round(H.getOxyLoss(), 1) - crewmemberData["tox"] = round(H.getToxLoss(), 1) - crewmemberData["fire"] = round(H.getFireLoss(), 1) - crewmemberData["brute"] = round(H.getBruteLoss(), 1) - - crewmemberData["name"] = "Unknown" - crewmemberData["rank"] = "Unknown" - if(H.wear_id && istype(H.wear_id, /obj/item/weapon/card/id) ) - var/obj/item/weapon/card/id/I = H.wear_id - crewmemberData["name"] = I.name - crewmemberData["rank"] = I.rank - else if(H.wear_id && istype(H.wear_id, /obj/item/device/pda) ) - var/obj/item/device/pda/P = H.wear_id - crewmemberData["name"] = (P.id ? P.id.name : "Unknown") - crewmemberData["rank"] = (P.id ? P.id.rank : "Unknown") - - var/area/A = get_area(H) - crewmemberData["area"] = sanitize(A.name) - crewmemberData["x"] = pos.x - crewmemberData["y"] = pos.y - - // Works around list += list2 merging lists; it's not pretty but it works - crewmembers += "temporary item" - crewmembers[crewmembers.len] = crewmemberData - - crewmembers = sortByKey(crewmembers, "name") - - data["crewmembers"] = crewmembers - - ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) - if(!ui) - ui = new(user, src, ui_key, "crew_monitor.tmpl", "Crew Monitoring Computer", 900, 800) - - // adding a template with the key "mapContent" enables the map ui functionality - ui.add_template("mapContent", "crew_monitor_map_content.tmpl") - // adding a template with the key "mapHeader" replaces the map header content - ui.add_template("mapHeader", "crew_monitor_map_header.tmpl") - - ui.set_initial_data(data) - ui.open() - - // should make the UI auto-update; doesn't seem to? - ui.set_auto_update(1) - - -/obj/machinery/computer/crew/proc/scan() - for(var/mob/living/carbon/human/H in mob_list) - if(istype(H.w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/C = H.w_uniform - if (C.has_sensor) - tracked |= C - return 1 + crew_monitor.ui_interact(user) diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 4c48faea..8b49259e 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -1,16 +1,3 @@ -#define AIRLOCK_WIRE_IDSCAN 1 -#define AIRLOCK_WIRE_MAIN_POWER1 2 -#define AIRLOCK_WIRE_MAIN_POWER2 3 -#define AIRLOCK_WIRE_DOOR_BOLTS 4 -#define AIRLOCK_WIRE_BACKUP_POWER1 5 -#define AIRLOCK_WIRE_BACKUP_POWER2 6 -#define AIRLOCK_WIRE_OPEN_DOOR 7 -#define AIRLOCK_WIRE_AI_CONTROL 8 -#define AIRLOCK_WIRE_ELECTRIFY 9 -#define AIRLOCK_WIRE_SAFETY 10 -#define AIRLOCK_WIRE_SPEED 11 -#define AIRLOCK_WIRE_LIGHT 12 - /* New methods: pulse - sends a pulse into a wire for hacking purposes @@ -30,47 +17,6 @@ shock - has a chance of electrocuting its target. */ -//This generates the randomized airlock wire assignments for the game. -/proc/RandomAirlockWires() - var/list/wire_assignments = CreateRandomAirlockWires() - - globalAirlockIndexToFlag = wire_assignments[2] - globalAirlockIndexToWireColor = wire_assignments[3] - globalAirlockWireColorToIndex = wire_assignments[4] - return wire_assignments[1] - -/proc/CreateRandomAirlockWires() - //to make this not randomize the wires, just set index to 1 and increment it in the flag for loop (after doing everything else). - var/list/wires = list(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - var/airlockIndexToFlag = list(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - var/airlockIndexToWireColor = list(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - var/airlockWireColorToIndex = list(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - var/flagIndex = 1 - for (var/flag=1, flag<4096, flag+=flag) - var/valid = 0 - var/list/colorList = list(AIRLOCK_WIRE_IDSCAN, AIRLOCK_WIRE_MAIN_POWER1, AIRLOCK_WIRE_MAIN_POWER2, AIRLOCK_WIRE_DOOR_BOLTS, - AIRLOCK_WIRE_BACKUP_POWER1, AIRLOCK_WIRE_BACKUP_POWER2, AIRLOCK_WIRE_OPEN_DOOR, AIRLOCK_WIRE_AI_CONTROL, AIRLOCK_WIRE_ELECTRIFY, - AIRLOCK_WIRE_SAFETY, AIRLOCK_WIRE_SPEED, AIRLOCK_WIRE_LIGHT) - - while (!valid) - var/colorIndex = pick(colorList) - if(wires[colorIndex]==0) - valid = 1 - wires[colorIndex] = flag - airlockIndexToFlag[flagIndex] = flag - airlockIndexToWireColor[flagIndex] = colorIndex - airlockWireColorToIndex[colorIndex] = flagIndex - colorList -= colorIndex - flagIndex+=1 - return list(wires, airlockIndexToFlag, airlockIndexToWireColor, airlockWireColorToIndex) - -/* Example: -Airlock wires color -> flag are { 64, 128, 256, 2, 16, 4, 8, 32, 1 }. -Airlock wires color -> index are { 7, 8, 9, 2, 5, 3, 4, 6, 1 }. -Airlock index -> flag are { 1, 2, 4, 8, 16, 32, 64, 128, 256 }. -Airlock index -> wire color are { 9, 4, 6, 7, 5, 8, 1, 2, 3 }. -*/ - /obj/machinery/door/airlock name = "Airlock" icon = 'icons/obj/doors/Doorint.dmi' @@ -80,21 +26,20 @@ Airlock index -> wire color are { 9, 4, 6, 7, 5, 8, 1, 2, 3 }. explosion_resistance = 15 var/aiControlDisabled = 0 //If 1, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in. var/hackProof = 0 // if 1, this door can't be hacked by the AI - var/secondsMainPowerLost = 0 //The number of seconds until power is restored. - var/secondsBackupPowerLost = 0 //The number of seconds until power is restored. + var/electrified_until = 0 // World time when the door is no longer electrified. -1 if it is permanently electrified until someone fixes it. + var/main_power_lost_until = 0 //World time when main power is restored. + var/backup_power_lost_until = -1 //World time when backup power is restored. var/spawnPowerRestoreRunning = 0 var/welded = null var/locked = 0 var/lights = 1 // bolt lights show by default - var/wires = 4095 - secondsElectrified = 0 //How many seconds remain until the door is no longer electrified. -1 if it is permanently electrified until someone fixes it. + var/datum/wires/airlock/wires = null var/aiDisabledIdScanner = 0 var/aiHacking = 0 var/obj/machinery/door/airlock/closeOther = null var/closeOtherId = null var/list/signalers[12] var/lockdownbyai = 0 - autoclose = 1 var/assembly_type = /obj/structure/door_assembly var/mineral = null var/justzap = 0 @@ -103,10 +48,9 @@ Airlock index -> wire color are { 9, 4, 6, 7, 5, 8, 1, 2, 3 }. var/obj/item/weapon/airlock_electronics/electronics = null var/hasShocked = 0 //Prevents multiple shocks from happening var/secured_wires = 0 //for mapping use - var/list/airlockIndexToFlag - var/list/airlockWireColorToFlag - var/list/airlockIndexToWireColor - var/list/airlockWireColorToIndex + var/door_sound='sound/machines/airlock.ogg' + var/door_sound_distance=30 + autoclose = 1 /obj/machinery/door/airlock/command name = "Airlock" @@ -143,6 +87,8 @@ Airlock index -> wire color are { 9, 4, 6, 7, 5, 8, 1, 2, 3 }. icon = 'icons/obj/doors/Doorglass.dmi' opacity = 0 glass = 1 + door_sound='sound/machines/windowdoor.ogg' + door_sound_distance=100 /obj/machinery/door/airlock/centcom name = "Airlock" @@ -260,6 +206,17 @@ Airlock index -> wire color are { 9, 4, 6, 7, 5, 8, 1, 2, 3 }. mineral = "uranium" var/last_event = 0 +/obj/machinery/door/airlock/process() + // Deliberate no call to parent. + if(main_power_lost_until > 0 && world.time >= main_power_lost_until) + regainMainPower() + + if(backup_power_lost_until > 0 && world.time >= backup_power_lost_until) + regainBackupPower() + + else if(electrified_until > 0 && world.time >= electrified_until) + electrify(0) + /obj/machinery/door/airlock/uranium/process() if(world.time > last_event+20) if(prob(50)) @@ -336,6 +293,7 @@ About the new airlock wires panel: * one wire for controling door safetys. When active, door does not close on someone. When cut, door will ruin someone's shit. When pulsed, door will immedately ruin someone's shit. * one wire for controlling door speed. When active, dor closes at normal rate. When cut, door does not close manually. When pulsed, door attempts to close every tick. */ +// You can find code for the airlock wires in the wire datum folder. @@ -360,176 +318,14 @@ About the new airlock wires panel: /obj/machinery/door/airlock/bumpopen(mob/living/simple_animal/user as mob) ..(user) - -/obj/machinery/door/airlock/proc/pulse(var/wireColor) - //var/wireFlag = airlockWireColorToFlag[wireColor] //not used in this function - var/wireIndex = airlockWireColorToIndex[wireColor] - switch(wireIndex) - if(AIRLOCK_WIRE_IDSCAN) - //Sending a pulse through this flashes the red light on the door (if the door has power). - if((src.arePowerSystemsOn()) && (!(stat & NOPOWER))) - do_animate("deny") - if(AIRLOCK_WIRE_MAIN_POWER1, AIRLOCK_WIRE_MAIN_POWER2) - //Sending a pulse through either one causes a breaker to trip, disabling the door for 10 seconds if backup power is connected, or 1 minute if not (or until backup power comes back on, whichever is shorter). - src.loseMainPower() - if(AIRLOCK_WIRE_DOOR_BOLTS) - //one wire for door bolts. Sending a pulse through this drops door bolts if they're not down (whether power's on or not), - //raises them if they are down (only if power's on) - if(!src.locked) - src.lock() - else - src.unlock() - src.updateUsrDialog() - - if(AIRLOCK_WIRE_BACKUP_POWER1, AIRLOCK_WIRE_BACKUP_POWER2) - //two wires for backup power. Sending a pulse through either one causes a breaker to trip, but this does not disable it unless main power is down too (in which case it is disabled for 1 minute or however long it takes main power to come back, whichever is shorter). - src.loseBackupPower() - if(AIRLOCK_WIRE_AI_CONTROL) - if(src.aiControlDisabled == 0) - src.aiControlDisabled = 1 - else if(src.aiControlDisabled == -1) - src.aiControlDisabled = 2 - src.updateDialog() - spawn(10) - if(src.aiControlDisabled == 1) - src.aiControlDisabled = 0 - else if(src.aiControlDisabled == 2) - src.aiControlDisabled = -1 - src.updateDialog() - if(AIRLOCK_WIRE_ELECTRIFY) - //one wire for electrifying the door. Sending a pulse through this electrifies the door for 30 seconds. - if(src.secondsElectrified==0) - shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])") - usr.attack_log += text("\[[time_stamp()]\] Electrified the [name] at [x] [y] [z]") - src.secondsElectrified = 30 - spawn(10) - //TODO: Move this into process() and make pulsing reset secondsElectrified to 30 - while (src.secondsElectrified>0) - src.secondsElectrified-=1 - if(src.secondsElectrified<0) - src.secondsElectrified = 0 -// src.updateUsrDialog() //Commented this line out to keep the airlock from clusterfucking you with electricity. --NeoFite - sleep(10) - if(AIRLOCK_WIRE_OPEN_DOOR) - //tries to open the door without ID - //will succeed only if the ID wire is cut or the door requires no access - if(!src.requiresID() || src.check_access(null)) - if(density) open() - else close() - if(AIRLOCK_WIRE_SAFETY) - safe = !safe - if(!src.density) - close() - src.updateUsrDialog() - - if(AIRLOCK_WIRE_SPEED) - normalspeed = !normalspeed - src.updateUsrDialog() - - if(AIRLOCK_WIRE_LIGHT) - lights = !lights - src.updateUsrDialog() - - -/obj/machinery/door/airlock/proc/cut(var/wireColor) - var/wireFlag = airlockWireColorToFlag[wireColor] - var/wireIndex = airlockWireColorToIndex[wireColor] - wires &= ~wireFlag - switch(wireIndex) - if(AIRLOCK_WIRE_MAIN_POWER1, AIRLOCK_WIRE_MAIN_POWER2) - //Cutting either one disables the main door power, but unless backup power is also cut, the backup power re-powers the door in 10 seconds. While unpowered, the door may be crowbarred open, but bolts-raising will not work. Cutting these wires may electocute the user. - src.loseMainPower() - src.shock(usr, 50) - src.updateUsrDialog() - if(AIRLOCK_WIRE_DOOR_BOLTS) - //Cutting this wire also drops the door bolts, and mending it does not raise them. (This is what happens now, except there are a lot more wires going to door bolts at present) - src.lock() - src.updateUsrDialog() - if(AIRLOCK_WIRE_BACKUP_POWER1, AIRLOCK_WIRE_BACKUP_POWER2) - //Cutting either one disables the backup door power (allowing it to be crowbarred open, but disabling bolts-raising), but may electocute the user. - src.loseBackupPower() - src.shock(usr, 50) - src.updateUsrDialog() - if(AIRLOCK_WIRE_AI_CONTROL) - //one wire for AI control. Cutting this prevents the AI from controlling the door unless it has hacked the door through the power connection (which takes about a minute). If both main and backup power are cut, as well as this wire, then the AI cannot operate or hack the door at all. - //aiControlDisabled: If 1, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in. - if(src.aiControlDisabled == 0) - src.aiControlDisabled = 1 - else if(src.aiControlDisabled == -1) - src.aiControlDisabled = 2 - src.updateUsrDialog() - if(AIRLOCK_WIRE_ELECTRIFY) - //Cutting this wire electrifies the door, so that the next person to touch the door without insulated gloves gets electrocuted. - if(src.secondsElectrified != -1) - shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])") - usr.attack_log += text("\[[time_stamp()]\] Electrified the [name] at [x] [y] [z]") - src.secondsElectrified = -1 - if (AIRLOCK_WIRE_SAFETY) - safe = 0 - src.updateUsrDialog() - - if(AIRLOCK_WIRE_SPEED) - autoclose = 0 - src.updateUsrDialog() - - if(AIRLOCK_WIRE_LIGHT) - lights = 0 - src.updateUsrDialog() - -/obj/machinery/door/airlock/proc/mend(var/wireColor) - var/wireFlag = airlockWireColorToFlag[wireColor] - var/wireIndex = airlockWireColorToIndex[wireColor] //not used in this function - wires |= wireFlag - switch(wireIndex) - if(AIRLOCK_WIRE_MAIN_POWER1, AIRLOCK_WIRE_MAIN_POWER2) - if((!src.isWireCut(AIRLOCK_WIRE_MAIN_POWER1)) && (!src.isWireCut(AIRLOCK_WIRE_MAIN_POWER2))) - src.regainMainPower() - src.shock(usr, 50) - src.updateUsrDialog() - if(AIRLOCK_WIRE_BACKUP_POWER1, AIRLOCK_WIRE_BACKUP_POWER2) - if((!src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER1)) && (!src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER2))) - src.regainBackupPower() - src.shock(usr, 50) - src.updateUsrDialog() - if(AIRLOCK_WIRE_AI_CONTROL) - //one wire for AI control. Cutting this prevents the AI from controlling the door unless it has hacked the door through the power connection (which takes about a minute). If both main and backup power are cut, as well as this wire, then the AI cannot operate or hack the door at all. - //aiControlDisabled: If 1, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in. - if(src.aiControlDisabled == 1) - src.aiControlDisabled = 0 - else if(src.aiControlDisabled == 2) - src.aiControlDisabled = -1 - src.updateUsrDialog() - if(AIRLOCK_WIRE_ELECTRIFY) - if(src.secondsElectrified == -1) - src.secondsElectrified = 0 - - if (AIRLOCK_WIRE_SAFETY) - safe = 1 - src.updateUsrDialog() - - if(AIRLOCK_WIRE_SPEED) - autoclose = 1 - if(!src.density) - close() - src.updateUsrDialog() - - if(AIRLOCK_WIRE_LIGHT) - lights = 1 - src.updateUsrDialog() - - /obj/machinery/door/airlock/proc/isElectrified() - if(src.secondsElectrified != 0) + if(src.electrified_until != 0) return 1 return 0 -/obj/machinery/door/airlock/proc/isWireColorCut(var/wireColor) - var/wireFlag = airlockWireColorToFlag[wireColor] - return ((src.wires & wireFlag) == 0) - /obj/machinery/door/airlock/proc/isWireCut(var/wireIndex) - var/wireFlag = airlockIndexToFlag[wireIndex] - return ((src.wires & wireFlag) == 0) + // You can find the wires in the datum folder. + return wires.IsIndexCut(wireIndex) /obj/machinery/door/airlock/proc/canAIControl() return ((src.aiControlDisabled!=1) && (!src.isAllPowerLoss())); @@ -538,67 +334,89 @@ About the new airlock wires panel: return ((src.aiControlDisabled==1) && (!hackProof) && (!src.isAllPowerLoss())); /obj/machinery/door/airlock/proc/arePowerSystemsOn() - if (stat & NOPOWER) + if (stat & (NOPOWER|BROKEN)) return 0 - return (src.secondsMainPowerLost==0 || src.secondsBackupPowerLost==0) + return (src.main_power_lost_until==0 || src.backup_power_lost_until==0) /obj/machinery/door/airlock/requiresID() return !(src.isWireCut(AIRLOCK_WIRE_IDSCAN) || aiDisabledIdScanner) /obj/machinery/door/airlock/proc/isAllPowerLoss() - if(stat & NOPOWER) + if(stat & (NOPOWER|BROKEN)) + return 1 + if(mainPowerCablesCut() && backupPowerCablesCut()) return 1 - if(src.isWireCut(AIRLOCK_WIRE_MAIN_POWER1) || src.isWireCut(AIRLOCK_WIRE_MAIN_POWER2)) - if(src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER1) || src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER2)) - return 1 return 0 -/obj/machinery/door/airlock/proc/regainMainPower() - if(src.secondsMainPowerLost > 0) - src.secondsMainPowerLost = 0 +/obj/machinery/door/airlock/proc/mainPowerCablesCut() + return src.isWireCut(AIRLOCK_WIRE_MAIN_POWER1) || src.isWireCut(AIRLOCK_WIRE_MAIN_POWER2) + +/obj/machinery/door/airlock/proc/backupPowerCablesCut() + return src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER1) || src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER2) /obj/machinery/door/airlock/proc/loseMainPower() - if(src.secondsMainPowerLost <= 0) - src.secondsMainPowerLost = 60 - if(src.secondsBackupPowerLost < 10) - src.secondsBackupPowerLost = 10 - if(!src.spawnPowerRestoreRunning) - src.spawnPowerRestoreRunning = 1 - spawn(0) - var/cont = 1 - while (cont) - sleep(10) - cont = 0 - if(src.secondsMainPowerLost>0) - if((!src.isWireCut(AIRLOCK_WIRE_MAIN_POWER1)) && (!src.isWireCut(AIRLOCK_WIRE_MAIN_POWER2))) - src.secondsMainPowerLost -= 1 - src.updateDialog() - cont = 1 + main_power_lost_until = mainPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) + // If backup power is permanently disabled then activate in 10 seconds if possible, otherwise it's already enabled or a timer is already running + if(backup_power_lost_until == -1 && !backupPowerCablesCut()) + backup_power_lost_until = world.time + SecondsToTicks(10) - if(src.secondsBackupPowerLost>0) - if((!src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER1)) && (!src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER2))) - src.secondsBackupPowerLost -= 1 - src.updateDialog() - cont = 1 - src.spawnPowerRestoreRunning = 0 - src.updateDialog() + // Disable electricity if required + if(electrified_until && isAllPowerLoss()) + electrify(0) /obj/machinery/door/airlock/proc/loseBackupPower() - if(src.secondsBackupPowerLost < 60) - src.secondsBackupPowerLost = 60 + backup_power_lost_until = backupPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) + // Disable electricity if required + if(electrified_until && isAllPowerLoss()) + electrify(0) + +/obj/machinery/door/airlock/proc/regainMainPower() + if(!mainPowerCablesCut()) + main_power_lost_until = 0 + // If backup power is currently active then disable, otherwise let it count down and disable itself later + if(!backup_power_lost_until) + backup_power_lost_until = -1 + update_icon() /obj/machinery/door/airlock/proc/regainBackupPower() - if(src.secondsBackupPowerLost > 0) - src.secondsBackupPowerLost = 0 + if(!backupPowerCablesCut()) + // Restore backup power only if main power is offline, otherwise permanently disable + backup_power_lost_until = main_power_lost_until == 0 ? -1 : 0 + update_icon() + +/obj/machinery/door/airlock/proc/electrify(var/duration, var/feedback = 0) + var/message = "" + if(src.isWireCut(AIRLOCK_WIRE_ELECTRIFY) && arePowerSystemsOn()) + message = text("The electrification wire is cut - Door permanently electrified.") + src.electrified_until = -1 + else if(duration && !arePowerSystemsOn()) + message = text("The door is unpowered - Cannot electrify the door.") + src.electrified_until = 0 + else if(!duration && electrified_until != 0) + message = "The door is now un-electrified." + src.electrified_until = 0 + else if(duration) //electrify door for the given duration seconds + if(usr) + shockedby += text("\[[time_stamp()]\] - [usr](ckey:[usr.ckey])") + usr.attack_log += text("\[[time_stamp()]\] Electrified the [name] at [x] [y] [z]") + else + shockedby += text("\[[time_stamp()]\] - EMP)") + message = "The door is now electrified [duration == -1 ? "permanently" : "for [duration] second\s"]." + src.electrified_until = duration == -1 ? -1 : world.time + SecondsToTicks(duration) + + if(feedback && message) + usr << message // shock user with probability prb (if all connections & power are working) // returns 1 if shocked, 0 otherwise // The preceding comment was borrowed from the grille's shock script /obj/machinery/door/airlock/shock(mob/user, prb) - if(!arePowerSystemsOn()) + if((stat & (NOPOWER)) || !src.arePowerSystemsOn()) // unpowered? no shock return 0 if(hasShocked) return 0 //Already shocked someone recently? + if(!prob(prb)) + return 0 //you lucked out, no shock for you if(..()) hasShocked = 1 sleep(10) @@ -610,6 +428,9 @@ About the new airlock wires panel: /obj/machinery/door/airlock/update_icon() if(overlays) overlays.Cut() + overlays=list() + if(emergency && arePowerSystemsOn()) + overlays += image('icons/obj/doors/doorint.dmi', "elights") if(density) if(locked && lights) icon_state = "door_locked" @@ -650,111 +471,31 @@ About the new airlock wires panel: return /obj/machinery/door/airlock/attack_ai(mob/user as mob) - if (!check_synth_access(user)) - return + ui_interact(user) - //Separate interface for the AI. - user.set_machine(src) - var/t1 = text("Airlock Control
\n") - if(src.secondsMainPowerLost > 0) - if((!src.isWireCut(AIRLOCK_WIRE_MAIN_POWER1)) && (!src.isWireCut(AIRLOCK_WIRE_MAIN_POWER2))) - t1 += text("Main power is offline for [] seconds.
\n", src.secondsMainPowerLost) - else - t1 += text("Main power is offline indefinitely.
\n") - else - t1 += text("Main power is online.") +/obj/machinery/door/airlock/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + var/data[0] + data["main_power_loss"] = round(main_power_lost_until > 0 ? max(main_power_lost_until - world.time, 0) / 10 : main_power_lost_until, 1) + data["backup_power_loss"] = round(backup_power_lost_until > 0 ? max(backup_power_lost_until - world.time, 0) / 10 : backup_power_lost_until, 1) + data["electrified"] = round(electrified_until > 0 ? max(electrified_until - world.time, 0) / 10 : electrified_until, 1) + data["open"] = !density - if(src.secondsBackupPowerLost > 0) - if((!src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER1)) && (!src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER2))) - t1 += text("Backup power is offline for [] seconds.
\n", src.secondsBackupPowerLost) - else - t1 += text("Backup power is offline indefinitely.
\n") - else if(src.secondsMainPowerLost > 0) - t1 += text("Backup power is online.") - else - t1 += text("Backup power is offline, but will turn on if main power fails.") - t1 += "
\n" - - if(src.isWireCut(AIRLOCK_WIRE_IDSCAN)) - t1 += text("IdScan wire is cut.
\n") - else if(src.aiDisabledIdScanner) - t1 += text("IdScan disabled. Enable?
\n", src) - else - t1 += text("IdScan enabled. Disable?
\n", src) - - if(src.isWireCut(AIRLOCK_WIRE_MAIN_POWER1)) - t1 += text("Main Power Input wire is cut.
\n") - if(src.isWireCut(AIRLOCK_WIRE_MAIN_POWER2)) - t1 += text("Main Power Output wire is cut.
\n") - if(src.secondsMainPowerLost == 0) - t1 += text("Temporarily disrupt main power?.
\n", src) - if(src.secondsBackupPowerLost == 0) - t1 += text("Temporarily disrupt backup power?.
\n", src) - - if(src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER1)) - t1 += text("Backup Power Input wire is cut.
\n") - if(src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER2)) - t1 += text("Backup Power Output wire is cut.
\n") - - if(src.isWireCut(AIRLOCK_WIRE_DOOR_BOLTS)) - t1 += text("Door bolt control wire is cut.
\n") - else if(!src.locked) - t1 += text("Door bolts are up. Drop them?
\n", src) - else - t1 += text("Door bolts are down.") - if(src.arePowerSystemsOn()) - t1 += text(" Raise?
\n", src) - else - t1 += text(" Cannot raise door bolts due to power failure.
\n") - - if(src.isWireCut(AIRLOCK_WIRE_LIGHT)) - t1 += text("Door bolt lights wire is cut.
\n") - else if(!src.lights) - t1 += text("Door lights are off. Enable?
\n", src) - else - t1 += text("Door lights are on. Disable?
\n", src) - - if(src.isWireCut(AIRLOCK_WIRE_ELECTRIFY)) - t1 += text("Electrification wire is cut.
\n") - if(src.secondsElectrified==-1) - t1 += text("Door is electrified indefinitely. Un-electrify it?
\n", src) - else if(src.secondsElectrified>0) - t1 += text("Door is electrified temporarily ([] seconds). Un-electrify it?
\n", src.secondsElectrified, src) - else - t1 += text("Door is not electrified. Electrify it for 30 seconds? Or, Electrify it indefinitely until someone cancels the electrification?
\n", src, src) - - if(src.isWireCut(AIRLOCK_WIRE_SAFETY)) - t1 += text("Door force sensors not responding.
\n") - else if(src.safe) - t1 += text("Door safeties operating normally. Override?
\n",src) - else - t1 += text("Danger. Door safeties disabled. Restore?
\n",src) - - if(src.isWireCut(AIRLOCK_WIRE_SPEED)) - t1 += text("Door timing circuitry not responding.
\n") - else if(src.normalspeed) - t1 += text("Door timing circuitry operating normally. Override?
\n",src) - else - t1 += text("Warning. Door timing circuitry operating abnormally. Restore?
\n",src) - - - - - if(src.welded) - t1 += text("Door appears to have been welded shut.
\n") - else if(!src.locked) - if(src.density) - t1 += text("Open door
\n", src) - else - t1 += text("Close door
\n", src) - - t1 += text("

Close

\n", src) - user << browse(t1, "window=airlock") - onclose(user, "airlock") - -//aiDisable - 1 idscan, 2 disrupt main power, 3 disrupt backup power, 4 drop door bolts, 5 un-electrify door, 7 close door -//aiEnable - 1 idscan, 4 raise door bolts, 5 electrify door for 30 seconds, 6 electrify door indefinitely, 7 open door + var/commands[0] + commands[++commands.len] = list("name" = "IdScan", "command"= "idscan", "active" = !aiDisabledIdScanner, "enabled" = "Enabled", "disabled" = "Disable", "danger" = 0, "act" = 1) + commands[++commands.len] = list("name" = "Bolts", "command"= "bolts", "active" = !locked, "enabled" = "Raised", "disabled" = "Dropped", "danger" = 0, "act" = 0) + commands[++commands.len] = list("name" = "Bolt Lights", "command"= "lights", "active" = lights, "enabled" = "Enabled", "disabled" = "Disable", "danger" = 0, "act" = 1) + commands[++commands.len] = list("name" = "Safeties", "command"= "safeties", "active" = safe, "enabled" = "Nominal", "disabled" = "Overridden", "danger" = 1, "act" = 0) + commands[++commands.len] = list("name" = "Timing", "command"= "timing", "active" = normalspeed, "enabled" = "Nominal", "disabled" = "Overridden", "danger" = 1, "act" = 0) + commands[++commands.len] = list("name" = "Door State", "command"= "open", "active" = density, "enabled" = "Closed", "disabled" = "Opened", "danger" = 0, "act" = 0) + commands[++commands.len] = list("name" = "Emergency Access", "command"= "emergency", "active" = !emergency, "enabled" = "Disabled", "disabled" = "Enabled", "danger" = 0, "act" = 0) + data["commands"] = commands + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "door_control.tmpl", "Door Controls - [src]", 600, 375) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) /obj/machinery/door/airlock/proc/hack(mob/user as mob) if(src.aiHacking==0) @@ -813,16 +554,17 @@ About the new airlock wires panel: s.set_up(5, 1, src) s.start() return ..() + /obj/machinery/door/airlock/attack_paw(mob/user as mob) return src.attack_hand(user) /obj/machinery/door/airlock/attack_hand(mob/user as mob) - if(!istype(usr, /mob/living/silicon)) + if(!istype(user, /mob/living/silicon)) if(src.isElectrified()) if(src.shock(user, 100)) return - // No. -- cib + // No. -- cib -- double no -- sound /** if(ishuman(user) && prob(40) && src.density) var/mob/living/carbon/human/H = user @@ -841,53 +583,17 @@ About the new airlock wires panel: **/ if(src.p_open) - user.set_machine(src) - var/t1 = text("Access Panel
\n") - - //t1 += text("[]: ", airlockFeatureNames[airlockWireColorToIndex[9]]) - var/list/wires = list( - "Orange" = 1, - "Dark red" = 2, - "White" = 3, - "Yellow" = 4, - "Red" = 5, - "Blue" = 6, - "Green" = 7, - "Grey" = 8, - "Black" = 9, - "Gold" = 10, - "Aqua" = 11, - "Pink" = 12 - ) - for(var/wiredesc in wires) - var/is_uncut = src.wires & airlockWireColorToFlag[wires[wiredesc]] - t1 += "[wiredesc] wire: " - if(!is_uncut) - t1 += "Mend" - else - t1 += "Cut " - t1 += "Pulse " - if(src.signalers[wires[wiredesc]]) - t1 += "Detach signaler" - else - t1 += "Attach signaler" - t1 += "
" - - t1 += text("
\n[]
\n[]
\n[]
\n[]
\n[]
\n[]", (src.locked ? "The door bolts have fallen!" : "The door bolts look up."), (src.lights ? "The door bolt lights are on." : "The door bolt lights are off!"), ((src.arePowerSystemsOn()) ? "The test light is on." : "The test light is off!"), (src.aiControlDisabled==0 ? "The 'AI control allowed' light is on." : "The 'AI control allowed' light is off."), (src.safe==0 ? "The 'Check Wiring' light is on." : "The 'Check Wiring' light is off."), (src.normalspeed==0 ? "The 'Check Timing Mechanism' light is on." : "The 'Check Timing Mechanism' light is off.")) - - t1 += text("

Close

\n", src) - - user << browse(t1, "window=airlock") - onclose(user, "airlock") - + wires.Interact(user) else ..(user) return -/obj/machinery/door/airlock/proc/check_synth_access(mob/user as mob) +/obj/machinery/door/airlock/CanUseTopic(var/mob/user, href_list) + if(!issilicon(user)) + return STATUS_CLOSE if(operating < 0) //emagged user << "Unable to interface: Internal error." - return 0 + return STATUS_CLOSE if(!src.canAIControl()) if(src.canAIHack(user)) src.hack(user) @@ -896,249 +602,105 @@ About the new airlock wires panel: user << "Unable to interface: Connection timed out." else user << "Unable to interface: Connection refused." - return 0 - return 1 + return STATUS_CLOSE + return STATUS_INTERACTIVE /obj/machinery/door/airlock/Topic(href, href_list, var/nowindow = 0) - if(!nowindow) - ..() - if(usr.stat || usr.restrained()|| usr.small) - return - add_fingerprint(usr) - if(href_list["close"]) - usr << browse(null, "window=airlock") - if(usr.machine==src) - usr.unset_machine() - return + if(..()) + return 1 - if((in_range(src, usr) && istype(src.loc, /turf)) && src.p_open) - usr.set_machine(src) - if(href_list["wires"]) - var/t1 = text2num(href_list["wires"]) - if(!( istype(usr.get_active_hand(), /obj/item/weapon/wirecutters) )) - usr << "You need wirecutters!" - return - if(src.isWireColorCut(t1)) - src.mend(t1) + var/activate = text2num(href_list["activate"]) + switch (href_list["command"]) + if("idscan") + if(src.isWireCut(AIRLOCK_WIRE_IDSCAN)) + usr << "The IdScan wire has been cut - IdScan feature permanently disabled." + else if(activate && src.aiDisabledIdScanner) + src.aiDisabledIdScanner = 0 + usr << "IdScan feature has been enabled." + else if(!activate && !src.aiDisabledIdScanner) + src.aiDisabledIdScanner = 1 + usr << "IdScan feature has been disabled." + if("main_power") + if(!main_power_lost_until) + src.loseMainPower() + if("backup_power") + if(!backup_power_lost_until) + src.loseBackupPower() + if("bolts") + if(src.isWireCut(AIRLOCK_WIRE_DOOR_BOLTS)) + usr << "The door bolt control wire has been cut - Door bolts permanently dropped." + else if(activate && src.lock()) + usr << "The door bolts have been dropped." + else if(!activate && src.unlock()) + usr << "The door bolts have been raised." + if("electrify_temporary") + if(activate && src.isWireCut(AIRLOCK_WIRE_ELECTRIFY)) + usr << text("The electrification wire is cut - Door permanently electrified.") + else if(!activate && electrified_until != 0) + usr << "The door is now un-electrified." + src.electrified_until = 0 + else if(activate) //electrify door for 30 seconds + shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])") + usr.attack_log += text("\[[time_stamp()]\] Electrified the [name] at [x] [y] [z]") + usr << "The door is now electrified for thirty seconds." + src.electrified_until = world.time + SecondsToTicks(30) + if("electrify_permanently") + if(src.isWireCut(AIRLOCK_WIRE_ELECTRIFY)) + usr << text("The electrification wire is cut - Cannot electrify the door.") + else if(!activate && electrified_until != 0) + usr << "The door is now un-electrified." + src.electrified_until = 0 + else if(activate) + shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])") + usr.attack_log += text("\[[time_stamp()]\] Electrified the [name] at [x] [y] [z]") + usr << "The door is now electrified." + electrified_until = -1 + if("open") + if(src.welded) + usr << text("The airlock has been welded shut!") + else if(src.locked) + usr << text("The door bolts are down!") + else if(activate && density) + open() + else if(!activate && !density) + close() + if("safeties") + // Safeties! We don't need no stinking safeties! + if (src.isWireCut(AIRLOCK_WIRE_SAFETY)) + usr << text("The safety wire is cut - Cannot secure the door.") + else if (activate && src.safe) + safe = 0 + else if (!activate && !src.safe) + safe = 1 + if("timing") + // Door speed control + if(src.isWireCut(AIRLOCK_WIRE_SPEED)) + usr << text("The timing wire is cut - Cannot alter timing.") + else if (activate && src.normalspeed) + normalspeed = 0 + else if (!activate && !src.normalspeed) + normalspeed = 1 + if("lights") + // Bolt lights + if(src.isWireCut(AIRLOCK_WIRE_LIGHT)) + usr << "The bolt lights wire has been cut - The door bolt lights are permanently disabled." + else if (!activate && src.lights) + lights = 0 + usr << "The door bolt lights have been disabled." + else if (activate && !src.lights) + lights = 1 + usr << "The door bolt lights have been enabled." + if("emergency") + // Emergency access + if (src.emergency) + emergency = 0 + usr << "Emergency access has been disabled." else - src.cut(t1) - else if(href_list["pulse"]) - var/t1 = text2num(href_list["pulse"]) - if(!istype(usr.get_active_hand(), /obj/item/device/multitool)) - usr << "You need a multitool!" - return - if(src.isWireColorCut(t1)) - usr << "You can't pulse a cut wire." - return - else - src.pulse(t1) - else if(href_list["signaler"]) - var/wirenum = text2num(href_list["signaler"]) - if(!istype(usr.get_active_hand(), /obj/item/device/assembly/signaler)) - usr << "You need a signaller!" - return - if(src.isWireColorCut(wirenum)) - usr << "You can't attach a signaller to a cut wire." - return - var/obj/item/device/assembly/signaler/R = usr.get_active_hand() - if(R.secured) - usr << "This radio can't be attached!" - return - var/mob/M = usr - M.drop_item() - R.loc = src - R.airlock_wire = wirenum - src.signalers[wirenum] = R - else if(href_list["remove-signaler"]) - var/wirenum = text2num(href_list["remove-signaler"]) - if(!(src.signalers[wirenum])) - usr << "There's no signaller attached to that wire!" - return - var/obj/item/device/assembly/signaler/R = src.signalers[wirenum] - R.loc = usr.loc - R.airlock_wire = null - src.signalers[wirenum] = null + emergency = 1 + usr << "Emergency access has been enabled." - - if(istype(usr, /mob/living/silicon)) - if (!check_synth_access(usr)) - return - - //AI - //aiDisable - 1 idscan, 2 disrupt main power, 3 disrupt backup power, 4 drop door bolts, 5 un-electrify door, 7 close door, 8 door safties, 9 door speed - //aiEnable - 1 idscan, 4 raise door bolts, 5 electrify door for 30 seconds, 6 electrify door indefinitely, 7 open door, 8 door safties, 9 door speed - if(href_list["aiDisable"]) - var/code = text2num(href_list["aiDisable"]) - switch (code) - if(1) - //disable idscan - if(src.isWireCut(AIRLOCK_WIRE_IDSCAN)) - usr << "The IdScan wire has been cut - The IdScan feature is already disabled." - else if(src.aiDisabledIdScanner) - usr << "The IdScan feature is already disabled." - else - usr << "The IdScan feature has been disabled." - src.aiDisabledIdScanner = 1 - if(2) - //disrupt main power - if(src.secondsMainPowerLost == 0) - src.loseMainPower() - else - usr << "Main power is already offline." - if(3) - //disrupt backup power - if(src.secondsBackupPowerLost == 0) - src.loseBackupPower() - else - usr << "Backup power is already offline." - if(4) - //drop door bolts - if(src.isWireCut(AIRLOCK_WIRE_DOOR_BOLTS)) - usr << "The door bolt control wire has been cut - The door bolts are already dropped." - else if(src.locked) - usr << "The door bolts are already dropped." - else - src.lock() - usr << "The door bolts have been dropped." - if(5) - //un-electrify door - if(src.isWireCut(AIRLOCK_WIRE_ELECTRIFY)) - usr << text("The electrification wire is cut - Cannot un-electrify the door.") - else if(secondsElectrified != 0) - usr << "The door is now un-electrified." - src.secondsElectrified = 0 - if(7) - //close door - if(src.welded) - usr << text("The airlock has been welded shut!") - else if(src.locked) - usr << text("The door bolts are down!") - else if(!src.density) - close() - else - open() - if(8) - // Safeties! We don't need no stinking safeties! - if (src.isWireCut(AIRLOCK_WIRE_SAFETY)) - usr << text("Control to door sensors is disabled.") - else if (src.safe) - safe = 0 - else - usr << text("Firmware reports safeties already overridden.") - if(9) - // Door speed control - if(src.isWireCut(AIRLOCK_WIRE_SPEED)) - usr << text("Control to door timing circuitry has been severed.") - else if (src.normalspeed) - normalspeed = 0 - else - usr << text("Door timing circuity already accelerated.") - if(10) - // Bolt lights - if(src.isWireCut(AIRLOCK_WIRE_LIGHT)) - usr << "The bolt lights wire has been cut - The door bolt lights are already disabled." - else if (src.lights) - lights = 0 - usr << "The door bolt lights have been disabled." - else - usr << "The door bolt lights are already disabled!" - - else if(href_list["aiEnable"]) - var/code = text2num(href_list["aiEnable"]) - switch (code) - if(1) - //enable idscan - if(src.isWireCut(AIRLOCK_WIRE_IDSCAN)) - usr << "The IdScan wire has been cut - The IdScan feature cannot be enabled." - else if(src.aiDisabledIdScanner) - usr << "The IdScan feature has been enabled." - src.aiDisabledIdScanner = 0 - else - usr << "The IdScan feature is already enabled." - if(4) - //raise door bolts - if(src.isWireCut(AIRLOCK_WIRE_DOOR_BOLTS)) - usr << "The door bolt control wire has been cut - The door bolts cannot be raised." - else if(!src.locked) - usr << "The door bolts are already raised." - else - if(src.unlock()) - usr << "The door bolts have been raised." - else - usr << "Unable to raise door bolts." - if(5) - //electrify door for 30 seconds - if(src.isWireCut(AIRLOCK_WIRE_ELECTRIFY)) - usr << text("The electrification wire has been cut.
\n") - else if(src.secondsElectrified==-1) - usr << text("The door is already indefinitely electrified. You'd have to un-electrify it before you can re-electrify it with a non-forever duration.
\n") - else if(src.secondsElectrified!=0) - usr << text("The door is already electrified. Cannot re-electrify it while it's already electrified.
\n") - else - shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])") - usr.attack_log += text("\[[time_stamp()]\] Electrified the [name] at [x] [y] [z]") - usr << "The door is now electrified for thirty seconds." - src.secondsElectrified = 30 - spawn(10) - while (src.secondsElectrified>0) - src.secondsElectrified-=1 - if(src.secondsElectrified<0) - src.secondsElectrified = 0 - sleep(10) - if(6) - //electrify door indefinitely - if(src.isWireCut(AIRLOCK_WIRE_ELECTRIFY)) - usr << text("The electrification wire has been cut.
\n") - else if(src.secondsElectrified==-1) - usr << text("The door is already indefinitely electrified.
\n") - else if(src.secondsElectrified!=0) - usr << text("The door is already electrified. You can't re-electrify it while it's already electrified.
\n") - else - shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])") - usr.attack_log += text("\[[time_stamp()]\] Electrified the [name] at [x] [y] [z]") - usr << "The door is now electrified." - src.secondsElectrified = -1 - if(7) - //open door - if(src.welded) - usr << text("The airlock has been welded shut!") - else if(src.locked) - usr << text("The door bolts are down!") - else if(src.density) - open() - else - close() - if (8) - // Safeties! Maybe we do need some stinking safeties! - if (src.isWireCut(AIRLOCK_WIRE_SAFETY)) - usr << text("Control to door sensors is disabled.") - else if (!src.safe) - safe = 1 - else - usr << text("Firmware reports safeties already in place.") - if(9) - // Door speed control - if(src.isWireCut(AIRLOCK_WIRE_SPEED)) - usr << text("Control to door timing circuitry has been severed.") - else if (!src.normalspeed) - normalspeed = 1 - else - usr << text("Door timing circuity currently operating normally.") - if(10) - // Bolt lights - if(src.isWireCut(AIRLOCK_WIRE_LIGHT)) - usr << "The bolt lights wire has been cut - The door bolt lights cannot be enabled." - else if (!src.lights) - lights = 1 - usr << "The door bolt lights have been enabled" - else - usr << "The door bolt lights are already enabled!" - - add_fingerprint(usr) update_icon() - if(!nowindow) - updateUsrDialog() - return + return 1 /obj/machinery/door/airlock/attackby(C as obj, mob/user as mob) //world << text("airlock attackby src [] obj [] mob []", src, C, user) @@ -1258,6 +820,9 @@ About the new airlock wires panel: ignite(is_hot(C)) ..() +/obj/machinery/door/airlock/proc/play_sliding_noise() + playsound(src.loc, door_sound, door_sound_distance, 1) + /obj/machinery/door/airlock/open(var/forced=0) if( operating || welded || locked ) return 0 @@ -1265,10 +830,7 @@ About the new airlock wires panel: if( !arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_OPEN_DOOR) ) return 0 use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people - if(istype(src, /obj/machinery/door/airlock/glass)) - playsound(src.loc, 'sound/machines/windowdoor.ogg', 100, 1) - else - playsound(src.loc, 'sound/machines/airlock.ogg', 30, 1) + play_sliding_noise() if(src.closeOther != null && istype(src.closeOther, /obj/machinery/door/airlock/) && !src.closeOther.density) src.closeOther.close() return ..() @@ -1284,7 +846,7 @@ About the new airlock wires panel: if(locate(/mob/living) in turf) // playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 0) //THE BUZZING IT NEVER STOPS -Pete spawn (60) - close() + autoclose() return for(var/turf/turf in locs) @@ -1307,10 +869,7 @@ About the new airlock wires panel: location.add_blood(M) use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people - if(istype(src, /obj/machinery/door/airlock/glass)) - playsound(src.loc, 'sound/machines/windowdoor.ogg', 30, 1) - else - playsound(src.loc, 'sound/machines/airlock.ogg', 30, 1) + play_sliding_noise() for(var/turf/turf in locs) var/obj/structure/window/killthis = (locate(/obj/structure/window) in turf) if(killthis) @@ -1319,36 +878,35 @@ About the new airlock wires panel: return /obj/machinery/door/airlock/proc/lock(var/forced=0) - if (operating || src.locked) return + if(locked) + return 0 + + if (operating && !forced) + return 0 src.locked = 1 for(var/mob/M in range(1,src)) M.show_message("You hear a click from the bottom of the door.", 2) update_icon() + return 1 /obj/machinery/door/airlock/proc/unlock(var/forced=0) - if (operating || !src.locked) return + if(!src.locked) + return - if (forced || (src.arePowerSystemsOn())) //only can raise bolts if power's on - src.locked = 0 - for(var/mob/M in range(1,src)) - M.show_message("You hear a click from the bottom of the door.", 2) - update_icon() - return 1 - return 0 + if (!forced) + if(operating || !src.arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_DOOR_BOLTS)) return + + src.locked = 0 + for(var/mob/M in range(1,src)) + M.show_message("You hear a click from the bottom of the door.", 2) + update_icon() + return 1 /obj/machinery/door/airlock/New() ..() - //wires - if (!secured_wires) - airlockWireColorToFlag = globalAirlockWireColorToFlag - airlockIndexToFlag = globalAirlockIndexToFlag - airlockIndexToWireColor = globalAirlockIndexToWireColor - airlockWireColorToIndex = globalAirlockWireColorToIndex - else - randomize_wires() - + wires = new(src) if(src.closeOtherId != null) spawn (5) for (var/obj/machinery/door/airlock/A in world) @@ -1356,13 +914,6 @@ About the new airlock wires panel: src.closeOther = A break -/obj/machinery/door/airlock/proc/randomize_wires() - var/wire_assignments = CreateRandomAirlockWires() - airlockWireColorToFlag = wire_assignments[1] - airlockIndexToFlag = wire_assignments[2] - airlockIndexToWireColor = wire_assignments[3] - airlockWireColorToIndex = wire_assignments[4] - /obj/machinery/door/airlock/proc/prison_open() src.unlock() src.open() diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 59ec1262..122bf611 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -22,6 +22,7 @@ var/glass = 0 var/normalspeed = 1 var/heat_proof = 0 // For glass airlocks/opacity firedoors + var/emergency = 0 var/air_properties_vary_with_direction = 0 //Multi-tile doors @@ -72,7 +73,7 @@ if(istype(AM, /obj/machinery/bot)) var/obj/machinery/bot/bot = AM - if(src.check_access(bot.botcard)) + if(src.check_access(bot.botcard) || emergency == 1) if(density) open() return @@ -80,7 +81,7 @@ if(istype(AM, /obj/mecha)) var/obj/mecha/mecha = AM if(density) - if(mecha.occupant && (src.allowed(mecha.occupant) || src.check_access_list(mecha.operation_req_access))) + if(mecha.occupant && (src.allowed(mecha.occupant) || src.check_access_list(mecha.operation_req_access) || emergency == 1)) open() else flick("door_deny", src) @@ -112,8 +113,10 @@ user = null if(density) - if(allowed(user)) open() - else flick("door_deny", src) + if(allowed(user) || src.emergency == 1) + open() + else + flick("door_deny", src) return /obj/machinery/door/meteorhit(obj/M as obj) @@ -152,7 +155,7 @@ open() operating = -1 return 1 - if(src.allowed(user)) + if(src.allowed(user) || src.emergency == 1) if(src.density) open() else diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 3907bbbc..cbd416ac 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -142,6 +142,24 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ return 1 +/obj/machinery/hologram/holopad/proc/face_atom(var/atom/A) + if( !hologram || !A || !hologram.x || !hologram.y || !A.x || !A.y ) return + var/dx = A.x - hologram.x + var/dy = A.y - hologram.y + if(!dx && !dy) // Wall items are graphically shifted but on the floor + if(A.pixel_y > 16) hologram.dir = NORTH + else if(A.pixel_y < -16)hologram.dir = SOUTH + else if(A.pixel_x > 16) hologram.dir = EAST + else if(A.pixel_x < -16)hologram.dir = WEST + return + + if(abs(dx) < abs(dy)) + if(dy > 0) hologram.dir = NORTH + else hologram.dir = SOUTH + else + if(dx > 0) hologram.dir = EAST + else hologram.dir = WEST + /* * Hologram */ diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index 731cb229..b7f38e6a 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -110,18 +110,43 @@ Class Procs: var/uid var/manual = 0 var/global/gl_uid = 1 + var/area/myArea + var/interact_offline = 0 // Can the machine be interacted with while de-powered. /obj/machinery/New() - ..() - machines += src + addAtProcessing() + return ..() /obj/machinery/Del() - machines -= src + if (src in machines) + removeAtProcessing() ..() +/obj/machinery/proc/addAtProcessing() + if (use_power) + myArea = get_area_master(src) + machines += src + +/obj/machinery/proc/removeAtProcessing() + if (myArea) + myArea = null + machines -= src + /obj/machinery/process()//If you dont use process or power why are you here return PROCESS_KILL +/obj/machinery/CanUseTopic(var/mob/user, var/be_close) + if(!interact_offline && (stat & (NOPOWER|BROKEN))) + return STATUS_CLOSE + return ..() + +/obj/machinery/CouldUseTopic(var/mob/user) + ..() + user.set_machine(src) + +/obj/machinery/CouldNotUseTopic(var/mob/user) + usr.unset_machine() + /obj/machinery/emp_act(severity) if(use_power && stat == 0) use_power(7500/severity) @@ -187,37 +212,6 @@ Class Procs: /obj/machinery/proc/inoperable(var/additional_flags = 0) return (stat & (NOPOWER|BROKEN|additional_flags)) -/obj/machinery/Topic(href, href_list) - ..() - if(inoperable()) - return 1 - if(usr.restrained() || usr.lying || usr.stat) - return 1 - if ( ! (istype(usr, /mob/living/carbon/human) || \ - istype(usr, /mob/living/silicon) || \ - istype(usr, /mob/living/carbon/monkey)) ) - usr << "\red You don't have the dexterity to do this!" - return 1 - - var/norange = 0 - if(istype(usr, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = usr - if(istype(H.l_hand, /obj/item/tk_grab)) - norange = 1 - else if(istype(H.r_hand, /obj/item/tk_grab)) - norange = 1 - - if(!norange) - if ((!in_range(src, usr) || !istype(src.loc, /turf)) && !istype(usr, /mob/living/silicon)) - return 1 - - src.add_fingerprint(usr) - - var/area/A = get_area(src) - A.master.powerupdate = 1 - - return 0 - /obj/machinery/attack_ai(mob/user as mob) if(isrobot(user)) // For some reason attack_robot doesn't work @@ -231,7 +225,7 @@ Class Procs: return src.attack_hand(user) /obj/machinery/attack_hand(mob/user as mob) - if(inoperable(MAINT)) + if(!interact_offline && stat & (NOPOWER|BROKEN|MAINT)) return 1 if(user.lying || user.stat) return 1 diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index 07f6fabb..102c4947 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -51,6 +51,7 @@ var/global/list/obj/item/device/pda/PDAs = list() var/obj/item/weapon/card/id/id = null //Making it possible to slot an ID card into the PDA so it can function as both. var/ownjob = null //related to above + var/ownrank = null // this one is rank, never alt title var/obj/item/device/paicard/pai = null // A slot for a personal AI device @@ -208,9 +209,13 @@ var/global/list/obj/item/device/pda/PDAs = list() detonate = 0 -/obj/item/device/pda/ai/proc/set_name_and_job(newname as text, newjob as text) +/obj/item/device/pda/ai/proc/set_name_and_job(newname as text, newjob as text, newrank as null|text) owner = newname ownjob = newjob + if(newrank) + ownrank = newrank + else + ownrank = ownjob name = newname + " (" + ownjob + ")" @@ -571,6 +576,7 @@ var/global/list/obj/item/device/pda/PDAs = list() id_check(U, 1) if("UpdateInfo") ownjob = id.assignment + ownrank = id.rank name = "PDA-[owner] ([ownjob])" if("Eject")//Ejects the cart, only done from hub. if (!isnull(cartridge)) @@ -1103,6 +1109,7 @@ var/global/list/obj/item/device/pda/PDAs = list() if(!owner) owner = idcard.registered_name ownjob = idcard.assignment + ownrank = idcard.rank name = "PDA-[owner] ([ownjob])" user << "Card scanned." else diff --git a/code/game/objects/items/devices/uplinks.dm b/code/game/objects/items/devices/uplinks.dm index 44a06ad4..f1c63cfc 100644 --- a/code/game/objects/items/devices/uplinks.dm +++ b/code/game/objects/items/devices/uplinks.dm @@ -176,6 +176,7 @@ A list of items and costs is stored under the datum of every game mode, alongsid randomItems.Add("/obj/item/clothing/shoes/syndigaloshes") //No-Slip Syndicate Shoes randomItems.Add("/obj/item/weapon/plastique") //C4 randomItems.Add("/obj/item/weapon/storage/box/syndie_kit/masks_gas") + randomItems.Add("/obj/item/shoe_cover") if(uses > 0) randomItems.Add("/obj/item/weapon/soap/syndie") //Syndicate Soap @@ -207,7 +208,7 @@ A list of items and costs is stored under the datum of every game mode, alongsid uses -= 3 if("/obj/item/ammo_magazine/a357" , "/obj/item/clothing/shoes/syndigaloshes" , "/obj/item/weapon/plastique", "/obj/item/weapon/card/id/syndicate" , "/obj/item/weapon/storage/box/syndie_kit/masks_gas") uses -= 2 - if("/obj/item/weapon/soap/syndie" , "/obj/item/weapon/storage/toolbox/syndicate" , "/obj/item/weapon/storage/box/syndie_kit/masks") + if("/obj/item/weapon/soap/syndie" , "/obj/item/weapon/storage/toolbox/syndicate" , "/obj/item/weapon/storage/box/syndie_kit/masks", "/obj/item/shoe_cover") uses -= 1 del(randomItems) return buyItem @@ -281,7 +282,9 @@ A list of items and costs is stored under the datum of every game mode, alongsid feedback_add_details("traitor_uplink_items_bought","SR") if("/obj/item/clothing/gloves/force/syndicate") feedback_add_details("traitor_uplink_items_bought","FG") - + if("/obj/item/shoe_cover") + feedback_add_details("traitor_uplink_items_bought","SFS") +/obj/item/shoe_cover /obj/item/device/uplink/Topic(href, href_list) diff --git a/code/game/objects/items/shoe_cover.dm b/code/game/objects/items/shoe_cover.dm new file mode 100644 index 00000000..0336bdd2 --- /dev/null +++ b/code/game/objects/items/shoe_cover.dm @@ -0,0 +1,17 @@ +/obj/item/shoe_cover + name = "strange fabric" + desc = "A fabirc that allows for silent walking." + icon = 'icons/obj/items.dmi' + icon_state = "sheet-cloth" + +/obj/item/shoe_cover/afterattack(var/obj/A as obj, mob/user as mob, proximity) + if(!proximity) + return + + if (istype(A, /obj/item/clothing/shoes) && get_dist(src,A) <= 1) + var/obj/item/clothing/shoes/S = A + user << "You attahed the [src] to [S] making movement silent" + S.silent = 1 + S.desc += " They appear to have some type of fabric soles" + user.drop_from_inventory(src) + del(src) diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index f9f3297d..ee4d49f4 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -128,4 +128,27 @@ var/rendered = "[M.name]: [text]" mo.show_message(rendered, 2) */ - return \ No newline at end of file + return + + +/obj/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/custom_state = default_state) + // Calling Topic without a corresponding window open causes runtime errors + if(!nowindow && ..()) + return 1 + + // In the far future no checks are made in an overriding Topic() beyond if(..()) return + // Instead any such checks are made in CanUseTopic() + var/obj/host = nano_host() + if(host.CanUseTopic(usr, href_list, custom_state) == STATUS_INTERACTIVE) + CouldUseTopic(usr) + return 0 + + CouldNotUseTopic(usr) + return 1 + +/obj/proc/CouldUseTopic(var/mob/user) + var/atom/host = nano_host() + host.add_fingerprint(user) + +/obj/proc/CouldNotUseTopic(var/mob/user) + // Nada \ No newline at end of file diff --git a/code/game/sound.dm b/code/game/sound.dm index 36bce87a..3ba985a9 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -7,6 +7,19 @@ var/list/clown_sound = list('sound/effects/clownstep1.ogg','sound/effects/clowns var/list/swing_hit_sound = list('sound/weapons/genhit1.ogg', 'sound/weapons/genhit2.ogg', 'sound/weapons/genhit3.ogg') var/list/hiss_sound = list('sound/voice/hiss1.ogg','sound/voice/hiss2.ogg','sound/voice/hiss3.ogg','sound/voice/hiss4.ogg') var/list/page_sound = list('sound/effects/pageturn1.ogg', 'sound/effects/pageturn2.ogg','sound/effects/pageturn3.ogg') + +//FOOTSTEPS +var/list/defaultfootsteps = list('sound/effects/footsteps/tile1.wav','sound/effects/footsteps/tile2.wav','sound/effects/footsteps/tile3.wav','sound/effects/footsteps/tile4.wav') +var/list/concretefootsteps = list('sound/effects/footsteps/concrete1.wav','sound/effects/footsteps/concrete2.wav','sound/effects/footsteps/concrete3.wav','sound/effects/footsteps/concrete4.wav') +var/list/grassfootsteps = list('sound/effects/footsteps/grass1.wav','sound/effects/footsteps/grass2.wav','sound/effects/footsteps/grass3.wav','sound/effects/footsteps/grass4.wav') +var/list/dirtfootsteps = list('sound/effects/footsteps/dirt1.wav','sound/effects/footsteps/dirt2.wav','sound/effects/footsteps/dirt3.wav','sound/effects/footsteps/dirt4.wav') +var/list/waterfootsteps = list('sound/effects/footsteps/slosh1.wav','sound/effects/footsteps/slosh2.wav','sound/effects/footsteps/slosh3.wav','sound/effects/footsteps/slosh4.wav') +var/list/sandfootsteps = list('sound/effects/footsteps/sand1.wav','sound/effects/footsteps/sand2.wav','sound/effects/footsteps/sand3.wav','sound/effects/footsteps/sand4.wav') +var/list/gravelstep = list('sound/effects/footsteps/gravel1.wav','sound/effects/footsteps/gravel2.wav','sound/effects/footsteps/gravel3.wav','sound/effects/footsteps/gravel4.wav') + +var/list/footstepfx = list("defaultstep","concretestep","grassstep","dirtstep","waterstep","sandstep") + + //var/list/gun_sound = list('sound/weapons/Gunshot.ogg', 'sound/weapons/Gunshot2.ogg','sound/weapons/Gunshot3.ogg','sound/weapons/Gunshot4.ogg') /proc/playsound(var/atom/source, soundin, vol as num, vary, extrarange as num, falloff, var/is_global) @@ -37,6 +50,11 @@ var/const/FALLOFF_SOUNDS = 0.5 /mob/proc/playsound_local(var/turf/turf_source, soundin, vol as num, vary, frequency, falloff, is_global) if(!src.client || ear_deaf > 0) return + + if(soundin in footstepfx) + if(!(src.client.prefs.asfx_togs & ASFX_FOOTSTEPS)) + return + soundin = get_sfx(soundin) var/sound/S = sound(soundin) @@ -53,34 +71,34 @@ var/const/FALLOFF_SOUNDS = 0.5 if(isturf(turf_source)) // 3D sounds, the technology is here! var/turf/T = get_turf(src) - + //sound volume falloff with distance var/distance = get_dist(T, turf_source) - + S.volume -= max(distance - world.view, 0) * 2 //multiplicative falloff to add on top of natural audio falloff. - + //sound volume falloff with pressure var/pressure_factor = 1.0 - + var/datum/gas_mixture/hearer_env = T.return_air() var/datum/gas_mixture/source_env = turf_source.return_air() - + if (hearer_env && source_env) var/pressure = min(hearer_env.return_pressure(), source_env.return_pressure()) - + if (pressure < ONE_ATMOSPHERE) pressure_factor = max((pressure - SOUND_MINIMUM_PRESSURE)/(ONE_ATMOSPHERE - SOUND_MINIMUM_PRESSURE), 0) else //in space pressure_factor = 0 - + if (distance <= 1) pressure_factor = max(pressure_factor, 0.15) //hearing through contact - + S.volume *= pressure_factor - + if (S.volume <= 0) return //no volume means no sound - + var/dx = turf_source.x - T.x // Hearing from the right/left S.x = dx var/dz = turf_source.y - T.y // Hearing from infront/behind @@ -113,4 +131,10 @@ var/const/FALLOFF_SOUNDS = 0.5 if ("hiss") soundin = pick(hiss_sound) if ("pageturn") soundin = pick(page_sound) //if ("gunshot") soundin = pick(gun_sound) + if ("defaultstep") soundin = pick(defaultfootsteps) + if ("concretestep") soundin = pick(concretefootsteps) + if ("grassstep") soundin = pick(grassfootsteps) + if ("dirtstep") soundin = pick(dirtfootsteps) + if ("waterstep") soundin = pick(waterfootsteps) + if ("sandstep") soundin = pick(sandfootsteps) return soundin \ No newline at end of file diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm index 58383bfd..bcebd599 100644 --- a/code/game/turfs/simulated/floor.dm +++ b/code/game/turfs/simulated/floor.dm @@ -33,9 +33,11 @@ var/list/wood_icons = list("wood","wood-broken") var/lava = 0 var/broken = 0 var/burnt = 0 + var/fire_damage = 0 var/mineral = "metal" var/obj/item/stack/tile/floor_tile = new/obj/item/stack/tile/plasteel + footstep_sound = "defaultstep" /turf/simulated/floor/New() ..() @@ -76,11 +78,31 @@ var/list/wood_icons = list("wood","wood-broken") return /turf/simulated/floor/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(!burnt && prob(5)) + if(exposed_temperature > 500+T0C && !(fire_damage >= heat_capacity)) + fire_damage += round((exposed_temperature * thermal_conductivity)*10) + if(!burnt && fire_damage >= round(heat_capacity / 3)) burn_tile() - else if(prob(1) && !is_plating()) + else if(!is_plating() && fire_damage > round(heat_capacity / 1.5)) //What are these numbers, I have no idea, I pulled them out of a hat. No really I did. make_plating() burn_tile() + for(var/turf/simulated/floor/RT in AdjacentTurfs()) + if(!RT.burnt) + RT.burn_tile() + else if(fire_damage > heat_capacity && prob(1)) + for(var/turf/simulated/floor/RT in AdjacentTurfs()) + if(istype(RT) && (RT.x == src.x || RT.y == src.y) && prob(20)) + RT.burnPipes() + RT.ReplaceWithLattice() + burnPipes() + ReplaceWithLattice() + return + +/turf/simulated/floor/proc/burnPipes() + for(var/obj/machinery/M in src) + if(istype(M, /obj/machinery)) + M.blob_act() + for(var/obj/structure/disposalpipe/DP in src) + blob_act() return /turf/simulated/floor/adjacent_fire_act(turf/simulated/floor/adj_turf, datum/gas_mixture/adj_air, adj_temp, adj_volume) @@ -558,5 +580,6 @@ turf/simulated/floor/proc/update_icon() icon_state = "plating" burnt = 0 broken = 0 + fire_damage = 0 else user << "\blue You need more welding fuel to complete this task." \ No newline at end of file diff --git a/code/game/turfs/simulated/floor_types.dm b/code/game/turfs/simulated/floor_types.dm index 0b330cc6..50a81113 100644 --- a/code/game/turfs/simulated/floor_types.dm +++ b/code/game/turfs/simulated/floor_types.dm @@ -140,11 +140,13 @@ /turf/simulated/shuttle/floor name = "floor" icon_state = "floor" + footstep_sound = "concretefootsteps" /turf/simulated/shuttle/plating name = "plating" icon = 'icons/turf/floors.dmi' icon_state = "plating" + footstep_sound = "concretefootsteps" /turf/simulated/shuttle/plating/vox //Vox skipjack plating oxygen = 0 @@ -162,19 +164,23 @@ /turf/simulated/floor/beach name = "Beach" icon = 'icons/misc/beach.dmi' + footstep_sound = "sandstep" /turf/simulated/floor/beach/sand name = "Sand" icon_state = "sand" + footstep_sound = "sandstep" /turf/simulated/floor/beach/coastline name = "Coastline" icon = 'icons/misc/beach2.dmi' icon_state = "sandwater" + footstep_sound = "sandstep" /turf/simulated/floor/beach/water name = "Water" icon_state = "water" + footstep_sound = "waterstep" /turf/simulated/floor/beach/water/New() ..() @@ -184,6 +190,7 @@ name = "Grass patch" icon_state = "grass1" floor_tile = new/obj/item/stack/tile/grass + footstep_sound = "grassstep" New() floor_tile.New() //I guess New() isn't ran on objects spawned without the definition of a turf to house them, ah well. @@ -201,6 +208,8 @@ name = "Carpet" icon_state = "carpet" floor_tile = new/obj/item/stack/tile/carpet + footstep_sound = null + New() floor_tile.New() //I guess New() isn't ran on objects spawned without the definition of a turf to house them, ah well. @@ -226,6 +235,7 @@ name = "snow" icon = 'icons/turf/snow.dmi' icon_state = "snow" + footstep_sound = "grassstep" /turf/simulated/floor/plating/snow/ex_act(severity) return \ No newline at end of file diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index 1d85dce3..9a0e5a06 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -2,6 +2,7 @@ icon = 'icons/turf/space.dmi' name = "\proper space" icon_state = "0" + footstep_sound = null //Override to make sure because yeah temperature = T0C thermal_conductivity = OPEN_HEAT_TRANSFER_COEFFICIENT diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 34639d44..2a7d3ac7 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -26,6 +26,8 @@ var/has_resources var/list/resources + var/footstep_sound = null + /turf/New() ..() for(var/atom/movable/AM as mob|obj in src) @@ -97,6 +99,7 @@ if(movement_disabled) usr << "\red Movement is admin-disabled." //This is to identify lag problems return + ..() //vvvvv Infared beam stuff vvvvv @@ -121,6 +124,19 @@ if(M:lastarea.has_gravity == 0) inertial_drift(M) + var/mob/living/carbon/human/MOB = M + if(istype(MOB) && !MOB.lying && footstep_sound) + if(istype(MOB.shoes, /obj/item/clothing/shoes) && !MOB.shoes:silent) + if(MOB.m_intent == "run") + if(MOB.footstep >= 2) + MOB.footstep = 0 + else + MOB.footstep++ + if(MOB.footstep == 0) + playsound(MOB, footstep_sound, 50, 1) // this will get annoying very fast. - Tell them to mute it then -_- + else + playsound(MOB, footstep_sound, 40, 1) + /* if(M.flags & NOGRAV) inertial_drift(M) diff --git a/code/game/turfs/unsimulated/beach.dm b/code/game/turfs/unsimulated/beach.dm index 844f986e..5284e9c8 100644 --- a/code/game/turfs/unsimulated/beach.dm +++ b/code/game/turfs/unsimulated/beach.dm @@ -5,15 +5,18 @@ /turf/unsimulated/beach/sand name = "Sand" icon_state = "sand" + footstep_sound = "sandstep" /turf/unsimulated/beach/coastline name = "Coastline" icon = 'icons/misc/beach2.dmi' icon_state = "sandwater" + footstep_sound = "sandstep" /turf/unsimulated/beach/water name = "Water" icon_state = "water" + footstep_sound = "waterstep" /turf/unsimulated/beach/water/New() ..() diff --git a/code/global.dm b/code/global.dm index 90c139f7..6baaf9f9 100644 --- a/code/global.dm +++ b/code/global.dm @@ -195,13 +195,6 @@ var/forceblob = 0 // nanomanager, the manager for Nano UIs var/datum/nanomanager/nanomanager = new() - //airlockWireColorToIndex takes a number representing the wire color, e.g. the orange wire is always 1, the dark red wire is always 2, etc. It returns the index for whatever that wire does. - //airlockIndexToWireColor does the opposite thing - it takes the index for what the wire does, for example AIRLOCK_WIRE_IDSCAN is 1, AIRLOCK_WIRE_POWER1 is 2, etc. It returns the wire color number. - //airlockWireColorToFlag takes the wire color number and returns the flag for it (1, 2, 4, 8, 16, etc) -var/list/globalAirlockWireColorToFlag = RandomAirlockWires() -var/list/globalAirlockIndexToFlag -var/list/globalAirlockIndexToWireColor -var/list/globalAirlockWireColorToIndex var/list/APCWireColorToFlag = RandomAPCWires() var/list/APCIndexToFlag var/list/APCIndexToWireColor diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index ff749537..9b07c86a 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -16,6 +16,7 @@ var/obj/item/device/assembly_holder/holder = null var/cooldown = 0//To prevent spam var/wires = WIRE_RECEIVE | WIRE_PULSE + var/datum/wires/connected = null // currently only used by timer/signaler var/const/WIRE_RECEIVE = 1 //Allows Pulsed(0) to call Activate() var/const/WIRE_PULSE = 2 //Allows Pulse(0) to act on the holder diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm index 87aa04b9..ae18faed 100644 --- a/code/modules/assembly/signaler.dm +++ b/code/modules/assembly/signaler.dm @@ -118,14 +118,11 @@ pulse(var/radio = 0) - if(istype(src.loc, /obj/machinery/door/airlock) && src.airlock_wire && src.wires) - var/obj/machinery/door/airlock/A = src.loc - A.pulse(src.airlock_wire) - else if(holder) - holder.process_activation(src, 1, 0) + if(src.connected && src.wires) + connected.Pulse(src) else - ..(radio) - return 1 + return ..(radio) + receive_signal(datum/signal/signal) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index daed9a39..dd6963e3 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -46,6 +46,7 @@ datum/preferences var/be_special = 0 //Special role selection var/UI_style = "Midnight" var/toggles = TOGGLES_DEFAULT + var/asfx_togs = ASFX_DEFAULT var/UI_style_color = "#ffffff" var/UI_style_alpha = 255 @@ -496,7 +497,10 @@ datum/preferences HTML += "" var/rank = job.title lastJob = job - if(jobban_isbanned(user, rank)) + if(jobban_isbanned(user, rank) == "Whitelisted Job") + HTML += "[rank] \[WHITELISTED]" + continue + else if(jobban_isbanned(user, rank)) HTML += "[rank] \[BANNED]" continue if(!job.player_old_enough(user.client)) diff --git a/code/modules/client/preferences_ambience.dm b/code/modules/client/preferences_ambience.dm new file mode 100644 index 00000000..fe75a1ed --- /dev/null +++ b/code/modules/client/preferences_ambience.dm @@ -0,0 +1,52 @@ +/var/global/asfx_togs = list( + /client/proc/Toggle_asfx, + /client/proc/Toggle_footsteps, + /client/proc/Toggle_asfx_vote +) + +/client/verb/asf_toggle() + set name = "Open ASFX Tab" + set category = "Preferences" + set desc = "Open the ambiance sound effects toggle tab" + + verbs ^= asfx_togs + return + +/client/proc/Toggle_asfx() //Allgnew ambience should be added here so it works with this verb until someone better at things comes up with a fix that isn't awful + set name = "Hear/Silence Ambience" + set category = "SoundFx Prefs" + set desc = "Toggles hearing ambient sound effects" + prefs.asfx_togs ^= ASFX_AMBIENCE + prefs.save_preferences() + if(prefs.asfx_togs & ASFX_AMBIENCE) + src << "You will now hear ambient sounds." + else + src << "You will no longer hear ambient sounds." + src << sound(null, repeat = 0, wait = 0, volume = 0, channel = 1) + src << sound(null, repeat = 0, wait = 0, volume = 0, channel = 2) + feedback_add_details("admin_verb","TSFXAmbi") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/Toggle_footsteps() + set name = "Hear/Silence Footsteps" + set category = "SoundFx Prefs" + set desc = "Toggles hearing footstep sound effects" + + prefs.asfx_togs ^= ASFX_FOOTSTEPS + prefs.save_preferences() + if(prefs.asfx_togs & ASFX_FOOTSTEPS) + src << "You will now hear footstep sounds." + else + src << "You will no longer hear footstep sounds." + feedback_add_details("admin_verb","TSFXFS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/Toggle_asfx_vote() + set name = "Hear/Silence Vote Alarm" + set category = "SoundFx Prefs" + set desc = "Toggles hearing of the vote alarm" + prefs.asfx_togs ^= ASFX_VOTE + prefs.save_preferences() + if(prefs.asfx_togs & ASFX_VOTE) + src << "You will now hear the vote alarm." + else + src << "You will no longer hear the vote alarm." + feedback_add_details("admin_verb","TSFXFV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! \ No newline at end of file diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index d30d80c1..b8237e1e 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -56,6 +56,7 @@ S["toggles"] >> toggles S["UI_style_color"] >> UI_style_color S["UI_style_alpha"] >> UI_style_alpha + S["asfx_togs"] >> asfx_togs //Sanitize ooccolor = sanitize_hexcolor(ooccolor, initial(ooccolor)) @@ -64,6 +65,7 @@ be_special = sanitize_integer(be_special, 0, 65535, initial(be_special)) default_slot = sanitize_integer(default_slot, 1, MAX_SAVE_SLOTS, initial(default_slot)) toggles = sanitize_integer(toggles, 0, 65535, initial(toggles)) + asfx_togs = sanitize_integer(asfx_togs, 0, 65535, initial(asfx_togs)) UI_style_color = sanitize_hexcolor(UI_style_color, initial(UI_style_color)) UI_style_alpha = sanitize_integer(UI_style_alpha, 0, 255, initial(UI_style_alpha)) @@ -84,6 +86,7 @@ S["be_special"] << be_special S["default_slot"] << default_slot S["toggles"] << toggles + S["asfx_togs"] << asfx_togs return 1 diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm index 78d8b6d7..0586d8e0 100644 --- a/code/modules/client/preferences_toggles.dm +++ b/code/modules/client/preferences_toggles.dm @@ -121,7 +121,7 @@ feedback_add_details("admin_verb","TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - +/* /client/verb/Toggle_Soundscape() //All new ambience should be added here so it works with this verb until someone better at things comes up with a fix that isn't awful set name = "Hear/Silence Ambience" set category = "Preferences" @@ -135,6 +135,7 @@ src << sound(null, repeat = 0, wait = 0, volume = 0, channel = 1) src << sound(null, repeat = 0, wait = 0, volume = 0, channel = 2) feedback_add_details("admin_verb","TAmbi") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +*/ //be special /client/verb/toggle_be_special(role in be_special_flags) diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 8f32efd1..aa3344b0 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -280,6 +280,7 @@ BLIND // can't see anything slowdown = SHOES_SLOWDOWN species_restricted = list("exclude","Unathi","Tajaran") sprite_sheets = list("Vox" = 'icons/mob/species/vox/shoes.dmi') + var/silent = 0 /obj/item/clothing/shoes/update_clothing_icon() if (ismob(src.loc)) diff --git a/code/modules/mining/mine_turfs.dm b/code/modules/mining/mine_turfs.dm index e3924ece..45bbe58a 100644 --- a/code/modules/mining/mine_turfs.dm +++ b/code/modules/mining/mine_turfs.dm @@ -400,6 +400,7 @@ var/list/artifact_spawn = list() // Runtime fix for geometry loading before cont var/dug = 0 //0 = has not yet been dug, 1 = has already been dug var/overlay_detail has_resources = 1 + footstep_sound = "gravelstep" /turf/simulated/floor/plating/airless/asteroid/New() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index ea02387b..3f643656 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -482,39 +482,52 @@ var/obj/vehicle/V = AM V.RunOver(src) +// Get rank from ID, ID inside PDA, PDA, ID in wallet, etc. +/mob/living/carbon/human/proc/get_authentification_rank(var/if_no_id = "No id", var/if_no_job = "No job") + var/obj/item/device/pda/pda = wear_id + if (istype(pda)) + if (pda.id) + return pda.id.rank + else + return pda.ownrank + else + var/obj/item/weapon/card/id/id = get_idcard() + if(id) + return id.rank ? id.rank : if_no_job + else + return if_no_id + //gets assignment from ID or ID inside PDA or PDA itself //Useful when player do something with computers /mob/living/carbon/human/proc/get_assignment(var/if_no_id = "No id", var/if_no_job = "No job") var/obj/item/device/pda/pda = wear_id - var/obj/item/weapon/card/id/id = wear_id if (istype(pda)) - if (pda.id && istype(pda.id, /obj/item/weapon/card/id)) - . = pda.id.assignment + if (pda.id) + return pda.id.assignment else - . = pda.ownjob - else if (istype(id)) - . = id.assignment + return pda.ownjob else - return if_no_id - if (!.) - . = if_no_job - return + var/obj/item/weapon/card/id/id = get_idcard() + if(id) + return id.assignment ? id.assignment : if_no_job + else + return if_no_id //gets name from ID or ID inside PDA or PDA itself //Useful when player do something with computers /mob/living/carbon/human/proc/get_authentification_name(var/if_no_id = "Unknown") var/obj/item/device/pda/pda = wear_id - var/obj/item/weapon/card/id/id = wear_id if (istype(pda)) if (pda.id) - . = pda.id.registered_name + return pda.id.registered_name else - . = pda.owner - else if (istype(id)) - . = id.registered_name + return pda.owner else - return if_no_id - return + var/obj/item/weapon/card/id/id = get_idcard() + if(id) + return id.registered_name + else + return if_no_id //repurposed proc. Now it combines get_id_name() and get_face_name() to determine a mob's name variable. Made into a seperate proc as it'll be useful elsewhere /mob/living/carbon/human/proc/get_visible_name() @@ -955,13 +968,13 @@ if(!lastpuke) lastpuke = 1 - src << "You feel nauseous..." + src << "You feel nauseous..." spawn(150) //15 seconds until second warning - src << "You feel like you are about to throw up!" + src << "You feel like you are about to throw up!" spawn(100) //and you have 10 more for mad dash to the bucket Stun(5) - src.visible_message("[src] throws up!","You throw up!") + src.visible_message("[src] throws up!","You throw up!") playsound(loc, 'sound/effects/splat.ogg', 50, 1) var/turf/location = loc @@ -1077,6 +1090,7 @@ else target.show_message("\blue You hear a voice that seems to echo around the room: [say]") usr.show_message("\blue You project your mind into [target.real_name]: [say]") + log_say("[key_name(usr)] sent a telepathic message to [key_name(target)]: [say]") for(var/mob/dead/observer/G in world) G.show_message("Telepathic message from [src] to [target]: [say]") diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 1f275969..8f202afa 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -49,4 +49,5 @@ var/attacktext = "attacks" var/attack_sound = null var/friendly = "nuzzles" - var/wall_smash = 0 \ No newline at end of file + var/wall_smash = 0 + var/footstep = 0 \ No newline at end of file diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 961b5969..b4495d47 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -75,6 +75,7 @@ var/list/ai_verbs_default = list( var/mob/living/silicon/ai/parent = null + var/apc_override = 0 //hack for letting the AI use its APC even when visionless var/camera_light_on = 0 //Defines if the AI toggled the light on the camera it's looking through. var/datum/trackable/track = null var/last_announcement = "" @@ -820,5 +821,8 @@ var/list/ai_verbs_default = list( return 1 return 0 +/mob/living/silicon/ai/proc/is_in_chassis() + return istype(loc, /turf) + #undef AI_CHECK_WIRELESS #undef AI_CHECK_RADIO diff --git a/code/modules/nano/JSON Writer.dm b/code/modules/nano/JSON Writer.dm index 3cd3520f..65b9d99e 100644 --- a/code/modules/nano/JSON Writer.dm +++ b/code/modules/nano/JSON Writer.dm @@ -1,7 +1,7 @@ json_writer proc - WriteObject(list/L) + WriteObject(list/L, cached_data = null) . = "{" var/i = 1 for(var/k in L) @@ -9,7 +9,9 @@ json_writer . += {"\"[k]\":[write(val)]"} if(i++ < L.len) . += "," - .+= "}" + if(cached_data) + . = copytext(., 1, lentext(.)) + ",\"cached\":[cached_data]}" + . += "}" write(val) if(isnum(val)) diff --git a/code/modules/nano/_JSON.dm b/code/modules/nano/_JSON.dm index 5692e643..4f70e6e6 100644 --- a/code/modules/nano/_JSON.dm +++ b/code/modules/nano/_JSON.dm @@ -7,6 +7,6 @@ proc var/static/json_reader/_jsonr = new() return _jsonr.ReadObject(_jsonr.ScanJson(json)) - list2json(list/L) + list2json(list/L, var/cached_data = null) var/static/json_writer/_jsonw = new() - return _jsonw.WriteObject(L) + return _jsonw.WriteObject(L, cached_data) diff --git a/code/modules/nano/modules/alarm_monitor.dm b/code/modules/nano/modules/alarm_monitor.dm new file mode 100644 index 00000000..888b1c74 --- /dev/null +++ b/code/modules/nano/modules/alarm_monitor.dm @@ -0,0 +1,96 @@ +/obj/nano_module/alarm_monitor + name = "Alarm monitor" + var/list_cameras = 0 // Whether or not to list camera references. A future goal would be to merge this with the enginering/security camera console. Currently really only for AI-use. + var/list/datum/alarm_handler/alarm_handlers // The particular list of alarm handlers this alarm monitor should present to the user. + +/obj/nano_module/alarm_monitor/ai + list_cameras = 1 + +/obj/nano_module/alarm_monitor/ai/New() + ..() + alarm_handlers = alarm_manager.all_handlers + +/obj/nano_module/alarm_monitor/borg/New() + ..() + alarm_handlers = alarm_manager.all_handlers + +/obj/nano_module/alarm_monitor/engineering/New() + ..() + alarm_handlers = list(atmosphere_alarm, fire_alarm, power_alarm) + +/obj/nano_module/alarm_monitor/security/New() + ..() + alarm_handlers = list(camera_alarm, motion_alarm) + +/obj/nano_module/alarm_monitor/proc/register(var/object, var/procName) + for(var/datum/alarm_handler/AH in alarm_handlers) + AH.register(object, procName) + +/obj/nano_module/alarm_monitor/proc/unregister(var/object) + for(var/datum/alarm_handler/AH in alarm_handlers) + AH.unregister(object) + +/obj/nano_module/alarm_monitor/proc/all_alarms() + var/list/all_alarms = new() + for(var/datum/alarm_handler/AH in alarm_handlers) + all_alarms += AH.alarms + + return all_alarms + +/obj/nano_module/alarm_monitor/proc/major_alarms() + var/list/all_alarms = new() + for(var/datum/alarm_handler/AH in alarm_handlers) + all_alarms += AH.major_alarms() + + return all_alarms + +/obj/nano_module/alarm_monitor/proc/minor_alarms() + var/list/all_alarms = new() + for(var/datum/alarm_handler/AH in alarm_handlers) + all_alarms += AH.minor_alarms() + + return all_alarms + +/obj/nano_module/alarm_monitor/ai/Topic(ref, href_list) + if(..()) + return 1 + if(href_list["switchTo"]) + var/obj/machinery/camera/C = locate(href_list["switchTo"]) in cameranet.cameras + if(!C) + + return + + usr.switch_to_camera(C) + return 1 + +/obj/nano_module/alarm_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + var/data[0] + + var/categories[0] + for(var/datum/alarm_handler/AH in alarm_handlers) + categories[++categories.len] = list("category" = AH.category, "alarms" = list()) + for(var/datum/alarm/A in AH.major_alarms()) + var/cameras[0] + var/lost_sources[0] + + if(list_cameras) + for(var/obj/machinery/camera/C in A.cameras()) + cameras[++cameras.len] = C.nano_structure() + for(var/datum/alarm_source/AS in A.sources) + if(!AS.source) + lost_sources[++lost_sources.len] = AS.source_name + + categories[categories.len]["alarms"] += list(list( + "name" = sanitize(A.alarm_name()), + "origin_lost" = A.origin == null, + "has_cameras" = cameras.len, + "cameras" = cameras, + "lost_sources" = lost_sources.len ? sanitize(english_list(lost_sources, nothing_text = "", and_text = ", ")) : "")) + data["categories"] = categories + + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "alarm_monitor.tmpl", "Alarm Monitoring Console", 800, 800) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) diff --git a/code/modules/nano/modules/crew_monitor.dm b/code/modules/nano/modules/crew_monitor.dm new file mode 100644 index 00000000..c27c0446 --- /dev/null +++ b/code/modules/nano/modules/crew_monitor.dm @@ -0,0 +1,88 @@ +/obj/nano_module/crew_monitor + name = "Crew monitor" + var/list/tracked = new + +/obj/nano_module/crew_monitor/Topic(href, href_list) + if(..()) return + var/turf/T = get_turf(src) + if (!T || !(T.z in config.player_levels)) + usr << "Unable to establish a connection: You're too far away from the station!" + return 0 + if(href_list["close"] ) + var/mob/user = usr + var/datum/nanoui/ui = nanomanager.get_open_ui(user, src, "main") + usr.unset_machine() + ui.close() + return 0 + if(href_list["update"]) + src.updateDialog() + return 1 + +/obj/nano_module/crew_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + user.set_machine(src) + src.scan() + + var/data[0] + var/turf/T = get_turf(src) + var/list/crewmembers = list() + for(var/obj/item/clothing/under/C in src.tracked) + + var/turf/pos = get_turf(C) + + if((C) && (C.has_sensor) && (pos) && (T && pos.z == T.z) && (C.sensor_mode != SUIT_SENSOR_OFF)) + if(istype(C.loc, /mob/living/carbon/human)) + + var/mob/living/carbon/human/H = C.loc + if(H.w_uniform != C) + continue + + var/list/crewmemberData = list("dead"=0, "oxy"=-1, "tox"=-1, "fire"=-1, "brute"=-1, "area"="", "x"=-1, "y"=-1) + + crewmemberData["sensor_type"] = C.sensor_mode + crewmemberData["name"] = H.get_authentification_name(if_no_id="Unknown") + crewmemberData["rank"] = H.get_authentification_rank(if_no_id="Unknown", if_no_job="No Job") + crewmemberData["assignment"] = H.get_assignment(if_no_id="Unknown", if_no_job="No Job") + + if(C.sensor_mode >= SUIT_SENSOR_BINARY) + crewmemberData["dead"] = H.stat > 1 + + if(C.sensor_mode >= SUIT_SENSOR_VITAL) + crewmemberData["oxy"] = round(H.getOxyLoss(), 1) + crewmemberData["tox"] = round(H.getToxLoss(), 1) + crewmemberData["fire"] = round(H.getFireLoss(), 1) + crewmemberData["brute"] = round(H.getBruteLoss(), 1) + + if(C.sensor_mode >= SUIT_SENSOR_TRACKING) + var/area/A = get_area(H) + crewmemberData["area"] = sanitize(A.name) + crewmemberData["x"] = pos.x + crewmemberData["y"] = pos.y + + crewmembers[++crewmembers.len] = crewmemberData + + crewmembers = sortByKey(crewmembers, "name") + + data["crewmembers"] = crewmembers + + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + if(!ui) + ui = new(user, src, ui_key, "crew_monitor.tmpl", "Crew Monitoring Computer", 900, 800) + + // adding a template with the key "mapContent" enables the map ui functionality + ui.add_template("mapContent", "crew_monitor_map_content.tmpl") + // adding a template with the key "mapHeader" replaces the map header content + ui.add_template("mapHeader", "crew_monitor_map_header.tmpl") + + ui.set_initial_data(data) + ui.open() + + // should make the UI auto-update; doesn't seem to? + ui.set_auto_update(1) + +/obj/nano_module/crew_monitor/proc/scan() + for(var/mob/living/carbon/human/H in mob_list) + if(istype(H.w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/C = H.w_uniform + if (C.has_sensor) + tracked |= C + return 1 diff --git a/code/modules/nano/modules/power_monitor.dm b/code/modules/nano/modules/power_monitor.dm new file mode 100644 index 00000000..2b6a7183 --- /dev/null +++ b/code/modules/nano/modules/power_monitor.dm @@ -0,0 +1,60 @@ +/obj/nano_module/power_monitor + name = "Power monitor" + var/list/grid_sensors + var/active_sensor = null //name_tag of the currently selected sensor + +/obj/nano_module/power_monitor/New() + ..() + refresh_sensors() + +/obj/nano_module/power_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + var/list/data = list() + var/list/sensors = list() + // Focus: If it remains null if no sensor is selected and UI will display sensor list, otherwise it will display sensor reading. + var/obj/machinery/power/sensor/focus = null + + // Build list of data from sensor readings. + for(var/obj/machinery/power/sensor/S in grid_sensors) + sensors.Add(list(list( + "name" = S.name_tag, + "alarm" = S.check_grid_warning() + ))) + if(S.name_tag == active_sensor) + focus = S + + data["all_sensors"] = sensors + if(focus) + data["focus"] = focus.return_reading_data() + + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "power_monitor.tmpl", "Power Monitoring Console", 800, 500) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +// Proc: refresh_sensors() +// Parameters: None +// Description: Refreshes list of active sensors kept on this computer. +/obj/nano_module/power_monitor/proc/refresh_sensors() + grid_sensors = list() + var/turf/T = get_turf(src) + for(var/obj/machinery/power/sensor/S in machines) + if((T && S.loc.z == T.z) || (S.long_range)) // Consoles have range on their Z-Level. Sensors with long_range var will work between Z levels. + if(S.name_tag == "#UNKN#") // Default name. Shouldn't happen! + warning("Powernet sensor with unset ID Tag! [S.x]X [S.y]Y [S.z]Z") + else + grid_sensors += S + +// Proc: Topic() +// Parameters: 2 (href, href_list - allows us to process UI clicks) +// Description: Allows us to process UI clicks, which are relayed in form of hrefs. +/obj/nano_module/power_monitor/Topic(href, href_list) + if(..()) + return + if( href_list["clear"] ) + active_sensor = null + if( href_list["refresh"] ) + refresh_sensors() + else if( href_list["setsensor"] ) + active_sensor = href_list["setsensor"] diff --git a/code/modules/nano/modules/rcon.dm b/code/modules/nano/modules/rcon.dm new file mode 100644 index 00000000..5be77f7a --- /dev/null +++ b/code/modules/nano/modules/rcon.dm @@ -0,0 +1,116 @@ +/obj/nano_module/rcon + name = "Power RCON" + + var/list/known_SMESs = null + var/list/known_breakers = null + // Allows you to hide specific parts of the UI + var/hide_SMES = 0 + var/hide_SMES_details = 0 + var/hide_breakers = 0 + +/obj/nano_module/rcon/ui_interact(mob/user, ui_key = "rcon", datum/nanoui/ui=null, force_open=1) + FindDevices() // Update our devices list + var/data[0] + + // SMES DATA (simplified view) + var/list/smeslist[0] + for(var/obj/machinery/power/smes/buildable/SMES in known_SMESs) + smeslist.Add(list(list( + "charge" = round(SMES.Percentage()), + "input_set" = SMES.input_attempt, + "input_val" = round(SMES.input_level), + "output_set" = SMES.output_attempt, + "output_val" = round(SMES.output_level), + "output_load" = round(SMES.output_used), + "RCON_tag" = SMES.RCon_tag + ))) + + data["smes_info"] = sortByKey(smeslist, "RCON_tag") + + // BREAKER DATA (simplified view) + var/list/breakerlist[0] + for(var/obj/machinery/power/breakerbox/BR in known_breakers) + breakerlist.Add(list(list( + "RCON_tag" = BR.RCon_tag, + "enabled" = BR.on + ))) + data["breaker_info"] = breakerlist + data["hide_smes"] = hide_SMES + data["hide_smes_details"] = hide_SMES_details + data["hide_breakers"] = hide_breakers + + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "rcon.tmpl", "RCON Console", 600, 400) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +// Proc: Topic() +// Parameters: 2 (href, href_list - allows us to process UI clicks) +// Description: Allows us to process UI clicks, which are relayed in form of hrefs. +/obj/nano_module/rcon/Topic(href, href_list) + if(..()) + return + + if(href_list["smes_in_toggle"]) + var/obj/machinery/power/smes/buildable/SMES = GetSMESByTag(href_list["smes_in_toggle"]) + if(SMES) + SMES.toggle_input() + if(href_list["smes_out_toggle"]) + var/obj/machinery/power/smes/buildable/SMES = GetSMESByTag(href_list["smes_out_toggle"]) + if(SMES) + SMES.toggle_output() + if(href_list["smes_in_set"]) + var/obj/machinery/power/smes/buildable/SMES = GetSMESByTag(href_list["smes_in_set"]) + if(SMES) + var/inputset = input(usr, "Enter new input level (0-[SMES.input_level_max])", "SMES Input Power Control") as num + SMES.set_input(inputset) + if(href_list["smes_out_set"]) + var/obj/machinery/power/smes/buildable/SMES = GetSMESByTag(href_list["smes_out_set"]) + if(SMES) + var/outputset = input(usr, "Enter new output level (0-[SMES.output_level_max])", "SMES Input Power Control") as num + SMES.set_output(outputset) + + if(href_list["toggle_breaker"]) + var/obj/machinery/power/breakerbox/toggle = null + for(var/obj/machinery/power/breakerbox/breaker in known_breakers) + if(breaker.RCon_tag == href_list["toggle_breaker"]) + toggle = breaker + if(toggle) + if(toggle.update_locked) + usr << "The breaker box was recently toggled. Please wait before toggling it again." + else + toggle.auto_toggle() + if(href_list["hide_smes"]) + hide_SMES = !hide_SMES + if(href_list["hide_smes_details"]) + hide_SMES_details = !hide_SMES_details + if(href_list["hide_breakers"]) + hide_breakers = !hide_breakers + + +// Proc: GetSMESByTag() +// Parameters: 1 (tag - RCON tag of SMES we want to look up) +// Description: Looks up and returns SMES which has matching RCON tag +/obj/nano_module/rcon/proc/GetSMESByTag(var/tag) + if(!tag) + return + + for(var/obj/machinery/power/smes/buildable/S in known_SMESs) + if(S.RCon_tag == tag) + return S + +// Proc: FindDevices() +// Parameters: None +// Description: Refreshes local list of known devices. +/obj/nano_module/rcon/proc/FindDevices() + known_SMESs = new /list() + for(var/obj/machinery/power/smes/buildable/SMES in machines) + if(SMES.RCon_tag && (SMES.RCon_tag != "NO_TAG") && SMES.RCon) + known_SMESs.Add(SMES) + + known_breakers = new /list() + for(var/obj/machinery/power/breakerbox/breaker in machines) + if(breaker.RCon_tag != "NO_TAG") + known_breakers.Add(breaker) diff --git a/code/modules/nano/nanoexternal.dm b/code/modules/nano/nanoexternal.dm index 50e2b706..b029019f 100644 --- a/code/modules/nano/nanoexternal.dm +++ b/code/modules/nano/nanoexternal.dm @@ -1,5 +1,5 @@ - // This file contains all Nano procs/definitions for external classes/objects - + // This file contains all Nano procs/definitions for external classes/objects + /** * Called when a Nano UI window is closed * This is how Nano handles closed windows @@ -15,7 +15,7 @@ if (istype(ui)) ui.close() - + if(ui.ref) var/href = "close=1" src.Topic(href, params2list(href), ui.ref) // this will direct to the atom's Topic() proc via client.Topic() @@ -31,14 +31,14 @@ * ui_interact is currently defined for /atom/movable * * @param user /mob The mob who is interacting with this ui - * @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main") - * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui + * @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main") + * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui * @param force_open boolean Force the UI to (re)open, even if it's already open * * @return nothing */ -/atom/movable/proc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) +/atom/movable/proc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/nano_ui/master_ui = null, var/datum/topic_state/custom_state = null) return - + // Used by the Nano UI Manager (/datum/nanomanager) to track UIs opened by this mob /mob/var/list/open_uis = list() diff --git a/code/modules/nano/nanointeraction.dm b/code/modules/nano/nanointeraction.dm new file mode 100644 index 00000000..96f1f3cc --- /dev/null +++ b/code/modules/nano/nanointeraction.dm @@ -0,0 +1,131 @@ +/atom/movable/proc/nano_host() + return src + +/obj/nano_module/nano_host() + return loc + + +/atom/movable/proc/CanUseTopic(var/mob/user, href_list, var/datum/topic_state/custom_state) + return user.can_use_topic(nano_host(), custom_state) + + +/mob/proc/can_use_topic(var/mob/user, var/datum/topic_state/custom_state) + return STATUS_CLOSE // By default no mob can do anything with NanoUI + +/mob/dead/observer/can_use_topic() + if(check_rights(R_ADMIN, 0, src)) + return STATUS_INTERACTIVE // Admins are more equal + return STATUS_UPDATE // Ghosts can view updates + +/mob/living/silicon/pai/can_use_topic(var/src_object) + if(src_object == src && !stat) + return STATUS_INTERACTIVE + else + return ..() + +/mob/living/silicon/robot/can_use_topic(var/src_object, var/datum/topic_state/custom_state) + if(stat || !client) + return STATUS_CLOSE + if(lockcharge || stunned || weakened) + return STATUS_DISABLED + if(custom_state.flags & NANO_IGNORE_DISTANCE) + return STATUS_INTERACTIVE + // robots can interact with things they can see within their view range + if((src_object in view(src)) && get_dist(src_object, src) <= src.client.view) + return STATUS_INTERACTIVE // interactive (green visibility) + return STATUS_DISABLED // no updates, completely disabled (red visibility) + +/mob/living/silicon/robot/syndicate/can_use_topic(var/src_object) + . = ..() + if(. != STATUS_INTERACTIVE) + return + + if(z in config.admin_levels) // Syndicate borgs can interact with everything on the admin level + return STATUS_INTERACTIVE + if(istype(get_area(src), /area/syndicate_station)) // If elsewhere, they can interact with everything on the syndicate shuttle + return STATUS_INTERACTIVE + if(istype(src_object, /obj/machinery)) // Otherwise they can only interact with emagged machinery + var/obj/machinery/Machine = src_object + if(Machine.emagged) + return STATUS_INTERACTIVE + return STATUS_UPDATE + +/mob/living/silicon/ai/can_use_topic(var/src_object) + if(!client || check_unable(1)) + return STATUS_CLOSE + // Prevents the AI from using Topic on admin levels (by for example viewing through the court/thunderdome cameras) + // unless it's on the same level as the object it's interacting with. + var/turf/T = get_turf(src_object) + if(!T || !(z == T.z || (T.z in config.player_levels))) + return STATUS_CLOSE + + // If an object is in view then we can interact with it + if(src_object in view(client.view, src)) + return STATUS_INTERACTIVE + + // If we're installed in a chassi, rather than transfered to an inteliCard or other container, then check if we have camera view + if(is_in_chassis()) + //stop AIs from leaving windows open and using then after they lose vision + //apc_override is needed here because AIs use their own APC when powerless + if(cameranet && !cameranet.checkTurfVis(get_turf(src_object))) + return apc_override ? STATUS_INTERACTIVE : STATUS_CLOSE + return STATUS_INTERACTIVE + else if(get_dist(src_object, src) <= client.view) // View does not return what one would expect while installed in an inteliCard + return STATUS_INTERACTIVE + + return STATUS_CLOSE + +/mob/living/proc/shared_living_nano_interaction(var/src_object) + if (src.stat != CONSCIOUS) + return STATUS_CLOSE // no updates, close the interface + else if (restrained() || lying || stat || stunned || weakened) + return STATUS_UPDATE // update only (orange visibility) + return STATUS_INTERACTIVE + +//Some atoms such as vehicles might have special rules for how mobs inside them interact with NanoUI. +/atom/proc/contents_nano_distance(var/src_object, var/mob/living/user) + return user.shared_living_nano_distance(src_object) + +/mob/living/proc/shared_living_nano_distance(var/atom/movable/src_object) + if(!isturf(src_object.loc)) + if(src_object.loc == src) // Item in the inventory + return STATUS_INTERACTIVE + if(src.contents.Find(src_object.loc)) // A hidden uplink inside an item + return STATUS_INTERACTIVE + + if (!(src_object in view(4, src))) // If the src object is not in visable, disable updates + return STATUS_CLOSE + + var/dist = get_dist(src_object, src) + if (dist <= 1) + return STATUS_INTERACTIVE // interactive (green visibility) + else if (dist <= 2) + return STATUS_UPDATE // update only (orange visibility) + else if (dist <= 4) + return STATUS_DISABLED // no updates, completely disabled (red visibility) + return STATUS_CLOSE + +/mob/living/can_use_topic(var/src_object, var/datum/topic_state/custom_state) + . = shared_living_nano_interaction(src_object) + if(. == STATUS_INTERACTIVE && !(custom_state.flags & NANO_IGNORE_DISTANCE)) + if(loc) + . = loc.contents_nano_distance(src_object, src) + else + . = shared_living_nano_distance(src_object) + if(STATUS_INTERACTIVE) + return STATUS_UPDATE + +/mob/living/carbon/human/can_use_topic(var/src_object, var/datum/topic_state/custom_state) + . = shared_living_nano_interaction(src_object) + if(. == STATUS_INTERACTIVE && !(custom_state.flags & NANO_IGNORE_DISTANCE)) + . = shared_living_nano_distance(src_object) + if(. == STATUS_UPDATE && (TK in mutations)) // If we have telekinesis and remain close enough, allow interaction. + return STATUS_INTERACTIVE + +/var/global/datum/topic_state/default_state = new() + +/datum/topic_state + var/flags = 0 + +/datum/topic_state/proc/href_list(var/mob/user) + return list() \ No newline at end of file diff --git a/code/modules/nano/nanomanager.dm b/code/modules/nano/nanomanager.dm index cb0659cd..bb73178e 100644 --- a/code/modules/nano/nanomanager.dm +++ b/code/modules/nano/nanomanager.dm @@ -21,7 +21,7 @@ "nano/js/",\ "nano/templates/"\ ) - + var/list/filenames = null for (var/path in nano_asset_dirs) filenames = flist(path) @@ -29,6 +29,7 @@ if(copytext(filename, length(filename)) != "/") // filenames which end in "/" are actually directories, which we want to ignore if(fexists(path + filename)) asset_files.Add(fcopy_rsc(path + filename)) // add this file to asset_files for sending to clients when they connect + return /** @@ -47,15 +48,15 @@ if (isnull(ui)) // no ui has been passed, so we'll search for one { ui = get_open_ui(user, src_object, ui_key) - } - if (!isnull(ui)) + } + if (!isnull(ui)) // The UI is already open if (!force_open) ui.push_data(data) return ui else //testing("nanomanager/try_update_ui mob [user.name] [src_object:name] [ui_key] [force_open] - forcing opening of ui") - ui.close() + ui.close() return null /** @@ -72,14 +73,14 @@ if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) //testing("nanomanager/get_open_ui mob [user.name] [src_object:name] [ui_key] - there are no uis open") return null - else if (isnull(open_uis[src_object_key][ui_key]) || !istype(open_uis[src_object_key][ui_key], /list)) + else if (isnull(open_uis[src_object_key][ui_key]) || !istype(open_uis[src_object_key][ui_key], /list)) //testing("nanomanager/get_open_ui mob [user.name] [src_object:name] [ui_key] - there are no uis open for this object") return null for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) if (ui.user == user) return ui - + //testing("nanomanager/get_open_ui mob [user.name] [src_object:name] [ui_key] - ui not found") return null @@ -90,21 +91,21 @@ * * @return int The number of uis updated */ -/datum/nanomanager/proc/update_uis(src_object) +/datum/nanomanager/proc/update_uis(src_object) var/src_object_key = "\ref[src_object]" if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) - return 0 + return 0 var/update_count = 0 - for (var/ui_key in open_uis[src_object_key]) + for (var/ui_key in open_uis[src_object_key]) for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) - if(ui && ui.src_object && ui.user) + if(ui && ui.src_object && ui.user && ui.src_object.nano_host()) ui.process(1) update_count++ return update_count - + /** - * Update /nanoui uis belonging to user + * Update /nanoui uis belonging to user * * @param user /mob The mob who owns the uis * @param src_object /obj|/mob If src_object is provided, only update uis which are attached to src_object (optional) @@ -117,15 +118,15 @@ return 0 // has no open uis var/update_count = 0 - for (var/datum/nanoui/ui in user.open_uis) + for (var/datum/nanoui/ui in user.open_uis) if ((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key)) ui.process(1) update_count++ - + return update_count - + /** - * Close /nanoui uis belonging to user + * Close /nanoui uis belonging to user * * @param user /mob The mob who owns the uis * @param src_object /obj|/mob If src_object is provided, only close uis which are attached to src_object (optional) @@ -136,16 +137,16 @@ /datum/nanomanager/proc/close_user_uis(var/mob/user, src_object = null, ui_key = null) if (isnull(user.open_uis) || !istype(user.open_uis, /list) || open_uis.len == 0) //testing("nanomanager/close_user_uis mob [user.name] has no open uis") - return 0 // has no open uis + return 0 // has no open uis var/close_count = 0 - for (var/datum/nanoui/ui in user.open_uis) + for (var/datum/nanoui/ui in user.open_uis) if ((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key)) ui.close() close_count++ - - //testing("nanomanager/close_user_uis mob [user.name] closed [open_uis.len] of [close_count] uis") - + + //testing("nanomanager/close_user_uis mob [user.name] closed [open_uis.len] of [close_count] uis") + return close_count /** @@ -185,14 +186,15 @@ return 0 // wasn't open processing_uis.Remove(ui) - ui.user.open_uis.Remove(ui) + if(ui.user) // Sanity check in case a user has been deleted (say a blown up borg watching the alarm interface) + ui.user.open_uis.Remove(ui) var/list/uis = open_uis[src_object_key][ui.ui_key] uis.Remove(ui) - + //testing("nanomanager/ui_closed mob [ui.user.name] [ui.src_object:name] [ui.ui_key] - user.open_uis [ui.user.open_uis.len] | uis [uis.len] | processing_uis [processing_uis.len]") - + return 1 - + /** * This is called on user logout * Closes/clears all uis attached to the user's /mob @@ -202,7 +204,7 @@ * @return nothing */ -// +// /datum/nanomanager/proc/user_logout(var/mob/user) //testing("nanomanager/user_logout user [user.name]") return close_user_uis(user) @@ -218,7 +220,7 @@ */ /datum/nanomanager/proc/user_transferred(var/mob/oldMob, var/mob/newMob) //testing("nanomanager/user_transferred from mob [oldMob.name] to mob [newMob.name]") - if (isnull(oldMob.open_uis) || !istype(oldMob.open_uis, /list) || open_uis.len == 0) + if (!oldMob || isnull(oldMob.open_uis) || !istype(oldMob.open_uis, /list) || open_uis.len == 0) //testing("nanomanager/user_transferred mob [oldMob.name] has no open uis") return 0 // has no open uis @@ -230,9 +232,9 @@ newMob.open_uis.Add(ui) oldMob.open_uis.Cut() - + return 1 // success - + /** * Sends all nano assets to the client * This is called on user login @@ -243,6 +245,6 @@ */ /datum/nanomanager/proc/send_resources(client) - for(var/file in asset_files) + for(var/file in asset_files) client << browse_rsc(file) // send the file to the client diff --git a/code/modules/nano/nanoui.dm b/code/modules/nano/nanoui.dm index b4d20155..912d473e 100644 --- a/code/modules/nano/nanoui.dm +++ b/code/modules/nano/nanoui.dm @@ -6,12 +6,7 @@ nanoui class (or whatever Byond calls classes) nanoui is used to open and update nano browser uis **********************************************************/ - -#define STATUS_INTERACTIVE 2 // GREEN Visability -#define STATUS_UPDATE 1 // ORANGE Visability -#define STATUS_DISABLED 0 // RED Visability - -/datum/nanoui +/datum/nanoui // the user who opened this ui var/mob/user // the object this ui "belongs" to @@ -57,8 +52,12 @@ nanoui is used to open and update nano browser uis // the current status/visibility of the ui var/status = STATUS_INTERACTIVE - // Only allow users with a certain user.stat to get updates. Defaults to 0 (concious) - var/allowed_user_stat = 0 // -1 = ignore, 0 = alive, 1 = unconcious or alive, 2 = dead concious or alive + // Relationship between a master interface and its children. Used in update_status + var/datum/nanoui/master_ui + var/list/datum/nanoui/children = list() + var/datum/topic_state/custom_state = null + + var/cached_data = null /** * Create a new nanoui instance. @@ -74,17 +73,22 @@ nanoui is used to open and update nano browser uis * * @return /nanoui new nanoui object */ -/datum/nanoui/New(nuser, nsrc_object, nui_key, ntemplate_filename, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null) +/datum/nanoui/New(nuser, nsrc_object, nui_key, ntemplate_filename, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null, var/datum/nanoui/master_ui = null, var/datum/topic_state/custom_state = null) user = nuser src_object = nsrc_object ui_key = nui_key window_id = "[ui_key]\ref[src_object]" + src.master_ui = master_ui + if(master_ui) + master_ui.children += src + src.custom_state = custom_state ? custom_state : new/datum/topic_state() + // add the passed template filename as the "main" template, this is required add_template("main", ntemplate_filename) if (ntitle) - title = ntitle + title = sanitize(ntitle) if (nwidth) width = nwidth if (nheight) @@ -103,13 +107,13 @@ nanoui is used to open and update nano browser uis add_script("libraries.min.js") // A JS file comprising of jQuery, doT.js and jQuery Timer libraries (compressed together) add_script("nano_utility.js") // The NanoUtility JS, this is used to store utility functions. add_script("nano_template.js") // The NanoTemplate JS, this is used to render templates. - add_script("nano_state_manager.js") // The NanoStateManager JS, it handles updates from the server and passes data to the current state - add_script("nano_state.js") // The NanoState JS, this is the base state which all states must inherit from - add_script("nano_state_default.js") // The NanoStateDefault JS, this is the "default" state (used by all UIs by default), which inherits from NanoState + add_script("nano_state_manager.js") // The NanoStateManager JS, it handles updates from the server and passes data to the current state + add_script("nano_state.js") // The NanoState JS, this is the base state which all states must inherit from + add_script("nano_state_default.js") // The NanoStateDefault JS, this is the "default" state (used by all UIs by default), which inherits from NanoState add_script("nano_base_callbacks.js") // The NanoBaseCallbacks JS, this is used to set up (before and after update) callbacks which are common to all UIs add_script("nano_base_helpers.js") // The NanoBaseHelpers JS, this is used to set up template helpers which are common to all UIs add_stylesheet("shared.css") // this CSS sheet is common to all UIs - add_stylesheet("icons.css") // this CSS sheet is common to all UIs + add_stylesheet("icons.css") // this CSS sheet is common to all UIs /** * Set the current status (also known as visibility) of this ui. @@ -127,7 +131,7 @@ nanoui is used to open and update nano browser uis update() else status = state - if (push_update || status == 0) + if (push_update || status == 0) push_data(null, 1) // Update the UI, force the update in case the status is 0, data is null so that previous data is used /** @@ -138,34 +142,14 @@ nanoui is used to open and update nano browser uis * @return nothing */ /datum/nanoui/proc/update_status(var/push_update = 0) - if (istype(user, /mob/living/silicon/ai)) - set_status(STATUS_INTERACTIVE, push_update) // interactive (green visibility) - else if (istype(user, /mob/living/silicon/robot)) - if (src_object in view(7, user)) // robots can see and interact with things they can see within 7 tiles - set_status(STATUS_INTERACTIVE, push_update) // interactive (green visibility) - else - set_status(STATUS_DISABLED, push_update) // no updates, completely disabled (red visibility) + var/atom/movable/host = src_object.nano_host() + var/new_status = host.CanUseTopic(user, list(), custom_state) + if(master_ui) + new_status = min(new_status, master_ui.status) + if(new_status == STATUS_CLOSE) + close() else - var/dist = get_dist(src_object, user) - - if (dist > 4) - close() - return - - if ((allowed_user_stat > -1) && (user.stat > allowed_user_stat)) - set_status(STATUS_DISABLED, push_update) // no updates, completely disabled (red visibility) - else if (user.restrained() || user.lying) - set_status(STATUS_UPDATE, push_update) // update only (orange visibility) - else if (istype(src_object, /obj/item/device/uplink/hidden)) // You know what if they have the uplink open let them use the UI - set_status(STATUS_INTERACTIVE, push_update) // Will build in distance checks on the topics for sanity. - else if (!(src_object in view(4, user))) // If the src object is not in visable, set status to 0 - set_status(STATUS_DISABLED, push_update) // interactive (green visibility) - else if (dist <= 1) - set_status(STATUS_INTERACTIVE, push_update) // interactive (green visibility) - else if (dist <= 2) - set_status(STATUS_UPDATE, push_update) // update only (orange visibility) - else if (dist <= 4) - set_status(STATUS_DISABLED, push_update) // no updates, completely disabled (red visibility) + set_status(new_status, push_update) /** * Set the ui to auto update (every master_controller tick) @@ -203,7 +187,7 @@ nanoui is used to open and update nano browser uis "showMap" = show_map, "mapZLevel" = map_z_level, "user" = list("name" = user.name) - ) + ) return config_data /** @@ -215,12 +199,12 @@ nanoui is used to open and update nano browser uis */ /datum/nanoui/proc/get_send_data(var/list/data) var/list/config_data = get_config_data() - + var/list/send_data = list("config" = config_data) - + if (!isnull(data)) send_data["data"] = data - + return send_data /** @@ -267,11 +251,11 @@ nanoui is used to open and update nano browser uis */ /datum/nanoui/proc/add_template(key, filename) templates[key] = filename - + /** * Set the layout key for use in the frontend Javascript * The layout key is the basic layout key for the page - * Two files are loaded on the client based on the layout key varable: + * Two files are loaded on the client based on the layout key varable: * -> a template in /nano/templates with the filename "layout_.tmpl * -> a CSS stylesheet in /nano/css with the filename "layout_.css * @@ -301,7 +285,7 @@ nanoui is used to open and update nano browser uis */ /datum/nanoui/proc/set_auto_update_content(nstate) auto_update_content = nstate - + /** * Set the state key for use in the frontend Javascript * @@ -310,8 +294,8 @@ nanoui is used to open and update nano browser uis * @return nothing */ /datum/nanoui/proc/set_state_key(nstate_key) - state_key = nstate_key - + state_key = nstate_key + /** * Toggle showing the map ui * @@ -321,7 +305,7 @@ nanoui is used to open and update nano browser uis */ /datum/nanoui/proc/set_show_map(nstate) show_map = nstate - + /** * Toggle showing the map ui * @@ -352,21 +336,21 @@ nanoui is used to open and update nano browser uis // before the UI opens, add the layout files based on the layout key add_stylesheet("layout_[layout_key].css") add_template("layout", "layout_[layout_key].tmpl") - + var/head_content = "" - + for (var/filename in scripts) head_content += " " - + for (var/filename in stylesheets) head_content += " " - + var/template_data_json = "{}" // An empty JSON object if (templates.len > 0) template_data_json = list2json(templates) var/list/send_data = get_send_data(initial_data) - var/initial_data_json = list2json(send_data) + var/initial_data_json = list2json(send_data, cached_data) var/url_parameters_json = list2json(list("src" = "\ref[src]")) @@ -378,7 +362,7 @@ nanoui is used to open and update nano browser uis [head_content] - +