From 0f6d0a9e4f72bb3272244a3d20c7493d7ab71a1a Mon Sep 17 00:00:00 2001 From: Woodratt Date: Mon, 9 Oct 2017 21:55:16 -0700 Subject: [PATCH] Porting see down through open spaces Ported 'see-down' open spaces from Vore, who ported it from Eris Ported talking and visible messages traveling upwards through open spaces Tested, seems to work? Does not seem to break anything, maybe. Maybe made a changelog. --- code/__defines/_planes+layers.dm | 54 ++++++++ code/modules/mob/living/say.dm | 19 ++- code/modules/multiz/open_space_controller.dm | 111 ++++++++++++++++ code/modules/multiz/turf.dm | 85 +++++++++--- code/modules/multiz/zshadow.dm | 133 +++++++++++++++++++ html/changelogs/Woodrat-MultiZ.yml | 37 ++++++ icons/mob/talk.dmi | Bin 2513 -> 3948 bytes icons/turf/open_space.dmi | Bin 0 -> 288 bytes polaris.dme | 3 + 9 files changed, 419 insertions(+), 23 deletions(-) create mode 100644 code/__defines/_planes+layers.dm create mode 100644 code/modules/multiz/open_space_controller.dm create mode 100644 code/modules/multiz/zshadow.dm create mode 100644 html/changelogs/Woodrat-MultiZ.yml create mode 100644 icons/turf/open_space.dmi diff --git a/code/__defines/_planes+layers.dm b/code/__defines/_planes+layers.dm new file mode 100644 index 0000000000..129236125c --- /dev/null +++ b/code/__defines/_planes+layers.dm @@ -0,0 +1,54 @@ +/*This file is a list of all preclaimed planes & layers + +All planes & layers should be given a value here instead of using a magic/arbitrary number. + +After fiddling with planes and layers for some time, I figured I may as well provide some documentation: + +What are planes? + Think of Planes as a sort of layer for a layer - if plane X is a larger number than plane Y, the highest number for a layer in X will be below the lowest + number for a layer in Y. + Planes also have the added bonus of having planesmasters. + +What are Planesmasters? + Planesmasters, when in the sight of a player, will have its appearance properties (for example, colour matrices, alpha, transform, etc) + applied to all the other objects in the plane. This is all client sided. + Usually you would want to add the planesmaster as an invisible image in the client's screen. + +What can I do with Planesmasters? + You can: Make certain players not see an entire plane, + Make an entire plane have a certain colour matrices, + Make an entire plane transform in a certain way, + Make players see a plane which is hidden to normal players - I intend to implement this with the antag HUDs for example. + 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.) + 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. + +How do I add a plane? + Think of where you want the plane to appear, look through the pre-existing planes and find where it is above and where it is below + Slot it in in that place, and change the pre-existing planes, making sure no plane shares a number. + Add a description with a comment as to what the plane does. + +How do I make something a planesmaster? + Add the PLANE_MASTER appearance flag to the appearance_flags variable. + +What is the naming convention for planes or layers? + Make sure to use the name of your object before the _LAYER or _PLANE, eg: [NAME_OF_YOUR_OBJECT HERE]_LAYER or [NAME_OF_YOUR_OBJECT HERE]_PLANE + Also, as it's a define, it is standard practice to use capital letters for the variable so people know this. + +*/ + +#define DEFAULT_PLANE 0 // BYOND's default value for plane, the "base plane" + +#define SPACE_PLANE -32 // Reserved for use in space/parallax + +#define PARALLAX_PLANE -30 // Reserved for use in space/parallax + +// OPENSPACE_PLANE reserves all planes between OPENSPACE_PLANE_START and OPENSPACE_PLANE_END inclusive +#define OPENSPACE_PLANE_START -23 +#define OPENSPACE_PLANE_END -8 +#define OPENSPACE_PLANE -25 // /turf/simulated/open will use OPENSPACE_PLANE + z (Valid z's being 2 thru 17) + +#define OVER_OPENSPACE_PLANE -7 diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index dea3c82461..652e11f6a3 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -302,6 +302,21 @@ proc/get_radio_key_from_channel(var/channel) var/image/speech_bubble = image('icons/mob/talk.dmi',src,"[speech_type][speech_bubble_test]") spawn(30) qdel(speech_bubble) + // VOREStation Edit - Attempt Multi-Z Talking + var/mob/above = src.shadow + while(!QDELETED(above)) + var/turf/ST = get_turf(above) + if(ST) + var/list/results = get_mobs_and_objs_in_view_fast(ST, world.view) + var/image/z_speech_bubble = image('icons/mob/talk.dmi', above, "h[speech_bubble_test]") + spawn(30) qdel(z_speech_bubble) + for(var/item in results["mobs"]) + if(item != above && !(item in listening)) + listening[item] = z_speech_bubble + listening_obj |= results["objs"] + above = above.shadow + // VOREStation Edit End + //Main 'say' and 'whisper' message delivery for(var/mob/M in listening) spawn(0) //Using spawns to queue all the messages for AFTER this proc is done, and stop runtimes @@ -310,12 +325,12 @@ proc/get_radio_key_from_channel(var/channel) var/dst = get_dist(get_turf(M),get_turf(src)) if(dst <= message_range || (M.stat == DEAD && !forbid_seeing_deadchat)) //Inside normal message range, or dead with ears (handled in the view proc) - M << speech_bubble + M << (listening[M] || speech_bubble) // VOREStation Edit - Send the image attached to shadow mob if available M.hear_say(message, verb, speaking, alt_name, italics, src, speech_sound, sound_vol) if(whispering) //Don't even bother with these unless whispering if(dst > message_range && dst <= w_scramble_range) //Inside whisper scramble range - M << speech_bubble + M << (listening[M] || speech_bubble) // VOREStation Edit - Send the image attached to shadow mob if available M.hear_say(stars(message), verb, speaking, alt_name, italics, src, speech_sound, sound_vol*0.2) if(dst > w_scramble_range && dst <= world.view) //Inside whisper 'visible' range M.show_message("[src.name] [w_not_heard].", 2) diff --git a/code/modules/multiz/open_space_controller.dm b/code/modules/multiz/open_space_controller.dm new file mode 100644 index 0000000000..fc6c7e5484 --- /dev/null +++ b/code/modules/multiz/open_space_controller.dm @@ -0,0 +1,111 @@ +// +// Controller handling icon updates of open space turfs +// + +/var/global/open_space_initialised = FALSE +/var/global/datum/controller/process/open_space/OS_controller = null +/var/global/image/over_OS_darkness = image('icons/turf/open_space.dmi', "black_open") + +/datum/controller/process/open_space + var/list/turfs_to_process = list() // List of turfs queued for update. + var/list/turfs_to_process_old = null // List of turfs currently being updated. + +/datum/controller/process/open_space/setup() + . = ..() + name = "openspace" + schedule_interval = world.tick_lag // every second + start_delay = 30 SECONDS + OS_controller = src + over_OS_darkness.plane = OVER_OPENSPACE_PLANE + over_OS_darkness.layer = MOB_LAYER + initialize_open_space() + + // Pre-process open space once once before the round starts. Wait 20 seconds so other stuff has time to finish. + spawn(200) + doWork(1) + +/datum/controller/process/open_space/copyStateFrom(var/datum/controller/process/open_space/other) + . = ..() + OS_controller = src + +/datum/controller/process/open_space/doWork() + // We use a different list so any additions to the update lists during a delay from scheck() + // don't cause things to be cut from the list without being updated. + turfs_to_process_old = turfs_to_process + turfs_to_process = list() + + for(last_object in turfs_to_process_old) + var/turf/T = last_object + if(!QDELETED(T)) + update_turf(T) + SCHECK + +/datum/controller/process/open_space/proc/update_turf(var/turf/T) + for(var/atom/movable/A in T) + A.fall() + T.update_icon() + +/datum/controller/process/open_space/proc/add_turf(var/turf/T, var/recursive = 0) + ASSERT(isturf(T)) + turfs_to_process += T + if(recursive > 0) + var/turf/above = GetAbove(T) + if(above && isopenspace(above)) + add_turf(above, recursive) + +// Do the initial updates of open space turfs when the game starts. This will lag! +/datum/controller/process/open_space/proc/initialize_open_space() + // Do initial setup from bottom to top. + for(var/zlevel = 1 to world.maxz) + for(var/turf/simulated/open/T in block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel))) + add_turf(T) + open_space_initialised = TRUE + +/turf/simulated/open/initialize() + . = ..() + if(open_space_initialised) + // log_debug("[src] ([x],[y],[z]) queued for update for initialize()") + OS_controller.add_turf(src) + +/turf/Entered(atom/movable/AM) + . = ..() + if(open_space_initialised && !AM.invisibility && isobj(AM)) + var/turf/T = GetAbove(src) + if(isopenspace(T)) + // log_debug("[T] ([T.x],[T.y],[T.z]) queued for update for [src].Entered([AM])") + OS_controller.add_turf(T, 1) + +/turf/Exited(atom/movable/AM) + . = ..() + if(open_space_initialised && !AM.invisibility && isobj(AM)) + var/turf/T = GetAbove(src) + if(isopenspace(T)) + // log_debug("[T] ([T.x],[T.y],[T.z]) queued for update for [src].Exited([AM])") + OS_controller.add_turf(T, 1) + +/obj/update_icon() + . = ..() + if(open_space_initialised && !invisibility && isturf(loc)) + var/turf/T = GetAbove(src) + if(isopenspace(T)) + // log_debug("[T] ([T.x],[T.y],[T.z]) queued for update for [src].update_icon()") + OS_controller.add_turf(T, 1) + +// Ouch... this is painful. But is there any other way? +/* - No for now +/obj/New() + . = ..() + if(open_space_initialised && !invisibility) + var/turf/T = GetAbove(src) + if(isopenspace(T)) + // log_debug("[T] ([T.x],[T.y],[T.z]) queued for update for [src]New()") + OS_controller.add_turf(T, 1) +*/ + +// Just as New() we probably should hook Destroy() If we can think of something more efficient, lets hear it. +/obj/Destroy() + if(open_space_initialised && !invisibility && isturf(loc)) + var/turf/T = GetAbove(src) + if(isopenspace(T)) + OS_controller.add_turf(T, 1) + . = ..() // Important that this be at the bottom, or we will have been moved to nullspace. diff --git a/code/modules/multiz/turf.dm b/code/modules/multiz/turf.dm index b6845d076f..161adfb80d 100644 --- a/code/modules/multiz/turf.dm +++ b/code/modules/multiz/turf.dm @@ -13,13 +13,19 @@ /turf/space/CanZPass(atom, direction) return 1 +// +// Open Space - "empty" turf that lets stuff fall thru it to the layer below +// + /turf/simulated/open name = "open space" icon = 'icons/turf/space.dmi' icon_state = "" - layer = 0 + desc = "\..." density = 0 + plane = OPENSPACE_PLANE_START pathweight = 100000 //Seriously, don't try and path over this one numbnuts + dynamic_lighting = 0 // Someday lets do proper lighting z-transfer. Until then we are leaving this off so it looks nicer. var/turf/below @@ -42,42 +48,75 @@ AM.fall() /turf/simulated/open/proc/update() + plane = OPENSPACE_PLANE + src.z below = GetBelow(src) turf_changed_event.register(below, src, /turf/simulated/open/update_icon) - var/turf/simulated/T = get_step(src,NORTH) - if(T) - turf_changed_event.register(T, src, /turf/simulated/open/update_icon) levelupdate() for(var/atom/movable/A in src) A.fall() - update_icon() + OS_controller.add_turf(src, 1) // override to make sure nothing is hidden /turf/simulated/open/levelupdate() for(var/obj/O in src) O.hide(0) +/turf/simulated/open/examine(mob/user, distance, infix, suffix) + if(..(user, 2)) + var/depth = 1 + for(var/T = GetBelow(src); isopenspace(T); T = GetBelow(T)) + depth += 1 + to_chat(user, "It is about [depth] levels deep.") + +/** +* Update icon and overlays of open space to be that of the turf below, plus any visible objects on that turf. +*/ /turf/simulated/open/update_icon() + overlays = list() // Edit - Overlays are being crashy when modified. + update_icon_edge()// Add - Get grass into open spaces and whatnot. + var/turf/below = GetBelow(src) if(below) - underlays = list(image(icon = below.icon, icon_state = below.icon_state)) - underlays += below.overlays.Copy() + var/below_is_open = isopenspace(below) - var/list/noverlays = list() - if(!istype(below,/turf/space)) - noverlays += image(icon =icon, icon_state = "empty", layer = 2.2) + if(below_is_open) + underlays = below.underlays + else + var/image/bottom_turf = image(icon = below.icon, icon_state = below.icon_state, dir=below.dir, layer=below.layer) + bottom_turf.plane = src.plane + bottom_turf.color = below.color + underlays = list(bottom_turf) + // VOREStation Edit - Hack workaround to byond crash bug - Include the magic overlay holder object. + overlays += below.overlays + // if(below.overlay_holder) + // overlays += (below.overlays + below.overlay_holder.overlays) + // else + // overlays += below.overlays + // VOREStation Edit End - var/turf/simulated/T = get_step(src,NORTH) - if(istype(T) && !istype(T,/turf/simulated/open)) - noverlays += image(icon ='icons/turf/cliff.dmi', icon_state = "metal", layer = 2.2) + // get objects (not mobs, they are handled by /obj/zshadow) + var/list/o_img = list() + for(var/obj/O in below) + if(O.invisibility) continue // Ignore objects that have any form of invisibility + if(O.loc != below) continue // Ignore multi-turf objects not directly below + var/image/temp2 = image(O, dir = O.dir, layer = O.layer) + temp2.plane = src.plane + temp2.color = O.color + temp2.overlays += O.overlays + // TODO Is pixelx/y needed? + o_img += temp2 + var/overlays_pre = overlays.len + overlays += o_img + var/overlays_post = overlays.len + if(overlays_post != (overlays_pre + o_img.len)) //Here we go! + world.log << "Corrupted openspace turf at [x],[y],[z] being replaced. Pre: [overlays_pre], Post: [overlays_post]" + new /turf/simulated/open(src) + return //Let's get out of here. - var/obj/structure/stairs/S = locate() in below - if(S && S.loc == below) - var/image/I = image(icon = S.icon, icon_state = "below", dir = S.dir, layer = 2.2) - I.pixel_x = S.pixel_x - I.pixel_y = S.pixel_y - noverlays += I + if(!below_is_open) + overlays += over_OS_darkness - overlays = noverlays + return 0 + return PROCESS_KILL // Straight copy from space. /turf/simulated/open/attackby(obj/item/C as obj, mob/user as mob) @@ -115,4 +154,8 @@ //Most things use is_plating to test if there is a cover tile on top (like regular floors) /turf/simulated/open/is_plating() - return 1 \ No newline at end of file + return TRUE + +/turf/simulated/open/is_space() + var/turf/below = GetBelow(src) + return !below || below.is_space() \ No newline at end of file diff --git a/code/modules/multiz/zshadow.dm b/code/modules/multiz/zshadow.dm new file mode 100644 index 0000000000..42dee28f2b --- /dev/null +++ b/code/modules/multiz/zshadow.dm @@ -0,0 +1,133 @@ +/mob // TODO: rewrite as obj. + var/mob/zshadow/shadow + +/mob/zshadow + plane = OVER_OPENSPACE_PLANE + name = "shadow" + desc = "Z-level shadow" + status_flags = GODMODE + anchored = 1 + unacidable = 1 + density = 0 + opacity = 0 // Don't trigger lighting recalcs gah! TODO - consider multi-z lighting. + auto_init = FALSE // We do not need to be initialize()d + var/mob/owner = null // What we are a shadow of. + +/mob/zshadow/can_fall() + return FALSE + +/mob/zshadow/New(var/mob/L) + if(!istype(L)) + qdel(src) + return + ..() // I'm cautious about this, but its the right thing to do. + owner = L + sync_icon(L) + +/mob/Destroy() + if(shadow) + qdel(shadow) + shadow = null + . = ..() + +/mob/zshadow/examine(mob/user, distance, infix, suffix) + return owner.examine(user, distance, infix, suffix) + +// Relay some stuff they hear +/mob/zshadow/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "", var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) + if(speaker && speaker.z != src.z) + return // Only relay speech on our acutal z, otherwise we might relay sounds that were themselves relayed up! + if(isliving(owner)) + verb += " from above" + return owner.hear_say(message, verb, language, alt_name, italics, speaker, speech_sound, sound_vol) + +/mob/zshadow/proc/sync_icon(var/mob/M) + name = M.name + icon = M.icon + icon_state = M.icon_state + //color = M.color + color = "#848484" + overlays = M.overlays + transform = M.transform + dir = M.dir + if(shadow) + shadow.sync_icon(src) + +/mob/living/Move() + . = ..() + check_shadow() + +/mob/living/forceMove() + . = ..() + check_shadow() + +/mob/living/on_mob_jump() + // We're about to be admin-jumped. + // Unfortuantely loc isn't set until after this proc is called. So we must spawn() so check_shadow executes with the new loc. + . = ..() + if(shadow) + spawn(0) + check_shadow() + +/mob/living/proc/check_shadow() + var/mob/M = src + if(isturf(M.loc)) + var/turf/simulated/open/OS = GetAbove(src) + while(OS && istype(OS)) + if(!M.shadow) + M.shadow = new /mob/zshadow(M) + M.shadow.forceMove(OS) + M = M.shadow + OS = GetAbove(M) + // The topmost level does not need a shadow! + if(M.shadow) + qdel(M.shadow) + M.shadow = null + +// +// Handle cases where the owner mob might have changed its icon or overlays. +// + +/mob/living/update_icons() + . = ..() + if(shadow) + shadow.sync_icon(src) + +// WARNING - the true carbon/human/update_icons does not call ..(), therefore we must sideways override this. +// But be careful, we don't want to screw with that proc. So lets be cautious about what we do here. +/mob/living/carbon/human/update_icons() + . = ..() + if(shadow) + shadow.sync_icon(src) + +/mob/set_dir(new_dir) + . = ..() + if(shadow) + shadow.set_dir(new_dir) + +// Transfer messages about what we are doing to upstairs +/mob/visible_message(var/message, var/self_message, var/blind_message) + . = ..() + if(shadow) + shadow.visible_message(message, self_message, blind_message) + +// We should show the typing indicator so people above us can tell we're about to talk. +/mob/set_typing_indicator(var/state) + var/old_typing = src.typing + . = ..() + if(shadow && old_typing != src.typing) + shadow.set_typing_indicator(state) // Okay the real proc changed something! That means we should handle things too + +/mob/zshadow/set_typing_indicator(var/state) + if(!typing_indicator) + typing_indicator = new + typing_indicator.icon = 'icons/mob/talk.dmi' //VOREStation Edit - Looks better on the right with job icons. + typing_indicator.icon_state = "typing" + if(state && !typing) + overlays += typing_indicator + typing = 1 + else if(!state && typing) + overlays -= typing_indicator + typing = 0 + if(shadow) + shadow.set_typing_indicator(state) diff --git a/html/changelogs/Woodrat-MultiZ.yml b/html/changelogs/Woodrat-MultiZ.yml new file mode 100644 index 0000000000..0058ee3623 --- /dev/null +++ b/html/changelogs/Woodrat-MultiZ.yml @@ -0,0 +1,37 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment +################################# + +# Your name. +author: Woodrat + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. +# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. +changes: + - rscadd: "Added 'see down' in open spaces from Vore." + - rscadd: "Added talking and visible messages upward through open space from Vore." \ No newline at end of file diff --git a/icons/mob/talk.dmi b/icons/mob/talk.dmi index acd94a74c3add2ee0fb1c4977de9c47f8f9ccd81..ef15e9da6d0bd857a6968d7900fb4e62d48b6f5c 100644 GIT binary patch literal 3948 zcmb_fXIN9|5E4n zq=#}9fkX^~Qltuj0HK*cVj$&)eRgm8d++}F&Y3y$Jm2%aGxN@z2Ny5cN=qn9001Cu zch2TA0DuIqAYkVX!NUVl{ENW29CO7b%*Hp=8xe3PEFd@t0HX5CaO9hBb)^!>-yIW~ z%4hk{x;)Mj=bxBUM3Zp-RR@w1ZtHWHr%KIhr6{?o()ed&rbA#944tptm+x@XirVw> zqV-1IrA`y+4om5+f@4zs{{L9*5L4ZC)1`N?>T$U``V+MLie4Jy;#=pv_zMOx#n&s6 zKoHd@_pPIk$dWtr+DLWeG2`^n2l@C!y@3&jcSnwIJy7)ZhRGFajk(XApPbMqH4%KX zZpV_eNYyLSy^X7}C0Uulg||?`yKl%*y>u_)RZ?S7r=ctUr)^f|k1Mp_LuAvddL*wl zPM;S4d9xeh1B;kPyak^M6JNlpHvRUaOQt{zs!X$fvF2YYTmA2&a;M@*cegPDYkd8} zmTbID`@-E)Xt&^4T3WYs=Iy}6)#~FETV9tEe=7SH6;OB*|5;Ya!5Vy;`F%~m`WHJJ zt1D6Y%j2+HDpaMe?^GX%u7qT_JijMR+sI2>+Hy~Z30~olRb648fud(=WsUTSanb0= z+0Hw05t241{lhi0OOL%9srdwO~ zGy$fzdal(j%lGo57XjLjwMg>(*e7WYt-qk_M^OuyU|-+aq8D@c6X^680gtH~7>r$p zCN@OluZYIFxZeXI03D`X2W&fC(L1p)8+8CtniDRa&(BHiOM(t8dh(L$zpJAT6(MOyZ z8{?clG)%%8JxmwsYx$JScAHsacw?g;TR*xhUYkLsS`T22km zB*{Kcvu9XiU>m-3YjZZrL_A(?qvB`uG)%5L`$fQ`&`Jsp-o**3SD8HtxUn520n5#hq<-w zijCdM-C{k%<0kVtR-p6lc&3~dsHiv?8yi3N*V(J;KJwmVVT+=J=A8rmpy9e6i+p8whWKmmEz2BV2y-?FzgI!f!a;GrQ0M15_cW~lYcKG$%t3}0MZlXLAIOsM$YJg$uEH*JR60n8s^sc~q zCU9a z4wc*a_px6u%9^}q$ZURkbM|F`S-9w+7U|d2cqHA3E+*;~j#_`}Tw=uZ4}DCYcyc_*t@WV4-xPA!G)T0(!K-+M0|sD{x9Rntl_>_4rpXtLw=jXI`IGyDe6& zXL+=~vyF;vv}lTLf$z62Me@&;oI0j5vs0H%38cvGjqBX_R8SpS8j!ovsG?3e=|%4u zB7&hJYeSI9iHTO{7HTTNB4&xB@0xXu#@gx#Ms5e-o1jqxL$+dCd2RrZo2(!Vx_?QK zjXi*uq9~wv76kmyrm>4c`{L$21s8nsud;V&okgk%dD&pL@FgqOp;B%mSr-AtPi}lf z+Z*2V#p#^Hcp=N9%duV(N490+zj@35&24C_nMZy-+V-c61bJpx7|Fn3Ow(EhH^l!F zk>9fMWN{;cUG-isY+w8IpO;Th7Y3maWrs3)Ab3tC+wrQ{g@G`KKvWpL2LiigL5df? zAELbL?<&4+6}eTZoA_npQ;$xHdZTHClq=7Bt^WOC7@F5dwA?pj3*o!udW_*P9;$ps zUbO;3KCJ>rwvLmZ+`xE-gLA$6i1$_ZSUw=TgI5A|NHKWY2@t@23k1vWYd9 zt7Rk`LY@q6STLp(zD$SyDW)0`{~-cA(^~&oaJa=q!h|+Fa6cNo6!PvP*i6zZL;xJ? z%f=4%=jkJriK>~cZ0L^wU3xejQZ94ry5K8>((LFccNk!su^qF;f;R;`I`lYb?=a`v zw+8V(3juu|`8!1Z4xRi2scOi`IY~H`c2R zxqJ=wUq$vH6IN?F-yP79NGlprpM*)zJS5(?5IS$cc4J$Uh1VdiF^-4vxg|Lft$bVF zF|N<_DL+n9jB@Eb@=s1e7HV) zrqU;iW)VbB{%!J?8$mj)g|eVK2pz)!;Tl63Y4!5hE%tba&f+x!+_mPT+2Ct>{Jywm zc^=i}kFG5tnHqkG8TT9c3Y=GKe69EBl^N_D$j3D+rMU{HDUSB331WAugk9zZ9R2{s z9b<}=I)WTX5iF+fm^mVZ&Ru|Elr}wl#Zy#l6FEo7!gs`sZ=7#E^AXnj+bf!yWr&8h zCqL(q;i{sC`1z&}w6|az8wworgVyKA;tu3cWr`L#Q(~YuhsU4JDu7wNq3@cABUXM*gPU9U2q_-rIGTWbc|1Y(s!ciaa4?2zN12+WN3dCRH=Bu;sLec?_k`njtz2AP zS>+QB+t8in#IC79?FWuf$MVuvFNpS5SKV*A^`(~2t4Zm=989ZNiW~d(c5n15(J#IN zL!;;zEJOst+VZYuf@=X?e6*y-JKwV3oq;pkciK?QBV{=s``ME8xlTv9SIkF+nC8Z= zA5MZ32Q@8Cg1SRd}H7@5L7j+eQ%A~=Zxx4^kuE?wY0HX?b4#ZzZQ>s^#KT;>9~ zbMu9f2pLZwN(ifQ_GkLyIDs*R$mTcfV(+oE-;StMjM`Z_HHSI@;LvD4?Rvk7f?xp; z_{K5M9Hc~h8k7uaofn}wwqPUQz&R0%X-dG_zC0Og_Q8>=-mQ&{yfEqt}8KIQQ;#zXWb-u9NQS;b%`>i)Ku}+Rs!Y(F@8G1%&V?HokP! zboeY?Bj8)2zt?6Ai8V|*AtT^l*!x9F)BAM9EP7|lLU&I_4##Q`^-*%szZ2O+imSR6zZD*1k}x} zzp!d;$)j)%h!zq+{?ulVUZI$JmEt-rQ+9GXc>VXdueAQax#rNSuPmTl6S~**z_O6Y pizkA0UKypltKH{sSZbJzl0yE7MT%1?X!`cE=~bB+K2 literal 2513 zcmbuBdo&sxv=e&6SP*L(X^fVbur z!!1fmN}9et9%mGB@{gsqNwFIqfKMqYf%c{chbDVmz7~zV`cv}Nq(mj9wDO9ASMV%b z?d&10W%fivA&P2x&C>dHmten@VeOTdb{3oOKF4aEjjK6vpYc+BGbtNSSWLk-Eu5Nw z+f|`ryz&;;klV)eP?0yX`p?H0)-z?iDa`a%b7QaQX9lLOnBfm*9;5jvaFzDhO@zLQ zPy4M$T~(XGRd4#Azv)Lwmhn$QuX*lFB_I>Q-6uyI62iwcf75qL*sl#3&%mmFK9$LQC&QK zSRTBp#zd?9-l)s2Lw4xkDU%A%)M9)++=J7~`4dkQjm{aWOuF1Q1D}indw#jRBTBuW z6Rez>{3<1&#_!z*{q&9QTI9<$aH{&SfqLE66TY4qjqPpfsyT?SDjJDX|2%4Z=Z^Xi zBz!;&%8VZFd1lUglXpm2e&uOsA&r7KcRA{5sC^f!dnXKcP%aP%CKf4rHj$V^As<_0 zuyIO6V~7!vZhx}nSD|@NG3gDxvlE?~mbS`T{MyjakQ!aIm?MCuKvvKAoo@`eAoeE} zNTj_tdB~E8T&| zsuT7DGqLwSFPxJtiarDlErdrzNUyUH)ub#-&UAsTL_0DE1Jq0YP&BfmasL$MATDtz z+i6*Be{y90=piRvU?~WgI4vM6%DcJMy~VJ;wQL*)gAu)z23pExXm_(x zVn|;*n`ncF(97PAm~B6^E+kb{NKYN2vu20{f}^1G`E#q9!hfR|nbGU{i!eB9VDLCR}D1!wb~8$8|+n zLlob%tAcLxFlCuvHgJVK+Bd#8iVwssQF%;>q%9E8)6=6mAWi#V*2PKjjnlM@dPS0x z+EHLaN>}uOb!z075O>3Hv92WgJWRO`wPbNmK9kn`lvfS$lC>_~WKI(Zggy?3Q&$YW z18_GqGvV^x{(Ou|7Fh4ok3OL9Bre)@@;$euS58K-a`VHF=e)kuit=}m(=vi8hIY%V z#}X(a8V7mg%5sc|NywPc%l`qv0(XOI_bkLU((hnl`4F0^?~AkDyq1}45%qYVzE**Ja}|vWo1DM zjdzAZiGt*gZ{r%>6g_PfM}3Df!t--&D`0($T`;)nlW=QxWqG-=x#{{Gv#DAH{I8g= zcK9`rz(QJETPLm?;XeuYEEV^2#slRkz;vjKa=<@a%OqNqh-oq8d@DA%FglE*d! ztusrwBA_3@$eQUqlXb&FNrKEV=k+HJ@|iJqnuL^9Pxn@?VH^STds=GlI0K{1?b_do zfUcMPt7z29KH7~G=}0|Y|k?`C}vtd9>A=U*&;FUs9>gZ$!! z1}KP}q5a@{iE$-kfL9n1h|{mdg|bqj@nBE5*Gr^q&OJRqd1RT$!3(JJg;*G|Lo-pT zao9yB9PJ(o#zNQEQszIQQYy22iM`Dm!1eg;#v?ak-3u&y_)2hykoc$IWE?EFdU8E`gy9Y9>C zK>qJ+t!C_5)1wYnFqs@JlMSgRR1(vr-ZV)J4am^`>;L)}bU>i0qljOcqQtTJg-Yf##E}$y9Q}>Rd7Y$PDgIyw7FkgN%e# zKx#vU(&ewyI<{=jYaI3_*Uu|CZYjZi`g4Ty2NzD<1-~#EZ9e^Sxw7^zf2N$z3vn$^rW>gCi8Nxf9?DyWN8h5>pdS>B8S2fnDx5Df*Ebb#4i qWFW&otRutvxLcm`vG3-sTxllN!NP6O#6j^@Q}Xo;@OXSID)T?tsMVza diff --git a/icons/turf/open_space.dmi b/icons/turf/open_space.dmi new file mode 100644 index 0000000000000000000000000000000000000000..8c5810fffc6c59bab278f9ae861bac9b81e2c6a6 GIT binary patch literal 288 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!3HE3&8=$zQWaGp5hX6E#mPmP1tppJc|egl z!C?hOrQg2<7kvEsM9W)O>)e_1!5cyiE*d|0q;uX!^JD;!WA^CL8IQ9*=QSD&y1a$F zgG6&gr%c&l5p2}m1!VH<%#Sq?gN|JX3S&zt>Os zj0V07CJyWwQVW<(crP%{Vtc{h%Usg1jM2*BGK1`bOx)7NjHwdLPu8FH-w3ph!PC{x JWt~$(695H5Z^r-t literal 0 HcmV?d00001 diff --git a/polaris.dme b/polaris.dme index 57ab75931e..3b110087cb 100644 --- a/polaris.dme +++ b/polaris.dme @@ -18,6 +18,7 @@ #include "code\stylesheet.dm" #include "code\world.dm" #include "code\__defines\_compile_options.dm" +#include "code\__defines\_planes+layers.dm" #include "code\__defines\admin.dm" #include "code\__defines\appearance.dm" #include "code\__defines\atmos.dm" @@ -1809,9 +1810,11 @@ #include "code\modules\multiz\_stubs.dm" #include "code\modules\multiz\basic.dm" #include "code\modules\multiz\movement.dm" +#include "code\modules\multiz\open_space_controller.dm" #include "code\modules\multiz\pipes.dm" #include "code\modules\multiz\structures.dm" #include "code\modules\multiz\turf.dm" +#include "code\modules\multiz\zshadow.dm" #include "code\modules\nano\nanoexternal.dm" #include "code\modules\nano\nanomanager.dm" #include "code\modules\nano\nanomapgen.dm"