diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index b899e3fe4c7..00e6ae1bb80 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -86,6 +86,11 @@ #define CHAT_LAYER 12.0001 // Do not insert layers between these two values #define CHAT_LAYER_MAX 12.9999 +/// This plane masks out lighting to create an "emissive" effect, ie for glowing lights in otherwise dark areas. +#define EMISSIVE_PLANE 13 +/// The render target used by the emissive. +#define EMISSIVE_RENDER_TARGET "*EMISSIVE_PLANE" + #define LIGHTING_PLANE 15 #define LIGHTING_LAYER 15 diff --git a/code/__DEFINES/lighting.dm b/code/__DEFINES/lighting.dm index 34528090e86..b0a38ec8087 100644 --- a/code/__DEFINES/lighting.dm +++ b/code/__DEFINES/lighting.dm @@ -91,6 +91,21 @@ #define FLASH_LIGHT_POWER 3 #define FLASH_LIGHT_RANGE 3.8 +#define EMISSIVE_BLOCK_GENERIC 1 +/// Uses a dedicated render_target object to copy the entire appearance in real time to the blocking layer. For things that can change in appearance a lot from the base state, like humans. +#define EMISSIVE_BLOCK_UNIQUE 2 + +/// The color applied to all emissive overlays. +#define EMISSIVE_COLOR "#FFFFFF" +/// The color applied to all emissive blockers. Is meant to be the exact opposite of the EMMISIVE_COLOR +#define EM_BLOCK_COLOR "#000000" +/// A set of appearance flags applied to all emissive and emissive blocker overlays. +#define EMISSIVE_APPEARANCE_FLAGS (KEEP_APART|KEEP_TOGETHER|RESET_COLOR|RESET_TRANSFORM) +/// The color matrix used to mask out emissive blockers on the emissive plane. Alpha should default to zero, be solely dependent on the RGB value of [EMISSIVE_COLOR], and be independant of the RGB value of [EM_BLOCK_COLOR]. +#define EM_MASK_MATRIX list(0,0,0,1/3, 0,0,0,1/3, 0,0,0,1/3, 0,0,0,0, 1,1,1,0) +/// A globaly cached version of [EM_MASK_MATRIX] for quick access. +GLOBAL_LIST_INIT(em_mask_matrix, EM_MASK_MATRIX) + /// Returns the red part of a #RRGGBB hex sequence as number #define GETREDPART(hexa) hex2num(copytext(hexa, 2, 4)) diff --git a/code/__HELPERS/lighting.dm b/code/__HELPERS/lighting.dm new file mode 100644 index 00000000000..fcba21227ab --- /dev/null +++ b/code/__HELPERS/lighting.dm @@ -0,0 +1,12 @@ +/// Produces a mutable appearance glued to the [EMISSIVE_PLANE] dyed to be the [EMISSIVE_COLOR]. +/proc/emissive_appearance(icon, icon_state = "", layer = FLOAT_LAYER, alpha = 255, appearance_flags = NONE) + var/mutable_appearance/appearance = mutable_appearance(icon, icon_state, layer, EMISSIVE_PLANE, alpha, appearance_flags | EMISSIVE_APPEARANCE_FLAGS) + appearance.color = EMISSIVE_COLOR + appearance.alpha = alpha + return appearance + +/// Produces a mutable appearance glued to the [EMISSIVE_PLANE] dyed to be the [EM_BLOCK_COLOR]. +/proc/emissive_blocker(icon, icon_state = "", layer = FLOAT_LAYER, alpha = 255, appearance_flags = NONE) + var/mutable_appearance/appearance = mutable_appearance(icon, icon_state, layer, EMISSIVE_PLANE, alpha, appearance_flags | EMISSIVE_APPEARANCE_FLAGS) + appearance.color = EM_BLOCK_COLOR + return appearance diff --git a/code/_onclick/hud/plane_master.dm b/code/_onclick/hud/plane_master.dm index d49722e79ee..e470ea0e2b3 100644 --- a/code/_onclick/hud/plane_master.dm +++ b/code/_onclick/hud/plane_master.dm @@ -41,5 +41,26 @@ mouse_opacity = MOUSE_OPACITY_TRANSPARENT /obj/screen/plane_master/lighting/backdrop(mob/mymob) + . = ..() mymob.overlay_fullscreen("lighting_backdrop_lit", /obj/screen/fullscreen/lighting_backdrop/lit) mymob.overlay_fullscreen("lighting_backdrop_unlit", /obj/screen/fullscreen/lighting_backdrop/unlit) + +/obj/screen/plane_master/lighting/Initialize() + . = ..() + add_filter("emissives", 1, alpha_mask_filter(render_source = EMISSIVE_RENDER_TARGET, flags = MASK_INVERSE)) + +/** + * Things placed on this mask the lighting plane. Doesn't render directly. + * + * Gets masked by blocking plane. Use for things that you want blocked by + * mobs, items, etc. + */ +/obj/screen/plane_master/emissive + name = "emissive plane master" + plane = EMISSIVE_PLANE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + render_target = EMISSIVE_RENDER_TARGET + +/obj/screen/plane_master/emissive/Initialize() + . = ..() + add_filter("em_block_masking", 1, color_matrix_filter(GLOB.em_mask_matrix)) diff --git a/code/datums/mutable_appearance.dm b/code/datums/mutable_appearance.dm index 4d870e1296c..1904ab0ff81 100644 --- a/code/datums/mutable_appearance.dm +++ b/code/datums/mutable_appearance.dm @@ -10,15 +10,16 @@ // And yes this does have to be in the constructor, BYOND ignores it if you set it as a normal var // Helper similar to image() -/proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER, plane = FLOAT_PLANE) +/proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER, plane = FLOAT_PLANE, alpha = 255, appearance_flags = NONE) var/mutable_appearance/MA = new() MA.icon = icon MA.icon_state = icon_state MA.layer = layer MA.plane = plane + MA.alpha = alpha + MA.appearance_flags |= appearance_flags return MA - /mutable_appearance/clean/New() . = ..() alpha = 255 diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 564a820bb83..6c0984dd091 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -32,6 +32,11 @@ var/moving_diagonally = 0 //0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move var/list/client_mobs_in_contents + /// Either FALSE, [EMISSIVE_BLOCK_GENERIC], or [EMISSIVE_BLOCK_UNIQUE] + var/blocks_emissive = FALSE + ///Internal holder for emissive blocker object, do not use directly use blocks_emissive + var/atom/movable/emissive_blocker/em_block + /atom/movable/attempt_init(loc, ...) var/turf/T = get_turf(src) if(T && SSatoms.initialized != INITIALIZATION_INSSATOMS && GLOB.space_manager.is_zlevel_dirty(T.z)) @@ -39,9 +44,36 @@ return . = ..() +/atom/movable/Initialize(mapload) + . = ..() + switch(blocks_emissive) + if(EMISSIVE_BLOCK_GENERIC) + var/mutable_appearance/gen_emissive_blocker = mutable_appearance(icon, icon_state, plane = EMISSIVE_PLANE, alpha = src.alpha) + gen_emissive_blocker.color = EM_BLOCK_COLOR + gen_emissive_blocker.dir = dir + gen_emissive_blocker.appearance_flags |= appearance_flags + add_overlay(list(gen_emissive_blocker)) + if(EMISSIVE_BLOCK_UNIQUE) + render_target = ref(src) + em_block = new(src, render_target) + add_overlay(list(em_block)) + +/atom/movable/proc/update_emissive_block() + if(!blocks_emissive) + return + else if (blocks_emissive == EMISSIVE_BLOCK_GENERIC) + var/mutable_appearance/gen_emissive_blocker = emissive_blocker(icon, icon_state, alpha = src.alpha, appearance_flags = src.appearance_flags) + gen_emissive_blocker.dir = dir + return gen_emissive_blocker + else if(blocks_emissive == EMISSIVE_BLOCK_UNIQUE) + if(!em_block && !QDELETED(src)) + render_target = ref(src) + em_block = new(src, render_target) + add_overlay(list(em_block)) + /atom/movable/Destroy() unbuckle_all_mobs(force = TRUE) - + QDEL_NULL(em_block) . = ..() if(loc) loc.handle_atom_del(src) diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index e390634bb34..e9fc6de202f 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -33,6 +33,7 @@ var/in_use_lights = 0 // TO BE IMPLEMENTED var/toggle_sound = 'sound/items/wirecutter.ogg' + blocks_emissive = EMISSIVE_BLOCK_GENERIC /obj/machinery/camera/Initialize(mapload) . = ..() diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm index 95a9caae804..c1ec259b093 100644 --- a/code/game/machinery/computer/computer.dm +++ b/code/game/machinery/computer/computer.dm @@ -14,18 +14,13 @@ var/processing = 0 var/icon_keyboard = "generic_key" var/icon_screen = "generic" - var/light_range_on = 2 - var/light_power_on = 1 - var/overlay_layer + var/light_range_on = 1 + var/light_power_on = 0.7 /// Are we in the middle of a flicker event? var/flickering = FALSE /// Are we forcing the icon to be represented in a no-power state? var/force_no_power_icon_state = FALSE -/obj/machinery/computer/New() - overlay_layer = layer - ..() - /obj/machinery/computer/Initialize() ..() power_change() @@ -38,6 +33,7 @@ /obj/machinery/computer/extinguish_light() set_light(0) + underlays.Cut() visible_message("[src] grows dim, its screen barely readable.") /* @@ -73,28 +69,31 @@ flickering = FALSE /obj/machinery/computer/update_icon() - overlays.Cut() + cut_overlays() + underlays.Cut() if((stat & NOPOWER) || force_no_power_icon_state) if(icon_keyboard) - overlays += image(icon,"[icon_keyboard]_off",overlay_layer) + add_overlay("[icon_keyboard]_off") return + // This whole block lets screens ignore lighting and be visible even in the darkest room + var/overlay_state = icon_screen if(stat & BROKEN) - overlays += image(icon,"[icon_state]_broken",overlay_layer) - else - overlays += image(icon,icon_screen,overlay_layer) - - if(icon_keyboard) - overlays += image(icon, icon_keyboard ,overlay_layer) + overlay_state = "[icon_state]_broken" + add_overlay("[overlay_state]") + if(!(stat & BROKEN) && light) + underlays += emissive_appearance(icon, "[icon_state]_light_mask") + if(!icon_keyboard) + add_overlay("[icon_keyboard]") /obj/machinery/computer/power_change() ..() - update_icon() if((stat & (BROKEN|NOPOWER))) set_light(0) else set_light(light_range_on, light_power_on) + update_icon() /obj/machinery/computer/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) switch(damage_type) diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index 04da69784eb..b603092a659 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -25,7 +25,7 @@ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF flags = NODECONSTRUCT var/mode = null - + icon_screen = "cellconsole_on" //Used for logging people entering cryosleep and important items they are carrying. var/list/frozen_crew = list() var/list/frozen_items = list() diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 8fc82f71c39..0b8b8396446 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -2,6 +2,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect /obj/item name = "item" icon = 'icons/obj/items.dmi' + blocks_emissive = EMISSIVE_BLOCK_GENERIC move_resist = null // Set in the Initialise depending on the item size. Unless it's overriden by a specific item var/discrete = 0 // used in item_attack.dm to make an item not show an attack message to viewers diff --git a/code/modules/lighting/emissive_blocker.dm b/code/modules/lighting/emissive_blocker.dm new file mode 100644 index 00000000000..b2102dea8b0 --- /dev/null +++ b/code/modules/lighting/emissive_blocker.dm @@ -0,0 +1,40 @@ +/** + * Internal atom that copies an appearance on to the blocker plane + * + * This means that the atom in question will block any emissive sprites. + * This should only be used internally. If you are directly creating more of these, + * you're almost guaranteed to be doing something wrong. + */ +/atom/movable/emissive_blocker + name = "emissive blocker" + plane = EMISSIVE_PLANE + layer = FLOAT_LAYER + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + appearance_flags = EMISSIVE_APPEARANCE_FLAGS + +/atom/movable/emissive_blocker/Initialize(mapload, source) + . = ..() + verbs.Cut() //Cargo culting from lighting object, this maybe affects memory usage? + + render_source = source + color = EM_BLOCK_COLOR + +/atom/movable/emissive_blocker/ex_act(severity) + return FALSE + +/atom/movable/emissive_blocker/singularity_act() + return + +/atom/movable/emissive_blocker/singularity_pull() + return + +/atom/movable/emissive_blocker/blob_act() + return + +/atom/movable/emissive_blocker/onTransitZ() + return + +//Prevents people from moving these after creation, because they shouldn't be. +/atom/movable/emissive_blocker/forceMove(atom/destination, no_tp = FALSE, harderforce = FALSE) + if(harderforce) + return ..() diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 1eaa3e02308..6134cd93555 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -3,6 +3,7 @@ hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPMINDSHIELD_HUD,IMPCHEM_HUD,IMPTRACK_HUD,SPECIALROLE_HUD,GLAND_HUD) pressure_resistance = 25 mob_biotypes = MOB_ORGANIC | MOB_HUMANOID + blocks_emissive = EMISSIVE_BLOCK_UNIQUE //Marking colour and style var/list/m_colours = DEFAULT_MARKING_COLOURS //All colours set to #000000. var/list/m_styles = DEFAULT_MARKING_STYLES //All markings set to None. diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 7f9eae2c26f..cd06156b763 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -533,6 +533,8 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) overlays.Cut() // Force all overlays to regenerate update_fire() update_icons() + update_emissive_block() + /* --------------------------------------- */ //vvvvvv UPDATE_INV PROCS vvvvvv diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index f1dfbf25a07..3147d86bf30 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -12,6 +12,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( bubble_icon = "robot" universal_understand = 1 deathgasp_on_death = TRUE + blocks_emissive = EMISSIVE_BLOCK_UNIQUE var/sight_mode = 0 var/custom_name = "" diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 300c3ae0e44..167f704bd6e 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -33,6 +33,7 @@ icon_dead = "parrot_dead" pass_flags = PASSTABLE can_collar = TRUE + blocks_emissive = EMISSIVE_BLOCK_UNIQUE var/list/clean_speak = list( "Hi", diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index fe3cb652939..06134a3b3a8 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -6,6 +6,7 @@ throwforce = 10 dont_save = TRUE //to avoid it messing up in buildmode saving var/datum/mind/mind + blocks_emissive = EMISSIVE_BLOCK_GENERIC var/stat = 0 //Whether a mob is alive or dead. TODO: Move this to living - Nodrak diff --git a/icons/obj/computer.dmi b/icons/obj/computer.dmi index 7157f054dc7..9812a4aca22 100644 Binary files a/icons/obj/computer.dmi and b/icons/obj/computer.dmi differ diff --git a/icons/obj/cryogenic2.dmi b/icons/obj/cryogenic2.dmi index 88533a5d928..e81778f3a0b 100644 Binary files a/icons/obj/cryogenic2.dmi and b/icons/obj/cryogenic2.dmi differ diff --git a/paradise.dme b/paradise.dme index e4f21527a35..60e0876c019 100644 --- a/paradise.dme +++ b/paradise.dme @@ -115,6 +115,7 @@ #include "code\__HELPERS\heap.dm" #include "code\__HELPERS\icon_smoothing.dm" #include "code\__HELPERS\icons.dm" +#include "code\__HELPERS\lighting.dm" #include "code\__HELPERS\lists.dm" #include "code\__HELPERS\matrices.dm" #include "code\__HELPERS\mobs.dm" @@ -1745,6 +1746,7 @@ #include "code\modules\library\computers\checkout.dm" #include "code\modules\library\computers\public.dm" #include "code\modules\lighting\__lighting_docs.dm" +#include "code\modules\lighting\emissive_blocker.dm" #include "code\modules\lighting\lighting_area.dm" #include "code\modules\lighting\lighting_atom.dm" #include "code\modules\lighting\lighting_corner.dm"