diff --git a/.github/workflows/autowiki.yml b/.github/workflows/autowiki.yml index 82d0ac76f32f..fec68f2ca332 100644 --- a/.github/workflows/autowiki.yml +++ b/.github/workflows/autowiki.yml @@ -8,7 +8,7 @@ permissions: jobs: autowiki: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - name: "Check for AUTOWIKI_USERNAME" id: secrets_set @@ -32,7 +32,7 @@ jobs: run: | sudo dpkg --add-architecture i386 sudo apt update || true - sudo apt install -o APT::Immediate-Configure=false libssl1.1:i386 + sudo apt install -o APT::Immediate-Configure=false zlib1g-dev:i386 libssl-dev:i386 bash tools/ci/install_rust_g.sh - name: Compile and generate Autowiki files if: steps.secrets_set.outputs.SECRETS_ENABLED diff --git a/code/__DEFINES/colours.dm b/code/__DEFINES/colours.dm index b7457f9ae478..5fa106715f39 100644 --- a/code/__DEFINES/colours.dm +++ b/code/__DEFINES/colours.dm @@ -142,6 +142,9 @@ * * Important note: colours can end up significantly different from the basic html picture, especially when saturated */ + +/// Full white. rgb(255, 255, 255) +#define LIGHT_COLOR_WHITE "#FFFFFF" /// Bright but quickly dissipating neon green. rgb(100, 200, 100) #define LIGHT_COLOUR_GREEN "#64C864" /// Electric green. rgb(0, 255, 0) diff --git a/code/__DEFINES/conflict.dm b/code/__DEFINES/conflict.dm index 30b2627bb1b0..241bcb469622 100644 --- a/code/__DEFINES/conflict.dm +++ b/code/__DEFINES/conflict.dm @@ -90,6 +90,8 @@ #define ATTACH_IGNORE_EMPTY (1<<5) /// This attachment should activate if you attack() with it attached. #define ATTACH_MELEE (1<<6) +/// Override for attachies so you can fire them with a single hand . ONLY FOR PROJECTILES!! +#define ATTACH_WIELD_OVERRIDE (1<<7) //Ammo magazine defines, for flags_magazine @@ -218,6 +220,7 @@ #define UNIFORM_HAS_SENSORS 1 #define UNIFORM_FORCED_SENSORS 2 +#define EYE_PROTECTION_NEGATIVE -1 #define EYE_PROTECTION_NONE 0 #define EYE_PROTECTION_FLAVOR 1 #define EYE_PROTECTION_FLASH 2 diff --git a/code/__DEFINES/cooldowns.dm b/code/__DEFINES/cooldowns.dm index e1f221dccbde..9368caf3c6e3 100644 --- a/code/__DEFINES/cooldowns.dm +++ b/code/__DEFINES/cooldowns.dm @@ -3,6 +3,7 @@ #define COOLDOWN_HIJACK_BARRAGE "gamemode_explosive_barrage" #define COOLDOWN_HIJACK_GROUND_CHECK "gamemode_ground_check" #define COOLDOWN_ITEM_HOOD_SOUND "item_hood_sound" +#define COOLDOWN_LIGHT "cooldown_light" //Define for ship alt #define COOLDOWN_ALTITUDE_CHANGE "altitude_change" diff --git a/code/__DEFINES/dcs/signals/atom/signals_atom.dm b/code/__DEFINES/dcs/signals/atom/signals_atom.dm index 0cbe28d0438c..7431c5593b17 100644 --- a/code/__DEFINES/dcs/signals/atom/signals_atom.dm +++ b/code/__DEFINES/dcs/signals/atom/signals_atom.dm @@ -26,5 +26,22 @@ ///from /turf/ChangeTurf #define COMSIG_ATOM_TURF_CHANGE "movable_turf_change" +//from atom/set_light(): (l_range, l_power, l_color) +#define COMSIG_ATOM_SET_LIGHT "atom_set_light" + +///Called right before the atom changes the value of light_range to a different one, from base atom/set_light_range(): (new_range) +#define COMSIG_ATOM_SET_LIGHT_RANGE "atom_set_light_range" +///Called right before the atom changes the value of light_power to a different one, from base atom/set_light_power(): (new_power) +#define COMSIG_ATOM_SET_LIGHT_POWER "atom_set_light_power" +///Called right before the atom changes the value of light_color to a different one, from base atom/set_light_color(): (new_color) +#define COMSIG_ATOM_SET_LIGHT_COLOR "atom_set_light_color" +///Called right before the atom changes the value of light_on to a different one, from base atom/set_light_on(): (new_value) +#define COMSIG_ATOM_SET_LIGHT_ON "atom_set_light_on" +///Called right before the atom changes the value of light_flags to a different one, from base atom/set_light_flags(): (new_value) +#define COMSIG_ATOM_SET_LIGHT_FLAGS "atom_set_light_flags" + +///from base of atom/set_opacity(): (new_opacity) +#define COMSIG_ATOM_SET_OPACITY "atom_set_opacity" + ///When the transform or an atom is varedited through vv topic. #define COMSIG_ATOM_VV_MODIFY_TRANSFORM "atom_vv_modify_transform" diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index 46168a8fce7f..8904c0295abf 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -136,8 +136,6 @@ #define ABOVE_FLY_LAYER 6 -#define ABOVE_LIGHTING_PLANE 16 - /// blip from motion detector #define BELOW_FULLSCREEN_LAYER 16.9 #define FULLSCREEN_LAYER 17 @@ -165,11 +163,44 @@ #define CINEMATIC_LAYER 21 -#define TYPING_LAYER 500 - /// for areas, so they appear above everything else on map file. #define AREAS_LAYER 999 +//---------- EMISSIVES ------------- +//Layering order of these is not particularly meaningful. +//Important part is the seperation of the planes for control via plane_master + +/// This plane masks out lighting to create an "emissive" effect, ie for glowing lights in otherwise dark areas. +#define EMISSIVE_PLANE 90 +/// The render target used by the emissive layer. +#define EMISSIVE_RENDER_TARGET "*EMISSIVE_PLANE" +/// The layer you should use if you _really_ don't want an emissive overlay to be blocked. +#define EMISSIVE_LAYER_UNBLOCKABLE 9999 + +#define LIGHTING_BACKPLANE_LAYER 14.5 + +#define LIGHTING_RENDER_TARGET "LIGHT_PLANE" + +#define SHADOW_RENDER_TARGET "SHADOW_RENDER_TARGET" + +/// Plane for balloon text (text that fades up) +#define BALLOON_CHAT_PLANE 110 +/// Bubble for typing indicators +#define TYPING_LAYER 500 + +#define O_LIGHTING_VISUAL_PLANE 120 +#define O_LIGHTING_VISUAL_LAYER 16 +#define O_LIGHTING_VISUAL_RENDER_TARGET "O_LIGHT_VISUAL_PLANE" + +#define LIGHTING_PRIMARY_LAYER 15 //The layer for the main lights of the station +#define LIGHTING_PRIMARY_DIMMER_LAYER 15.1 //The layer that dims the main lights of the station +#define LIGHTING_SECONDARY_LAYER 16 //The colourful, usually small lights that go on top + +#define LIGHTING_SHADOW_LAYER 17 //Where the shadows happen + +#define ABOVE_LIGHTING_PLANE 150 +#define ABOVE_LIGHTING_LAYER 18 + /*=============================*\ | | | PLANE DEFINES | @@ -191,6 +222,7 @@ #define GHOST_PLANE 80 +///--------------- FULLSCREEN RUNECHAT BUBBLES ------------ #define LIGHTING_PLANE 100 #define EXTERIOR_LIGHTING_PLANE 101 diff --git a/code/__DEFINES/lighting.dm b/code/__DEFINES/lighting.dm index 5a4ba7676233..097a0f5d5e71 100644 --- a/code/__DEFINES/lighting.dm +++ b/code/__DEFINES/lighting.dm @@ -1,5 +1,109 @@ +///Object doesn't use any of the light systems. Should be changed to add a light source to the object. +#define NO_LIGHT_SUPPORT 0 +///Light made with the lighting datums, applying a matrix. +#define STATIC_LIGHT 1 +///Light made by masking the lighting darkness plane. +#define MOVABLE_LIGHT 2 +///A mix of the above, cheaper on moving items than dynamic, but heavier on rendering than movable +#define HYBRID_LIGHT 3 +///Pointy light +#define DIRECTIONAL_LIGHT 4 + +#define LIGHT_ATTACHED (1<<0) + +#define MINIMUM_USEFUL_LIGHT_RANGE 1.4 + +#define LIGHTING_ICON 'icons/effects/lighting_object.dmi' // icon used for lighting shading effects +#define LIGHTING_ICON_BIG 'icons/effects/lighting_object_big.dmi' //! icon used for lighting shading effects + +#define ALPHA_TO_INTENSITY(alpha) (-(((clamp(alpha, 0, 22) - 22) / 6) ** 4) + 255) + + +#define LIGHT_RANGE_FIRE 3 //How many tiles standard fires glow. + #define LIGHTING_PLANE_ALPHA_VISIBLE 255 ///The dim natural vision of Yautja #define LIGHTING_PLANE_ALPHA_YAUTJA 235 +#define LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE 192 #define LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE 127 #define LIGHTING_PLANE_ALPHA_INVISIBLE 0 + + +#define FLASH_LIGHT_DURATION 2 +#define FLASH_LIGHT_POWER 3 +#define FLASH_LIGHT_RANGE 3.8 + +// Emissive blocking. +/// Uses vis_overlays to leverage caching so that very few new items need to be made for the overlay. For anything that doesn't change outline or opaque area much or at all. +#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 matrix applied to all emissive overlays. Should be solely dependent on alpha and not have RGB overlap with [EM_BLOCK_COLOR]. +#define EMISSIVE_COLOR list(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 1,1,1,0) +/// A globaly cached version of [EMISSIVE_COLOR] for quick access. +GLOBAL_LIST_INIT(emissive_color, EMISSIVE_COLOR) +/// The color matrix applied to all emissive blockers. Should be solely dependent on alpha and not have RGB overlap with [EMISSIVE_COLOR]. +#define EM_BLOCK_COLOR list(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) +/// A globaly cached version of [EM_BLOCK_COLOR] for quick access. +GLOBAL_LIST_INIT(em_block_color, EM_BLOCK_COLOR) +/// 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)) + +/// Returns the green part of a #RRGGBB hex sequence as number +#define GETGREENPART(hexa) hex2num(copytext(hexa, 4, 6)) + +/// Returns the blue part of a #RRGGBB hex sequence as number +#define GETBLUEPART(hexa) hex2num(copytext(hexa, 6, 8)) + +/// Parse the hexadecimal color into lumcounts of each perspective. +#define PARSE_LIGHT_COLOR(source) \ +do { \ + if (source.light_color != COLOR_WHITE) { \ + var/__light_color = source.light_color; \ + source.lum_r = GETREDPART(__light_color) / 255; \ + source.lum_g = GETGREENPART(__light_color) / 255; \ + source.lum_b = GETBLUEPART(__light_color) / 255; \ + } else { \ + source.lum_r = 1; \ + source.lum_g = 1; \ + source.lum_b = 1; \ + }; \ +} while (FALSE) + + +//Bay lighting engine shit, not in /code/modules/lighting because BYOND is being shit about it //thats how defines work, hello? +#define LIGHTING_INTERVAL 5 // frequency, in 1/10ths of a second, of the lighting process + +#define MOVABLE_MAX_RANGE 7 + +#define LIGHTING_FALLOFF 1 // type of falloff to use for lighting; 1 for circular, 2 for square +#define LIGHTING_LAMBERTIAN 0 // use lambertian shading for light sources +#define LIGHTING_HEIGHT 1 // height off the ground of light sources on the pseudo-z-axis, you should probably leave this alone +#define LIGHTING_ROUND_VALUE (1 / 64) //Value used to round lumcounts, values smaller than 1/129 don't matter (if they do, thanks sinking points), greater values will make lighting less precise, but in turn increase performance, VERY SLIGHTLY. + +/// If the max of the lighting lumcounts of each spectrum drops below this, disable luminosity on the lighting objects. Set to zero to disable soft lighting. Luminosity changes then work if it's lit at all. +#define LIGHTING_SOFT_THRESHOLD 0 + +// If I were you I'd leave this alone. +#define LIGHTING_BASE_MATRIX \ + list \ + ( \ + 1, 1, 1, 0, \ + 1, 1, 1, 0, \ + 1, 1, 1, 0, \ + 1, 1, 1, 0, \ + 0, 0, 0, 1 \ + ) \ + +#define LIGHTING_NO_UPDATE 0 +#define LIGHTING_VIS_UPDATE 1 +#define LIGHTING_CHECK_UPDATE 2 +#define LIGHTING_FORCE_UPDATE 3 diff --git a/code/__DEFINES/mob_hud.dm b/code/__DEFINES/mob_hud.dm index 2704e52d2f85..02f992694832 100644 --- a/code/__DEFINES/mob_hud.dm +++ b/code/__DEFINES/mob_hud.dm @@ -65,3 +65,6 @@ #define TRACKER_HIVE "Hive Core" #define TRACKER_LEADER "Leader" #define TRACKER_TUNNEL "Tunnel" + +//These are used to manage the same HUD having multiple sources +#define HUD_SOURCE_ADMIN "admin" diff --git a/code/__DEFINES/objects.dm b/code/__DEFINES/objects.dm index 0a34ac9d6fe9..a6b95c879ae4 100644 --- a/code/__DEFINES/objects.dm +++ b/code/__DEFINES/objects.dm @@ -168,3 +168,8 @@ var/list/RESTRICTED_CAMERA_NETWORKS = list( //Those networks can only be accesse // For reinforced table status #define RTABLE_WEAKENED 1 #define RTABLE_NORMAL 2 + +//Lights define +#define CHECKS_PASSED 1 +#define STILL_ON_COOLDOWN 2 +#define NO_LIGHT_STATE_CHANGE 3 diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 8bebb0628ee7..55ff78aaa953 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -203,8 +203,11 @@ // GUN TRAITS #define TRAIT_GUN_SILENCED "t_gun_silenced" + #define TRAIT_GUN_BIPODDED "t_gun_bipodded" +#define TRAIT_GUN_LIGHT_DEACTIVATED "t_gun_light_deactivated" + // Miscellaneous item traits. // Do NOT bloat this category, if needed make a new category (like shoe traits, xeno item traits...) diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index 6c913b016083..fe15e6d84c79 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -38,6 +38,32 @@ };\ } while(FALSE) +// binary search sorted insert +// IN: Object to be inserted +// LIST: List to insert object into +#define BINARY_INSERT_NUM(IN, LIST) \ + var/__BIN_CTTL = length(LIST);\ + if(!__BIN_CTTL) {\ + LIST += IN;\ + } else {\ + var/__BIN_LEFT = 1;\ + var/__BIN_RIGHT = __BIN_CTTL;\ + var/__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ + var/__BIN_ITEM;\ + while(__BIN_LEFT < __BIN_RIGHT) {\ + __BIN_ITEM = LIST[__BIN_MID];\ + if(__BIN_ITEM <= IN) {\ + __BIN_LEFT = __BIN_MID + 1;\ + } else {\ + __BIN_RIGHT = __BIN_MID;\ + };\ + __BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ + };\ + __BIN_ITEM = LIST[__BIN_MID];\ + __BIN_MID = __BIN_ITEM > IN ? __BIN_MID : __BIN_MID + 1;\ + LIST.Insert(__BIN_MID, IN);\ + } + //Like typesof() or subtypesof(), but returns a typecache instead of a list /proc/typecacheof(path, ignore_root_path, only_root_path = FALSE) if(ispath(path)) diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index 4c8e1fe31354..39d91b2ada26 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -325,28 +325,25 @@ world -/// Create a single [/icon] from a given [/atom] or [/image]. -/// -/// Very low-performance. Should usually only be used for HTML, where BYOND's -/// appearance system (overlays/underlays, etc.) is not available. -/// -/// Only the first argument is required. +// Creates a single icon from a given /atom or /image. Only the first argument is required. /proc/getFlatIcon(image/A, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE) //Define... defines. var/static/icon/flat_template = icon('icons/effects/effects.dmi', "nothing") #define BLANK icon(flat_template) - #define GENERATE_FLAT_IMAGE_ICON(ICON_VAR, IMG_SOURCE, icon_to_use, icon_state_to_use, dir_to_use)\ - var/icon/SELF_ICON=icon(icon(icon_to_use, icon_state_to_use, dir_to_use), "", SOUTH, no_anim ? 1 : null);\ - if(##IMG_SOURCE.alpha < 255)\ - SELF_ICON.Blend(rgb(255, 255, 255, ##IMG_SOURCE.alpha), ICON_MULTIPLY);\ - if(##IMG_SOURCE.color) {\ - if(islist(##IMG_SOURCE.color))\ - SELF_ICON.MapColors(arglist(##IMG_SOURCE.color));\ - else\ - SELF_ICON.Blend(##IMG_SOURCE.color, ICON_MULTIPLY);\ - }\ - ##ICON_VAR = SELF_ICON; + #define SET_SELF(SETVAR) do { \ + var/icon/SELF_ICON = icon(icon(curicon, curstate, base_icon_dir), "", SOUTH, no_anim ? 1 : null); \ + if(A.alpha < 255) { \ + SELF_ICON.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY);\ + } \ + if(A.color) { \ + if(islist(A.color)){ \ + SELF_ICON.MapColors(arglist(A.color))} \ + else{ \ + SELF_ICON.Blend(A.color, ICON_MULTIPLY)} \ + } \ + ##SETVAR=SELF_ICON;\ + } while (0) #define INDEX_X_LOW 1 #define INDEX_X_HIGH 2 #define INDEX_Y_LOW 3 @@ -361,10 +358,6 @@ world #define addY1 add_size[INDEX_Y_LOW] #define addY2 add_size[INDEX_Y_HIGH] - #define PROCESS_SET_UNDERLAYS 0 - #define PROCESS_SET_VIS_CONTENTS 1 - #define PROCESS_SET_OVERLAYS 2 - if(!A || A.alpha <= 0) return BLANK @@ -418,45 +411,44 @@ world var/curblend = A.blend_mode || defblend - var/atom/movable/AM = A - if(length(A.overlays) || length(A.underlays) || (istype(AM) && AM.vis_contents)) + if(length(A.overlays) || length(A.underlays)) var/icon/flat = BLANK // Layers will be a sorted list of icons/overlays, based on the order in which they are displayed var/list/layers = list() var/image/copy // Add the atom's icon itself, without pixel_x/y offsets. if(!noIcon) - copy = image(icon=curicon, icon_state=curstate, layer=A.layer, dir=base_icon_dir) + copy = image(icon = curicon, icon_state = curstate, layer = A.layer, dir = base_icon_dir) copy.color = A.color copy.alpha = A.alpha copy.blend_mode = curblend layers[copy] = A.layer - // Loop through the underlays, then vis_contents, then overlays, sorting them into the layers list - for(var/process_set in PROCESS_SET_UNDERLAYS to PROCESS_SET_OVERLAYS) - var/list/process + // Loop through the underlays, then overlays, sorting them into the layers list + for(var/process_set in 0 to 2) + var/list/process = process_set ? A.overlays : A.underlays switch(process_set) - if(PROCESS_SET_UNDERLAYS) + if(0) process = A.underlays - if(PROCESS_SET_VIS_CONTENTS) - if(!istype(AM)) - continue + if(1) process = A.vis_contents - else // PROCESS_SET_OVERLAYS + if(2) process = A.overlays - for(var/i in 1 to process.len) + for(var/i in 1 to length(process)) var/image/current = process[i] if(!current) continue - if(process_set == PROCESS_SET_VIS_CONTENTS && !istype(current)) + if(current.plane != FLOAT_PLANE && current.plane != A.plane) + continue + if(process_set == 1 && !istype(current)) current = image(icon = current.icon, icon_state = current.icon_state, layer = current.layer, dir = current.dir) var/current_layer = current.layer if(current_layer < 0) if(current_layer <= -1000) return flat - current_layer = A.layer + ((process_set ? 1000 : 0)+current_layer) / 1000 + current_layer = process_set + A.layer + current_layer / 1000 - for(var/p in 1 to layers.len) + for(var/p in 1 to length(layers)) var/image/cmp = layers[p] if(current_layer < layers[cmp]) layers.Insert(p, current) @@ -478,10 +470,8 @@ world if(I == copy) // 'I' is an /image based on the object being flattened. curblend = BLEND_OVERLAY add = icon(I.icon, I.icon_state, base_icon_dir) - else // 'I' is an /image - var/image_has_icon = I.icon - if(image_has_icon) - GENERATE_FLAT_IMAGE_ICON(add, I, I.icon, I.icon_state, base_icon_dir) + else // 'I' is an appearance object. + add = getFlatIcon(image(I), curdir, curicon, curstate, curblend, FALSE, no_anim) if(!add) continue // Find the new dimensions of the flat icon to fit the added overlay @@ -495,10 +485,10 @@ world if(flat_size ~! add_size) // Resize the flattened icon so the new icon fits flat.Crop( - addX1 - flatX1 + 1, - addY1 - flatY1 + 1, - addX2 - flatX1 + 1, - addY2 - flatY1 + 1 + addX1 - flatX1 + 1, + addY1 - flatY1 + 1, + addX2 - flatX1 + 1, + addY2 - flatY1 + 1 ) flat_size = add_size.Copy() @@ -523,7 +513,7 @@ world . = icon(flat, "", SOUTH) else //There's no overlays. if(!noIcon) - GENERATE_FLAT_IMAGE_ICON(., A, curicon, curstate, base_icon_dir) + SET_SELF(.) //Clear defines #undef flatX1 @@ -540,8 +530,8 @@ world #undef INDEX_Y_LOW #undef INDEX_Y_HIGH - #undef GENERATE_FLAT_IMAGE_ICON #undef BLANK + #undef SET_SELF /proc/getIconMask(atom/A)//By yours truly. Creates a dynamic mask for a mob/whatever. /N var/icon/alpha_mask = new(A.icon,A.icon_state)//So we want the default icon and icon state of A. diff --git a/code/__HELPERS/lighting.dm b/code/__HELPERS/lighting.dm new file mode 100644 index 000000000000..08c360849b58 --- /dev/null +++ b/code/__HELPERS/lighting.dm @@ -0,0 +1,11 @@ +/// 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 = GLOB.emissive_color + 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 = GLOB.em_block_color + return appearance diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 5b1a16011a05..9a6ee4362088 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -894,7 +894,7 @@ if(ismob(source)) var/mob/M = source has_nightvision = M.see_in_dark >= 12 - if(!has_nightvision && target_turf.lighting_lumcount == 0) + if(!has_nightvision && target_turf.get_lumcount() == 0) return FALSE while(current != target_turf) @@ -1227,12 +1227,11 @@ var/global/image/action_purple_power_up if(A.vars.Find(lowertext(varname))) return 1 else return 0 -//Returns: all the non-lighting areas in the world, sorted. +//Returns: all the areas in the world, sorted. /proc/return_sorted_areas() var/list/area/AL = list() for(var/area/A in GLOB.sorted_areas) - if(!A.lighting_subarea) - AL += A + AL += A return AL //Takes: Area type as text string or as typepath OR an instance of the area. @@ -1252,13 +1251,8 @@ var/global/image/action_purple_power_up var/area/A = GLOB.areas_by_type[areatype] // Fix it up with /area/var/related due to lighting shenanigans - var/list/area/LA - if(!length(A.related)) - LA = list(A) - else LA = A.related - for(var/area/Ai in LA) - for(var/turf/T in Ai) - turfs += T + for(var/turf/T in A) + turfs += T return turfs @@ -1382,7 +1376,7 @@ var/global/image/action_purple_power_up // if(AR.lighting_use_dynamic) //TODO: rewrite this code so it's not messed by lighting ~Carn // X.opacity = !X.opacity -// X.SetOpacity(!X.opacity) +// X.set_opacity(!X.opacity) toupdate += X @@ -1766,6 +1760,85 @@ var/list/WALLITEMS = list( if(location == src) return TRUE +GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) + +/// Version of view() which ignores darkness, because BYOND doesn't have it (I actually suggested it but it was tagged redundant, BUT HEARERS IS A T- /rant). +/proc/dview(range = world.view, center, invis_flags = 0) + if(!center) + return + + GLOB.dview_mob.loc = center + + GLOB.dview_mob.see_invisible = invis_flags + + . = view(range, GLOB.dview_mob) + GLOB.dview_mob.loc = null + +/mob/dview + name = "INTERNAL DVIEW MOB" + invisibility = 101 + density = FALSE + see_in_dark = 1e6 + var/ready_to_die = FALSE + +/mob/dview/Initialize() //Properly prevents this mob from gaining huds or joining any global lists + SHOULD_CALL_PARENT(FALSE) + if(flags_atom & INITIALIZED) + stack_trace("Warning: [src]([type]) initialized multiple times!") + flags_atom |= INITIALIZED + return INITIALIZE_HINT_NORMAL + +/mob/dview/Destroy(force = FALSE) + if(!ready_to_die) + stack_trace("ALRIGHT WHICH FUCKER TRIED TO DELETE *MY* DVIEW?") + + if (!force) + return QDEL_HINT_LETMELIVE + + log_world("EVACUATE THE SHITCODE IS TRYING TO STEAL MUH JOBS") + GLOB.dview_mob = new + return ..() + + +#define FOR_DVIEW(type, range, center, invis_flags) \ + GLOB.dview_mob.loc = center; \ + GLOB.dview_mob.see_invisible = invis_flags; \ + for(type in view(range, GLOB.dview_mob)) + +#define FOR_DVIEW_END GLOB.dview_mob.loc = null + +/proc/get_turf_pixel(atom/AM) + if(!istype(AM)) + return + + //Find AM's matrix so we can use it's X/Y pixel shifts + var/matrix/M = matrix(AM.transform) + + var/pixel_x_offset = AM.pixel_x + M.get_x_shift() + var/pixel_y_offset = AM.pixel_y + M.get_y_shift() + + //Irregular objects + var/icon/AMicon = icon(AM.icon, AM.icon_state) + var/AMiconheight = AMicon.Height() + var/AMiconwidth = AMicon.Width() + if(AMiconheight != world.icon_size || AMiconwidth != world.icon_size) + pixel_x_offset += ((AMiconwidth/world.icon_size)-1)*(world.icon_size*0.5) + pixel_y_offset += ((AMiconheight/world.icon_size)-1)*(world.icon_size*0.5) + + //DY and DX + var/rough_x = round(round(pixel_x_offset,world.icon_size)/world.icon_size) + var/rough_y = round(round(pixel_y_offset,world.icon_size)/world.icon_size) + + //Find coordinates + var/turf/T = get_turf(AM) //use AM's turfs, as it's coords are the same as AM's AND AM's coords are lost if it is inside another atom + if(!T) + return null + var/final_x = T.x + rough_x + var/final_y = T.y + rough_y + + if(final_x || final_y) + return locate(final_x, final_y, T.z) + //used to check if a mob can examine an object /atom/proc/can_examine(mob/user) if(!user.client) diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/fullscreen.dm index 0bf9dba3130b..fec62c35317f 100644 --- a/code/_onclick/hud/fullscreen.dm +++ b/code/_onclick/hud/fullscreen.dm @@ -196,3 +196,34 @@ /atom/movable/screen/fullscreen/weather/high icon_state = "impairedoverlay3" + +/atom/movable/screen/fullscreen/lighting_backdrop + icon = 'icons/mob/hud/screen1.dmi' + icon_state = "flash" + transform = matrix(200, 0, 0, 0, 200, 0) + plane = LIGHTING_PLANE + blend_mode = BLEND_OVERLAY + show_when_dead = TRUE + +/atom/movable/screen/fullscreen/lighting_backdrop/update_for_view(client_view) + return + +//Provides darkness to the back of the lighting plane +/atom/movable/screen/fullscreen/lighting_backdrop/lit_secondary + invisibility = INVISIBILITY_LIGHTING + layer = BACKGROUND_LAYER + LIGHTING_PRIMARY_DIMMER_LAYER + color = "#000" + alpha = 60 + +/atom/movable/screen/fullscreen/lighting_backdrop/backplane + invisibility = INVISIBILITY_LIGHTING + layer = LIGHTING_BACKPLANE_LAYER + color = "#000" + blend_mode = BLEND_ADD + +/atom/movable/screen/fullscreen/see_through_darkness + icon_state = "nightvision" + plane = LIGHTING_PLANE + layer = LIGHTING_PRIMARY_LAYER + blend_mode = BLEND_ADD + show_when_dead = TRUE diff --git a/code/_onclick/hud/rendering/plane_master.dm b/code/_onclick/hud/rendering/plane_master.dm index d29228f4c16e..91c0e24fae1f 100644 --- a/code/_onclick/hud/rendering/plane_master.dm +++ b/code/_onclick/hud/rendering/plane_master.dm @@ -76,6 +76,17 @@ appearance_flags = PLANE_MASTER | NO_CLIENT_COLOR | PIXEL_SCALE //byond internal end +/*! + * This system works by exploiting BYONDs color matrix filter to use layers to handle emissive blockers. + * + * Emissive overlays are pasted with an atom color that converts them to be entirely some specific color. + * Emissive blockers are pasted with an atom color that converts them to be entirely some different color. + * Emissive overlays and emissive blockers are put onto the same plane. + * The layers for the emissive overlays and emissive blockers cause them to mask eachother similar to normal BYOND objects. + * A color matrix filter is applied to the emissive plane to mask out anything that isn't whatever the emissive color is. + * This is then used to alpha mask the lighting plane. + */ + ///Contains all lighting objects /atom/movable/screen/plane_master/lighting name = "lighting plane master" @@ -83,10 +94,41 @@ blend_mode_override = BLEND_MULTIPLY mouse_opacity = MOUSE_OPACITY_TRANSPARENT +/atom/movable/screen/plane_master/lighting/backdrop(mob/mymob) + . = ..() + mymob.overlay_fullscreen("lighting_backdrop", /atom/movable/screen/fullscreen/lighting_backdrop/backplane) + mymob.overlay_fullscreen("lighting_backdrop_lit_secondary", /atom/movable/screen/fullscreen/lighting_backdrop/lit_secondary) + +/atom/movable/screen/plane_master/lighting/Initialize() + . = ..() + add_filter("emissives", 1, alpha_mask_filter(render_source = EMISSIVE_RENDER_TARGET, flags = MASK_INVERSE)) + add_filter("object_lighting", 2, alpha_mask_filter(render_source = O_LIGHTING_VISUAL_RENDER_TARGET, flags = MASK_INVERSE)) + /atom/movable/screen/plane_master/lighting/exterior name = "exterior lighting plane master" plane = EXTERIOR_LIGHTING_PLANE +/** + * Handles emissive overlays and emissive blockers. + */ +/atom/movable/screen/plane_master/emissive + name = "emissive plane master" + plane = EMISSIVE_PLANE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + render_target = EMISSIVE_RENDER_TARGET + render_relay_plane = null + +/atom/movable/screen/plane_master/emissive/Initialize() + . = ..() + add_filter("em_block_masking", 1, color_matrix_filter(GLOB.em_mask_matrix)) + +/atom/movable/screen/plane_master/above_lighting + name = "above lighting plane master" + plane = ABOVE_LIGHTING_PLANE + appearance_flags = PLANE_MASTER //should use client color + blend_mode = BLEND_OVERLAY + render_relay_plane = RENDER_PLANE_GAME + /atom/movable/screen/plane_master/runechat name = "runechat plane master" plane = RUNECHAT_PLANE @@ -94,6 +136,14 @@ blend_mode = BLEND_OVERLAY render_relay_plane = RENDER_PLANE_NON_GAME +/atom/movable/screen/plane_master/o_light_visual + name = "overlight light visual plane master" + plane = O_LIGHTING_VISUAL_PLANE + render_target = O_LIGHTING_VISUAL_RENDER_TARGET + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + blend_mode = BLEND_MULTIPLY + blend_mode_override = BLEND_MULTIPLY + /atom/movable/screen/plane_master/runechat/backdrop(mob/mymob) . = ..() remove_filter("AO") diff --git a/code/controllers/_DynamicAreaLighting_TG.dm b/code/controllers/_DynamicAreaLighting_TG.dm deleted file mode 100644 index 9b6088b9bc1e..000000000000 --- a/code/controllers/_DynamicAreaLighting_TG.dm +++ /dev/null @@ -1,406 +0,0 @@ -/* - Modified DynamicAreaLighting for TGstation - Coded by Carnwennan - - This is TG's 'new' lighting system. It's basically a heavily modified combination of Forum_Account's and - ShadowDarke's respective lighting libraries. Credits, where due, to them. - - Like sd_DAL (what we used to use), it changes the shading overlays of areas by splitting each type of area into sub-areas - by using the var/tag variable and moving turfs into the contents list of the correct sub-area. This method is - much less costly than using overlays or objects. - - Unlike sd_DAL however it uses a queueing system. Everytime we call a change to opacity or luminosity - (through SetOpacity() or SetLuminosity()) we are simply updating variables and scheduling certain lights/turfs for an - update. Actual updates are handled periodically by the lighting_controller. This carries additional overheads, however it - means that each thing is changed only once per lighting_controller.processing_interval ticks. Allowing for greater control - over how much priority we'd like lighting updates to have. It also makes it possible for us to simply delay updates by - setting lighting_controller.processing = 0 at say, the start of a large explosion, waiting for it to finish, and then - turning it back on with lighting_controller.processing = 1. - - Unlike our old system there are hardcoded maximum luminositys (different for certain atoms). - This is to cap the cost of creating lighting effects. - (without this, an atom with luminosity of 20 would have to update 41^2 turfs!) :s - - Also, in order for the queueing system to work, each light remembers the effect it casts on each turf. This is going to - have larger memory requirements than our previous system but it's easily worth the hassle for the greater control we - gain. It also reduces cost of removing lighting effects by a lot! - - Known Issues/TODO: - Shuttles still do not have support for dynamic lighting (I hope to fix this at some point) - No directional lighting support. (prototype looked ugly) -*/ - -#define LIGHTING_CIRCULAR 1 //comment this out to use old square lighting effects. -#define LIGHTING_LAYER 10 //Drawing layer for lighting overlays -#define LIGHTING_ICON 'icons/effects/ss13_dark_alpha6.dmi' //Icon used for lighting shading effects -#define LIGHTING_STATES 6 - -// Update these lists if the luminosity cap -// of 8 is removed -GLOBAL_LIST_INIT(comp1table, list( - 0, - 0.934, - 1.868, - 2.802, - 3.736, - 4.67, - 5.604, - 6.538, - 7.472, -)) -GLOBAL_LIST_INIT(comp2table, list( - 0, - 0.427, - 0.854, - 1.281, - 1.708, - 2.135, - 2.562, - 2.989, - 3.416, -)) -/datum/light_source - var/atom/owner - var/changed = 1 - var/list/effect = list() - var/__x = 0 //x coordinate at last update - var/__y = 0 //y coordinate at last update - var/__z = 0 //z coordinate at last update - -#define turf_update_lumcount(T, amount)\ - T.lighting_lumcount += amount;\ - if(!T.lighting_changed){\ - SSlighting.changed_turfs += T;\ - T.lighting_changed = TRUE;\ - } - -#define ls_remove_effect(ls)\ - for(var/t in ls.effect){\ - var/turf/T = t;\ - turf_update_lumcount(T, -ls.effect[T]);\ - }\ - ls.effect.Cut(); - -/datum/light_source/New(atom/A) - if(!istype(A)) - CRASH("The first argument to the light object's constructor must be the atom that is the light source. Expected atom, received '[A]' instead.") - ..() - owner = A - __x = owner.x - __y = owner.y - __z = owner.z - // the lighting object maintains a list of all light sources - SSlighting.lights.Add(src) - -//Check a light to see if its effect needs reprocessing. If it does, remove any old effect and create a new one -/datum/light_source/proc/check() - if(!owner) - ls_remove_effect(src) - return TRUE //causes it to be removed from our list of lights. The garbage collector will then destroy it. - - if(owner.luminosity > 8) - owner.luminosity = 8 - - changed = FALSE - - ls_remove_effect(src) - if(owner.loc && owner.luminosity > 0) - for(var/turf/T in view(owner.luminosity, owner)) - var/dist - var/dx = abs(T.x - __x) - var/dy = abs(T.y - __y) - // Use dx+1 and dy+1 because lists use 1-based indexing - if(dx >= dy) - dist = (GLOB.comp1table[dx+1]) + (GLOB.comp2table[dy+1]) - else - dist = (GLOB.comp2table[dx+1]) + (GLOB.comp1table[dy+1]) - var/delta_lumen = owner.luminosity - dist - if(delta_lumen > 0) - effect[T] = delta_lumen - turf_update_lumcount(T, delta_lumen) - return FALSE - else - owner.light = null - return TRUE - -/datum/light_source/proc/changed() - if(owner) - __x = owner.x - __y = owner.y - - if(!changed) - changed = 1 - SSlighting.lights.Add(src) - - -/datum/light_source/proc/remove_effect() - // before we apply the effect we remove the light's current effect. - for(var/turf/T in effect) // negate the effect of this light source - turf_update_lumcount(T, -effect[T]) - effect.Cut() // clear the effect list - -/atom - var/datum/light_source/light - var/trueLuminosity = 0 // Typically 'luminosity' squared. The builtin luminosity must remain linear. - // We may read it, but NEVER set it directly. - -//Movable atoms with opacity when they are constructed will trigger nearby lights to update -//Movable atoms with luminosity when they are constructed will create a light_source automatically -/atom/movable/Initialize(mapload, ...) - . = ..() - if(opacity) - if(isturf(loc)) - var/turf/T = loc - if(T.lighting_lumcount > 1) - UpdateAffectingLights() - if(luminosity) - if(light) WARNING("[type] - Don't set lights up manually during New(), We do it automatically.") - trueLuminosity = luminosity * luminosity - light = new(src) - -//Objects with opacity will trigger nearby lights to update at next lighting process. -/atom/movable/Destroy() - if(opacity) - if(isturf(loc)) - var/turf/T = loc - if(T.lighting_lumcount > 1) - UpdateAffectingLights() - . = ..() - -/atom/vv_edit_var(var_name, var_value) - switch(var_name) - if(NAMEOF(src, luminosity)) - SetLuminosity(var_value) - return ..() - -//Sets our luminosity. -//If we have no light it will create one. -//If we are setting luminosity to 0 the light will be cleaned up by the controller and garbage collected once all its -//queues are complete. -//if we have a light already it is merely updated, rather than making a new one. -/atom/proc/SetLuminosity(new_luminosity, trueLum = FALSE, atom/source) - if(new_luminosity < 0) - new_luminosity = 0 - if(!trueLum) - new_luminosity *= new_luminosity - if(light) - if(trueLuminosity != new_luminosity) //non-luminous lights are removed from the lights list in add_effect() - light.changed() - else - if(new_luminosity) - light = new(src) - trueLuminosity = new_luminosity - if (trueLuminosity < 1) - luminosity = 0 - else if (trueLuminosity <= 100) - luminosity = sqrtTable[trueLuminosity] - else - luminosity = sqrt(trueLuminosity) - -//This slightly modifies human luminosity. Source of light do NOT stack. -//When you drop a light source it should keep a running total of your actual luminosity and set it accordingly. -/mob/SetLuminosity(new_luminosity, trueLum, atom/source) - LAZYREMOVE(luminosity_sources, source) - if(source) - UnregisterSignal(source, COMSIG_PARENT_QDELETING) - var/highest_luminosity = 0 - for(var/luminosity_source as anything in luminosity_sources) - var/lumonisity_rating = luminosity_sources[luminosity_source] - if(highest_luminosity < lumonisity_rating) - highest_luminosity = lumonisity_rating - if(source && new_luminosity > 0) - LAZYSET(luminosity_sources, source, new_luminosity) - RegisterSignal(source, COMSIG_PARENT_QDELETING, PROC_REF(remove_luminosity_source)) - if(new_luminosity < highest_luminosity) - new_luminosity = highest_luminosity - return ..() - -/mob/proc/remove_luminosity_source(atom/source) - SetLuminosity(0, FALSE, source) - -/area/SetLuminosity(new_luminosity) //we don't want dynamic lighting for areas - luminosity = !!new_luminosity - trueLuminosity = luminosity - - -//change our opacity (defaults to toggle), and then update all lights that affect us. -/atom/proc/SetOpacity(new_opacity) - if(new_opacity == null) - new_opacity = !opacity //default = toggle opacity - else if(opacity == new_opacity) - return FALSE //opacity hasn't changed! don't bother doing anything - opacity = new_opacity //update opacity, the below procs now call light updates. - return TRUE - -/turf/SetOpacity(new_opacity) - . = ..() - //only bother if opacity changed - if(!.) - return - if(lighting_lumcount) //only bother with an update if our turf is currently affected by a light - UpdateAffectingLights() - -/atom/movable/SetOpacity(new_opacity) - . = ..() - // only bother if opacity changed - if(!.) - return - // only bother with an update if we're on a turf - if(isturf(loc)) - var/turf/T = loc - // only bother with an update if our turf is currently affected by a light - if(T.lighting_lumcount) - UpdateAffectingLights() - - -/turf - var/lighting_lumcount = 0 - var/lighting_changed = 0 - var/cached_lumcount = 0 - -/turf/open/space - lighting_lumcount = 4 //starlight - -/turf/proc/update_lumcount(amount, removing = 0) - lighting_lumcount += amount - - if(!lighting_changed) - SSlighting.changed_turfs += src - lighting_changed = 1 - -/turf/proc/lighting_tag(const/level) - var/area/A = loc - return A.tagbase + "sd_L[level]" - -/turf/proc/build_lighting_area(const/tag, const/level) - var/area/Area = loc - var/area/A = new Area.type() // create area if it wasn't found - // replicate vars - for(var/V in Area.vars) - switch(V) - if ("contents","lighting_overlay", "overlays") - continue - else - if(issaved(Area.vars[V])) A.vars[V] = Area.vars[V] - - A.tag = tag - A.lighting_subarea = 1 - A.lighting_space = 0 // in case it was copied from a space subarea - - A.SetLightLevel(level) - Area.related += A - - if(SSweather.is_weather_event && SSweather.map_holder.should_affect_area(A)) - A.overlays += SSweather.curr_master_turf_overlay - - return A - -/turf/proc/shift_to_subarea() - lighting_changed = 0 - var/area/Area = loc - - if(!istype(Area) || !Area.lighting_use_dynamic) return - - var/level = min(max(round(lighting_lumcount,1),0),LIGHTING_STATES) - var/new_tag = lighting_tag(level) - - if(Area.tag!=new_tag) //skip if already in this area - var/area/A = locate(new_tag) // find an appropriate area - - if (!A) - A = build_lighting_area(new_tag, level) - - A.contents += src // move the turf into the area - -// Dedicated lighting sublevel for space turfs -// helps us depower things in space, remove space fire alarms, -// and evens out space lighting -/turf/open/space/lighting_tag(level) - var/area/A = loc - return A.tagbase + "sd_L_space" -/turf/open/space/build_lighting_area(tag, level) - var/area/A = ..(tag,4) - A.lighting_space = 1 - A.SetLightLevel(4) - A.icon_state = null - return A - - -/area - var/lighting_use_dynamic = 1 //Turn this flag off to prevent sd_DynamicAreaLighting from affecting this area - var/image/lighting_overlay //tracks the darkness image of the area for easy removal - var/lighting_subarea = 0 //tracks whether we're a lighting sub-area - var/lighting_space = 0 // true for space-only lighting subareas - var/tagbase - var/exterior_light = 2 - -/area/proc/SetLightLevel(light) - if(!src) return - if(light <= 0) - light = 0 - luminosity = 1 - if(light > LIGHTING_STATES) - light = LIGHTING_STATES - - if(lighting_overlay) - overlays -= lighting_overlay - lighting_overlay.icon_state = "[light]" - else - lighting_overlay = image(LIGHTING_ICON,,num2text(light),LIGHTING_LAYER) - lighting_overlay.plane = ceiling <= CEILING_GLASS ? EXTERIOR_LIGHTING_PLANE : LIGHTING_PLANE - if (light < 6) - overlays.Add(lighting_overlay) - -/area/proc/SetDynamicLighting() - src.lighting_use_dynamic = 1 - for(var/turf/T in src.contents) - turf_update_lumcount(T, 0) - -/area/proc/InitializeLighting() //TODO: could probably improve this bit ~Carn - tagbase = "[type]" - if(!tag) tag = tagbase - if(!lighting_use_dynamic) - if(!lighting_subarea) // see if this is a lighting subarea already - //show the dark overlay so areas, not yet in a lighting subarea, won't be bright as day and look silly. - SetLightLevel(4) - -//#undef LIGHTING_LAYER -#undef LIGHTING_CIRCULAR -//#undef LIGHTING_ICON - -#define LIGHTING_MAX_LUMINOSITY_STATIC 8 //Maximum luminosity to reduce lag. -#define LIGHTING_MAX_LUMINOSITY_MOBILE 6 //Moving objects have a lower max luminosity since these update more often. (lag reduction) -#define LIGHTING_MAX_LUMINOSITY_TURF 1 //turfs have a severely shortened range to protect from inevitable floor-lighttile spam. - -//set the changed status of all lights which could have possibly lit this atom. -//We don't need to worry about lights which lit us but moved away, since they will have change status set already -//This proc can cause lots of lights to be updated. :( -/atom/proc/UpdateAffectingLights() -// for(var/atom/A in oview(LIGHTING_MAX_LUMINOSITY_STATIC-1,src)) -// if(A.light) -// A.light.changed() //force it to update at next process() - -/atom/movable/UpdateAffectingLights() - if(isturf(loc)) - loc.UpdateAffectingLights() - -/turf/UpdateAffectingLights() - for(var/atom/A in oview(LIGHTING_MAX_LUMINOSITY_STATIC-1,src)) - if(A.light) - A.light.changed() - -//caps luminosity effects max-range based on what type the light's owner is. -/atom/proc/get_light_range() - return min(luminosity, LIGHTING_MAX_LUMINOSITY_STATIC) - -/atom/movable/get_light_range() - return min(luminosity, LIGHTING_MAX_LUMINOSITY_MOBILE) - -/obj/structure/machinery/light/get_light_range() - return min(luminosity, LIGHTING_MAX_LUMINOSITY_STATIC) - -/turf/get_light_range() - return min(luminosity, LIGHTING_MAX_LUMINOSITY_TURF) - -#undef LIGHTING_MAX_LUMINOSITY_STATIC -#undef LIGHTING_MAX_LUMINOSITY_MOBILE -#undef LIGHTING_MAX_LUMINOSITY_TURF diff --git a/code/controllers/subsystem/lighting.dm b/code/controllers/subsystem/lighting.dm index 26ee4c9a648e..3c3d14468bc2 100644 --- a/code/controllers/subsystem/lighting.dm +++ b/code/controllers/subsystem/lighting.dm @@ -1,68 +1,124 @@ SUBSYSTEM_DEF(lighting) - name = "Lighting" + name = "Lighting" + wait = 2 init_order = SS_INIT_LIGHTING - priority = SS_PRIORITY_LIGHTING - wait = 0.4 SECONDS - runlevels = RUNLEVELS_DEFAULT|RUNLEVEL_LOBBY - var/list/datum/light_source/lights_current = list() - var/list/datum/light_source/lights = list() + //debug var for tracking updates before init is complete + var/duplicate_shadow_updates_in_init = 0 + ///Total times shadows were updated, debug + var/total_shadow_calculations = 0 - var/list/turf/changed_turfs_current = list() - var/list/turf/changed_turfs = list() + ///Whether the SS has begun setting up yet + var/started = FALSE + var/static/list/static_sources_queue = list() //! List of static lighting sources queued for update. + var/static/list/corners_queue = list() //! List of lighting corners queued for update. + var/static/list/objects_queue = list() //! List of lighting objects queued for update. -/datum/controller/subsystem/lighting/stat_entry(msg) - msg = "L:[lights.len]; T:[changed_turfs.len]" - return ..() + var/static/list/mask_queue = list() //! List of hybrid lighting sources queued for update. /datum/controller/subsystem/lighting/Initialize(timeofday) - for(var/thing in lights) - var/datum/light_source/L = thing - if(L) - L.check() - lights.Cut() + started = TRUE + if(!initialized) + //Handle static lightnig + create_all_lighting_objects() + fire(FALSE, TRUE) + return SS_INIT_SUCCESS - var/z_start = 1 - var/z_finish = world.maxz +/datum/controller/subsystem/lighting/stat_entry() + . = ..("ShCalcs:[total_shadow_calculations]|SourcQ:[static_sources_queue.len]|CcornQ:[corners_queue.len]|ObjQ:[objects_queue.len]|HybrQ:[mask_queue.len]") - var/list/init_turfs = block(locate(1,1,z_start),locate(world.maxx,world.maxy,z_finish)) +/datum/controller/subsystem/lighting/fire(resumed, init_tick_checks) + MC_SPLIT_TICK_INIT(3) + if(!init_tick_checks) + MC_SPLIT_TICK + var/updators_num = 0 + while(updators_num < length(static_sources_queue)) + updators_num += 1 - for(var/turf/thing in init_turfs) - if(istype(thing)) - thing.shift_to_subarea() + var/datum/static_light_source/L = static_sources_queue[updators_num] + L.update_corners() - return SS_INIT_SUCCESS + if(!QDELETED(L)) + L.needs_update = LIGHTING_NO_UPDATE + else + updators_num -= 1 + if(init_tick_checks) + if(!TICK_CHECK) + continue + static_sources_queue.Cut(1, updators_num + 1) + updators_num = 0 + stoplag() + else if (MC_TICK_CHECK) + break + if(updators_num) + static_sources_queue.Cut(1, updators_num + 1) + updators_num = 0 + if(!init_tick_checks) + MC_SPLIT_TICK -/datum/controller/subsystem/lighting/fire(resumed = FALSE) - if(!resumed) - lights_current = lights - lights = list() - changed_turfs_current = changed_turfs - changed_turfs = list() + while(updators_num < length(corners_queue)) + updators_num += 1 + var/datum/static_lighting_corner/C = corners_queue[updators_num] + C.needs_update = FALSE //update_objects() can call qdel if the corner is storing no data + C.update_objects() - while(lights_current.len) - var/datum/light_source/L = lights_current[lights_current.len] - lights_current.len-- - if(!L) - continue - if(!L.owner || L.changed) - L.check() - if(MC_TICK_CHECK) - return - - while(changed_turfs_current.len) - var/turf/T = changed_turfs_current[changed_turfs_current.len] - changed_turfs_current.len-- - if(!T) + if(init_tick_checks) + if(!TICK_CHECK) + continue + corners_queue.Cut(1, updators_num + 1) + updators_num = 0 + stoplag() + else if (MC_TICK_CHECK) + break + if(updators_num) + corners_queue.Cut(1, updators_num + 1) + updators_num = 0 + if(!init_tick_checks) + MC_SPLIT_TICK + + while(updators_num < length(objects_queue)) + updators_num += 1 + + var/datum/static_lighting_object/O = objects_queue[updators_num] + if (QDELETED(O)) continue - if(T.lighting_changed) - if(T.lighting_lumcount != T.cached_lumcount) - T.cached_lumcount = T.lighting_lumcount - T.shift_to_subarea() - T.lighting_changed = FALSE - if (MC_TICK_CHECK) - return + O.update() + O.needs_update = FALSE + + if(init_tick_checks) + if(!TICK_CHECK) + continue + objects_queue.Cut(1, updators_num + 1) + updators_num = 0 + else if (MC_TICK_CHECK) + break + if(updators_num) + objects_queue.Cut(1, updators_num + 1) + updators_num = 0 + if(!init_tick_checks) + MC_SPLIT_TICK + + while(updators_num > length(mask_queue)) + updators_num += 1 + + var/atom/movable/lighting_mask/mask_to_update = mask_queue[updators_num] + mask_to_update.calculate_lighting_shadows() + + if(init_tick_checks) + if(!TICK_CHECK) + continue + mask_queue.Cut(1, updators_num + 1) + updators_num = 0 + stoplag() + else if (MC_TICK_CHECK) + break + if(updators_num) + mask_queue.Cut(1, updators_num + 1) + +/datum/controller/subsystem/lighting/Recover() + initialized = SSlighting.initialized + return ..() diff --git a/code/controllers/subsystem/weather.dm b/code/controllers/subsystem/weather.dm index 7610c007df90..325c45fe2300 100644 --- a/code/controllers/subsystem/weather.dm +++ b/code/controllers/subsystem/weather.dm @@ -47,7 +47,7 @@ SUBSYSTEM_DEF(weather) /datum/controller/subsystem/weather/proc/setup_weather_areas() weather_areas = list() for(var/area/A in all_areas) - if(A == A.master && A.weather_enabled && map_holder.should_affect_area(A)) + if(A.weather_enabled && map_holder.should_affect_area(A)) weather_areas += A curr_master_turf_overlay = new /obj/effect/weather_vfx_holder @@ -146,8 +146,7 @@ SUBSYSTEM_DEF(weather) curr_master_turf_overlay.icon_state = weather_event_instance.turf_overlay_icon_state curr_master_turf_overlay.alpha = weather_event_instance.turf_overlay_alpha for(var/area/area as anything in weather_areas) - for(var/area/subarea as anything in area.related) - subarea.overlays += curr_master_turf_overlay + area.overlays += curr_master_turf_overlay update_mobs_weather() SEND_GLOBAL_SIGNAL(COMSIG_GLOB_WEATHER_CHANGE) @@ -169,8 +168,7 @@ SUBSYSTEM_DEF(weather) message_admins(SPAN_BLUE("Weather Event of unknown type [weather_event_type] ending after [DisplayTimeText(world.time - current_event_start_time)].")) for(var/area/area as anything in weather_areas) - for(var/area/subarea as anything in area.related) - subarea.overlays -= curr_master_turf_overlay + area.overlays -= curr_master_turf_overlay if (map_holder.no_weather_turf_icon_state) curr_master_turf_overlay.icon_state = map_holder.no_weather_turf_icon_state diff --git a/code/datums/components/overlay_lighting.dm b/code/datums/components/overlay_lighting.dm new file mode 100644 index 000000000000..00a5e86b5d60 --- /dev/null +++ b/code/datums/components/overlay_lighting.dm @@ -0,0 +1,513 @@ +///For switchable lights, is it on and currently emitting light? +#define LIGHTING_ON (1<<0) +///Is the parent attached to something else, its loc? Then we need to keep an eye of this. +#define LIGHTING_ATTACHED (1<<1) + +#define GET_PARENT (parent_attached_to || parent) + +#define SHORT_CAST 2 + + +/** + * Movable atom overlay-based lighting component. + * + * * Component works by applying a visual object to the parent target. + * + * * The component tracks the parent's loc to determine the current_holder. + * * The current_holder is either the parent or its loc, whichever is on a turf. If none, then the current_holder is null and the light is not visible. + * + * * Lighting works at its base by applying a dark overlay and "cutting" said darkness with light, adding (possibly colored) transparency. + * * This component uses the visible_mask visual object to apply said light mask on the darkness. + * + * * The main limitation of this system is that it uses a limited number of pre-baked geometrical shapes, but for most uses it does the job. + * + * * Another limitation is for big lights: you only see the light if you see the object emiting it. + * * For small objects this is good (you can't see them behind a wall), but for big ones this quickly becomes prety clumsy. +*/ +/datum/component/overlay_lighting + ///How far the light reaches, float. + var/range = 1 + ///Ceiling of range, integer without decimal entries. + var/lumcount_range = 0 + ///How much this light affects the dynamic_lumcount of turfs. + var/lum_power = 0.5 + ///Transparency value. + var/set_alpha = 0 + ///For light sources that can be turned on and off. + var/overlay_lighting_flags = NONE + + ///Cache of the possible light overlays, according to size. + var/static/list/light_overlays = list( + "32" = 'icons/effects/light_overlays/light_32.dmi', + "64" = 'icons/effects/light_overlays/light_64.dmi', + "96" = 'icons/effects/light_overlays/light_96.dmi', + "128" = 'icons/effects/light_overlays/light_128.dmi', + "160" = 'icons/effects/light_overlays/light_160.dmi', + "192" = 'icons/effects/light_overlays/light_192.dmi', + "224" = 'icons/effects/light_overlays/light_224.dmi', + "256" = 'icons/effects/light_overlays/light_256.dmi', + "288" = 'icons/effects/light_overlays/light_288.dmi', + "320" = 'icons/effects/light_overlays/light_320.dmi', + "352" = 'icons/effects/light_overlays/light_352.dmi', + "384" = 'icons/effects/light_overlays/light_384.dmi', + "416" = 'icons/effects/light_overlays/light_416.dmi', + ) + + ///Overlay effect to cut into the darkness and provide light. + var/image/visible_mask + ///Lazy list to track the turfs being affected by our light, to determine their visibility. + var/list/turf/affected_turfs + ///Movable atom currently holding the light. Parent might be a flashlight, for example, but that might be held by a mob or something else. + var/atom/movable/current_holder + ///Movable atom the parent is attached to. For example, a flashlight into a helmet or gun. We'll need to track the thing the parent is attached to as if it were the parent itself. + var/atom/movable/parent_attached_to + ///Whether we're a directional light + var/directional = FALSE + ///A cone overlay for directional light, it's alpha and color are dependant on the light + var/image/cone + ///Current tracked direction for the directional cast behaviour + var/current_direction + ///Tracks current directional x offset so we dont update unecessarily + var/directional_offset_x + ///Tracks current directional y offset so we dont update unecessarily + var/directional_offset_y + ///Cast range for the directional cast (how far away the atom is moved) + var/cast_range = 2 + + +/datum/component/overlay_lighting/Initialize(_range, _power, _color, starts_on, is_directional) + if(!ismovable(parent)) + return COMPONENT_INCOMPATIBLE + + var/atom/movable/movable_parent = parent + if(movable_parent.light_system != MOVABLE_LIGHT && movable_parent.light_system != DIRECTIONAL_LIGHT) + stack_trace("[type] added to [parent], with [movable_parent.light_system] value for the light_system var. Use [MOVABLE_LIGHT]/[DIRECTIONAL_LIGHT] instead.") + return COMPONENT_INCOMPATIBLE + + . = ..() + + visible_mask = image('icons/effects/light_overlays/light_32.dmi', icon_state = "light") + visible_mask.plane = O_LIGHTING_VISUAL_PLANE + visible_mask.appearance_flags = RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM + visible_mask.alpha = 0 + if(is_directional) + directional = TRUE + cone = image('icons/effects/light_overlays/light_cone.dmi', icon_state = "light") + cone.plane = O_LIGHTING_VISUAL_PLANE + cone.appearance_flags = RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM + cone.alpha = 110 + cone.transform = cone.transform.Translate(-32, -32) + set_direction(movable_parent.dir) + if(!isnull(_range)) + movable_parent.set_light_range(_range) + set_range(parent, movable_parent.light_range) + if(!isnull(_power)) + movable_parent.set_light_power(_power) + set_power(parent, movable_parent.light_power) + if(!isnull(_color)) + movable_parent.set_light_color(_color) + set_color(parent, movable_parent.light_color) + if(!isnull(starts_on)) + movable_parent.set_light_on(starts_on) + +/datum/component/overlay_lighting/RegisterWithParent() + . = ..() + if(directional) + RegisterSignal(parent, COMSIG_ATOM_DIR_CHANGE, PROC_REF(on_parent_dir_change)) + RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_parent_moved)) + RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(check_holder)) + RegisterSignal(parent, COMSIG_ATOM_SET_LIGHT_RANGE, PROC_REF(set_range)) + RegisterSignal(parent, COMSIG_ATOM_SET_LIGHT_POWER, PROC_REF(set_power)) + RegisterSignal(parent, COMSIG_ATOM_SET_LIGHT_COLOR, PROC_REF(set_color)) + RegisterSignal(parent, COMSIG_ATOM_SET_LIGHT_ON, PROC_REF(on_toggle)) + RegisterSignal(parent, COMSIG_ATOM_SET_LIGHT_FLAGS, PROC_REF(on_light_flags_change)) + var/atom/movable/movable_parent = parent + if(movable_parent.light_flags & LIGHT_ATTACHED) + overlay_lighting_flags |= LIGHTING_ATTACHED + set_parent_attached_to(ismovable(movable_parent.loc) ? movable_parent.loc : null) + check_holder() + if(movable_parent.light_on) + turn_on() + + + +/datum/component/overlay_lighting/UnregisterFromParent() + overlay_lighting_flags &= ~LIGHTING_ATTACHED + set_parent_attached_to(null) + set_holder(null) + clean_old_turfs() + UnregisterSignal(parent, list( + COMSIG_MOVABLE_MOVED, + COMSIG_ATOM_SET_LIGHT_RANGE, + COMSIG_ATOM_SET_LIGHT_POWER, + COMSIG_ATOM_SET_LIGHT_COLOR, + COMSIG_ATOM_SET_LIGHT_ON, + COMSIG_ATOM_SET_LIGHT_FLAGS, + )) + if(directional) + UnregisterSignal(parent, COMSIG_ATOM_DIR_CHANGE) + if(overlay_lighting_flags & LIGHTING_ON) + turn_off() + return ..() + + +/datum/component/overlay_lighting/Destroy() + set_parent_attached_to(null) + set_holder(null) + clean_old_turfs() + visible_mask = null + cone = null + parent_attached_to = null + return ..() + + +///Clears the affected_turfs lazylist, removing from its contents the effects of being near the light. +/datum/component/overlay_lighting/proc/clean_old_turfs() + for(var/turf/lit_turf as anything in affected_turfs) + lit_turf.dynamic_lumcount -= lum_power + affected_turfs = null + + +///Populates the affected_turfs lazylist, adding to its contents the effects of being near the light. +/datum/component/overlay_lighting/proc/get_new_turfs() + if(!current_holder) + return + LAZYINITLIST(affected_turfs) + if(range <= 2) + //Range here is 1 because actual range of lighting mask is 1 tile even if it says that range is 2 + for(var/turf/lit_turf in RANGE_TURFS(1, current_holder.loc)) + lit_turf.dynamic_lumcount += lum_power + affected_turfs += lit_turf + else + for(var/turf/lit_turf in view(lumcount_range, get_turf(current_holder))) + lit_turf.dynamic_lumcount += lum_power + affected_turfs += lit_turf + + +///Clears the old affected turfs and populates the new ones. +/datum/component/overlay_lighting/proc/make_luminosity_update() + clean_old_turfs() + if(!isturf(current_holder?.loc)) + return + if(directional) + cast_directional_light() + get_new_turfs() + + +///Adds the luminosity and source for the afected movable atoms to keep track of their visibility. +/datum/component/overlay_lighting/proc/add_dynamic_lumi() + LAZYSET(current_holder.affected_movable_lights, src, lumcount_range + 1) + current_holder.underlays += visible_mask + current_holder.update_dynamic_luminosity() + if(directional) + current_holder.underlays += cone + +///Removes the luminosity and source for the afected movable atoms to keep track of their visibility. +/datum/component/overlay_lighting/proc/remove_dynamic_lumi() + LAZYREMOVE(current_holder.affected_movable_lights, src) + current_holder.underlays -= visible_mask + current_holder.update_dynamic_luminosity() + if(directional) + current_holder.underlays -= cone + +///Called to change the value of parent_attached_to. +/datum/component/overlay_lighting/proc/set_parent_attached_to(atom/movable/new_parent_attached_to) + if(new_parent_attached_to == parent_attached_to) + return + + . = parent_attached_to + parent_attached_to = new_parent_attached_to + if(.) + var/atom/movable/old_parent_attached_to = . + UnregisterSignal(old_parent_attached_to, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_MOVED)) + if(old_parent_attached_to == current_holder) + RegisterSignal(old_parent_attached_to, COMSIG_PARENT_QDELETING, PROC_REF(on_holder_qdel)) + RegisterSignal(old_parent_attached_to, COMSIG_MOVABLE_MOVED, PROC_REF(on_holder_moved)) + if(parent_attached_to) + if(parent_attached_to == current_holder) + UnregisterSignal(current_holder, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_MOVED)) + RegisterSignal(parent_attached_to, COMSIG_PARENT_QDELETING, PROC_REF(on_parent_attached_to_qdel)) + RegisterSignal(parent_attached_to, COMSIG_MOVABLE_MOVED, PROC_REF(on_parent_attached_to_moved)) + check_holder() + + +///Called to change the value of current_holder. +/datum/component/overlay_lighting/proc/set_holder(atom/movable/new_holder) + if(new_holder == current_holder) + return + if(current_holder) + if(current_holder != parent && current_holder != parent_attached_to) + UnregisterSignal(current_holder, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_MOVED)) + if(directional) + UnregisterSignal(current_holder, COMSIG_ATOM_DIR_CHANGE) + if(overlay_lighting_flags & LIGHTING_ON) + remove_dynamic_lumi() + current_holder = new_holder + if(new_holder == null) + clean_old_turfs() + return + if(new_holder != parent && new_holder != parent_attached_to) + RegisterSignal(new_holder, COMSIG_PARENT_QDELETING, PROC_REF(on_holder_qdel)) + if(overlay_lighting_flags & LIGHTING_ON) + RegisterSignal(new_holder, COMSIG_MOVABLE_MOVED, PROC_REF(on_holder_moved)) + if(directional) + RegisterSignal(new_holder, COMSIG_ATOM_DIR_CHANGE, PROC_REF(on_holder_dir_change)) + if(directional && current_direction != new_holder.dir) + current_direction = new_holder.dir + if(overlay_lighting_flags & LIGHTING_ON) + add_dynamic_lumi() + make_luminosity_update() + + +///Used to determine the new valid current_holder from the parent's loc. +/datum/component/overlay_lighting/proc/check_holder() + var/atom/movable/movable_parent = GET_PARENT + if(isturf(movable_parent.loc)) + set_holder(movable_parent) + return + var/atom/inside = movable_parent.loc //Parent's loc + if(isnull(inside)) + set_holder(null) + return + if(isturf(inside.loc)) + set_holder(inside) + return + set_holder(null) + + +///Called when the current_holder is qdeleted, to remove the light effect. +/datum/component/overlay_lighting/proc/on_holder_qdel(atom/movable/source, force) + UnregisterSignal(current_holder, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_MOVED)) + if(directional) + UnregisterSignal(current_holder, COMSIG_ATOM_DIR_CHANGE) + set_holder(null) + + +///Called when current_holder changes loc. +/datum/component/overlay_lighting/proc/on_holder_moved(atom/movable/source, OldLoc, Dir, Forced) + if(!(overlay_lighting_flags & LIGHTING_ON)) + return + make_luminosity_update() + +///Called when the current_holder is qdeleted, to remove the light effect. +/datum/component/overlay_lighting/proc/on_parent_attached_to_qdel(atom/movable/source, force) + SIGNAL_HANDLER + UnregisterSignal(parent_attached_to, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_MOVED)) + if(directional) + UnregisterSignal(parent_attached_to, COMSIG_ATOM_DIR_CHANGE) + if(parent_attached_to == current_holder) + set_holder(null) + set_parent_attached_to(null) + +///Called when parent_attached_to changes loc. +/datum/component/overlay_lighting/proc/on_parent_attached_to_moved(atom/movable/source, OldLoc, Dir, Forced) + SIGNAL_HANDLER + check_holder() + if(!(overlay_lighting_flags & LIGHTING_ON) || !current_holder) + return + make_luminosity_update() + +///Called when parent changes loc. +/datum/component/overlay_lighting/proc/on_parent_moved(atom/movable/source, OldLoc, Dir, Forced) + SIGNAL_HANDLER + var/atom/movable/movable_parent = parent + if(overlay_lighting_flags & LIGHTING_ATTACHED) + set_parent_attached_to(ismovable(movable_parent.loc) ? movable_parent.loc : null) + check_holder() + if(!(overlay_lighting_flags & LIGHTING_ON) || !current_holder) + return + make_luminosity_update() + + +///Changes the range which the light reaches. 0 means no light, 7 is the maximum value. +/datum/component/overlay_lighting/proc/set_range(atom/source, new_range) + SIGNAL_HANDLER + if(range == new_range) + return + if(range == 0) + turn_off() + range = clamp(CEILING(new_range, 0.5), 1, 7) + var/pixel_bounds = ((range - 1) * 64) + 32 + lumcount_range = CEILING(range, 1) + if(current_holder && overlay_lighting_flags & LIGHTING_ON) + current_holder.underlays -= visible_mask + visible_mask.icon = light_overlays["[pixel_bounds]"] + if(pixel_bounds == 32) + visible_mask.transform = null + return + var/offset = (pixel_bounds - 32) * 0.5 + var/matrix/transform = new + transform.Translate(-offset, -offset) + visible_mask.transform = transform + directional_offset_x = 0 + directional_offset_y = 0 + if(current_holder && overlay_lighting_flags & LIGHTING_ON) + current_holder.underlays += visible_mask + if(directional) + cast_range = clamp(round(new_range * 0.5), 1, 3) + if(overlay_lighting_flags & LIGHTING_ON) + make_luminosity_update() + + +///Changes the intensity/brightness of the light by altering the visual object's alpha. +/datum/component/overlay_lighting/proc/set_power(atom/source, new_power) + SIGNAL_HANDLER + set_lum_power(new_power >= 0 ? 0.5 : -0.5) + set_alpha = min(230, (abs(new_power) * 120) + 30) + if(current_holder && overlay_lighting_flags & LIGHTING_ON) + current_holder.underlays -= visible_mask + visible_mask.alpha = set_alpha + if(current_holder && overlay_lighting_flags & LIGHTING_ON) + current_holder.underlays += visible_mask + + if(!directional) + return + + if(current_holder && overlay_lighting_flags & LIGHTING_ON) + current_holder.underlays -= cone + cone.alpha = min(200, (abs(new_power) * 90)+20) + if(current_holder && overlay_lighting_flags & LIGHTING_ON) + current_holder.underlays += cone + + +///Changes the light's color, pretty straightforward. +/datum/component/overlay_lighting/proc/set_color(atom/source, new_color) + SIGNAL_HANDLER + if(current_holder && overlay_lighting_flags & LIGHTING_ON) + current_holder.underlays -= visible_mask + visible_mask.color = new_color + if(current_holder && overlay_lighting_flags & LIGHTING_ON) + current_holder.underlays += visible_mask + + if(!directional) + return + + if(current_holder && overlay_lighting_flags & LIGHTING_ON) + current_holder.underlays -= cone + cone.color = new_color + if(current_holder && overlay_lighting_flags & LIGHTING_ON) + current_holder.underlays += cone + + +///Toggles the light on and off. +/datum/component/overlay_lighting/proc/on_toggle(atom/source, new_value) + SIGNAL_HANDLER + if(new_value) //Truthy value input, turn on. + turn_on() + return + turn_off() //Falsey value, turn off. + +///Triggered right after the parent light flags change. +/datum/component/overlay_lighting/proc/on_light_flags_change(atom/source, new_flags) + SIGNAL_HANDLER + var/atom/movable/movable_parent = parent + + if(new_flags & LIGHT_ATTACHED) // Gained the [LIGHT_ATTACHED] property + overlay_lighting_flags |= LIGHTING_ATTACHED + if(ismovable(movable_parent.loc)) + set_parent_attached_to(movable_parent.loc) + else // Lost the [LIGHT_ATTACHED] property + overlay_lighting_flags &= ~LIGHTING_ATTACHED + set_parent_attached_to(null) + +///Toggles the light on. +/datum/component/overlay_lighting/proc/turn_on() + if(overlay_lighting_flags & LIGHTING_ON) + return + overlay_lighting_flags |= LIGHTING_ON + if(current_holder) + add_dynamic_lumi(current_holder) + if(directional) + cast_directional_light() + if(current_holder && current_holder != parent && current_holder != parent_attached_to) + RegisterSignal(current_holder, COMSIG_MOVABLE_MOVED, PROC_REF(on_holder_moved)) + get_new_turfs() + + +///Toggles the light off. +/datum/component/overlay_lighting/proc/turn_off() + if(!(overlay_lighting_flags & LIGHTING_ON)) + return + if(current_holder) + remove_dynamic_lumi() + overlay_lighting_flags &= ~LIGHTING_ON + if(current_holder && current_holder != parent && current_holder != parent_attached_to) + UnregisterSignal(current_holder, COMSIG_MOVABLE_MOVED) + clean_old_turfs() + + +///Here we append the behavior associated to changing lum_power. +/datum/component/overlay_lighting/proc/set_lum_power(new_lum_power) + if(lum_power == new_lum_power) + return + . = lum_power + lum_power = new_lum_power + var/difference = . - lum_power + for(var/t in affected_turfs) + var/turf/lit_turf = t + lit_turf.dynamic_lumcount -= difference + +///Here we append the behavior associated to changing lum_power. +/datum/component/overlay_lighting/proc/cast_directional_light() + var/final_distance = cast_range + //Lower the distance by 1 if we're not looking at a cardinal direction, and we're not a short cast + if(final_distance > SHORT_CAST && !(ALL_CARDINALS & current_direction)) + final_distance -= 1 + var/turf/scanning = get_turf(current_holder) + for(var/i in 1 to final_distance) + var/turf/next_turf = get_step(scanning, current_direction) + if(isnull(next_turf) || IS_OPAQUE_TURF(next_turf)) + final_distance = i + break + scanning = next_turf + + current_holder.underlays -= visible_mask + + var/translate_x = -((range - 1) * 32) + var/translate_y = translate_x + switch(current_direction) + if(NORTH) + translate_y += 32 * final_distance + if(SOUTH) + translate_y += -32 * final_distance + if(EAST) + translate_x += 32 * final_distance + if(WEST) + translate_x += -32 * final_distance + if((directional_offset_x != translate_x) || (directional_offset_y != translate_y)) + directional_offset_x = translate_x + directional_offset_y = translate_y + var/matrix/transform = matrix() + transform.Translate(translate_x, translate_y) + visible_mask.transform = transform + if(overlay_lighting_flags & LIGHTING_ON) + current_holder.underlays += visible_mask + +///Called when current_holder changes loc. +/datum/component/overlay_lighting/proc/on_holder_dir_change(atom/movable/source, olddir, newdir) + SIGNAL_HANDLER + set_direction(newdir) + +///Called when parent changes loc. +/datum/component/overlay_lighting/proc/on_parent_dir_change(atom/movable/source, olddir, newdir) + SIGNAL_HANDLER + + if(current_holder?.dir != newdir) + return + + set_direction(newdir) + +///Sets a new direction for the directional cast, then updates luminosity +/datum/component/overlay_lighting/proc/set_direction(newdir) + if(!newdir) + return + if(current_direction == newdir) + return + current_direction = newdir + if(overlay_lighting_flags & LIGHTING_ON) + make_luminosity_update() + +#undef LIGHTING_ON +#undef LIGHTING_ATTACHED +#undef GET_PARENT +#undef SHORT_CAST diff --git a/code/datums/effects/acid.dm b/code/datums/effects/acid.dm index 721fe27a0d0d..d89f7261a223 100644 --- a/code/datums/effects/acid.dm +++ b/code/datums/effects/acid.dm @@ -104,7 +104,7 @@ if(!acids_area) return - if(SSweather.is_weather_event && locate(acids_area.master) in SSweather.weather_areas) + if(SSweather.is_weather_event && locate(acids_area) in SSweather.weather_areas) //smothering_strength is 1-10, we use this to take a proportional amount off the stats duration = duration - (duration * (SSweather.weather_event_instance.fire_smothering_strength * 0.1)) damage_in_total_human = damage_in_total_human - (damage_in_total_human * (SSweather.weather_event_instance.fire_smothering_strength * 0.1)) diff --git a/code/datums/elements/light_blocking.dm b/code/datums/elements/light_blocking.dm new file mode 100644 index 000000000000..9fef194f1cd4 --- /dev/null +++ b/code/datums/elements/light_blocking.dm @@ -0,0 +1,43 @@ +/** + * Attached to movable atoms with opacity. Listens to them move and updates their old and new turf loc's opacity accordingly. + */ +/datum/element/light_blocking + element_flags = ELEMENT_DETACH + + +/datum/element/light_blocking/Attach(datum/target) + . = ..() + if(!ismovable(target)) + return ELEMENT_INCOMPATIBLE + RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(on_target_move)) + var/atom/movable/movable_target = target + if(!isturf(movable_target.loc)) + return + for(var/turf/turf_loc as anything in movable_target.locs) + turf_loc.add_opacity_source(target) + + +/datum/element/light_blocking/Detach(datum/target) + . = ..() + UnregisterSignal(target, list(COMSIG_MOVABLE_MOVED)) + var/atom/movable/movable_target = target + if(!isturf(movable_target.loc)) + return + for(var/turf/turf_loc as anything in movable_target.locs) + turf_loc.remove_opacity_source(target) + + +///Updates old and new turf loc opacities. +///Updates old and new turf loc opacities. +/datum/element/light_blocking/proc/on_target_move(atom/movable/source, atom/old_loc, dir, forced, list/old_locs) + SIGNAL_HANDLER + if(isturf(old_loc)) + if(old_locs) + for(var/turf/old_turf as anything in old_locs) + old_turf.remove_opacity_source(source) + else + var/turf/old_turf = old_loc + old_turf.remove_opacity_source(source) + if(isturf(source.loc)) + for(var/turf/new_turf as anything in source.locs) + new_turf.add_opacity_source(source) diff --git a/code/datums/emergency_calls/emergency_call.dm b/code/datums/emergency_calls/emergency_call.dm index 3d43917b2bc7..6d41c2d64d0b 100644 --- a/code/datums/emergency_calls/emergency_call.dm +++ b/code/datums/emergency_calls/emergency_call.dm @@ -8,12 +8,19 @@ /datum/game_mode var/list/datum/emergency_call/all_calls = list() //initialized at round start and stores the datums. var/datum/emergency_call/picked_calls[] = list() //Which distress calls are currently active + var/ert_dispatched = FALSE /datum/game_mode/proc/ares_online() var/name = "ARES Online" var/input = "ARES. Online. Good morning, marines." shipwide_ai_announcement(input, name, 'sound/AI/ares_online.ogg') +/datum/game_mode/proc/request_ert(user, ares = FALSE) + if(!user) + return FALSE + message_admins("[key_name(user)] has requested a Distress Beacon! [ares ? SPAN_ORANGE("(via ARES)") : ""] ([SSticker.mode.ert_dispatched ? SPAN_RED("A random ERT was dispatched previously.") : SPAN_GREEN("No previous random ERT dispatched.")]) [CC_MARK(user)] (SEND) (DENY) [ADMIN_JMP_USER(user)] [CC_REPLY(user)]") + return TRUE + //The distress call parent. Cannot be called itself due to "name" being a filtered target. /datum/emergency_call var/name = "name" @@ -106,6 +113,7 @@ give_action(M, /datum/action/join_ert, src) /datum/game_mode/proc/activate_distress() + ert_dispatched = TRUE var/datum/emergency_call/random_call = get_random_call() if(!istype(random_call, /datum/emergency_call)) //Something went horribly wrong return diff --git a/code/datums/mob_hud.dm b/code/datums/mob_hud.dm index aa1bc9b40783..65c5a47896fa 100644 --- a/code/datums/mob_hud.dm +++ b/code/datums/mob_hud.dm @@ -30,10 +30,18 @@ var/list/datum/mob_hud/huds = list( // Stop displaying a HUD to a specific person // (took off medical glasses) -/datum/mob_hud/proc/remove_hud_from(mob/user) +/datum/mob_hud/proc/remove_hud_from(mob/user, source) + if(length(hudusers[user]) && (source in hudusers[user])) + hudusers[user] -= source + + if(length(hudusers[user])) + return FALSE + for(var/mob/target in hudmobs) remove_from_single_hud(user, target) + hudusers -= user + return TRUE // Stop rendering a HUD on a target // "unenroll" them so to speak @@ -53,8 +61,24 @@ var/list/datum/mob_hud/huds = list( user.client.images -= target.clone.hud_list[i] // Allow user to view a HUD (putting on medical glasses) -/datum/mob_hud/proc/add_hud_to(mob/user) +/datum/mob_hud/proc/add_hud_to(mob/user, source) hudusers |= user + if(hudusers[user]) + hudusers[user] |= list(source) + else + hudusers[user] += list(source) + + for(var/mob/target in hudmobs) + add_to_single_hud(user, target) + +/// Refreshes the HUD, adding user and sources if missing and then calls to add the HUD +/datum/mob_hud/proc/refresh_hud(mob/user, list/source) + hudusers |= user + if(hudusers[user]) + hudusers[user] |= source + else + hudusers[user] += source + for(var/mob/target in hudmobs) add_to_single_hud(user, target) @@ -229,13 +253,13 @@ var/list/datum/mob_hud/huds = list( for(var/datum/mob_hud/hud in huds) if(istype(hud, /datum/mob_hud/xeno)) hud.remove_from_hud(src) - hud.remove_hud_from(src) + hud.remove_hud_from(src, src) else if (istype(hud, /datum/mob_hud/xeno_infection)) - hud.remove_hud_from(src) + hud.remove_hud_from(src, src) if (xeno_hostile_hud) xeno_hostile_hud = FALSE var/datum/mob_hud/hostile_hud = huds[MOB_HUD_XENO_HOSTILE] - hostile_hud.remove_hud_from(src) + hostile_hud.remove_hud_from(src, src) @@ -243,13 +267,7 @@ var/list/datum/mob_hud/huds = list( var/mob/M = source_mob ? source_mob : src for(var/datum/mob_hud/hud in huds) if(M in hud.hudusers) - readd_hud(hud) - -/mob/proc/readd_hud(datum/mob_hud/hud) - hud.add_hud_to(src) - - - + hud.refresh_hud(src, hud.hudusers[M]) //Medical HUDs diff --git a/code/game/area/BigRed.dm b/code/game/area/BigRed.dm index 59d7c40cd56f..57e062195a3c 100644 --- a/code/game/area/BigRed.dm +++ b/code/game/area/BigRed.dm @@ -654,6 +654,7 @@ minimap_color = MINIMAP_AREA_LZ icon_state = "tcomsatcham" requires_power = FALSE + is_resin_allowed = FALSE /area/bigredv2/landing/console name = "\improper LZ1 'Telecomms'" diff --git a/code/game/area/DesertDam.dm b/code/game/area/DesertDam.dm index 73635eb6385d..96e5eb1f62e5 100644 --- a/code/game/area/DesertDam.dm +++ b/code/game/area/DesertDam.dm @@ -682,7 +682,6 @@ /area/desert_dam/exterior requires_power = 1 always_unpowered = 1 - lighting_use_dynamic = 1 power_light = FALSE power_equip = FALSE power_environ = FALSE diff --git a/code/game/area/IceColony.dm b/code/game/area/IceColony.dm index 2c7bbee2969c..40289b92b5ad 100644 --- a/code/game/area/IceColony.dm +++ b/code/game/area/IceColony.dm @@ -28,7 +28,6 @@ icon_state = "cliff_blocked" requires_power = 1 always_unpowered = 1 - lighting_use_dynamic = 1 power_light = FALSE power_equip = FALSE power_environ = FALSE @@ -65,6 +64,7 @@ name = "\improper Emergency Landing Pad" icon_state = "landing_pad" minimap_color = MINIMAP_AREA_LZ + is_resin_allowed = FALSE //Everything around the physical landing pad @@ -79,6 +79,7 @@ name = "\improper Aerodrome Container Yard" icon_state = "container_yard" minimap_color = MINIMAP_AREA_LZ + is_resin_allowed = FALSE // // Valleys diff --git a/code/game/area/LV522_Chances_Claim.dm b/code/game/area/LV522_Chances_Claim.dm index 3d15d49c3df7..ffd6a5897e4e 100644 --- a/code/game/area/LV522_Chances_Claim.dm +++ b/code/game/area/LV522_Chances_Claim.dm @@ -52,7 +52,6 @@ name = "Chance's Claim - Dropship Alamo Landing Zone" icon_state = "shuttle" icon = 'icons/turf/area_shiva.dmi' - lighting_use_dynamic = TRUE /area/lv522/landing_zone_1/lz1_console name = "Chance's Claim - Dropship Alamo Console" @@ -75,7 +74,6 @@ name = "Chance's Claim - Dropship Normandy Landing Zone" icon_state = "shuttle2" icon = 'icons/turf/area_shiva.dmi' - lighting_use_dynamic = TRUE /area/lv522/landing_zone_2/lz2_console name = "Chance's Claim - Dropship Normandy Console" @@ -180,6 +178,7 @@ name = "North LZ1 - Spaceport" icon_state = "red" minimap_color = MINIMAP_AREA_LZ + is_resin_allowed = FALSE /area/lv522/indoors/lone_buildings/outdoor_bot name = "East LZ1 - Outdoor T-Comms" diff --git a/code/game/area/Sulaco.dm b/code/game/area/Sulaco.dm index 851025e1b63a..ae197537143a 100644 --- a/code/game/area/Sulaco.dm +++ b/code/game/area/Sulaco.dm @@ -117,7 +117,7 @@ //DISTRESS SHUTTLES /area/shuttle/distress - lighting_use_dynamic = FALSE + base_lighting_alpha = 255 unique = TRUE /area/shuttle/distress/start diff --git a/code/game/area/WhiskeyOutpost.dm b/code/game/area/WhiskeyOutpost.dm index 583a5dafc299..02d94dc942da 100644 --- a/code/game/area/WhiskeyOutpost.dm +++ b/code/game/area/WhiskeyOutpost.dm @@ -79,7 +79,6 @@ //ambience = list('sound/ambience/jungle_amb1.ogg') requires_power = 1 always_unpowered = 1 - lighting_use_dynamic = 1 power_light = FALSE power_equip = FALSE power_environ = FALSE @@ -180,7 +179,6 @@ ceiling = CEILING_UNDERGROUND_ALLOW_CAS requires_power = 1 always_unpowered = 1 - lighting_use_dynamic = 1 power_light = FALSE power_equip = FALSE power_environ = FALSE diff --git a/code/game/area/admin_level.dm b/code/game/area/admin_level.dm index 501047c46d08..a934b5f71570 100644 --- a/code/game/area/admin_level.dm +++ b/code/game/area/admin_level.dm @@ -3,14 +3,13 @@ /area/adminlevel ceiling = CEILING_METAL + base_lighting_alpha = 255 /area/adminlevel/bunker01 icon_state = "thunder" requires_power = FALSE statistic_exempt = TRUE flags_area = AREA_NOTUNNEL - luminosity = TRUE - lighting_use_dynamic = FALSE /area/adminlevel/bunker01/mainroom name = "\improper Bunker Main Room" @@ -78,8 +77,7 @@ ceiling = CEILING_UNDERGROUND_ALLOW_CAS always_unpowered = TRUE requires_power = TRUE - lighting_use_dynamic = TRUE - luminosity = FALSE + base_lighting_alpha = 0 /area/adminlevel/bunker01/caves/outpost name = "\improper Bunker Outpost" @@ -87,8 +85,6 @@ ceiling = CEILING_UNDERGROUND_ALLOW_CAS requires_power = TRUE always_unpowered = FALSE - lighting_use_dynamic = TRUE - luminosity = FALSE /area/adminlevel/bunker01/caves/xeno name = "\improper Bunker Xeno Hive" diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 2c274665fa19..826b2dc0585f 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -28,9 +28,6 @@ var/unique = TRUE var/has_gravity = 1 - var/area/master // master area used for power calcluations - // (original area before splitting due to sd_DAL) - var/list/related // the other areas of the same type as this // var/list/lights // list of all lights on this area var/list/all_doors = list() //Added by Strumpetplaya - Alarm Change - Contains a list of doors adjacent to this area var/air_doors_activated = 0 @@ -89,10 +86,8 @@ if(unique) GLOB.areas_by_type[type] = src ..() - master = src //moved outside the spawn(1) to avoid runtimes in lighting.dm when it references loc.loc.master ~Carn - related = list(src) - initialize_power_and_lighting() + initialize_power() /area/Initialize(mapload, ...) icon_state = "" //Used to reset the icon overlay, I assume. @@ -105,24 +100,26 @@ if(is_mainship_level(z)) GLOB.ship_areas += src -/area/proc/initialize_power_and_lighting(override_power) + if(base_lighting_alpha) + return INITIALIZE_HINT_ROUNDSTART + +/area/LateInitialize() + . = ..() + + update_base_lighting() + +/area/proc/initialize_power(override_power) if(requires_power) - luminosity = 0 if(override_power) //Reset everything if you want to override. power_light = TRUE power_equip = TRUE power_environ = TRUE - if(lighting_use_dynamic) - SetDynamicLighting() else power_light = FALSE //rastaf0 power_equip = FALSE //rastaf0 power_environ = FALSE //rastaf0 - luminosity = 1 - lighting_use_dynamic = 0 power_change() // all machines set to current power level, also updates lighting icon - InitializeLighting() /// Returns the correct ambience sound track for a client in this area /area/proc/get_sound_ambience(client/target) @@ -135,13 +132,12 @@ poweralm = state if(istype(source)) //Only report power alarms on the z-level where the source is located. var/list/cameras = list() - for (var/area/RA in related) - for (var/obj/structure/machinery/camera/C in RA) - cameras += C - if(state == 1) - C.network.Remove(CAMERA_NET_POWER_ALARMS) - else - C.network.Add(CAMERA_NET_POWER_ALARMS) + for (var/obj/structure/machinery/camera/C in src) + cameras += C + if(state == 1) + C.network.Remove(CAMERA_NET_POWER_ALARMS) + else + C.network.Add(CAMERA_NET_POWER_ALARMS) for (var/mob/living/silicon/aiPlayer in ai_mob_list) if(aiPlayer.z == source.z) if (state == 1) @@ -161,10 +157,9 @@ // return 0 //redudant //Check all the alarms before lowering atmosalm. Raising is perfectly fine. - for (var/area/RA in related) - for (var/obj/structure/machinery/alarm/AA in RA) - if ( !(AA.inoperable()) && !AA.shorted) - danger_level = max(danger_level, AA.danger_level) + for (var/obj/structure/machinery/alarm/AA in src) + if ( !(AA.inoperable()) && !AA.shorted) + danger_level = max(danger_level, AA.danger_level) if(danger_level != atmosalm) if (danger_level < 1 && atmosalm >= 1) @@ -172,9 +167,8 @@ air_doors_open() if (danger_level < 2 && atmosalm >= 2) - for(var/area/RA in related) - for(var/obj/structure/machinery/camera/C in RA) - C.network.Remove(CAMERA_NET_ATMOSPHERE_ALARMS) + for(var/obj/structure/machinery/camera/C in src) + C.network.Remove(CAMERA_NET_ATMOSPHERE_ALARMS) for(var/mob/living/silicon/aiPlayer in ai_mob_list) aiPlayer.cancelAlarm("Atmosphere", src, src) for(var/obj/structure/machinery/computer/station_alert/a in machines) @@ -182,11 +176,10 @@ if (danger_level >= 2 && atmosalm < 2) var/list/cameras = list() - for(var/area/RA in related) - //updateicon() - for(var/obj/structure/machinery/camera/C in RA) - cameras += C - C.network.Add(CAMERA_NET_ATMOSPHERE_ALARMS) + //updateicon() + for(var/obj/structure/machinery/camera/C in src) + cameras += C + C.network.Add(CAMERA_NET_ATMOSPHERE_ALARMS) for(var/mob/living/silicon/aiPlayer in ai_mob_list) aiPlayer.triggerAlarm("Atmosphere", src, cameras, src) for(var/obj/structure/machinery/computer/station_alert/a in machines) @@ -194,40 +187,38 @@ air_doors_close() atmosalm = danger_level - for(var/area/RA in related) - for (var/obj/structure/machinery/alarm/AA in RA) - AA.update_icon() + for (var/obj/structure/machinery/alarm/AA in src) + AA.update_icon() return 1 return 0 /area/proc/air_doors_close() - if(!src.master.air_doors_activated) - src.master.air_doors_activated = 1 - for(var/obj/structure/machinery/door/firedoor/E in src.master.all_doors) - if(!E:blocked) - if(E.operating) - E:nextstate = OPEN - else if(!E.density) - INVOKE_ASYNC(E, TYPE_PROC_REF(/obj/structure/machinery/door, close)) + for(var/obj/structure/machinery/door/firedoor/E in all_doors) + if(E.blocked) + continue + + if(E.operating) + E.nextstate = OPEN + else if(!E.density) + E.close() + /area/proc/air_doors_open() - if(src.master.air_doors_activated) - src.master.air_doors_activated = 0 - for(var/obj/structure/machinery/door/firedoor/E in src.master.all_doors) - if(!E:blocked) - if(E.operating) - E:nextstate = OPEN - else if(E.density) - INVOKE_ASYNC(E, TYPE_PROC_REF(/obj/structure/machinery/door, open)) + for(var/obj/structure/machinery/door/firedoor/E in all_doors) + if(E.blocked) + continue + if(E.operating) + E.nextstate = OPEN + else if(E.density) + E.open() /area/proc/firealert() if(name == "Space") //no fire alarms in space return if(!(flags_alarm_state & ALARM_WARNING_FIRE)) - flags_alarm_state |= ALARM_WARNING_FIRE - master.flags_alarm_state |= ALARM_WARNING_FIRE //used for firedoor checks + flags_alarm_state |= ALARM_WARNING_FIRE //used for firedoor checks updateicon() mouse_opacity = MOUSE_OPACITY_TRANSPARENT for(var/obj/structure/machinery/door/firedoor/D in all_doors) @@ -237,10 +228,9 @@ else if(!D.density) INVOKE_ASYNC(D, TYPE_PROC_REF(/obj/structure/machinery/door, close)) var/list/cameras = list() - for(var/area/RA in related) - for (var/obj/structure/machinery/camera/C in RA) - cameras.Add(C) - C.network.Add(CAMERA_NET_FIRE_ALARMS) + for (var/obj/structure/machinery/camera/C in src) + cameras.Add(C) + C.network.Add(CAMERA_NET_FIRE_ALARMS) for (var/mob/living/silicon/ai/aiPlayer in ai_mob_list) aiPlayer.triggerAlarm("Fire", src, cameras, src) for (var/obj/structure/machinery/computer/station_alert/a in machines) @@ -248,8 +238,7 @@ /area/proc/firereset() if(flags_alarm_state & ALARM_WARNING_FIRE) - flags_alarm_state &= ~ALARM_WARNING_FIRE - master.flags_alarm_state &= ~ALARM_WARNING_FIRE //used for firedoor checks + flags_alarm_state &= ~ALARM_WARNING_FIRE //used for firedoor checks mouse_opacity = MOUSE_OPACITY_TRANSPARENT updateicon() for(var/obj/structure/machinery/door/firedoor/D in all_doors) @@ -258,9 +247,8 @@ D.nextstate = OPEN else if(D.density) INVOKE_ASYNC(D, TYPE_PROC_REF(/obj/structure/machinery/door, open)) - for(var/area/RA in related) - for (var/obj/structure/machinery/camera/C in RA) - C.network.Remove(CAMERA_NET_FIRE_ALARMS) + for (var/obj/structure/machinery/camera/C in src) + C.network.Remove(CAMERA_NET_FIRE_ALARMS) for (var/mob/living/silicon/ai/aiPlayer in ai_mob_list) aiPlayer.cancelAlarm("Fire", src, src) for (var/obj/structure/machinery/computer/station_alert/a in machines) @@ -278,16 +266,16 @@ /* /area/proc/toggle_evacuation() //toggles lights and creates an overlay. flags_alarm_state ^= ALARM_WARNING_EVAC - master.flags_alarm_state ^= ALARM_WARNING_EVAC + flags_alarm_state ^= ALARM_WARNING_EVAC //if(flags_alarm_state & ALARM_WARNING_EVAC) - // master.lightswitch = FALSE + // lightswitch = FALSE //lightswitch = FALSE //Lights going off. // else - // master.lightswitch = TRUE + // lightswitch = TRUE //lightswitch = TRUE //Coming on. - master.updateicon() + updateicon() - //master.power_change() + //power_change() /area/proc/toggle_shut_down() @@ -312,75 +300,72 @@ if(icon_state != I) icon_state = I //If the icon state changed, change it. Otherwise do nothing. /area/proc/powered(chan) // return true if the area has power to given channel - if(!master.requires_power) + if(!requires_power) return 1 - if(master.always_unpowered) + if(always_unpowered) return 0 switch(chan) if(POWER_CHANNEL_EQUIP) - return master.power_equip + return power_equip if(POWER_CHANNEL_LIGHT) - return master.power_light + return power_light if(POWER_CHANNEL_ENVIRON) - return master.power_environ + return power_environ return 0 /area/proc/update_power_channels(equip, light, environ) - if(!master) - CRASH("CALLED update_power_channels on non-master channel!") var/changed = FALSE - if(master.power_equip != equip) - master.power_equip = equip + if(power_equip != equip) + power_equip = equip changed = TRUE - if(master.power_light != light) - master.power_light = light + if(power_light != light) + power_light = light changed = TRUE - if(master.power_environ != environ) - master.power_environ = environ + if(power_environ != environ) + power_environ = environ changed = TRUE if(changed) //Something got changed power-wise, time for an update! power_change() // called when power status changes /area/proc/power_change() - for(var/area/RA in related) - for(var/obj/structure/machinery/M in RA) // for each machine in the area - if(!M.gc_destroyed) - M.power_change() // reverify power status (to update icons etc.) - if(flags_alarm_state) - RA.updateicon() + for(var/obj/structure/machinery/M in src) // for each machine in the area + if(!M.gc_destroyed) + M.power_change() // reverify power status (to update icons etc.) + if(flags_alarm_state) + updateicon() /area/proc/usage(chan, reset_oneoff = FALSE) var/used = 0 switch(chan) if(POWER_CHANNEL_LIGHT) - used += master.used_light + used += used_light if(POWER_CHANNEL_EQUIP) - used += master.used_equip + used += used_equip if(POWER_CHANNEL_ENVIRON) - used += master.used_environ + used += used_environ if(POWER_CHANNEL_ONEOFF) - used += master.used_oneoff + used += used_oneoff if(reset_oneoff) - master.used_oneoff = 0 + used_oneoff = 0 if(POWER_CHANNEL_TOTAL) - used += master.used_light + master.used_equip + master.used_environ + master.used_oneoff + used += used_light + used_equip + used_environ + used_oneoff if(reset_oneoff) - master.used_oneoff = 0 + used_oneoff = 0 return used /area/proc/use_power(amount, chan) switch(chan) if(POWER_CHANNEL_EQUIP) - master.used_equip += amount + used_equip += amount if(POWER_CHANNEL_LIGHT) - master.used_light += amount + used_light += amount if(POWER_CHANNEL_ENVIRON) - master.used_environ += amount + used_environ += amount if(POWER_CHANNEL_ONEOFF) - master.used_oneoff += amount + used_oneoff += amount /area/Entered(A,atom/OldLoc) if(ismob(A)) @@ -388,7 +373,7 @@ return var/mob/M = A var/area/old_area = get_area(OldLoc) - if(old_area.master == master) + if(old_area == src) return M?.client?.soundOutput?.update_ambience(src, null, TRUE) else if(istype(A, /obj/structure/machinery)) @@ -416,24 +401,21 @@ A.has_gravity = gravitystate - for(var/area/SubA in A.related) - SubA.has_gravity = gravitystate - - if(gravitystate) - for(var/mob/living/carbon/human/M in SubA) - thunk(M) - for(var/mob/M1 in SubA) - M1.make_floating(0) - else - for(var/mob/M in SubA) - if(M.Check_Dense_Object() && istype(src,/mob/living/carbon/human/)) - var/mob/living/carbon/human/H = src - if(istype(H.shoes, /obj/item/clothing/shoes/magboots) && (H.shoes.flags_inventory & NOSLIPPING)) //magboots + dense_object = no floaty effect - H.make_floating(0) - else - H.make_floating(1) + if(gravitystate) + for(var/mob/living/carbon/human/M in A) + thunk(M) + for(var/mob/M1 in A) + M1.make_floating(0) + else + for(var/mob/M in A) + if(M.Check_Dense_Object() && istype(src,/mob/living/carbon/human/)) + var/mob/living/carbon/human/H = src + if(istype(H.shoes, /obj/item/clothing/shoes/magboots) && (H.shoes.flags_inventory & NOSLIPPING)) //magboots + dense_object = no floaty effect + H.make_floating(0) else - M.make_floating(1) + H.make_floating(1) + else + M.make_floating(1) /area/proc/thunk(M) if(istype(get_turf(M), /turf/open/space)) // Can't fall onto nothing. diff --git a/code/game/area/areas_event.dm b/code/game/area/areas_event.dm index fe34d74363d5..c43dae3a65b2 100644 --- a/code/game/area/areas_event.dm +++ b/code/game/area/areas_event.dm @@ -47,20 +47,12 @@ structure: //events are not part of regular gameplay, therefore, no statistics statistic_exempt = TRUE - //no dynamic lighting - exterior_light = 0 - lighting_use_dynamic = FALSE + base_lighting_alpha = 255 //always powered requires_power = FALSE unlimited_power = TRUE -/area/event/Initialize() - . = ..() - if(exterior_light) - for(var/turf/T in contents) - T.update_lumcount(exterior_light) - //no dynamic lighting, unpowered. /area/event/unpowered name = "Open grounds (event)" @@ -75,7 +67,6 @@ structure: icon_state = "event_dyn" requires_power = TRUE unlimited_power = TRUE - lighting_use_dynamic = TRUE //no dynamic lighting, unpowered. /area/event/dynamic/unpowered @@ -83,13 +74,14 @@ structure: icon_state = "event_dyn_nopower" unlimited_power = FALSE + base_lighting_alpha = 255 //dynamic lighting, lit, powered. /area/event/dynamic/lit name = "Open grounds (event PDL)" icon_state = "event_dyn_lit" - exterior_light = 3 + base_lighting_alpha = 255 //dynamic lighting, lit, unpowered. /area/event/dynamic/lit/unpowered @@ -97,6 +89,7 @@ structure: icon_state = "event_dyn_lit_nopower" unlimited_power = FALSE + base_lighting_alpha = 255 //-----------------------CEILING_METAL-------------------------- @@ -122,7 +115,6 @@ structure: icon_state = "metal_dyn" requires_power = TRUE unlimited_power = TRUE - lighting_use_dynamic = TRUE //no dynamic lighting, unpowered. /area/event/metal/dynamic/unpowered @@ -137,7 +129,7 @@ structure: name = "Building interior (event PDL)" icon_state = "metal_dyn_lit" - exterior_light = 3 + base_lighting_alpha = 255 //dynamic lighting, lit, unpowered. /area/event/metal/dynamic/lit/unpowered @@ -176,7 +168,6 @@ structure: icon_state = "under_dyn" requires_power = TRUE unlimited_power = TRUE - lighting_use_dynamic = TRUE //no dynamic lighting, unpowered. /area/event/underground/dynamic/unpowered @@ -191,7 +182,7 @@ structure: name = "Small caves (event PDL)" icon_state = "under_dyn_lit" - exterior_light = 3 + base_lighting_alpha = 255 //dynamic lighting, lit, unpowered. /area/event/underground/dynamic/lit/unpowered @@ -232,7 +223,6 @@ structure: icon_state = "undercas_dyn" requires_power = TRUE unlimited_power = TRUE - lighting_use_dynamic = TRUE //no dynamic lighting, unpowered. /area/event/underground_no_CAS/dynamic/unpowered @@ -246,7 +236,7 @@ structure: name = "Caves (event PDL)" icon_state = "undercas_dyn_lit" - exterior_light = 3 + base_lighting_alpha = 255 //dynamic lighting, lit, unpowered. /area/event/underground_no_CAS/dynamic/lit/unpowered @@ -286,7 +276,6 @@ structure: icon_state = "deep_dyn" requires_power = TRUE unlimited_power = TRUE - lighting_use_dynamic = TRUE //no dynamic lighting, unpowered. /area/event/deep_underground/dynamic/unpowered @@ -301,7 +290,7 @@ structure: name = "Deep underground (event PDL)" icon_state = "deep_dyn_lit" - exterior_light = 3 + base_lighting_alpha = 255 //dynamic lighting, lit, unpowered. /area/event/deep_underground/dynamic/lit/unpowered diff --git a/code/game/area/chinook.dm b/code/game/area/chinook.dm index 62bbdee7bd17..b9cd440fd548 100644 --- a/code/game/area/chinook.dm +++ b/code/game/area/chinook.dm @@ -9,7 +9,6 @@ requires_power = TRUE statistic_exempt = TRUE flags_area = AREA_NOTUNNEL - lighting_use_dynamic = TRUE sound_environment = SOUND_ENVIRONMENT_ROOM unlimited_power = TRUE ceiling = CEILING_METAL diff --git a/code/game/area/kutjevo.dm b/code/game/area/kutjevo.dm index 48f18091df4a..422017c0a46b 100644 --- a/code/game/area/kutjevo.dm +++ b/code/game/area/kutjevo.dm @@ -8,20 +8,17 @@ icon_state = "kutjevo" can_build_special = TRUE //T-Comms structure temperature = 308.7 //kelvin, 35c, 95f - lighting_use_dynamic = 1 minimap_color = MINIMAP_AREA_ENGI /area/shuttle/drop1/kutjevo name = "Kutjevo - Dropship Alamo Landing Zone" icon_state = "shuttle" icon = 'icons/turf/area_kutjevo.dmi' - lighting_use_dynamic = 1 /area/shuttle/drop2/kutjevo name = "Kutjevo - Dropship Normandy Landing Zone" icon_state = "shuttle2" icon = 'icons/turf/area_kutjevo.dmi' - lighting_use_dynamic = 1 /area/kutjevo/exterior name = "Kutjevo - Exterior" diff --git a/code/game/area/prison_v3_fiorina.dm b/code/game/area/prison_v3_fiorina.dm index deb5d8758784..9c60f8173ab8 100644 --- a/code/game/area/prison_v3_fiorina.dm +++ b/code/game/area/prison_v3_fiorina.dm @@ -8,7 +8,6 @@ icon_state = "fiorina" can_build_special = TRUE //T-Comms structure temperature = T20C - lighting_use_dynamic = 1 ceiling = CEILING_GLASS minimap_color = MINIMAP_AREA_COLONY @@ -71,14 +70,13 @@ name = "Fiorina - LZ" is_landing_zone = TRUE minimap_color = MINIMAP_AREA_LZ + is_resin_allowed = FALSE /area/fiorina/lz/near_lzI name = "Fiorina - LZ1 Aux Port" - is_resin_allowed = FALSE /area/fiorina/lz/near_lzII name = "Fiorina - LZ2 Prison Port" - is_resin_allowed = FALSE /area/fiorina/lz/console_I name = "Fiorina - LZ1 Control Console" diff --git a/code/game/area/shiva.dm b/code/game/area/shiva.dm index 8ca47c03e5ca..e4939cd67e1e 100644 --- a/code/game/area/shiva.dm +++ b/code/game/area/shiva.dm @@ -7,14 +7,12 @@ icon_state = "shiva" can_build_special = TRUE //T-Comms structure temperature = ICE_COLONY_TEMPERATURE - lighting_use_dynamic = TRUE minimap_color = MINIMAP_AREA_COLONY /area/shuttle/drop1/shiva name = "Shiva's Snowball - Dropship Alamo Landing Zone" icon_state = "shuttle" icon = 'icons/turf/area_shiva.dmi' - lighting_use_dynamic = TRUE is_resin_allowed = FALSE minimap_color = MINIMAP_AREA_LZ @@ -22,7 +20,6 @@ name = "Shiva's Snowball - Dropship Normandy Landing Zone" icon_state = "shuttle2" icon = 'icons/turf/area_shiva.dmi' - lighting_use_dynamic = TRUE is_resin_allowed = FALSE minimap_color = MINIMAP_AREA_LZ diff --git a/code/game/area/shuttles.dm b/code/game/area/shuttles.dm index f61f13097d12..62c42406e795 100644 --- a/code/game/area/shuttles.dm +++ b/code/game/area/shuttles.dm @@ -1,13 +1,15 @@ /area/shuttle name = "Shuttle" requires_power = FALSE + unlimited_power = TRUE always_unpowered = FALSE icon_state = "shuttle" ceiling_muffle = TRUE // Loading the same shuttle map at a different time will produce distinct area instances. unique = FALSE - lighting_use_dynamic = FALSE + + base_lighting_alpha = 255 ///area/shuttle/Initialize() @@ -37,6 +39,7 @@ name = "Hyperspace" desc = "Weeeeee" ambience_exterior = 'sound/ambience/shuttle_fly_loop.ogg' + base_lighting_alpha = 255 /area/shuttle/vehicle_elevator name = "Vehicle ASRS" @@ -52,7 +55,7 @@ /area/shuttle/trijent_shuttle/elevator requires_power = TRUE - lighting_use_dynamic = FALSE + unlimited_power = FALSE powernet_name = "ground" /area/shuttle/trijent_shuttle/lz1 @@ -66,6 +69,7 @@ /area/shuttle/trijent_shuttle/omega name = "Trijent Omega" + /area/shuttle/escape_pod icon = 'icons/turf/area_almayer.dmi' icon_state = "lifeboat" diff --git a/code/game/area/space_station_13_areas.dm b/code/game/area/space_station_13_areas.dm index 226ca555cd57..df5e54a77013 100644 --- a/code/game/area/space_station_13_areas.dm +++ b/code/game/area/space_station_13_areas.dm @@ -18,7 +18,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station name = "\improper Space" requires_power = 1 always_unpowered = 1 - lighting_use_dynamic = 0 + base_lighting_alpha = 255 power_light = FALSE power_equip = FALSE power_environ = FALSE @@ -39,7 +39,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station ceiling = CEILING_METAL /area/admin/droppod - lighting_use_dynamic = FALSE + base_lighting_alpha = 255 /area/admin/droppod/holding name = "\improper Admin Supply Drops Droppod" @@ -56,9 +56,9 @@ NOTE: there are two lists of areas in the end of this file: centcom and station name = "start area" icon_state = "start" ceiling = CEILING_MAX - requires_power = 0 - luminosity = 1 - lighting_use_dynamic = 0 + requires_power = FALSE + static_lighting = FALSE + base_lighting_alpha = 255 has_gravity = 1 // === end remove @@ -72,6 +72,8 @@ NOTE: there are two lists of areas in the end of this file: centcom and station statistic_exempt = TRUE ceiling = CEILING_METAL + base_lighting_alpha = 255 + /area/centcom/control name = "\improper abandoned Centcom Control" @@ -86,6 +88,8 @@ NOTE: there are two lists of areas in the end of this file: centcom and station statistic_exempt = TRUE ceiling = CEILING_METAL + base_lighting_alpha = 255 + /area/tdome/tdome1 name = "\improper abandoned Thunderdome (Team 1)" icon_state = "green" diff --git a/code/game/area/strata.dm b/code/game/area/strata.dm index 1cf0eac58d1c..117cffa600d4 100644 --- a/code/game/area/strata.dm +++ b/code/game/area/strata.dm @@ -18,14 +18,16 @@ EXTERIOR is FUCKING FREEZING, and refers to areas out in the open and or exposed /area/shuttle/drop1/strata //Not in Sulaco.DM because holy shit we need to sort things. name = "Dropship Alamo Landing Zone" icon_state = "shuttle" - lighting_use_dynamic = 0 //No bad + base_lighting_alpha = 255 minimap_color = MINIMAP_AREA_LZ + is_resin_allowed = FALSE /area/shuttle/drop2/strata name = "Dropship Normandy Landing Zone" icon_state = "shuttle2" - lighting_use_dynamic = 0 + base_lighting_alpha = 255 minimap_color = MINIMAP_AREA_LZ + is_resin_allowed = FALSE /area/strata/ag name = "Above Ground Area" @@ -92,6 +94,7 @@ EXTERIOR is FUCKING FREEZING, and refers to areas out in the open and or exposed unlimited_power = 1 //So the DS computer always works for the Queen is_landing_zone = TRUE minimap_color = MINIMAP_AREA_LZ + is_resin_allowed = FALSE /area/strata/ag/exterior/landingzone_2 name = "Landing Zone 2 Pad - Ice Fields" @@ -100,6 +103,7 @@ EXTERIOR is FUCKING FREEZING, and refers to areas out in the open and or exposed unlimited_power = 1 //So the DS computer always works for the Queen is_landing_zone = TRUE minimap_color = MINIMAP_AREA_LZ + is_resin_allowed = FALSE /area/strata/ag/interior/nearlz1 name = "Landing Zone 1 - Mining Aerodrome" @@ -113,6 +117,7 @@ EXTERIOR is FUCKING FREEZING, and refers to areas out in the open and or exposed icon_state = "nearlz2" weather_enabled = TRUE //This LZ is outside, but consider disabling if it destroys the meta. minimap_color = MINIMAP_AREA_LZ + is_resin_allowed = FALSE /area/strata/ag/exterior/landingzone_valley name = "Landing Zone Valley" diff --git a/code/game/area/techtree.dm b/code/game/area/techtree.dm index 70b70e3852be..24510562ab57 100644 --- a/code/game/area/techtree.dm +++ b/code/game/area/techtree.dm @@ -1,5 +1,5 @@ /area/techtree - lighting_use_dynamic = FALSE + base_lighting_alpha = 255 ceiling = CEILING_MAX requires_power = 0 ambience_exterior = AMBIENCE_ALMAYER diff --git a/code/game/area/varadero.dm b/code/game/area/varadero.dm index 09b082f2acd6..682f3ded1c66 100644 --- a/code/game/area/varadero.dm +++ b/code/game/area/varadero.dm @@ -8,7 +8,6 @@ icon_state = "varadero" can_build_special = TRUE //T-Comms structure temperature = TROPICAL_TEMP - lighting_use_dynamic = TRUE minimap_color = MINIMAP_AREA_COLONY //shuttle stuff @@ -17,7 +16,6 @@ name = "New Varadero - Dropship Alamo Landing Zone" icon_state = "shuttle" icon = 'icons/turf/area_varadero.dmi' - lighting_use_dynamic = TRUE is_resin_allowed = FALSE minimap_color = MINIMAP_AREA_LZ @@ -26,7 +24,6 @@ name = "New Varadero - Dropship Normandy Landing Zone" icon_state = "shuttle2" icon = 'icons/turf/area_varadero.dmi' - lighting_use_dynamic = TRUE is_resin_allowed = FALSE minimap_color = MINIMAP_AREA_LZ @@ -35,7 +32,6 @@ /area/varadero/exterior name = "New Varadero - Exterior" ceiling = CEILING_NONE - lighting_use_dynamic = TRUE ambience_exterior = AMBIENCE_NV //soundscape_playlist @@ -119,7 +115,6 @@ name = "New Varadero - East Beach" is_resin_allowed = FALSE icon_state = "varadero1" - lighting_use_dynamic = TRUE minimap_color = MINIMAP_AREA_JUNGLE /area/varadero/exterior/monsoon @@ -130,7 +125,6 @@ /area/varadero/exterior/pool name = "New Varadero - Interior Pool" icon_state = "varadero1" - lighting_use_dynamic = TRUE minimap_color = MINIMAP_AREA_COMMAND_CAVE /area/varadero/exterior/eastocean @@ -292,8 +286,6 @@ power_light = FALSE power_equip = FALSE power_environ = FALSE - luminosity = 0 - lighting_use_dynamic = 1 sound_environment = SOUND_ENVIRONMENT_AUDITORIUM minimap_color = MINIMAP_AREA_CAVES @@ -311,8 +303,7 @@ power_light = FALSE power_equip = FALSE power_environ = FALSE - luminosity = 0 - lighting_use_dynamic = 1 + minimap_color = MINIMAP_AREA_RESEARCH_CAVE /area/varadero/interior_protected/caves/central diff --git a/code/game/atoms.dm b/code/game/atoms.dm index d2cad09edc83..e1541f8368b8 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -59,6 +59,36 @@ ///Default pixel y shifting for the atom's icon. var/base_pixel_y = 0 + //light stuff + + ///Light systems, only one of the three should be active at the same time. + var/light_system = STATIC_LIGHT + ///Range of the light in tiles. Zero means no light. + var/light_range = 0 + ///Intensity of the light. The stronger, the less shadows you will see on the lit area. + var/light_power = 1 + ///Hexadecimal RGB string representing the colour of the light. White by default. + var/light_color = COLOR_WHITE + ///Boolean variable for toggleable lights. Has no effect without the proper light_system, light_range and light_power values. + var/light_on = FALSE + ///Bitflags to determine lighting-related atom properties. + var/light_flags = NONE + ///Our light source. Don't fuck with this directly unless you have a good reason! + var/tmp/datum/dynamic_light_source/light + ///Any light sources that are "inside" of us, for example, if src here was a mob that's carrying a flashlight, that flashlight's light source would be part of this list. + var/tmp/list/hybrid_light_sources + + //Values should avoid being close to -16, 16, -48, 48 etc. + //Best keep them within 10 units of a multiple of 32, as when the light is closer to a wall, the probability + //that a shadow extends to opposite corners of the light mask square is increased, resulting in more shadow + //overlays. + ///x offset for dynamic lights on this atom + var/light_pixel_x + ///y offset for dynamic lights on this atom + var/light_pixel_y + ///typepath for the lighting maskfor dynamic light sources + var/light_mask_type = null + ///The color this atom will be if we choose to draw it on the minimap var/minimap_color = MINIMAP_SOLID @@ -77,7 +107,7 @@ Make sure the return value equals the return value of the parent so that the directive is properly returned. */ //=========================================================================== -/atom/Destroy() +/atom/Destroy(force) orbiters = null // The component is attached to us normally and will be deleted elsewhere QDEL_NULL(reagents) QDEL_NULL(light) @@ -327,6 +357,12 @@ Parameters are passed from New. CRASH("Warning: [src]([type]) initialized multiple times!") flags_atom |= INITIALIZED + if(light_system != MOVABLE_LIGHT && light_system != DIRECTIONAL_LIGHT && light_power && light_range) + update_light() + if(isturf(loc) && opacity) + var/turf/opaque_turf = loc + opaque_turf.directional_opacity = ALL_CARDINALS // No need to recalculate it in this case, it's guaranteed to be on afterwards anyways. + pass_flags = pass_flags_cache[type] if (isnull(pass_flags)) pass_flags = new() @@ -569,6 +605,37 @@ Parameters are passed from New. var/icon/I = icon(icon, icon_state, dir) return (I.Width() + I.Height()) * 0.5 +/** + * If this object has lights, turn it on/off. + * user: the mob actioning this + * toggle_on: if TRUE, will try to turn ON the light. Opposite if FALSE + * cooldown: how long until you can toggle the light on/off again + * sparks: if a spark effect will be generated + * forced: if TRUE and toggle_on = FALSE, will cause the light to turn on in cooldown second + * originated_turf: if not null, will check if the obj_turf is closer than distance_max to originated_turf, and the proc will return if not + * distance_max: used to check if originated_turf is close to obj.loc +*/ +/atom/proc/turn_light(mob/user, toggle_on , cooldown = 1 SECONDS, sparks = FALSE, forced = FALSE, light_again = FALSE) + if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_LIGHT) && !forced) + return STILL_ON_COOLDOWN + if(cooldown <= 0) + cooldown = 1 SECONDS + TIMER_COOLDOWN_START(src, COOLDOWN_LIGHT, cooldown) + if(toggle_on == light_on) + return NO_LIGHT_STATE_CHANGE + if(light_again && !toggle_on) //Is true when turn light is called by nightfall and the light is already on + addtimer(CALLBACK(src, PROC_REF(reset_light)), cooldown + 1) + if(sparks && light_on) + var/datum/effect_system/spark_spread/spark_system = new + spark_system.set_up(5, 0, src) + spark_system.attach(src) + spark_system.start(src) + return CHECKS_PASSED + +///Turn on the light, should be called by a timer +/atom/proc/reset_light() + turn_light(null, TRUE, 1 SECONDS, FALSE, TRUE) + /** * Return the markup to for the dropdown list for the VV panel for this atom * diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 17b37ce76630..9b64833cb547 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -21,13 +21,24 @@ var/datum/component/orbiter/orbiting + /// 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 + + ///Lazylist to keep track on the sources of illumination. + var/list/affected_movable_lights + ///Highest-intensity light affecting us, which determines our visibility. + var/affecting_dynamic_lumi = 0 + //=========================================================================== -/atom/movable/Destroy() +/atom/movable/Destroy(force) for(var/atom/movable/I in contents) qdel(I) if(pulledby) pulledby.stop_pulling() QDEL_NULL(launch_metadata) + QDEL_NULL(em_block) if(loc) loc.on_stored_atom_del(src) //things that container need to do when a movable atom inside it is deleted @@ -38,6 +49,9 @@ . = ..() moveToNullspace() //so we move into null space. Must be after ..() b/c atom's Dispose handles deleting our lighting stuff + QDEL_NULL(light) + QDEL_NULL(static_light) + //=========================================================================== //Overlays @@ -60,6 +74,48 @@ return src.master.attack_hand(a, b, c) 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 = GLOB.em_block_color + gen_emissive_blocker.dir = dir + gen_emissive_blocker.appearance_flags |= appearance_flags + overlays += gen_emissive_blocker + if(EMISSIVE_BLOCK_UNIQUE) + render_target = ref(src) + em_block = new(src, render_target) + overlays += list(em_block) + if(opacity) + AddElement(/datum/element/light_blocking) + if(light_system == MOVABLE_LIGHT) + AddComponent(/datum/component/overlay_lighting) + if(light_system == DIRECTIONAL_LIGHT) + AddComponent(/datum/component/overlay_lighting, is_directional = TRUE) + +/* + +///Updates this movables emissive overlay +/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 + if(blocks_emissive == EMISSIVE_BLOCK_UNIQUE) + if(!em_block) + render_target = ref(src) + em_block = new(src, render_target) + return em_block + +/atom/movable/update_overlays() + . = ..() + + . += update_emissive_block() + +*/ + /atom/movable/vv_get_dropdown() . = ..() VV_DROPDOWN_OPTION(VV_HK_EDIT_PARTICLES, "Edit Particles") @@ -251,10 +307,10 @@ if(light) //Clone lighting if(!clone.light) - clone.SetLuminosity(luminosity) //Create clone light + clone.set_light(luminosity) //Create clone light else if(clone.light) - clone.SetLuminosity(0) //Kill clone light + clone.set_light(0) //Kill clone light /atom/movable/proc/destroy_clone() clones.Remove(src.clone) @@ -279,3 +335,23 @@ //if((force < (move_resist * MOVE_FORCE_THROW_RATIO)) || (move_resist == INFINITY)) // return return throw_atom(target, range, speed, thrower, spin) + +///Keeps track of the sources of dynamic luminosity and updates our visibility with the highest. +/atom/movable/proc/update_dynamic_luminosity() + var/highest = 0 + for(var/i in affected_movable_lights) + if(affected_movable_lights[i] <= highest) + continue + highest = affected_movable_lights[i] + if(highest == affecting_dynamic_lumi) + return + luminosity -= affecting_dynamic_lumi + affecting_dynamic_lumi = highest + luminosity += affecting_dynamic_lumi + + +///Helper to change several lighting overlay settings. +/atom/movable/proc/set_light_range_power_color(range, power, color) + set_light_range(range) + set_light_power(power) + set_light_color(color) diff --git a/code/game/machinery/ARES/ARES.dm b/code/game/machinery/ARES/ARES.dm index de4140ef1b81..4bfd5b98ad26 100644 --- a/code/game/machinery/ARES/ARES.dm +++ b/code/game/machinery/ARES/ARES.dm @@ -157,6 +157,7 @@ COOLDOWN_DECLARE(ares_distress_cooldown) COOLDOWN_DECLARE(ares_nuclear_cooldown) + COOLDOWN_DECLARE(ares_quarters_cooldown) /obj/structure/machinery/computer/ares_console/proc/link_systems(datum/ares_link/new_link = GLOB.ares_link, override) if(link && !override) diff --git a/code/game/machinery/ARES/ARES_procs.dm b/code/game/machinery/ARES/ARES_procs.dm index a335a36994e5..bd3fa6536be0 100644 --- a/code/game/machinery/ARES/ARES_procs.dm +++ b/code/game/machinery/ARES/ARES_procs.dm @@ -208,6 +208,7 @@ GLOBAL_LIST_INIT(maintenance_categories, list( data["distresstime"] = ares_distress_cooldown data["distresstimelock"] = DISTRESS_TIME_LOCK + data["quarterstime"] = ares_quarters_cooldown data["mission_failed"] = SSticker.mode.is_in_endgame data["nuketimelock"] = NUCLEAR_TIME_LOCK data["nuke_available"] = nuke_available @@ -506,16 +507,18 @@ GLOBAL_LIST_INIT(maintenance_categories, list( // -- Emergency Buttons -- // if("general_quarters") - if(security_level == SEC_LEVEL_RED || security_level == SEC_LEVEL_DELTA) - to_chat(usr, SPAN_WARNING("Alert level is already red or above, General Quarters cannot be called.")) + if(!COOLDOWN_FINISHED(src, ares_quarters_cooldown)) + to_chat(usr, SPAN_WARNING("It has not been long enough since the last General Quarters call!")) playsound(src, 'sound/machines/buzz-two.ogg', 15, 1) return FALSE - set_security_level(2, no_sound = TRUE, announce = FALSE) + if(security_level < SEC_LEVEL_RED) + set_security_level(SEC_LEVEL_RED, no_sound = TRUE, announce = FALSE) shipwide_ai_announcement("ATTENTION! GENERAL QUARTERS. ALL HANDS, MAN YOUR BATTLESTATIONS.", MAIN_AI_SYSTEM, 'sound/effects/GQfullcall.ogg') log_game("[key_name(usr)] has called for general quarters via ARES.") message_admins("[key_name_admin(usr)] has called for general quarters via ARES.") var/datum/ares_link/link = GLOB.ares_link link.log_ares_security("General Quarters", "[last_login] has called for general quarters via ARES.") + COOLDOWN_START(src, ares_quarters_cooldown, 10 MINUTES) . = TRUE if("evacuation_start") @@ -563,7 +566,7 @@ GLOBAL_LIST_INIT(maintenance_categories, list( for(var/client/admin in GLOB.admins) if((R_ADMIN|R_MOD) & admin.admin_holder.rights) playsound_client(admin,'sound/effects/sos-morse-code.ogg',10) - message_admins("[key_name(usr)] has requested a Distress Beacon (via ARES)! [CC_MARK(usr)] (SEND) (DENY) [ADMIN_JMP_USER(usr)] [CC_REPLY(usr)]") + SSticker.mode.request_ert(usr, TRUE) to_chat(usr, SPAN_NOTICE("A distress beacon request has been sent to USCM High Command.")) COOLDOWN_START(src, ares_distress_cooldown, COOLDOWN_COMM_REQUEST) return TRUE diff --git a/code/game/machinery/air_alarm.dm b/code/game/machinery/air_alarm.dm index 28e045163f06..16512a944be1 100644 --- a/code/game/machinery/air_alarm.dm +++ b/code/game/machinery/air_alarm.dm @@ -136,8 +136,6 @@ /obj/structure/machinery/alarm/proc/first_run() alarm_area = get_area(src) - if (alarm_area.master) - alarm_area = alarm_area.master area_uid = alarm_area.uid if (name == "alarm") name = "[alarm_area.name] Air Alarm" @@ -203,11 +201,10 @@ /obj/structure/machinery/alarm/proc/elect_master() if(!alarm_area) return 0 - for (var/area/A in alarm_area.related) - for (var/obj/structure/machinery/alarm/AA in A) - if (!(AA.inoperable())) - alarm_area.master_air_alarm = AA - return 1 + for (var/obj/structure/machinery/alarm/AA in alarm_area) + if (!(AA.inoperable())) + alarm_area.master_air_alarm = AA + return 1 return 0 /obj/structure/machinery/alarm/proc/get_danger_level(current_value, list/danger_levels) @@ -314,9 +311,8 @@ /obj/structure/machinery/alarm/proc/apply_mode() //propagate mode to other air alarms in the area //TODO: make it so that players can choose between applying the new mode to the room they are in (related area) vs the entire alarm area - for (var/area/RA in alarm_area.related) - for (var/obj/structure/machinery/alarm/AA in RA) - AA.mode = mode + for (var/obj/structure/machinery/alarm/AA in alarm_area) + AA.mode = mode switch(mode) if(AALARM_MODE_SCRUBBING) diff --git a/code/game/machinery/bio-dome_floodlights.dm b/code/game/machinery/bio-dome_floodlights.dm index 488cf1ed79cc..a1f028a79f30 100644 --- a/code/game/machinery/bio-dome_floodlights.dm +++ b/code/game/machinery/bio-dome_floodlights.dm @@ -60,9 +60,9 @@ spawn(rand(0,50)) if(F.is_lit) //Shut it down - F.SetLuminosity(0) + F.set_light(0) else - F.SetLuminosity(F.lum_value) + F.set_light(F.lum_value) F.is_lit = !(F.is_lit) F.update_icon() return 0 @@ -101,7 +101,6 @@ if(fswitch?.floodlist) fswitch.floodlist -= src fswitch = null - SetLuminosity(0) return ..() /obj/structure/machinery/hydro_floodlight/update_icon() @@ -130,7 +129,7 @@ user.visible_message(SPAN_NOTICE("[user] finishes welding [src]'s damage."), \ SPAN_NOTICE("You finish welding [src]'s damage.")) if(is_lit) - SetLuminosity(lum_value) + set_light(lum_value) update_icon() return 1 else @@ -161,7 +160,7 @@ if(do_after(user, 50, INTERRUPT_ALL, BUSY_ICON_HOSTILE) && !damaged) //Not when it's already damaged. if(!src) return 0 damaged = 1 - SetLuminosity(0) + set_light(0) user.visible_message(SPAN_DANGER("[user] slashes up [src]!"), SPAN_DANGER("You slash up [src]!")) playsound(src, 'sound/weapons/blade1.ogg', 25, 1) diff --git a/code/game/machinery/bots/bots.dm b/code/game/machinery/bots/bots.dm index aff1efa3de66..b7bd61337ee4 100644 --- a/code/game/machinery/bots/bots.dm +++ b/code/game/machinery/bots/bots.dm @@ -3,7 +3,8 @@ /obj/structure/machinery/bot icon = 'icons/obj/structures/machinery/aibots.dmi' layer = MOB_LAYER - luminosity = 3 + light_system = MOVABLE_LIGHT + light_range = 3 use_power = USE_POWER_NONE var/obj/item/card/id/botcard // the ID card that the bot "holds" var/on = 1 @@ -15,6 +16,12 @@ var/open = 0//Maint panel var/locked = 1 +/obj/structure/machinery/bot/Initialize(mapload, ...) + . = ..() + + if(light_range) + set_light_on(TRUE) + /obj/structure/machinery/bot/Destroy() QDEL_NULL(botcard) . = ..() @@ -24,12 +31,12 @@ if(stat) return 0 on = 1 - SetLuminosity(initial(luminosity)) + set_light(initial(luminosity)) return 1 /obj/structure/machinery/bot/proc/turn_off() on = 0 - SetLuminosity(0) + set_light(0) /obj/structure/machinery/bot/proc/explode() qdel(src) @@ -38,10 +45,6 @@ if(health <= 0) explode() -/obj/structure/machinery/bot/Destroy() - SetLuminosity(0) - . = ..() - /obj/structure/machinery/bot/get_examine_text(mob/user) . = ..() if(health < maxhealth) diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 20208a573c61..4d17e4a08803 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -79,7 +79,7 @@ network = list() cameranet.removeCamera(src) stat |= EMPED - SetLuminosity(0) + set_light(0) triggerCameraAlarm() spawn(900) network = previous_network diff --git a/code/game/machinery/computer/almayer_control.dm b/code/game/machinery/computer/almayer_control.dm index 7d63a2e8c3af..012c1d9eea4a 100644 --- a/code/game/machinery/computer/almayer_control.dm +++ b/code/game/machinery/computer/almayer_control.dm @@ -11,11 +11,11 @@ /// requesting a distress beacon COOLDOWN_DECLARE(cooldown_request) /// requesting evac - COOLDOWN_DECLARE(cooldown_destruct) + COOLDOWN_DECLARE(cooldown_destruct) /// messaging HC (admins) COOLDOWN_DECLARE(cooldown_central) /// making a ship announcement - COOLDOWN_DECLARE(cooldown_message) + COOLDOWN_DECLARE(cooldown_message) var/list/messagetitle = list() var/list/messagetext = list() @@ -230,7 +230,7 @@ for(var/client/admin_client as anything in GLOB.admins) if((R_ADMIN|R_MOD) & admin_client.admin_holder.rights) admin_client << 'sound/effects/sos-morse-code.ogg' - message_admins("[key_name(usr)] has requested a Distress Beacon! [CC_MARK(usr)] (SEND) (DENY) [ADMIN_JMP_USER(usr)] [CC_REPLY(usr)]") + SSticker.mode.request_ert(usr) to_chat(usr, SPAN_NOTICE("A distress beacon request has been sent to USCM Central Command.")) COOLDOWN_START(src, cooldown_request, COOLDOWN_COMM_REQUEST) diff --git a/code/game/machinery/computer/area_air_control.dm b/code/game/machinery/computer/area_air_control.dm index e3f16a988c86..22f4211aa8ee 100644 --- a/code/game/machinery/computer/area_air_control.dm +++ b/code/game/machinery/computer/area_air_control.dm @@ -139,14 +139,10 @@ var/turf/T_src = get_turf(src) if(!T_src.loc) return 0 var/area/A_src = T_src.loc - if (A_src.master) - A_src = A_src.master var/turf/T_scrub = get_turf(scrubber) if(!T_scrub.loc) return 0 var/area/A_scrub = T_scrub.loc - if (A_scrub.master) - A_scrub = A_scrub.master if(A_scrub != A_src) return 0 @@ -160,14 +156,11 @@ var/turf/T = get_turf(src) if(!T.loc) return - var/area/A = T.loc - if (A.master) - A = A.master for(var/obj/structure/machinery/portable_atmospherics/powered/scrubber/huge/scrubber in machines ) var/turf/T2 = get_turf(scrubber) if(T2 && T2.loc) - var/area/A2 = T2.loc - if(istype(A2) && A2.master && A2.master == A ) + var/area/A = T2.loc + if(istype(A) && A) connectedscrubbers += scrubber found = 1 diff --git a/code/game/machinery/computer/camera_console.dm b/code/game/machinery/computer/camera_console.dm index d4feca457f4a..281c548227b0 100644 --- a/code/game/machinery/computer/camera_console.dm +++ b/code/game/machinery/computer/camera_console.dm @@ -17,12 +17,12 @@ var/atom/movable/screen/map_view/cam_screen var/atom/movable/screen/background/cam_background - /// All turfs within range of the currently active camera - var/list/range_turfs = list() - var/colony_camera_mapload = TRUE var/admin_console = FALSE + /// All the plane masters that need to be applied. + var/list/cam_plane_masters + /obj/structure/machinery/computer/cameras/Initialize(mapload) . = ..() // Map name has to start and end with an A-Z character, @@ -33,6 +33,16 @@ if(colony_camera_mapload && mapload && is_ground_level(z)) network = list(CAMERA_NET_COLONY) + cam_plane_masters = list() + for(var/plane in subtypesof(/atom/movable/screen/plane_master) - /atom/movable/screen/plane_master/blackness) + var/atom/movable/screen/plane_master/instance = new plane() + instance.assigned_map = map_name + instance.del_on_map_removal = FALSE + if(instance.blend_mode_override) + instance.blend_mode = instance.blend_mode_override + instance.screen_loc = "[map_name]:CENTER" + cam_plane_masters += instance + // Initialize map objects cam_screen = new cam_screen.icon = null @@ -51,7 +61,6 @@ qdel(cam_screen) QDEL_NULL(cam_background) qdel(cam_background) - range_turfs = null last_camera_turf = null concurrent_users = null return ..() @@ -74,6 +83,10 @@ if(inoperable()) return UI_DISABLED +//Closes UI if you move away from console. +/obj/structure/machinery/computer/cameras/ui_state(mob/user) + return GLOB.not_incapacitated_and_adjacent_strict_state + /obj/structure/machinery/computer/cameras/tgui_interact(mob/user, datum/tgui/ui) // Update UI ui = SStgui.try_update_ui(user, src, ui) @@ -94,6 +107,8 @@ // Register map objects user.client.register_map_obj(cam_screen) user.client.register_map_obj(cam_background) + for(var/plane in cam_plane_masters) + user.client.register_map_obj(plane) // Open UI ui = new(user, src, "CameraConsole", name) ui.open() @@ -176,13 +191,8 @@ var/list/visible_things = current.isXRay() ? range(current.view_range, cam_location) : view(current.view_range, cam_location) var/list/visible_turfs = list() - range_turfs.Cut() - var/area/A for(var/turf/visible_turf in visible_things) - range_turfs += visible_turf - A = visible_turf.loc - if(!A.lighting_use_dynamic || visible_turf.lighting_lumcount >= 1) - visible_turfs += visible_turf + visible_turfs += visible_turf var/list/bbox = get_bbox_of_atoms(visible_turfs) var/size_x = bbox[3] - bbox[1] + 1 @@ -192,18 +202,6 @@ cam_background.icon_state = "clear" cam_background.fill_rect(1, 1, size_x, size_y) - START_PROCESSING(SSfastobj, src) // fastobj to somewhat keep pace with lighting updates - -/obj/structure/machinery/computer/cameras/process() - if(current) - var/list/visible_turfs = list() - var/area/A - for(var/turf/visible_turf as anything in range_turfs) - A = visible_turf.loc - if(!A.lighting_use_dynamic || visible_turf.lighting_lumcount >= 1) - visible_turfs += visible_turf - cam_screen.vis_contents = visible_turfs - /obj/structure/machinery/computer/cameras/ui_close(mob/user) var/user_ref = WEAKREF(user) var/is_living = isliving(user) @@ -215,10 +213,8 @@ if(length(concurrent_users) == 0 && is_living) current = null last_camera_turf = null - range_turfs = list() if(use_power) update_use_power(USE_POWER_IDLE) - STOP_PROCESSING(SSfastobj, src) user.unset_interaction() /obj/structure/machinery/computer/cameras/proc/show_camera_static() diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index edc39faf3ddc..f7ea31fba36a 100644 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -200,7 +200,7 @@ for(var/client/C in GLOB.admins) if((R_ADMIN|R_MOD) & C.admin_holder.rights) C << 'sound/effects/sos-morse-code.ogg' - message_admins("[key_name(usr)] has requested a Distress Beacon! [CC_MARK(usr)] (SEND) (DENY) [ADMIN_JMP_USER(usr)] [CC_REPLY(usr)]") + SSticker.mode.request_ert(usr) to_chat(usr, SPAN_NOTICE("A distress beacon request has been sent to USCM Central Command.")) cooldown_request = world.time diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index ed7335ea8778..eb9aed4f71e4 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -185,7 +185,6 @@ GLOBAL_LIST_INIT(frozen_items, list(SQUAD_MARINE_1 = list(), SQUAD_MARINE_2 = li flags_atom |= USES_HEARING /obj/structure/machinery/cryopod/Destroy() - SetLuminosity(0) QDEL_NULL(occupant) QDEL_NULL(announce) . = ..() @@ -368,7 +367,7 @@ GLOBAL_LIST_INIT(frozen_items, list(SQUAD_MARINE_1 = list(), SQUAD_MARINE_2 = li qdel(G) icon_state = "body_scanner_open" - SetLuminosity(0) + set_light(0) if(occupant.key) occupant.ghostize(0) @@ -509,7 +508,7 @@ GLOBAL_LIST_INIT(frozen_items, list(SQUAD_MARINE_1 = list(), SQUAD_MARINE_2 = li mob.forceMove(src) occupant = mob icon_state = "body_scanner_closed" - SetLuminosity(2) + set_light(2) time_entered = world.time start_processing() @@ -532,7 +531,7 @@ GLOBAL_LIST_INIT(frozen_items, list(SQUAD_MARINE_1 = list(), SQUAD_MARINE_2 = li occupant = null stop_processing() icon_state = "body_scanner_open" - SetLuminosity(0) + set_light(0) playsound(src, 'sound/machines/pod_open.ogg', 30) #ifdef OBJECTS_PROXY_SPEECH diff --git a/code/game/machinery/doors/alarmlock.dm b/code/game/machinery/doors/alarmlock.dm index b6b9a6133a84..c55250fdc04d 100644 --- a/code/game/machinery/doors/alarmlock.dm +++ b/code/game/machinery/doors/alarmlock.dm @@ -25,6 +25,7 @@ return ..() /obj/structure/machinery/door/airlock/alarmlock/receive_signal(datum/signal/signal) +/* ..() if(inoperable()) return @@ -32,10 +33,6 @@ var/alarm_area = signal.data["zone"] var/alert = signal.data["alert"] - var/area/our_area = get_area(src) - if (our_area.master) - our_area = our_area.master - if(alarm_area == our_area.name) switch(alert) if("severe") @@ -44,3 +41,4 @@ if("minor", "clear") autoclose = 0 open() +*/ diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index b54658b24245..578ef368f5d9 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -49,7 +49,7 @@ /obj/structure/machinery/door/Destroy() . = ..() if(filler && width > 1) - filler.SetOpacity(0)// Ehh... let's hope there are no walls there. Must fix this + filler.set_opacity(0)// Ehh... let's hope there are no walls there. Must fix this filler = null density = FALSE @@ -64,12 +64,12 @@ bound_width = width * world.icon_size bound_height = world.icon_size filler = get_step(src,EAST) - filler.SetOpacity(opacity) + filler.set_opacity(opacity) else bound_width = world.icon_size bound_height = width * world.icon_size filler = get_step(src,NORTH) - filler.SetOpacity(opacity) + filler.set_opacity(opacity) //process() //return @@ -222,9 +222,9 @@ operating = TRUE do_animate("opening") icon_state = "door0" - SetOpacity(FALSE) + set_opacity(FALSE) if(filler) - filler.SetOpacity(opacity) + filler.set_opacity(opacity) addtimer(CALLBACK(src, PROC_REF(finish_open)), openspeed) return TRUE @@ -255,9 +255,9 @@ /obj/structure/machinery/door/proc/finish_close() update_icon() if(visible && !glass) - SetOpacity(TRUE) + set_opacity(TRUE) if(filler) - filler.SetOpacity(opacity) + filler.set_opacity(opacity) operating = FALSE /obj/structure/machinery/door/proc/requiresID() @@ -279,15 +279,15 @@ if(dir in list(EAST, WEST)) bound_width = width * world.icon_size bound_height = world.icon_size - filler.SetOpacity(0) + filler.set_opacity(0) filler = (get_step(src,EAST)) //Find new turf - filler.SetOpacity(opacity) + filler.set_opacity(opacity) else bound_width = world.icon_size bound_height = width * world.icon_size - filler.SetOpacity(0) + filler.set_opacity(0) filler = (get_step(src,NORTH)) //Find new turf - filler.SetOpacity(opacity) + filler.set_opacity(opacity) /obj/structure/machinery/door/morgue diff --git a/code/game/machinery/doors/multi_tile.dm b/code/game/machinery/doors/multi_tile.dm index 2a49b8696a9f..f58d50f3a8cf 100644 --- a/code/game/machinery/doors/multi_tile.dm +++ b/code/game/machinery/doors/multi_tile.dm @@ -235,11 +235,11 @@ //We have to find these again since these doors are used on shuttles a lot so the turfs changes /obj/structure/machinery/door/airlock/multi_tile/almayer/proc/update_filler_turfs() for(var/turf/T in multi_filler) - T.SetOpacity(null) + T.set_opacity(null) multi_filler = list() for(var/turf/T in get_filler_turfs()) - T.SetOpacity(opacity) + T.set_opacity(opacity) multi_filler += list(T) /obj/structure/machinery/door/airlock/multi_tile/proc/get_filler_turfs() diff --git a/code/game/machinery/doors/poddoor.dm b/code/game/machinery/doors/poddoor.dm index da6137e5e8cb..b2d836ee476f 100644 --- a/code/game/machinery/doors/poddoor.dm +++ b/code/game/machinery/doors/poddoor.dm @@ -16,9 +16,9 @@ /obj/structure/machinery/door/poddoor/Initialize() . = ..() if(density) - SetOpacity(1) + set_opacity(1) else - SetOpacity(0) + set_opacity(0) update_icon() /obj/structure/machinery/door/poddoor/update_icon() @@ -42,7 +42,7 @@ operating = 1 flick("[base_icon_state]c0", src) icon_state = "[base_icon_state]0" - SetOpacity(0) + set_opacity(0) sleep(15) density = FALSE operating = 0 @@ -84,7 +84,7 @@ playsound(loc, 'sound/machines/blastdoor.ogg', 20, 0) flick("[base_icon_state]c0", src) icon_state = "[base_icon_state]0" - SetOpacity(0) + set_opacity(0) addtimer(CALLBACK(src, PROC_REF(finish_open)), openspeed) return TRUE @@ -102,7 +102,7 @@ flick("[base_icon_state]c1", src) icon_state = "[base_icon_state]1" density = TRUE - SetOpacity(initial(opacity)) + set_opacity(initial(opacity)) addtimer(CALLBACK(src, PROC_REF(finish_close)), openspeed) return @@ -123,13 +123,13 @@ /obj/structure/machinery/door/poddoor/two_tile/proc/start_opening() flick("[base_icon_state]c0", src) icon_state = "[base_icon_state]0" - SetOpacity(0) - f1.SetOpacity(0) - f2.SetOpacity(0) + set_opacity(0) + f1.set_opacity(0) + f2.set_opacity(0) /obj/structure/machinery/door/poddoor/two_tile/four_tile/start_opening() - f3.SetOpacity(0) - f4.SetOpacity(0) + f3.set_opacity(0) + f4.set_opacity(0) ..() /obj/structure/machinery/door/poddoor/two_tile/proc/open_fully() @@ -169,14 +169,14 @@ ..() /obj/structure/machinery/door/poddoor/two_tile/proc/close_fully() - SetOpacity(initial(opacity)) - f1.SetOpacity(initial(opacity)) - f2.SetOpacity(initial(opacity)) + set_opacity(initial(opacity)) + f1.set_opacity(initial(opacity)) + f2.set_opacity(initial(opacity)) operating = 0 /obj/structure/machinery/door/poddoor/two_tile/four_tile/close_fully() - f3.SetOpacity(initial(opacity)) - f4.SetOpacity(initial(opacity)) + f3.set_opacity(initial(opacity)) + f4.set_opacity(initial(opacity)) ..() /obj/structure/machinery/door/poddoor/two_tile @@ -194,8 +194,8 @@ f2 = new/obj/structure/machinery/door/poddoor/filler_object (get_step(src,dir)) f1.density = density f2.density = density - f1.SetOpacity(opacity) - f2.SetOpacity(opacity) + f1.set_opacity(opacity) + f2.set_opacity(opacity) /obj/structure/machinery/door/poddoor/two_tile/Destroy() QDEL_NULL(f1) @@ -223,8 +223,8 @@ f4 = new/obj/structure/machinery/door/poddoor/filler_object (get_step(f3,dir)) f3.density = density f4.density = density - f3.SetOpacity(opacity) - f4.SetOpacity(opacity) + f3.set_opacity(opacity) + f4.set_opacity(opacity) /obj/structure/machinery/door/poddoor/two_tile/four_tile/Destroy() QDEL_NULL(f3) diff --git a/code/game/machinery/doors/railing.dm b/code/game/machinery/doors/railing.dm index 8449d5d52256..c86adb2e970b 100644 --- a/code/game/machinery/doors/railing.dm +++ b/code/game/machinery/doors/railing.dm @@ -22,7 +22,7 @@ if(density)//Allows preset-open to work layer = closed_layer - SetOpacity(initial(opacity)) + set_opacity(initial(opacity)) /obj/structure/machinery/door/poddoor/railing/update_icon() if(density) diff --git a/code/game/machinery/doors/runed_sandstone.dm b/code/game/machinery/doors/runed_sandstone.dm index d67398baa305..4bf66dfdc8d8 100644 --- a/code/game/machinery/doors/runed_sandstone.dm +++ b/code/game/machinery/doors/runed_sandstone.dm @@ -100,7 +100,7 @@ operating = TRUE do_animate("opening") icon_state = "door0" - SetOpacity(FALSE) + set_opacity(0) addtimer(CALLBACK(src, PROC_REF(finish_open)), openspeed) return @@ -109,9 +109,9 @@ layer = open_layer density = FALSE update_icon() - SetOpacity(0) + set_opacity(0) if(filler) - filler.SetOpacity(opacity) + filler.set_opacity(opacity) if(operating) operating = FALSE @@ -130,7 +130,7 @@ operating = TRUE density = TRUE - SetOpacity(TRUE) + set_opacity(1) layer = closed_layer do_animate("closing") diff --git a/code/game/machinery/doors/shutters.dm b/code/game/machinery/doors/shutters.dm index 39ecbd806e64..da904f255c40 100644 --- a/code/game/machinery/doors/shutters.dm +++ b/code/game/machinery/doors/shutters.dm @@ -26,7 +26,7 @@ icon_state = "[base_icon_state]0" sleep(15) density = FALSE - SetOpacity(0) + set_opacity(0) operating = 0 return return @@ -46,7 +46,7 @@ /obj/structure/machinery/door/poddoor/shutters/finish_open() density = FALSE layer = open_layer - SetOpacity(0) + set_opacity(0) if(operating) //emag again operating = FALSE @@ -63,7 +63,7 @@ layer = closed_layer density = TRUE if(visible) - SetOpacity(1) + set_opacity(1) playsound(loc, 'sound/machines/blastdoor.ogg', 25) addtimer(CALLBACK(src, PROC_REF(finish_close)), openspeed) diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm index 556eab541363..75d0de56dec0 100644 --- a/code/game/machinery/flasher.dm +++ b/code/game/machinery/flasher.dm @@ -26,10 +26,10 @@ ..() if ( !(stat & NOPOWER) ) icon_state = "[base_state]1" -// src.sd_SetLuminosity(2) +// src.sd_set_light(2) else icon_state = "[base_state]1-p" -// src.sd_SetLuminosity(0) +// src.sd_set_light(0) //Don't want to render prison breaks impossible /obj/structure/machinery/flasher/attackby(obj/item/W as obj, mob/user as mob) diff --git a/code/game/machinery/floodlight.dm b/code/game/machinery/floodlight.dm index 8eec83ed7b70..5f6cd02a4bf8 100644 --- a/code/game/machinery/floodlight.dm +++ b/code/game/machinery/floodlight.dm @@ -6,38 +6,40 @@ icon_state = "flood00" density = TRUE anchored = TRUE - var/on = 0 var/obj/item/cell/cell = null var/use = 0 var/unlocked = 0 var/open = 0 - var/brightness_on = 7 //can't remember what the maxed out value is + light_power = 2 unslashable = TRUE unacidable = TRUE + var/on_light_range = 6 + /obj/structure/machinery/floodlight/Initialize(mapload, ...) . = ..() cell = new /obj/item/cell(src) + if(light_on) + set_light(on_light_range) /obj/structure/machinery/floodlight/Destroy() QDEL_NULL(cell) - SetLuminosity(0) return ..() +/obj/structure/machinery/floodlight/turn_light(mob/user, toggle_on) + . = ..() + if(. == NO_LIGHT_STATE_CHANGE) + return + + if(toggle_on) + set_light(on_light_range) + else + set_light(0) + + /obj/structure/machinery/floodlight/proc/updateicon() - icon_state = "flood[open ? "o" : ""][open && cell ? "b" : ""]0[on]" -/* -/obj/structure/machinery/floodlight/process() - if(on && cell) - if(cell.charge >= use) - cell.use(use) - else - on = 0 - updateicon() - SetLuminosity(0) - src.visible_message(SPAN_WARNING("[src] shuts down due to lack of power!")) - return -*/ + icon_state = "flood[open ? "o" : ""][open && cell ? "b" : ""]0[light_on]" + /obj/structure/machinery/floodlight/attack_hand(mob/user as mob) if(open && cell) if(ishuman(user)) @@ -55,10 +57,9 @@ updateicon() return - if(on) - on = 0 - to_chat(user, SPAN_NOTICE(" You turn off the light.")) - SetLuminosity(0) + if(light_on) + to_chat(user, SPAN_NOTICE("You turn off the light.")) + turn_light(user, toggle_on = FALSE) unslashable = TRUE unacidable = TRUE else @@ -66,9 +67,8 @@ return if(cell.charge <= 0) return - on = 1 - to_chat(user, SPAN_NOTICE(" You turn on the light.")) - SetLuminosity(brightness_on) + to_chat(user, SPAN_NOTICE("You turn on the light.")) + turn_light(user, toggle_on = TRUE) unacidable = FALSE updateicon() @@ -121,9 +121,8 @@ name = "Landing Light" desc = "A powerful light stationed near landing zones to provide better visibility." icon_state = "flood01" - on = 1 + light_on = TRUE in_use = 1 - luminosity = 6 use_power = USE_POWER_NONE /obj/structure/machinery/floodlight/landing/attack_hand() diff --git a/code/game/machinery/groundmap_geothermal.dm b/code/game/machinery/groundmap_geothermal.dm index 4be9c53f0094..808c717e8891 100644 --- a/code/game/machinery/groundmap_geothermal.dm +++ b/code/game/machinery/groundmap_geothermal.dm @@ -269,9 +269,9 @@ F.is_lit = !F.is_lit if(!F.damaged) if(F.is_lit) //Shut it down - F.SetLuminosity(F.lum_value) + F.set_light(F.lum_value) else - F.SetLuminosity(0) + F.set_light(0) F.update_icon() return 0 @@ -315,7 +315,6 @@ health = 150 /obj/structure/machinery/colony_floodlight/Destroy() - SetLuminosity(0) if(fswitch) fswitch.floodlist -= src fswitch = null @@ -362,7 +361,7 @@ user.visible_message(SPAN_NOTICE("[user] screws [src]'s maintenance hatch closed."), \ SPAN_NOTICE("You screw [src]'s maintenance hatch closed.")) if(is_lit) - SetLuminosity(lum_value) + set_light(lum_value) update_icon() return TRUE diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 1acb47370d96..2f8f113ddd23 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -142,8 +142,8 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them. hologram.anchored = TRUE//So space wind cannot drag it. hologram.name = "[A.name] (Hologram)"//If someone decides to right click. - hologram.SetLuminosity(2) //hologram lighting - SetLuminosity(2) //pad lighting + hologram.set_light(2) //hologram lighting + set_light(2) //pad lighting icon_state = "holopad1" A.holo = src master = A//AI is the master. @@ -151,14 +151,14 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ return 1 /obj/structure/machinery/hologram/holopad/clear_holo() -// hologram.SetLuminosity(0)//Clear lighting. //handled by the lighting controller when its ower is deleted +// hologram.set_light(0)//Clear lighting. //handled by the lighting controller when its ower is deleted if(hologram) qdel(hologram)//Get rid of hologram. hologram = null if(master.holo == src) master.holo = null master = null//Null the master, since no-one is using it now. - SetLuminosity(0) //pad lighting (hologram lighting will be handled automatically since its owner was deleted) + set_light(0) //pad lighting (hologram lighting will be handled automatically since its owner was deleted) icon_state = "holopad0" use_power = USE_POWER_IDLE//Passive power usage. return 1 @@ -175,7 +175,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ var/area/holo_area = get_area(src) var/area/eye_area = get_area(master.eyeobj) - if(eye_area in holo_area.master.related) + if(eye_area == holo_area) return 1 clear_holo()//If not, we want to get rid of the hologram. diff --git a/code/game/machinery/igniter.dm b/code/game/machinery/igniter.dm index 5564ed0220a7..33f75c50e341 100644 --- a/code/game/machinery/igniter.dm +++ b/code/game/machinery/igniter.dm @@ -60,10 +60,10 @@ if ( !(stat & NOPOWER) && disable == 0 ) icon_state = "[base_state]" -// src.sd_SetLuminosity(2) +// src.sd_set_light(2) else icon_state = "[base_state]-p" -// src.sd_SetLuminosity(0) +// src.sd_set_light(0) /obj/structure/machinery/sparker/attackby(obj/item/W as obj, mob/user as mob) if (HAS_TRAIT(W, TRAIT_TOOL_SCREWDRIVER)) diff --git a/code/game/machinery/kitchen/smartfridge.dm b/code/game/machinery/kitchen/smartfridge.dm index ecf63a2a7902..f52350aa8db3 100644 --- a/code/game/machinery/kitchen/smartfridge.dm +++ b/code/game/machinery/kitchen/smartfridge.dm @@ -520,7 +520,7 @@ return 0 /obj/structure/machinery/smartfridge/chemistry/antag - req_one_access = list(ACCESS_ILLEGAL_PIRATE) + req_one_access = list(ACCESS_ILLEGAL_PIRATE, ACCESS_UPP_GENERAL, ACCESS_CLF_GENERAL) /obj/structure/machinery/smartfridge/chemistry/virology name = "\improper Smart Virus Storage" diff --git a/code/game/machinery/lightswitch.dm b/code/game/machinery/lightswitch.dm index ae57f27f2162..66eb0386713f 100644 --- a/code/game/machinery/lightswitch.dm +++ b/code/game/machinery/lightswitch.dm @@ -10,7 +10,6 @@ var/on = 1 var/area/area = null var/otherarea = null - // luminosity = 1 /obj/structure/machinery/light_switch/Initialize() . = ..() @@ -43,15 +42,14 @@ /obj/structure/machinery/light_switch/attack_hand(mob/user) on = !on - for(var/area/A in area.master.related) - A.lightswitch = on - A.updateicon() + area.lightswitch = on + area.updateicon() - for(var/obj/structure/machinery/light_switch/L in A) - L.on = on - L.updateicon() + for(var/obj/structure/machinery/light_switch/L in area) + L.on = on + L.updateicon() - area.master.power_change() + area.power_change() /obj/structure/machinery/light_switch/power_change() diff --git a/code/game/machinery/mining.dm b/code/game/machinery/mining.dm index 4f663c5fbca7..0662817174fc 100644 --- a/code/game/machinery/mining.dm +++ b/code/game/machinery/mining.dm @@ -26,4 +26,4 @@ icon_state = "furnace" density = TRUE anchored = TRUE - luminosity = 3 + light_range = 3 diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index ad8f5a189267..89c9e9277f4c 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -93,7 +93,6 @@ var/list/obj/structure/machinery/newscaster/allCasters = list() //Global list th var/c_locked=0; //Will our new channel be locked to public submissions? var/hitstaken = 0 //Death at 3 hits from an item with force>=15 var/datum/feed_channel/viewing_channel = null - luminosity = 0 anchored = TRUE @@ -111,7 +110,6 @@ var/list/obj/structure/machinery/newscaster/allCasters = list() //Global list th /obj/structure/machinery/newscaster/security_unit/Destroy() allCasters -= src - SetLuminosity(0) return ..() /obj/structure/machinery/newscaster/update_icon() diff --git a/code/game/machinery/vending/vendor_types/antag/antag_clothing.dm b/code/game/machinery/vending/vendor_types/antag/antag_clothing.dm index df047d6cf5fb..85210e1aaa3f 100644 --- a/code/game/machinery/vending/vendor_types/antag/antag_clothing.dm +++ b/code/game/machinery/vending/vendor_types/antag/antag_clothing.dm @@ -5,7 +5,8 @@ name = "\improper Suspicious Automated Equipment Rack" desc = "While similar in function to ColMarTech automated racks, this one is clearly not of USCM origin. Contains various equipment." icon_state = "antag_clothing" - req_access = list(ACCESS_ILLEGAL_PIRATE) + req_one_access = list(ACCESS_ILLEGAL_PIRATE, ACCESS_UPP_GENERAL, ACCESS_CLF_GENERAL) + req_access = null listed_products = list() diff --git a/code/game/machinery/vending/vendor_types/antag/antag_gear.dm b/code/game/machinery/vending/vendor_types/antag/antag_gear.dm index 84cd4a8bcec0..3e847b4919de 100644 --- a/code/game/machinery/vending/vendor_types/antag/antag_gear.dm +++ b/code/game/machinery/vending/vendor_types/antag/antag_gear.dm @@ -5,7 +5,8 @@ desc = "While similar in function to ColMarTech automated racks, this one is clearly not of USCM origin. Contains various gear." icon_state = "gear" - req_access = list(ACCESS_ILLEGAL_PIRATE) + req_one_access = list(ACCESS_ILLEGAL_PIRATE, ACCESS_UPP_GENERAL, ACCESS_CLF_GENERAL) + req_access = null listed_products = list() /obj/structure/machinery/cm_vending/gear/antag/Initialize() diff --git a/code/game/machinery/vending/vendor_types/antag/antag_guns_snowflake.dm b/code/game/machinery/vending/vendor_types/antag/antag_guns_snowflake.dm index a2cd462cd6a2..73a2c7346a4c 100644 --- a/code/game/machinery/vending/vendor_types/antag/antag_guns_snowflake.dm +++ b/code/game/machinery/vending/vendor_types/antag/antag_guns_snowflake.dm @@ -4,7 +4,8 @@ name = "\improper Suspicious Automated Guns Rack" desc = "While similar in function to ColMarTech automated racks, this one is clearly not of USCM origin. Contains various weapons, ammunition and explosives." icon_state = "antag_guns" - req_access = list(ACCESS_ILLEGAL_PIRATE) + req_one_access = list(ACCESS_ILLEGAL_PIRATE, ACCESS_UPP_GENERAL, ACCESS_CLF_GENERAL) + req_access = null use_snowflake_points = TRUE diff --git a/code/game/machinery/vending/vendor_types/antag/antag_guns_sorted.dm b/code/game/machinery/vending/vendor_types/antag/antag_guns_sorted.dm index 6267db911b09..ab319f1eca1d 100644 --- a/code/game/machinery/vending/vendor_types/antag/antag_guns_sorted.dm +++ b/code/game/machinery/vending/vendor_types/antag/antag_guns_sorted.dm @@ -4,7 +4,8 @@ name = "\improper Suspicious Automated Guns Rack" desc = "While similar in function to ColMarTech automated racks, this one is clearly not of USCM origin. Contains various weapons." icon_state = "antag_guns" - req_access = list(ACCESS_ILLEGAL_PIRATE) + req_one_access = list(ACCESS_ILLEGAL_PIRATE, ACCESS_UPP_GENERAL, ACCESS_CLF_GENERAL) + req_access = null listed_products = list() /obj/structure/machinery/cm_vending/sorted/cargo_guns/antag_guns/Initialize() diff --git a/code/game/machinery/vending/vendor_types/crew/vehicle_crew.dm b/code/game/machinery/vending/vendor_types/crew/vehicle_crew.dm index fb9b662be1bc..891a2a907b39 100644 --- a/code/game/machinery/vending/vendor_types/crew/vehicle_crew.dm +++ b/code/game/machinery/vending/vendor_types/crew/vehicle_crew.dm @@ -306,7 +306,7 @@ GLOBAL_LIST_INIT(cm_vending_clothing_vehicle_crew, list( list("Gloves", 0, /obj/item/clothing/gloves/yellow, MARINE_CAN_BUY_GLOVES, VENDOR_ITEM_MANDATORY), list("Tanker Armor", 0, /obj/item/clothing/suit/storage/marine/tanker, MARINE_CAN_BUY_ARMOR, VENDOR_ITEM_MANDATORY), list("M50 Tanker Helmet", 0, /obj/item/clothing/head/helmet/marine/tech/tanker, MARINE_CAN_BUY_HELMET, VENDOR_ITEM_MANDATORY), - list("SensorMate HUD", 0, /obj/item/clothing/glasses/hud/sensor, MARINE_CAN_BUY_GLASSES, VENDOR_ITEM_MANDATORY), + list("Medical Helmet Optic", 0, /obj/item/device/helmet_visor/medical, MARINE_CAN_BUY_GLASSES, VENDOR_ITEM_MANDATORY), list("Welding Kit", 0, /obj/item/tool/weldpack, MARINE_CAN_BUY_BACKPACK, VENDOR_ITEM_MANDATORY), list("MRE", 0, /obj/item/storage/box/MRE, MARINE_CAN_BUY_MRE, VENDOR_ITEM_MANDATORY), diff --git a/code/game/machinery/vending/vendor_types/engineering.dm b/code/game/machinery/vending/vendor_types/engineering.dm index 2fe6962e93bb..1983365bd661 100644 --- a/code/game/machinery/vending/vendor_types/engineering.dm +++ b/code/game/machinery/vending/vendor_types/engineering.dm @@ -94,7 +94,8 @@ ) /obj/structure/machinery/cm_vending/sorted/tech/tool_storage/antag - req_access = list(ACCESS_ILLEGAL_PIRATE) + req_one_access = list(ACCESS_ILLEGAL_PIRATE, ACCESS_UPP_GENERAL, ACCESS_CLF_GENERAL) + req_access = null /obj/structure/machinery/cm_vending/sorted/tech/electronics_storage name = "\improper Electronics Vendor" @@ -117,7 +118,8 @@ ) /obj/structure/machinery/cm_vending/sorted/tech/electronics_storage/antag - req_access = list(ACCESS_ILLEGAL_PIRATE) + req_one_access = list(ACCESS_ILLEGAL_PIRATE, ACCESS_UPP_GENERAL, ACCESS_CLF_GENERAL) + req_access = null /obj/structure/machinery/cm_vending/sorted/tech/comp_storage name = "\improper Component Storage Machine" @@ -146,7 +148,8 @@ ) /obj/structure/machinery/cm_vending/sorted/tech/comp_storage/antag - req_access = list(ACCESS_ILLEGAL_PIRATE) + req_one_access = list(ACCESS_ILLEGAL_PIRATE, ACCESS_UPP_GENERAL, ACCESS_CLF_GENERAL) + req_access = null //------COLONY-SPECIFIC VENDORS------- diff --git a/code/game/machinery/vending/vendor_types/intelligence_officer.dm b/code/game/machinery/vending/vendor_types/intelligence_officer.dm index a2afe897b495..ab38fcb1dfeb 100644 --- a/code/game/machinery/vending/vendor_types/intelligence_officer.dm +++ b/code/game/machinery/vending/vendor_types/intelligence_officer.dm @@ -14,8 +14,9 @@ GLOBAL_LIST_INIT(cm_vending_gear_intelligence_officer, list( list("Fulton Recovery Device", 10, /obj/item/stack/fulton, null, VENDOR_ITEM_REGULAR), list("Motion Detector", 15, /obj/item/device/motiondetector, null, VENDOR_ITEM_RECOMMENDED), list("Plastic Explosive", 10, /obj/item/explosive/plastic, null, VENDOR_ITEM_REGULAR), + list("Welding Visor", 5, /obj/item/device/helmet_visor/welding_visor, null, VENDOR_ITEM_REGULAR), + list("Medical Helmet Optic", 5, /obj/item/device/helmet_visor/medical, null, VENDOR_ITEM_REGULAR), list("Welding Goggles", 5, /obj/item/clothing/glasses/welding, null, VENDOR_ITEM_REGULAR), - list("Sensor Medical HUD", 5, /obj/item/clothing/glasses/hud/sensor, null, VENDOR_ITEM_REGULAR), list("POUCHES", 0, null, null, null), list("Large Magazine Pouch", 10, /obj/item/storage/pouch/magazine/large, null, VENDOR_ITEM_REGULAR), diff --git a/code/game/machinery/vending/vendor_types/medical.dm b/code/game/machinery/vending/vendor_types/medical.dm index 70ac7701973b..7f43f93cf8a4 100644 --- a/code/game/machinery/vending/vendor_types/medical.dm +++ b/code/game/machinery/vending/vendor_types/medical.dm @@ -227,7 +227,8 @@ /obj/structure/machinery/cm_vending/sorted/medical/antag name = "\improper Medical Equipment Vendor" desc = "A vending machine dispensing various pieces of medical equipment." - req_access = list(ACCESS_ILLEGAL_PIRATE) + req_one_access = list(ACCESS_ILLEGAL_PIRATE, ACCESS_UPP_GENERAL, ACCESS_CLF_GENERAL) + req_access = null vendor_theme = VENDOR_THEME_CLF /obj/structure/machinery/cm_vending/sorted/medical/marinemed @@ -267,7 +268,8 @@ /obj/structure/machinery/cm_vending/sorted/medical/marinemed/antag name = "\improper Basic Medical Supplies Vendor" desc = "A vending machine dispensing basic medical supplies." - req_access = list(ACCESS_ILLEGAL_PIRATE) + req_one_access = list(ACCESS_ILLEGAL_PIRATE, ACCESS_UPP_GENERAL, ACCESS_CLF_GENERAL) + req_access = null vendor_theme = VENDOR_THEME_CLF /obj/structure/machinery/cm_vending/sorted/medical/blood @@ -298,7 +300,8 @@ return /obj/structure/machinery/cm_vending/sorted/medical/blood/antag - req_access = list(ACCESS_ILLEGAL_PIRATE) + req_one_access = list(ACCESS_ILLEGAL_PIRATE, ACCESS_UPP_GENERAL, ACCESS_CLF_GENERAL) + req_access = null vendor_theme = VENDOR_THEME_CLF /obj/structure/machinery/cm_vending/sorted/medical/wall_med diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_engineer.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_engineer.dm index faff01f7f299..d43e53db4f0d 100644 --- a/code/game/machinery/vending/vendor_types/squad_prep/squad_engineer.dm +++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_engineer.dm @@ -54,7 +54,7 @@ GLOBAL_LIST_INIT(cm_vending_gear_engi, list( list("M56D Heavy Machine Gun", 24, /obj/item/storage/box/guncase/m56d, null, VENDOR_ITEM_REGULAR), list("UTILITIES", 0, null, null, null), - list("SensorMate Medical HUD", 12, /obj/item/clothing/glasses/hud/sensor, null, VENDOR_ITEM_REGULAR), + list("Medical Helmet Optic", 12, /obj/item/device/helmet_visor/medical, null, VENDOR_ITEM_REGULAR), list("Roller Bed", 5, /obj/item/roller, null, VENDOR_ITEM_REGULAR), list("Fulton Device Stack", 5, /obj/item/stack/fulton, null, VENDOR_ITEM_REGULAR), list("M3 B12 Pattern Armor", 24, /obj/item/clothing/suit/storage/marine/leader, null, VENDOR_ITEM_REGULAR), diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_leader.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_leader.dm index 9757576e6d0b..935469b13b47 100644 --- a/code/game/machinery/vending/vendor_types/squad_prep/squad_leader.dm +++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_leader.dm @@ -67,7 +67,7 @@ GLOBAL_LIST_INIT(cm_vending_gear_leader, list( list("Injector (Tricord)", 1, /obj/item/reagent_container/hypospray/autoinjector/tricord, null, VENDOR_ITEM_REGULAR), list("Health Analyzer", 4, /obj/item/device/healthanalyzer, null, VENDOR_ITEM_REGULAR), - list("SensorMate Medical HUD", 4, /obj/item/clothing/glasses/hud/sensor, null, VENDOR_ITEM_RECOMMENDED), + list("Medical Helmet Optic", 4, /obj/item/device/helmet_visor/medical, null, VENDOR_ITEM_RECOMMENDED), list("Roller Bed", 2, /obj/item/roller, null, VENDOR_ITEM_REGULAR), list("SPECIAL AMMUNITION", 0, null, null, null), diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_medic.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_medic.dm index 7c9682985298..d2ba88096131 100644 --- a/code/game/machinery/vending/vendor_types/squad_prep/squad_medic.dm +++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_medic.dm @@ -42,7 +42,6 @@ GLOBAL_LIST_INIT(cm_vending_gear_medic, list( list("MEDICAL UTILITIES", 0, null, null, null), list("Health Analyzer", 4, /obj/item/device/healthanalyzer, null, VENDOR_ITEM_REGULAR), - list("Medical HUD Glasses", 4, /obj/item/clothing/glasses/hud/health, null, VENDOR_ITEM_REGULAR), list("Roller Bed", 4, /obj/item/roller, null, VENDOR_ITEM_REGULAR), list("Stasis Bag", 6, /obj/item/bodybag/cryobag, null, VENDOR_ITEM_REGULAR), list("Pressurized Reagent Canister Pouch (EMPTY)", 3, /obj/item/storage/pouch/pressurized_reagent_canister, null, VENDOR_ITEM_REGULAR), @@ -110,7 +109,6 @@ GLOBAL_LIST_INIT(cm_vending_clothing_medic, list( list("Boots", 0, /obj/item/clothing/shoes/marine/knife, MARINE_CAN_BUY_SHOES, VENDOR_ITEM_MANDATORY), list("Uniform", 0, /obj/item/clothing/under/marine/medic, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_MANDATORY), list("Gloves", 0, /obj/item/clothing/gloves/marine, MARINE_CAN_BUY_GLOVES, VENDOR_ITEM_MANDATORY), - list("Medical HUD Glasses", 0, /obj/item/clothing/glasses/hud/health, MARINE_CAN_BUY_GLASSES, VENDOR_ITEM_MANDATORY), list("Headset", 0, /obj/item/device/radio/headset/almayer/marine, MARINE_CAN_BUY_EAR, VENDOR_ITEM_MANDATORY), list("Helmet", 0, /obj/item/clothing/head/helmet/marine/medic, MARINE_CAN_BUY_HELMET, VENDOR_ITEM_MANDATORY), list("MRE", 0, /obj/item/storage/box/MRE, MARINE_CAN_BUY_MRE, VENDOR_ITEM_MANDATORY), diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_rifleman.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_rifleman.dm index d92eaabf52c1..09881536901e 100644 --- a/code/game/machinery/vending/vendor_types/squad_prep/squad_rifleman.dm +++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_rifleman.dm @@ -76,7 +76,7 @@ GLOBAL_LIST_INIT(cm_vending_clothing_marine, list( list("Brown Webbing Vest", 15, /obj/item/clothing/accessory/storage/black_vest/brown_vest, null, VENDOR_ITEM_REGULAR), list("Black Webbing Vest", 15, /obj/item/clothing/accessory/storage/black_vest, null, VENDOR_ITEM_REGULAR), list("Drop Pouch", 15, /obj/item/clothing/accessory/storage/droppouch, null, VENDOR_ITEM_REGULAR), - list("SensorMate Medical HUD", 15, /obj/item/clothing/glasses/hud/sensor, null, VENDOR_ITEM_REGULAR), + list("Medical Helmet Optic", 15, /obj/item/device/helmet_visor/medical, null, VENDOR_ITEM_REGULAR), list("Roller Bed", 5, /obj/item/roller, null, VENDOR_ITEM_REGULAR), list("Fulton Device Stack", 5, /obj/item/stack/fulton, null, VENDOR_ITEM_REGULAR), list("B12 Pattern Marine Armor", 30, /obj/item/clothing/suit/storage/marine/leader, null, VENDOR_ITEM_REGULAR), diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_smartgunner.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_smartgunner.dm index 5560508ca4c1..d3a606ae6b41 100644 --- a/code/game/machinery/vending/vendor_types/squad_prep/squad_smartgunner.dm +++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_smartgunner.dm @@ -34,6 +34,7 @@ GLOBAL_LIST_INIT(cm_vending_gear_smartgun, list( list("Powerloader Certification", 45, /obj/item/pamphlet/skill/powerloader, null, VENDOR_ITEM_REGULAR), list("Roller Bed", 5, /obj/item/roller, null, VENDOR_ITEM_REGULAR), list("Fulton Device Stack", 5, /obj/item/stack/fulton, null, VENDOR_ITEM_REGULAR), + list("Medical Helmet Optic", 15, /obj/item/device/helmet_visor/medical, null, VENDOR_ITEM_REGULAR), list("RADIO KEYS", 0, null, null, null), list("Engineering Radio Encryption Key", 5, /obj/item/device/encryptionkey/engi, null, VENDOR_ITEM_REGULAR), diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_specialist.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_specialist.dm index e0900c3fd3c8..b09ae4aa15c5 100644 --- a/code/game/machinery/vending/vendor_types/squad_prep/squad_specialist.dm +++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_specialist.dm @@ -38,7 +38,7 @@ GLOBAL_LIST_INIT(cm_vending_gear_spec, list( list("UTILITIES", 0, null, null, null), list("Fire Extinguisher (Portable)", 5, /obj/item/tool/extinguisher/mini, null, VENDOR_ITEM_REGULAR), - list("SensorMate Medical HUD", 15, /obj/item/clothing/glasses/hud/sensor, null, VENDOR_ITEM_REGULAR), + list("Medical Helmet Optic", 15, /obj/item/device/helmet_visor/medical, null, VENDOR_ITEM_REGULAR), list("Roller Bed", 5, /obj/item/roller, null, VENDOR_ITEM_REGULAR), list("Fulton Device Stack", 5, /obj/item/stack/fulton, null, VENDOR_ITEM_REGULAR), list("Fuel Tank Strap Pouch", 5, /obj/item/storage/pouch/flamertank, null, VENDOR_ITEM_REGULAR), diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_tl.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_tl.dm index ceef80ab6952..4f64ca7e81fa 100644 --- a/code/game/machinery/vending/vendor_types/squad_prep/squad_tl.dm +++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_tl.dm @@ -35,7 +35,9 @@ GLOBAL_LIST_INIT(cm_vending_gear_tl, list( list("Motion Detector", 15, /obj/item/device/motiondetector, null, VENDOR_ITEM_RECOMMENDED), list("Plastic Explosive", 10, /obj/item/explosive/plastic, null, VENDOR_ITEM_REGULAR), list("Breaching Charge", 10, /obj/item/explosive/plastic/breaching_charge, null, VENDOR_ITEM_REGULAR), - list("SensorMate Medical HUD", 15, /obj/item/clothing/glasses/hud/sensor, null, VENDOR_ITEM_REGULAR), + list("Welding Visor", 5, /obj/item/device/helmet_visor/welding_visor, null, VENDOR_ITEM_REGULAR), + list("Medical Helmet Optic", 15, /obj/item/device/helmet_visor/medical, null, VENDOR_ITEM_REGULAR), + list("Welding Goggles", 5, /obj/item/clothing/glasses/welding, null, VENDOR_ITEM_REGULAR), list("M2 Night Vision Goggles", 30, /obj/item/prop/helmetgarb/helmet_nvg, null, VENDOR_ITEM_RECOMMENDED), list("Roller Bed", 5, /obj/item/roller, null, VENDOR_ITEM_REGULAR), list("Fulton Device Stack", 5, /obj/item/stack/fulton, null, VENDOR_ITEM_REGULAR), @@ -44,7 +46,6 @@ GLOBAL_LIST_INIT(cm_vending_gear_tl, list( list("Machete Pouch (Full)", 15, /obj/item/storage/pouch/machete/full, null, VENDOR_ITEM_REGULAR), list("Fire Extinguisher (Portable)", 5, /obj/item/tool/extinguisher/mini, null, VENDOR_ITEM_REGULAR), list("Whistle", 5, /obj/item/device/whistle, null, VENDOR_ITEM_REGULAR), - list("Welding Goggles", 5, /obj/item/clothing/glasses/welding, null, VENDOR_ITEM_REGULAR), list("Powerloader Certification", 45, /obj/item/pamphlet/skill/powerloader, null, VENDOR_ITEM_REGULAR), list("Insulated Gloves", 3, /obj/item/clothing/gloves/yellow, null, VENDOR_ITEM_REGULAR), diff --git a/code/game/objects/effects/acid_hole.dm b/code/game/objects/effects/acid_hole.dm index 549ab45953d2..415df0e7e5a7 100644 --- a/code/game/objects/effects/acid_hole.dm +++ b/code/game/objects/effects/acid_hole.dm @@ -165,7 +165,7 @@ F.forceMove(Target) F.setDir(pick(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST)) step_away(F,src,rand(1,5)) - F.SetLuminosity(0) + F.set_light(0) if(F.on && loc != user) - F.SetLuminosity(F.brightness_on) + F.set_light_on(F.on) return diff --git a/code/game/objects/effects/aliens.dm b/code/game/objects/effects/aliens.dm index b94ee6902321..49d758b52b19 100644 --- a/code/game/objects/effects/aliens.dm +++ b/code/game/objects/effects/aliens.dm @@ -332,7 +332,7 @@ if(!acids_area) return - if(SSweather.is_weather_event && locate(acids_area.master) in SSweather.weather_areas) + if(SSweather.is_weather_event && locate(acids_area) in SSweather.weather_areas) acid_strength = acid_strength + (SSweather.weather_event_instance.fire_smothering_strength * 0.33) //smothering_strength is 1-10, acid strength is a multiplier in_weather = SSweather.weather_event_instance.fire_smothering_strength else diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm index 43c3500813a4..9cf2aa3d8e09 100644 --- a/code/game/objects/effects/decals/cleanable/misc.dm +++ b/code/game/objects/effects/decals/cleanable/misc.dm @@ -35,12 +35,8 @@ name = "glowing goo" acid_damage = 1 icon_state = "greenglow" - luminosity = 1 - -/obj/effect/decal/cleanable/dirt/greenglow/Destroy() - SetLuminosity(0) - return ..() - + light_range = 1 + light_color = COLOUR_GREEN /obj/effect/decal/cleanable/flour name = "flour" desc = "It's still good. Four second rule!" @@ -58,7 +54,8 @@ density = FALSE anchored = TRUE layer = TURF_LAYER - luminosity = 1 + light_range = 1 + light_color = COLOUR_GREEN icon = 'icons/effects/effects.dmi' icon_state = "greenglow" @@ -68,10 +65,6 @@ . = ..() QDEL_IN(WEAKREF(src), 2 MINUTES) -/obj/effect/decal/cleanable/greenglow/Destroy() - SetLuminosity(0) - return ..() - /obj/effect/decal/cleanable/cobweb name = "cobweb" desc = "Somebody should remove that." diff --git a/code/game/objects/effects/effect_system/chemsmoke.dm b/code/game/objects/effects/effect_system/chemsmoke.dm index c2323c32c934..eeb17f7c98d0 100644 --- a/code/game/objects/effects/effect_system/chemsmoke.dm +++ b/code/game/objects/effects/effect_system/chemsmoke.dm @@ -199,10 +199,10 @@ smoke.pixel_x = -32 + rand(-8,8) smoke.pixel_y = -32 + rand(-8,8) walk_to(smoke, T) - smoke.SetOpacity(1) //switching opacity on after the smoke has spawned, and then + smoke.set_opacity(1) //switching opacity on after the smoke has spawned, and then sleep(150+rand(0,20)) // turning it off before it is deleted results in cleaner if(smoke.opacity) - smoke.SetOpacity(0) + smoke.set_opacity(0) fadeOut(smoke) qdel(smoke) diff --git a/code/game/objects/effects/effect_system/smoke.dm b/code/game/objects/effects/effect_system/smoke.dm index 2eb36930c542..da388b1be1e4 100644 --- a/code/game/objects/effects/effect_system/smoke.dm +++ b/code/game/objects/effects/effect_system/smoke.dm @@ -36,7 +36,7 @@ /obj/effect/particle_effect/smoke/Destroy() . = ..() if(opacity) - SetOpacity(0) + set_opacity(0) active_smoke_effects -= src cause_data = null @@ -53,7 +53,7 @@ else if(time_to_live == 1) alpha = 180 amount = 0 - SetOpacity(0) + set_opacity(0) apply_smoke_effect(get_turf(src)) diff --git a/code/game/objects/effects/glowshroom.dm b/code/game/objects/effects/glowshroom.dm index e54607d6f930..bebe0ec8b27f 100644 --- a/code/game/objects/effects/glowshroom.dm +++ b/code/game/objects/effects/glowshroom.dm @@ -39,13 +39,9 @@ else //if on the floor, glowshroom on-floor sprite icon_state = "glowshroomf" - SetLuminosity(round(potency/15)) + set_light(round(potency/15)) lastTick = world.timeofday -/obj/effect/glowshroom/Destroy() - SetLuminosity(0) - . = ..() - /obj/effect/glowshroom/proc/CalcDir(turf/location = loc) set background = 1 var/direction = 16 diff --git a/code/game/objects/effects/overlays.dm b/code/game/objects/effects/overlays.dm index d4cd293030c2..16f30eaf0fd2 100644 --- a/code/game/objects/effects/overlays.dm +++ b/code/game/objects/effects/overlays.dm @@ -145,7 +145,7 @@ name = "laser" anchored = TRUE mouse_opacity = MOUSE_OPACITY_ICON - luminosity = 2 + light_range = 2 icon = 'icons/obj/items/weapons/projectiles.dmi' icon_state = "laser_target_coordinate" effect_duration = 600 @@ -156,14 +156,13 @@ source_binoc.laser_cooldown = world.time + source_binoc.cooldown_duration source_binoc.coord = null source_binoc = null - SetLuminosity(0) . = ..() /obj/effect/overlay/temp/laser_target name = "laser" anchored = TRUE mouse_opacity = MOUSE_OPACITY_ICON - luminosity = 2 + light_range = 2 icon = 'icons/obj/items/weapons/projectiles.dmi' icon_state = "laser_target2" effect_duration = 600 @@ -198,7 +197,6 @@ source_binoc.laser = null source_binoc = null - SetLuminosity(0) . = ..() /obj/effect/overlay/temp/laser_target/ex_act(severity) //immune to explosions @@ -214,16 +212,12 @@ /obj/effect/overlay/temp/blinking_laser name = "blinking laser" anchored = TRUE - luminosity = 2 + light_range = 2 effect_duration = 10 mouse_opacity = MOUSE_OPACITY_TRANSPARENT icon = 'icons/obj/items/weapons/projectiles.dmi' icon_state = "laser_target3" -/obj/effect/overlay/temp/blinking_laser/Destroy() - SetLuminosity(0) - . = ..() - /obj/effect/overlay/temp/emp_sparks icon = 'icons/effects/effects.dmi' icon_state = "empdisable" diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 49a05f8966c0..4746829d5631 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -2,6 +2,8 @@ name = "item" icon = 'icons/obj/items/items.dmi' mouse_drag_pointer = MOUSE_ACTIVE_POINTER + light_system = MOVABLE_LIGHT + /// this saves our blood splatter overlay, which will be processed not to go over the edges of the sprite var/image/blood_overlay = null var/randpixel = 6 diff --git a/code/game/objects/items/devices/cictablet.dm b/code/game/objects/items/devices/cictablet.dm index b2707a20aa90..fc9bb015ece0 100644 --- a/code/game/objects/items/devices/cictablet.dm +++ b/code/game/objects/items/devices/cictablet.dm @@ -160,7 +160,7 @@ for(var/client/C in GLOB.admins) if((R_ADMIN|R_MOD) & C.admin_holder.rights) playsound_client(C,'sound/effects/sos-morse-code.ogg',10) - message_admins("[key_name(usr)] has requested a Distress Beacon! [CC_MARK(usr)] (SEND) (DENY) [ADMIN_JMP_USER(usr)] [CC_REPLY(usr)]") + SSticker.mode.request_ert(usr) to_chat(usr, SPAN_NOTICE("A distress beacon request has been sent to USCM Central Command.")) COOLDOWN_START(src, distress_cooldown, COOLDOWN_COMM_REQUEST) return TRUE diff --git a/code/game/objects/items/devices/dummy_tablet.dm b/code/game/objects/items/devices/dummy_tablet.dm index 92cfa4b90e26..d1036ebfa93b 100644 --- a/code/game/objects/items/devices/dummy_tablet.dm +++ b/code/game/objects/items/devices/dummy_tablet.dm @@ -12,6 +12,20 @@ linked_dummy = null . = ..() +/** + * Checks if the user is adjacent to the dummy + * + * Returns TRUE if the user is adjacent to the dummy, FALSE otherwise + * + * * arg-1: The user + */ +/obj/item/device/professor_dummy_tablet/proc/is_adjacent_to_dummy(mob/user) + if (get_dist(linked_dummy, user) > 1) + to_chat(user, "You are too far away to use the tablet.") + return FALSE + + return TRUE + /obj/item/device/professor_dummy_tablet/proc/link_mob(mob/living/carbon/human/H) linked_dummy = H @@ -20,6 +34,12 @@ interact(user) /obj/item/device/professor_dummy_tablet/interact(mob/user as mob) + if (isnull(linked_dummy)) + return + + if (!is_adjacent_to_dummy(user)) + return + user.set_interaction(src) var/dat = "
Name | Role | State | Location | SL Distance | Filter |
---|