diff --git a/code/__defines/_planes+layers.dm b/code/__defines/_planes+layers.dm index ebc2194960a..a8e3951a3ee 100644 --- a/code/__defines/_planes+layers.dm +++ b/code/__defines/_planes+layers.dm @@ -22,7 +22,7 @@ What can I do with Planesmasters? Planesmasters can be used as a neater way to deal with client images or potentially to do some neat things How do planes work? - A plane can be any integer from -100 to 100. (If you want more, bug lummox.) + A plane can be any integer from -10000 to 10000. All planes above 0, the 'base plane', are visible even when your character cannot 'see' them, for example, the HUD. All planes below 0, the 'base plane', are only visible when a character can see them. @@ -40,10 +40,14 @@ What is the naming convention for planes or layers? */ + #define SPACE_PLANE -82 // Reserved for use in space/parallax -#define PARALLAX_PLANE -80 // Reserved for use in space/parallax -#define SKYBOX_PLANE -79 // Skybox parallax -#define DUST_PLANE -78 // For dust overlay on space turfs. Should be above skybox for parallax effect. +#define PARALLAX_PLANE -81 // Reserved for use in space/parallax +#define SKYBOX_PLANE -80 // Skybox parallax +#define DUST_PLANE -79 // For dust overlay on space turfs. Should be above skybox for parallax effect. + +#define PLANE_LOOKINGGLASS -78 // For the Looking Glass holodecks +#define PLANE_LOOKINGGLASS_IMG -77 // For the Looking Glass holodecks // OPENSPACE_PLANE reserves all planes between OPENSPACE_PLANE_START and OPENSPACE_PLANE_END inclusive #define OPENSPACE_PLANE -75 // /turf/simulated/open will use OPENSPACE_PLANE + z (Valid z's being 2 thru 17) diff --git a/code/modules/looking_glass/lg_area.dm b/code/modules/looking_glass/lg_area.dm new file mode 100644 index 00000000000..b17c67449f5 --- /dev/null +++ b/code/modules/looking_glass/lg_area.dm @@ -0,0 +1,78 @@ +/area/looking_glass + name = "make a subtype" + + var/obj/effect/landmark/looking_glass/our_landmark + var/list/our_turfs = list() + var/list/our_optional_turfs = list() + + var/lg_id + + var/active = FALSE + +/area/looking_glass/Initialize() + . = ..() + our_landmark = locate() in src + if(!our_landmark) + testing("Looking glass area [name] couldn't find a landmark") + for(var/turf/simulated/floor/looking_glass/lgt in src) + our_turfs += lgt + if(lgt.optional) + our_optional_turfs += lgt + +/area/looking_glass/Destroy() + our_landmark = null + our_turfs.Cut() + return ..() + +/area/looking_glass/Entered(var/atom/movable/AM) + if(isliving(AM)) + var/mob/living/L = AM + if(L.client) + our_landmark?.gain_viewer(L.client) + +/area/looking_glass/Exited(var/atom/movable/AM) + if(isliving(AM)) + var/mob/living/L = AM + if(L.client) + our_landmark?.lose_viewer(L.client) + +/area/looking_glass/proc/begin_program(var/image/newimage) + if(!active) + for(var/trf in our_turfs) + var/turf/simulated/floor/looking_glass/lgt = trf + lgt.activate() + + our_landmark.take_image(newimage) + active = TRUE + +/area/looking_glass/proc/end_program() + if(active) + for(var/trf in our_turfs) + var/turf/simulated/floor/looking_glass/lgt = trf + lgt.deactivate() + + active = FALSE + + spawn(2 SECONDS) + our_landmark.drop_image() + +/area/looking_glass/proc/toggle_optional(var/transparent) + for(var/trf in our_optional_turfs) + var/turf/simulated/floor/looking_glass/lgt = trf + lgt.center = !transparent + if(active) + lgt.deactivate() + spawn(3 SECONDS) + lgt.activate() + +/area/looking_glass/lg_1 + name = "looking glass one" + lg_id = "one" + +/area/looking_glass/lg_2 + name = "looking glass two" + lg_id = "two" + +/area/looking_glass/lg_3 + name = "looking glass three" + lg_id = "three" diff --git a/code/modules/looking_glass/lg_console.dm b/code/modules/looking_glass/lg_console.dm new file mode 100644 index 00000000000..b7c2aef62ac --- /dev/null +++ b/code/modules/looking_glass/lg_console.dm @@ -0,0 +1,162 @@ +/obj/machinery/computer/looking_glass + name = "looking glass control" + desc = "Controls the looking glass displays in this room. Provided courtesy of KHI." + + icon_keyboard = "tech_key" + icon_screen = "holocontrol" + + var/static/list/supported_programs = list() + var/static/list/secret_programs = list() + + use_power = USE_POWER_IDLE + active_power_usage = 8000 + + var/current_program = "Off" + var/area/looking_glass/my_area + var/last_gravity_change = 0 + var/ready = TRUE + var/immersion = FALSE + + var/lg_id = "change_me" + +/obj/machinery/computer/looking_glass/Initialize() + . = ..() + for(var/area/looking_glass/lga in world) + if(lga.lg_id == lg_id) + my_area = lga + break + if(!istype(my_area)) + testing("Looking glass console [x],[y],[x] not in a looking glass area.") + if(!supported_programs.len) + supported_programs["Off"] = null + supported_programs["Diagnostics"] = image(icon = 'icons/skybox/skybox.dmi', icon_state = "diagnostic") + supported_programs["Space 1"] = image(icon = 'icons/skybox/skybox.dmi', icon_state = "space1") + supported_programs["Space 2"] = image(icon = 'icons/skybox/skybox.dmi', icon_state = "space2") + supported_programs["Space 3"] = image(icon = 'icons/skybox/skybox.dmi', icon_state = "space3") + supported_programs["Space 4"] = image(icon = 'icons/skybox/skybox.dmi', icon_state = "space4") + supported_programs["Space 5"] = image(icon = 'icons/skybox/skybox.dmi', icon_state = "space5") + supported_programs["Space 6"] = image(icon = 'icons/skybox/skybox.dmi', icon_state = "space6") + +/obj/machinery/computer/looking_glass/Destroy() + my_area = null + return ..() + +/obj/machinery/computer/looking_glass/attack_ai(var/mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/computer/looking_glass/attack_hand(var/mob/user as mob) + if(..()) + return + user.set_machine(src) + + ui_interact(user) + +/obj/machinery/computer/looking_glass/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + user.set_machine(src) + + var/list/data = list() + var/program_list[0] + + for(var/P in supported_programs) + program_list[++program_list.len] = P + + if(emagged) + for(var/P in secret_programs) + program_list[++program_list.len] = P + + data["supportedPrograms"] = program_list + data["currentProgram"] = current_program + data["immersion"] = immersion + if(my_area?.has_gravity) + data["gravity"] = 1 + else + data["gravity"] = null + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "lookingglass.tmpl", src.name, 400, 550) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(20) + +/obj/machinery/computer/looking_glass/Topic(href, href_list) + if(..()) + return 1 + if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) + usr.set_machine(src) + + if(href_list["program"]) + if(ready) + var/prog = href_list["program"] + if(prog == "Off") + current_program = "Off" + unload_program() + else if((prog in supported_programs) || (emagged && (prog in secret_programs))) + current_program = prog + load_program(prog) + else + visible_message("ERROR. Recalibrating displays.") + + else if(href_list["gravity"]) + toggle_gravity(my_area) + + else if(href_list["immersion"]) + immersion = !immersion + my_area.toggle_optional(immersion) + + src.add_fingerprint(usr) + + SSnanoui.update_uis(src) + +/obj/machinery/computer/looking_glass/emag_act(var/remaining_charges, var/mob/user as mob) + if (!emagged) + playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1) + emagged = 1 + to_chat(user, "You unlock several programs that were hidden somewhere in memory.") + log_game("[key_name(usr)] emagged the [name]") + return 1 + return + +/obj/machinery/computer/looking_glass/proc/load_program(var/prog_name) + ready = FALSE + VARSET_IN(src, ready, TRUE, 10 SECONDS) + + if(prog_name in supported_programs) + my_area.begin_program(supported_programs[prog_name]) + else if(prog_name in secret_programs) + my_area.begin_program(secret_programs[prog_name]) + +/obj/machinery/computer/looking_glass/proc/unload_program() + ready = FALSE + VARSET_IN(src, ready, TRUE, 10 SECONDS) + + my_area.end_program() + +/obj/machinery/computer/looking_glass/proc/toggle_gravity(var/area/A) + if(world.time < (last_gravity_change + 3 SECONDS)) + if(world.time < (last_gravity_change + 1 SECOND)) + return + visible_message("ERROR. Recalibrating gravity field.") + return + + last_gravity_change = world.time + + if(A.has_gravity) + A.gravitychange(0) + else + A.gravitychange(1) + +//This could all be done better, but it works for now. +/obj/machinery/computer/looking_glass/Destroy() + unload_program() + ..() + +/obj/machinery/computer/looking_glass/ex_act(severity) + unload_program() + ..() + +/obj/machinery/computer/looking_glass/power_change() + var/oldstat = stat + ..() + if (stat != oldstat && (stat & NOPOWER)) + unload_program() \ No newline at end of file diff --git a/code/modules/looking_glass/lg_imageholder.dm b/code/modules/looking_glass/lg_imageholder.dm new file mode 100644 index 00000000000..0d4cf5b157b --- /dev/null +++ b/code/modules/looking_glass/lg_imageholder.dm @@ -0,0 +1,66 @@ +#define LG_IMAGE_SIZE 736 + +/obj/effect/landmark/looking_glass + var/image/holding + + var/list/viewers + + var/lg_id //Area sets this for you + + mouse_opacity = 0 + +/obj/effect/landmark/looking_glass/Initialize() + . = ..() + viewers = list() + +/obj/effect/landmark/looking_glass/proc/gain_viewer(var/client/C) + if(C in viewers) + testing("Looking Glass [x],[y],[z] tried to add a duplicate viewer.") + viewers |= C + if(holding) + show_to(C) + +/obj/effect/landmark/looking_glass/proc/lose_viewer(var/client/C) + if(!(C in viewers)) + testing("Looking Glass [x],[y],[z] tried to remove a viewer it didn't have") + viewers -= C + if(holding) + unshow_to(C) + +/obj/effect/landmark/looking_glass/proc/show_to(var/client/C) + C.images |= holding + +/obj/effect/landmark/looking_glass/proc/unshow_to(var/client/C) + C.images -= holding + +/obj/effect/landmark/looking_glass/proc/take_image(var/image/newimage) + if(!istype(newimage)) + return + + if(holding) + for(var/client in viewers) + unshow_to(client) + + holding = newimage + newimage.plane = PLANE_LOOKINGGLASS_IMG + newimage.blend_mode = BLEND_MULTIPLY + newimage.appearance_flags = RESET_TRANSFORM + newimage.mouse_opacity = 0 + newimage.pixel_y = newimage.pixel_x = (LG_IMAGE_SIZE/-2) + 16 + newimage.loc = src + + for(var/client in viewers) + show_to(client) + +/obj/effect/landmark/looking_glass/proc/drop_image() + if(!holding) + return + + for(var/client in viewers) + unshow_to(client) + + holding.loc = null + holding = null + + +#undef LG_IMAGE_SIZE \ No newline at end of file diff --git a/code/modules/looking_glass/lg_turfs.dm b/code/modules/looking_glass/lg_turfs.dm new file mode 100644 index 00000000000..189790335d8 --- /dev/null +++ b/code/modules/looking_glass/lg_turfs.dm @@ -0,0 +1,88 @@ +/decl/flooring/looking_glass + name = "looking glass surface" + desc = "Too expensive to replace. Don't break it!" + icon = 'icons/turf/flooring/lg_origin.dmi' + icon_base = "origin" + build_type = null + damage_temperature = T0C+200 + + +/turf/simulated/floor/looking_glass + name = "looking glass surface" + icon = 'icons/turf/flooring/lg_origin.dmi' + icon_state = "origin_arrow" + initial_flooring = /decl/flooring/looking_glass + appearance_flags = TILE_BOUND + dynamic_lighting = FALSE + + var/center = FALSE + var/optional = FALSE + +/turf/simulated/floor/looking_glass/center + center = TRUE + icon_state = "origin_center" + +/turf/simulated/floor/looking_glass/optional + center = TRUE + optional = TRUE + icon_state = "origin_optional_arrow" + +/turf/simulated/floor/looking_glass/proc/activate() + set waitfor = FALSE + + icon_state = "origin_switching" + + animate(src, color = "#000000", time = 3 SECONDS) + + sleep(3 SECONDS) + + var/new_x = 0 + var/new_y = 0 + + if(dir & NORTH) + new_y = 112 + else if(dir & SOUTH) + new_y = -112 + + if(dir & EAST) + new_x = 112 + else if(dir & WEST) + new_x = -112 + + var/matrix/M = matrix() + var/mutable_appearance/MA = new (src) + + if(!center) + var/horizontal = (dir & (WEST|EAST)) + var/vertical = (dir & (NORTH|SOUTH)) + M.Scale(horizontal ? 8 : 1, vertical ? 8 : 1) + M.Translate(new_x, new_y) + MA.opacity = 1 + if(!optional) + MA.density = 1 + + MA.icon_state = "origin_active" + MA.plane = PLANE_LOOKINGGLASS + MA.layer = 0 + appearance = MA + + animate(src, color = "#FFFFFF", transform = M, time = 3 SECONDS) + +/turf/simulated/floor/looking_glass/proc/deactivate() + set waitfor = FALSE + + animate(src, color = "#000000", transform = matrix(), time = 3 SECONDS) + + sleep(3 SECONDS) + var/mutable_appearance/MA = new (src) + MA.opacity = 0 + MA.density = 0 + MA.icon_state = "origin_switching" + MA.plane = initial(plane) + MA.layer = initial(layer) + appearance = MA + + animate(src, color = null, time = 3 SECONDS) + sleep(3 SECONDS) + icon_state = "origin" + diff --git a/icons/turf/flooring/lg_origin.dmi b/icons/turf/flooring/lg_origin.dmi new file mode 100644 index 00000000000..d455723fd08 Binary files /dev/null and b/icons/turf/flooring/lg_origin.dmi differ diff --git a/nano/templates/lookingglass.tmpl b/nano/templates/lookingglass.tmpl new file mode 100644 index 00000000000..8f117e6f66f --- /dev/null +++ b/nano/templates/lookingglass.tmpl @@ -0,0 +1,14 @@ +

Current Loaded Programs:

+{{for data.supportedPrograms}} +
{{:helper.link(value, data.currentProgram == value ? 'check' : 'close', {'program' : value}, null, data.currentProgram == value ? 'linkOn' : null)}}
+{{/for}} + +
+
Area Gravity:
+ {{:helper.link(data.gravity ? 'On ' : 'Off', data.gravity ? 'check' : 'close', {'gravity' : 1}, null, data.gravity ? 'linkOn' : 'redButton')}} +
+ +
+
Full Immersion:
+ {{:helper.link(data.immersion ? 'On ' : 'Off', data.immersion ? 'check' : 'close', {'immersion' : 1}, null, data.immersion ? 'linkOn' : 'redButton')}} +
\ No newline at end of file