diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index ff7fa86120c..eef4c58f471 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -17,18 +17,20 @@ #define OPENSPACE_LAYER 600 //Openspace layer over all -#define TRANSPARENT_FLOOR_PLANE -10 //Transparent plane that shows openspace underneath the floor -#define OPENSPACE_PLANE -9 //Openspace plane below all turfs -#define OPENSPACE_BACKDROP_PLANE -8 //Black square just over openspace plane to guaranteed cover all in openspace turf +#define TRANSPARENT_FLOOR_PLANE -11 //Transparent plane that shows openspace underneath the floor +#define OPENSPACE_PLANE -10 //Openspace plane below all turfs +#define OPENSPACE_BACKDROP_PLANE -9 //Black square just over openspace plane to guaranteed cover all in openspace turf -#define FLOOR_PLANE -7 +#define FLOOR_PLANE -8 -#define GAME_PLANE -6 -#define GAME_PLANE_FOV_HIDDEN -5 -#define GAME_PLANE_UPPER -4 -#define GAME_PLANE_UPPER_FOV_HIDDEN -3 +#define GAME_PLANE -7 +#define GAME_PLANE_FOV_HIDDEN -6 +#define GAME_PLANE_UPPER -5 +#define GAME_PLANE_UPPER_FOV_HIDDEN -4 +///Slightly above the game plane but does not catch mouse clicks. Useful for certain visuals that should be clicked through, like seethrough trees +#define ABOVE_GAME_NO_MOUSE_PLANE -3 #define ABOVE_GAME_PLANE -2 // PLANE_SPACE layer(s) diff --git a/code/__HELPERS/see_through_maps.dm b/code/__HELPERS/see_through_maps.dm new file mode 100644 index 00000000000..d3fb5dc1bb7 --- /dev/null +++ b/code/__HELPERS/see_through_maps.dm @@ -0,0 +1,61 @@ +//For these defines, check also above for their actual shapes in-game and maybe get a better idea + +///Default shape. It's one tile above the atom +#define SEE_THROUGH_MAP_DEFAULT "default" +///A 3x3 area 2 tiles above the atom (trees love to be this shape) +#define SEE_THROUGH_MAP_THREE_X_THREE "3x3" +///2 tiles above the atom +#define SEE_THROUGH_MAP_DEFAULT_TWO_TALL "default_two_tall" +///two rows of three tiles above the atom (small but thick trees love these) +#define SEE_THROUGH_MAP_THREE_X_TWO "3x2" +///One row of three tiles above the atom, but offset one tile to the left because of how billboards work +#define SEE_THROUGH_MAP_BILLBOARD "billboard" + + +/**global statics for the see_through_component coordinate maps +* For ease of use, include a comment in the shape of the coordinate map, where O is nothing, X is a hidden tile and A is the object +* List-coordinate layout is list(relative_x, relative_y, relative_z) +* Turf finding algorithm needs the z and you can totally use it, but I can't think of any reason to ever do it +* Also it'd be really cool if you could keep the list-coordinates in here represent their actual relative coords, dont use tabs though since their spacing can differ +*/ +GLOBAL_LIST_INIT(see_through_maps, list( + // X + // A + SEE_THROUGH_MAP_DEFAULT = list( + /*----------------*/list(0, 1, 0) + ), + + // XXX + // XXX + // XXX + // OAO + SEE_THROUGH_MAP_THREE_X_THREE = list( + list(-1, 3, 0), list(0, 3, 0), list(1, 3, 0), + list(-1, 2, 0), list(0, 2, 0), list(1, 2, 0), + list(-1, 1, 0), list(0, 1, 0), list(1, 1, 0) + ), + + // X + // X + // A + SEE_THROUGH_MAP_DEFAULT_TWO_TALL = list( + /*----------------*/list(0, 2, 0), + /*----------------*/list(0, 1, 0) + ), + + // XXX + // XXX + // OAO + SEE_THROUGH_MAP_THREE_X_TWO = list( + list(-1, 2, 0), list(0, 2, 0), list(1, 2, 0), + list(-1, 1, 0), list(0, 1, 0), list(1, 1, 0) + ), + + /// XXX + /// OAO + SEE_THROUGH_MAP_BILLBOARD = list( + list(0, 1, 0), list(1, 1, 0), list(2, 1, 0) + ) +)) + + diff --git a/code/_onclick/hud/rendering/plane_master.dm b/code/_onclick/hud/rendering/plane_master.dm index 42c95d43b1c..cdcb4dc5da4 100644 --- a/code/_onclick/hud/rendering/plane_master.dm +++ b/code/_onclick/hud/rendering/plane_master.dm @@ -107,6 +107,15 @@ appearance_flags = PLANE_MASTER //should use client color blend_mode = BLEND_OVERLAY +/atom/movable/screen/plane_master/game_world_above_no_mouse + name = "above game world no mouse plane master" + plane = ABOVE_GAME_NO_MOUSE_PLANE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + + render_relay_plane = GAME_PLANE + appearance_flags = PLANE_MASTER + blend_mode = BLEND_OVERLAY + /atom/movable/screen/plane_master/massive_obj name = "massive object plane master" plane = MASSIVE_OBJ_PLANE @@ -292,3 +301,5 @@ name = "splashscreen plane" plane = SPLASHSCREEN_PLANE render_relay_plane = RENDER_PLANE_NON_GAME + + diff --git a/code/_onclick/hud/rendering/plane_master_controller.dm b/code/_onclick/hud/rendering/plane_master_controller.dm index 24a125afba4..1c2a4f75936 100644 --- a/code/_onclick/hud/rendering/plane_master_controller.dm +++ b/code/_onclick/hud/rendering/plane_master_controller.dm @@ -108,6 +108,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/plane_master_controller) GAME_PLANE_FOV_HIDDEN, GAME_PLANE_UPPER, GAME_PLANE_UPPER_FOV_HIDDEN, + ABOVE_GAME_NO_MOUSE_PLANE, ABOVE_GAME_PLANE, MASSIVE_OBJ_PLANE, GHOST_PLANE, diff --git a/code/datums/components/seethrough.dm b/code/datums/components/seethrough.dm new file mode 100644 index 00000000000..5a34ece333f --- /dev/null +++ b/code/datums/components/seethrough.dm @@ -0,0 +1,147 @@ +///A component that lets you turn an object invisible when you're standing on certain relative turfs to it, like behind a tree +/datum/component/seethrough + ///List of lists that represent relative coordinates to the source atom + var/list/relative_turf_coords + ///A list of turfs on which we make ourself transparent + var/list/watched_turfs + ///Associate list, with client = trickery_image. Track which client is being tricked with which image + var/list/tricked_mobs = list() + + ///Which alpha do we animate towards? + var/target_alpha + ///How long our fase in/out takes + var/animation_time + ///After we somehow moved (because ss13 is godless and does not respect anything), how long do we need to stand still to feel safe to setup our "behind" area again + var/perimeter_reset_timer + +///see_through_map is a define pointing to a specific map. It's basically defining the area which is considered behind. See see_through_maps.dm for a list of maps +/datum/component/seethrough/Initialize(see_through_map = SEE_THROUGH_MAP_DEFAULT, target_alpha = 100, animation_time = 0.5 SECONDS, perimeter_reset_timer = 2 SECONDS) + . = ..() + + relative_turf_coords = GLOB.see_through_maps[see_through_map] + + if(!isatom(parent) || !LAZYLEN(relative_turf_coords)) + return COMPONENT_INCOMPATIBLE + + relative_turf_coords = GLOB.see_through_maps[see_through_map] + src.relative_turf_coords = relative_turf_coords + src.target_alpha = target_alpha + src.animation_time = animation_time + src.perimeter_reset_timer = perimeter_reset_timer + + RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/dismantle_perimeter) + + setup_perimeter(parent) + +///Loop through a list with relative coordinate lists to mark those tiles and hide our parent when someone enters those tiles +/datum/component/seethrough/proc/setup_perimeter(atom/parent) + watched_turfs = list() + + for(var/list/coordinates as anything in relative_turf_coords) + var/turf/target = TURF_FROM_COORDS_LIST(list(parent.x + coordinates[1], parent.y + coordinates[2], parent.z + coordinates[3])) + + if(isnull(target)) + continue + + RegisterSignal(target, COMSIG_ATOM_ENTERED, .proc/on_entered) + RegisterSignal(target, COMSIG_ATOM_EXITED, .proc/on_exited) + + watched_turfs.Add(target) + +///Someone entered one of our tiles, so sent an override overlay and a cute animation to make us fade out a bit +/datum/component/seethrough/proc/on_entered(atom/source, atom/movable/entered) + SIGNAL_HANDLER + + if(!ismob(entered)) + return + + var/mob/mob = entered + + if(!mob.client) + RegisterSignal(mob, COMSIG_MOB_LOGIN, .proc/trick_mob) + return + + if(mob in tricked_mobs) + return + + trick_mob(mob) + +///Remove the screen object and make us appear solid to the client again +/datum/component/seethrough/proc/on_exited(atom/source, atom/movable/exited, direction) + SIGNAL_HANDLER + + if(!ismob(exited)) + return + + var/mob/mob = exited + + if(!mob.client) + UnregisterSignal(mob, COMSIG_MOB_LOGIN) + return + + var/turf/moving_to = get_turf(exited) + if(moving_to in watched_turfs) + return + + //Check if we're being 'tricked' + if(mob in tricked_mobs) + var/image/trickery_image = tricked_mobs[mob] + animate(trickery_image, alpha = 255, time = animation_time) + tricked_mobs.Remove(mob) + UnregisterSignal(mob, COMSIG_MOB_LOGOUT) + + //after playing the fade-in animation, remove the screen obj + addtimer(CALLBACK(src, /datum/component/seethrough/proc/clear_image, trickery_image, mob.client), animation_time) + +///Apply the trickery image and animation +/datum/component/seethrough/proc/trick_mob(mob/fool) + var/image/user_overlay = new(parent) + user_overlay.loc = parent + user_overlay.override = TRUE + //Special plane so we can click through the overlay + user_overlay.plane = ABOVE_GAME_NO_MOUSE_PLANE + + //These are inherited, but we already use the atom's loc so we end up at double the pixel offset + user_overlay.pixel_x = 0 + user_overlay.pixel_y = 0 + + fool.client.images += user_overlay + + animate(user_overlay, alpha = target_alpha, time = animation_time) + + tricked_mobs[fool] = user_overlay + RegisterSignal(fool, COMSIG_MOB_LOGOUT, .proc/on_client_disconnect) + + +///Unrout ourselves after we somehow moved, and start a timer so we can re-restablish our behind area after standing still for a bit +/datum/component/seethrough/proc/dismantle_perimeter() + SIGNAL_HANDLER + + for(var/turf in watched_turfs) + UnregisterSignal(turf, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_EXITED)) + + watched_turfs = null + clear_all_images() + + //Timer override, so if our atom keeps moving the timer is reset until they stop for X time + addtimer(CALLBACK(src, /datum/component/seethrough/proc/setup_perimeter, parent), perimeter_reset_timer, TIMER_OVERRIDE | TIMER_UNIQUE) + +///Remove a screen image from a client +/datum/component/seethrough/proc/clear_image(image/removee, client/remove_from) + remove_from?.images -= removee //player could've logged out during the animation, so check just in case + +/datum/component/seethrough/proc/clear_all_images() + for(var/mob/fool in tricked_mobs) + var/image/trickery_image = tricked_mobs[fool] + fool.client?.images -= trickery_image + UnregisterSignal(fool, COMSIG_MOB_LOGOUT) + + tricked_mobs.Cut() + +///Image is removed when they log out because client gets deleted, so drop the mob reference +/datum/component/seethrough/proc/on_client_disconnect(mob/fool) + SIGNAL_HANDLER + + tricked_mobs.Remove(fool) + UnregisterSignal(fool, COMSIG_MOB_LOGOUT) + RegisterSignal(fool, COMSIG_MOB_LOGIN, .proc/trick_mob) diff --git a/code/game/objects/structures/billboard.dm b/code/game/objects/structures/billboard.dm index 58462527063..93179ba48cf 100644 --- a/code/game/objects/structures/billboard.dm +++ b/code/game/objects/structures/billboard.dm @@ -3,12 +3,18 @@ desc = "A blank billboard, with space for all kinds of advertising." icon = 'icons/obj/billboard.dmi' icon_state = "billboard_blank" + plane = ABOVE_GAME_PLANE max_integrity = 1000 bound_width = 96 - bound_height = 64 + bound_height = 32 density = TRUE anchored = TRUE +/obj/structure/billboard/Initialize(mapload) + . = ..() + + AddComponent(/datum/component/seethrough, SEE_THROUGH_MAP_BILLBOARD) + /obj/structure/billboard/donk_n_go name = "\improper Donk-n-Go billboard" desc = "A billboard advertising Donk-n-Go, Donk Co's ever-present and ever-unhealthy fast-food venture: GET IN, GET FED, GET GONE!" diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm index a5e9104382c..76cbeb1e344 100644 --- a/code/game/objects/structures/flora.dm +++ b/code/game/objects/structures/flora.dm @@ -324,6 +324,15 @@ GLOBAL_LIST_EMPTY(flora_uprooting_tools_typepaths) delete_on_harvest = TRUE flora_flags = FLORA_HERBAL | FLORA_WOODEN +/obj/structure/flora/tree/Initialize(mapload) + . = ..() + + AddComponent(/datum/component/seethrough, get_seethrough_map()) + +///Return a see_through_map, examples in seethrough.dm +/obj/structure/flora/tree/proc/get_seethrough_map() + return SEE_THROUGH_MAP_DEFAULT + /obj/structure/flora/tree/harvest(mob/living/user) . = ..() var/turf/my_turf = get_turf(src) @@ -381,6 +390,9 @@ GLOBAL_LIST_EMPTY(flora_uprooting_tools_typepaths) pixel_x = -48 pixel_y = -20 +/obj/structure/flora/tree/jungle/get_seethrough_map() + return SEE_THROUGH_MAP_THREE_X_THREE + /obj/structure/flora/tree/jungle/style_2 icon_state = "tree2" /obj/structure/flora/tree/jungle/style_3 @@ -401,6 +413,9 @@ GLOBAL_LIST_EMPTY(flora_uprooting_tools_typepaths) icon = 'icons/obj/flora/jungletreesmall.dmi' icon_state = "tree1" +/obj/structure/flora/tree/jungle/small/get_seethrough_map() + return SEE_THROUGH_MAP_THREE_X_TWO + /obj/structure/flora/tree/jungle/small/style_2 icon_state = "tree2" /obj/structure/flora/tree/jungle/small/style_3 @@ -425,6 +440,9 @@ GLOBAL_LIST_EMPTY(flora_uprooting_tools_typepaths) icon = 'icons/obj/flora/pinetrees.dmi' icon_state = "pine_1" +/obj/structure/flora/tree/pine/get_seethrough_map() + return SEE_THROUGH_MAP_DEFAULT_TWO_TALL + /obj/structure/flora/tree/pine/style_2 icon_state = "pine_2" /obj/structure/flora/tree/pine/style_3 diff --git a/tgstation.dme b/tgstation.dme index f30bba94b9b..83824e5db4d 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -344,6 +344,7 @@ #include "code\__HELPERS\roundend.dm" #include "code\__HELPERS\sanitize_values.dm" #include "code\__HELPERS\screen_objs.dm" +#include "code\__HELPERS\see_through_maps.dm" #include "code\__HELPERS\shell.dm" #include "code\__HELPERS\spatial_info.dm" #include "code\__HELPERS\spawns.dm" @@ -852,6 +853,7 @@ #include "code\datums\components\rotation.dm" #include "code\datums\components\scope.dm" #include "code\datums\components\seclight_attachable.dm" +#include "code\datums\components\seethrough.dm" #include "code\datums\components\shell.dm" #include "code\datums\components\shielded.dm" #include "code\datums\components\shrink.dm"