From 25e8f73c7513e4f427a40f0ced703755ea3918f7 Mon Sep 17 00:00:00 2001 From: Neerti Date: Wed, 22 Jul 2020 05:19:39 -0400 Subject: [PATCH] Implements basic portals. --- .../objects/effects/map_effects/portal.dm | 197 ++++++++++++++++++ icons/effects/map_effects.dmi | Bin 6449 -> 9181 bytes .../mountains/spatial_anomaly.dmm | 62 ++++++ polaris.dme | 4 +- 4 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 code/game/objects/effects/map_effects/portal.dm create mode 100644 maps/submaps/surface_submaps/mountains/spatial_anomaly.dmm diff --git a/code/game/objects/effects/map_effects/portal.dm b/code/game/objects/effects/map_effects/portal.dm new file mode 100644 index 0000000000..a007a90cde --- /dev/null +++ b/code/game/objects/effects/map_effects/portal.dm @@ -0,0 +1,197 @@ +GLOBAL_LIST_EMPTY(all_portal_masters) + +// Portal map effects allow a mapper to join two distant places together, while looking hopefully seamlessly connected. +// This can allow for very strange PoIs that twist and turn in what appear to be physically impossible ways. + +/obj/effect/map_effect/portal + name = "portal subtype" + invisibility = 0 + plane = TURF_PLANE + layer = ABOVE_TURF_LAYER + var/obj/effect/map_effect/portal/counterpart = null // The portal line or master that this is connected to, on the 'other side'. + + // Information used to apply `pixel_[x|y]` offsets so that the visuals line up. + var/total_height = 0 // Measured in tiles. + var/total_width = 0 + + var/portal_distance_x = 0 // How far the portal is from the left edge, in tiles. + var/portal_distance_y = 0 // How far the portal is from the top edge. + + + +/obj/effect/map_effect/portal/Destroy() + vis_contents = null + if(counterpart) + counterpart.counterpart = null + counterpart = null + return ..() + + +/obj/effect/map_effect/portal/Crossed(atom/movable/AM) + if(AM.is_incorporeal()) + return + ..() + if(!AM) + return + if(!counterpart) + return + + AM.forceMove(counterpart.get_focused_turf()) + +/obj/effect/map_effect/portal/proc/get_focused_turf() + return get_step(get_turf(src), dir) + +/obj/effect/map_effect/portal/proc/calculate_dimensions() + var/highest_x = 0 + var/lowest_x = 0 + + var/highest_y = 0 + var/lowest_y = 0 + + // First pass is for finding the top right corner. + for(var/thing in vis_contents) + var/turf/T = thing + if(T.x > highest_x) + highest_x = T.x + if(T.y > highest_y) + highest_y = T.y + + lowest_x = highest_x + lowest_y = highest_y + + // Second one is for the bottom left corner. + for(var/thing in vis_contents) + var/turf/T = thing + if(T.x < lowest_x) + lowest_x = T.x + if(T.y < lowest_y) + lowest_y = T.y + + // Now calculate the dimensions. + total_width = (highest_x - lowest_x) + 1 + total_height = (highest_y - lowest_y) + 1 + + var/turf/focused_T = counterpart.get_focused_turf() + portal_distance_x = lowest_x - focused_T.x + portal_distance_y = lowest_y - focused_T.y + + + +// Portal masters manage everything else involving portals. +// This is the base type. Use `/side_a` or `/side_b` for actual portals. +/obj/effect/map_effect/portal/master + name = "portal master" + var/portal_id = "test" // For a portal to be made, both the A and B sides need to share the same ID value. + var/list/portal_lines = list() + +/obj/effect/map_effect/portal/master/Initialize() + GLOB.all_portal_masters += src + find_lines() + find_counterparts() + ..() + return INITIALIZE_HINT_LATELOAD + +/obj/effect/map_effect/portal/master/LateInitialize() + make_visuals() + apply_offset() + +/obj/effect/map_effect/portal/master/Destroy() + GLOB.all_portal_masters -= src + for(var/thing in portal_lines) + qdel(thing) + return ..() + +/obj/effect/map_effect/portal/master/proc/find_lines() + var/list/dirs_to_search = list( turn(dir, 90), turn(dir, -90) ) + + for(var/dir_to_search in dirs_to_search) + var/turf/current_T = get_turf(src) + while(current_T) + current_T = get_step(current_T, dir_to_search) + var/obj/effect/map_effect/portal/line/line = locate() in current_T + if(line) + portal_lines += line + line.my_master = src + else + break + +/obj/effect/map_effect/portal/master/proc/find_counterparts() + for(var/thing in GLOB.all_portal_masters) + var/obj/effect/map_effect/portal/master/M = thing + if(M == src) + continue + if(M.counterpart) + continue + + if(M.portal_id == src.portal_id) + counterpart = M + M.counterpart = src + if(portal_lines.len) + for(var/i = 1 to portal_lines.len) + var/obj/effect/map_effect/portal/line/our_line = portal_lines[i] + var/obj/effect/map_effect/portal/line/their_line = M.portal_lines[i] + our_line.counterpart = their_line + their_line.counterpart = our_line + + break + +/obj/effect/map_effect/portal/master/proc/make_visuals() + var/list/observed_turfs = list() + for(var/thing in portal_lines + src) + var/obj/effect/map_effect/portal/P = thing + P.icon_state = null + + var/turf/T = P.counterpart.get_focused_turf() + P.vis_contents += T + + var/list/things = dview(world.view, T) + for(var/turf/turf in things) + if(get_dir(turf, T) & P.dir) + if(turf in observed_turfs) // Avoid showing the same turf twice or more for improved performance. + continue + + P.vis_contents += turf + observed_turfs += turf + + P.calculate_dimensions() + + +/obj/effect/map_effect/portal/master/proc/apply_offset() + for(var/thing in portal_lines + src) + var/obj/effect/map_effect/portal/P = thing + + P.pixel_x = 32 * P.portal_distance_x + P.pixel_y = 32 * P.portal_distance_y + +/obj/effect/map_effect/portal/master/side_a + name = "portal master A" + icon_state = "portal_side_a" +// color = "#00FF00" + +/obj/effect/map_effect/portal/master/side_b + name = "portal master B" + icon_state = "portal_side_b" +// color = "#FF0000" + + + +// Portal lines extend out from the sides of portal masters, +// They let portals be longer than 1x1. +// Both sides MUST be the same length, meaning if side A is 1x3, side B must also be 1x3. +/obj/effect/map_effect/portal/line + name = "portal line" + var/obj/effect/map_effect/portal/master/my_master = null + +/obj/effect/map_effect/portal/line/Destroy() + if(my_master) + my_master.portal_lines -= src + my_master = null + return ..() + +/obj/effect/map_effect/portal/line/side_a + name = "portal line A" + icon_state = "portal_line_side_a" + +/obj/effect/map_effect/portal/line/side_b + name = "portal line B" + icon_state = "portal_line_side_b" \ No newline at end of file diff --git a/icons/effects/map_effects.dmi b/icons/effects/map_effects.dmi index 4f5bff755dd3a95877f1784653a05e157878cf97..0f28f9d2da8e936f2749a39bd4edfe70c083bf2f 100644 GIT binary patch literal 9181 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