diff --git a/_maps/map_files/AsteroidStation/AsteroidStation.dmm b/_maps/map_files/AsteroidStation/AsteroidStation.dmm index e21554301c0c..083aa613c571 100644 --- a/_maps/map_files/AsteroidStation/AsteroidStation.dmm +++ b/_maps/map_files/AsteroidStation/AsteroidStation.dmm @@ -29665,14 +29665,9 @@ /turf/open/floor/plasteel, /area/storage/primary) "iFj" = ( -/obj/structure/sign/barsign{ - pixel_y = 32 - }, -/obj/effect/turf_decal/trimline/neutral/filled/line/lower{ - dir = 1 - }, -/turf/open/floor/plasteel, -/area/hallway/primary/central) +/obj/machinery/barsign, +/turf/closed/wall, +/area/crew_quarters/bar) "iFF" = ( /obj/effect/turf_decal/trimline/neutral/filled/line/lower{ dir = 1 @@ -100472,8 +100467,8 @@ xWW xWW xWW xWW -aFi iFj +rkk hoR sIx asr diff --git a/_maps/map_files/DonutStation/DonutStation.dmm b/_maps/map_files/DonutStation/DonutStation.dmm index f97d23ff111b..805d22ee70f0 100644 --- a/_maps/map_files/DonutStation/DonutStation.dmm +++ b/_maps/map_files/DonutStation/DonutStation.dmm @@ -34316,9 +34316,6 @@ /turf/open/floor/plasteel, /area/quartermaster/miningdock) "oNx" = ( -/obj/structure/sign/barsign{ - pixel_y = 32 - }, /obj/machinery/door/poddoor/shutters/preopen{ id = "barshutters"; name = "bar shutters" @@ -45981,6 +45978,10 @@ }, /turf/open/floor/plasteel/dark/telecomms, /area/ai_monitored/turret_protected/ai) +"tPB" = ( +/obj/machinery/barsign, +/turf/closed/wall, +/area/crew_quarters/bar) "tPF" = ( /turf/open/floor/plasteel/white, /area/science/mixing) @@ -87587,7 +87588,7 @@ vDp wLr gxy jgR -gxy +tPB oNx rsE wuu diff --git a/_maps/map_files/GaxStation/GaxStation.dmm b/_maps/map_files/GaxStation/GaxStation.dmm index e908c6661c3a..14119967fc5a 100644 --- a/_maps/map_files/GaxStation/GaxStation.dmm +++ b/_maps/map_files/GaxStation/GaxStation.dmm @@ -24092,7 +24092,7 @@ /turf/open/floor/carpet, /area/hallway/secondary/entry) "lLC" = ( -/obj/structure/sign/barsign, +/obj/machinery/barsign/all_access, /turf/closed/wall, /area/crew_quarters/bar) "lLV" = ( diff --git a/_maps/map_files/IceMeta/IceMeta.dmm b/_maps/map_files/IceMeta/IceMeta.dmm index eb3dcccc5b9c..baa62490353c 100644 --- a/_maps/map_files/IceMeta/IceMeta.dmm +++ b/_maps/map_files/IceMeta/IceMeta.dmm @@ -33728,12 +33728,9 @@ /turf/open/floor/plasteel, /area/teleporter) "jPu" = ( -/obj/structure/sign/barsign{ - pixel_y = -32 - }, -/obj/effect/turf_decal/trimline/green/filled/line/lower, -/turf/open/floor/plasteel, -/area/hallway/primary/starboard) +/obj/machinery/barsign/all_access, +/turf/closed/wall, +/area/crew_quarters/bar) "jPy" = ( /obj/structure/closet/secure_closet/brig{ id = "Cell 1"; @@ -117656,8 +117653,8 @@ rkO bjd eHK lFl +bJD jPu -rfi nYc lgH ptc diff --git a/_maps/map_files/YogStation/YogStation.dmm b/_maps/map_files/YogStation/YogStation.dmm index bb74e7b7d5c6..ca2e76f0ae74 100644 --- a/_maps/map_files/YogStation/YogStation.dmm +++ b/_maps/map_files/YogStation/YogStation.dmm @@ -73325,7 +73325,7 @@ /turf/open/floor/plating, /area/maintenance/port/aft) "xSX" = ( -/obj/structure/sign/barsign, +/obj/machinery/barsign, /turf/closed/wall, /area/crew_quarters/bar) "xTe" = ( diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index 60a5237310a9..79566761613e 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -14882,9 +14882,6 @@ /turf/open/floor/carpet/black, /area/centcom/holding) "aFc" = ( -/obj/structure/sign/barsign{ - pixel_y = 32 - }, /obj/structure/chair/stool, /turf/open/floor/carpet/black, /area/centcom/holding) @@ -16347,22 +16344,8 @@ /turf/open/floor/holofloor, /area/holodeck/rec_center/basketball) "aHM" = ( -/obj/structure/chair, -/obj/effect/landmark/thunderdome/observe, -/obj/structure/sign/barsign{ - pixel_y = 32 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/plasteel/dark, +/obj/machinery/barsign, +/turf/closed/indestructible/riveted, /area/tdome/tdomeobserve) "aHN" = ( /obj/structure/chair, @@ -16647,9 +16630,6 @@ pixel_x = -3; pixel_y = 5 }, -/obj/structure/sign/barsign{ - pixel_y = -32 - }, /obj/effect/turf_decal/tile/neutral{ dir = 1 }, @@ -16698,9 +16678,6 @@ pixel_y = 5 }, /obj/structure/table/wood, -/obj/structure/sign/barsign{ - pixel_y = -32 - }, /obj/effect/turf_decal/tile/neutral{ dir = 1 }, @@ -69586,7 +69563,7 @@ aEp aEp aEp aEp -aHM +aHN aHW aHN aIl @@ -72413,7 +72390,7 @@ aGP aGK aGP aEp -aHM +aHN aHW aHN aIl @@ -73445,7 +73422,7 @@ aHf aHf aHf aIo -aEp +aHM aIL aIZ aIZ diff --git a/byond-extools.dll b/byond-extools.dll new file mode 100644 index 000000000000..37efe6c6e403 Binary files /dev/null and b/byond-extools.dll differ diff --git a/code/__DEFINES/_helpers.dm b/code/__DEFINES/_helpers.dm index 741efbfd2da5..9d8a9d7ab77d 100644 --- a/code/__DEFINES/_helpers.dm +++ b/code/__DEFINES/_helpers.dm @@ -1,6 +1,2 @@ -/// Takes a datum as input, returns its ref string, or a cached version of it -/// This allows us to cache \ref creation, which ensures it'll only ever happen once per datum, saving string tree time -/// It is slightly less optimal then a []'d datum, but the cost is massively outweighed by the potential savings -/// It will only work for datums mind, for datum reasons -/// : because of the embedded typecheck -#define text_ref(datum) (isdatum(datum) ? (datum:cached_ref ||= "\ref[datum]") : ("\ref[datum]")) +/// Takes a datum as input, returns its ref string +#define text_ref(datum) ref(datum) diff --git a/code/__DEFINES/cameranets.dm b/code/__DEFINES/cameranets.dm new file mode 100644 index 000000000000..e2da39997e38 --- /dev/null +++ b/code/__DEFINES/cameranets.dm @@ -0,0 +1,4 @@ +/// We only want chunk sizes that are to the power of 2. E.g: 2, 4, 8, 16, etc.. +#define CHUNK_SIZE 16 +/// Takes a position, transforms it into a chunk bounded position. Indexes at 1 so it'll land on actual turfs always +#define GET_CHUNK_COORD(v) (max((FLOOR(v, CHUNK_SIZE)), 1)) diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index caf05d322cd0..85d452daf06a 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -231,6 +231,8 @@ #define LIGHT_COLOR_LAVA "#C48A18" /// Bright, non-saturated red. Leaning slightly towards pink for visibility. rgb(250, 100, 75) #define LIGHT_COLOR_FLARE "#FA644B" +/// Vivid red. Leans a bit darker to accentuate red colors and leave other channels a bit dry. rgb(200, 25, 25) +#define LIGHT_COLOR_INTENSE_RED "#C81919" /// Weird color, between yellow and green, very slimy. rgb(175, 200, 75) #define LIGHT_COLOR_SLIME_LAMP "#AFC84B" /// Extremely diluted yellow, close to skin color (for some reason). rgb(250, 225, 175) diff --git a/code/__DEFINES/dcs/signals/mapping.dm b/code/__DEFINES/dcs/signals/mapping.dm new file mode 100644 index 000000000000..d80d60365802 --- /dev/null +++ b/code/__DEFINES/dcs/signals/mapping.dm @@ -0,0 +1,2 @@ +// Sent when the max plane offset changes : (old_max_offset, new_max_offset) +#define COMSIG_PLANE_OFFSET_INCREASE "plane_offset_increase" diff --git a/code/__DEFINES/dcs/signals/signals_client.dm b/code/__DEFINES/dcs/signals/signals_client.dm new file mode 100644 index 000000000000..07e5251cad3b --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_client.dm @@ -0,0 +1,14 @@ +// from /client/proc/change_view() : (new_size) +#define COMSIG_VIEW_SET "view_set" + +// from /client/proc/handle_popup_close() : (window_id) +#define COMSIG_POPUP_CLEARED "popup_cleared" + +/// Called after one or more verbs are added: (list of verbs added) +#define COMSIG_CLIENT_VERB_ADDED "client_verb_added" + +/// Called after one or more verbs are added: (list of verbs added) +#define COMSIG_CLIENT_VERB_REMOVED "client_verb_removed" + +/// Called after a client logs into a mob: (mob) +#define COMSIG_CLIENT_MOB_LOGIN "client_mob_changed" diff --git a/code/__DEFINES/dcs/signals/signals_global.dm b/code/__DEFINES/dcs/signals/signals_global.dm index 79e35ec3feed..1026950b9e5b 100644 --- a/code/__DEFINES/dcs/signals/signals_global.dm +++ b/code/__DEFINES/dcs/signals/signals_global.dm @@ -74,3 +74,7 @@ #define COMSIG_GLOB_LIGHT_MECHANISM_COMPLETED "!light_mechanism_completed" /// Global Signal sent when the crew wins the revolution (No arguments). #define COMSIG_GLOB_REVOLUTION_VICTORY "!revolution_victory" +/// Global signal sent when narsie summon count is updated: (new count) +#define COMSIG_NARSIE_SUMMON_UPDATE "!narsie_summon_update" +/// Global signal when starlight color is changed (old_star, new_star) +#define COMSIG_STARLIGHT_COLOR_CHANGED "!starlight_color_changed" diff --git a/code/__DEFINES/dcs/signals/signals_huds.dm b/code/__DEFINES/dcs/signals/signals_huds.dm new file mode 100644 index 000000000000..2d5d3eaa59cc --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_huds.dm @@ -0,0 +1,10 @@ +/// Sent from /datum/hud/proc/on_eye_change(): (atom/old_eye, atom/new_eye) +#define COMSIG_HUD_EYE_CHANGED "hud_eye_changed" +/// Sent from /datum/hud/proc/eye_z_changed() : (old_offset, new_offset) +#define COMSIG_HUD_OFFSET_CHANGED "hud_offset_changed" +/// Sent from /atom/movable/screen/lobby/button/collapse/proc/collapse_buttons() : () +#define COMSIG_HUD_LOBBY_COLLAPSED "hud_lobby_collapsed" +/// Sent from /atom/movable/screen/lobby/button/collapse/proc/expand_buttons() : () +#define COMSIG_HUD_LOBBY_EXPANDED "hud_lobby_expanded" +/// Sent from /atom/movable/screen/lobby/button/ready/Click() : () +#define COMSIG_HUD_PLAYER_READY_TOGGLE "hud_player_ready_toggle" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_living.dm index 56d9754eb42f..a5bfe7f8ee6d 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_living.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_living.dm @@ -39,8 +39,6 @@ #define COMSIG_LIVING_SET_BODY_POSITION "living_set_body_position" ///From post-can inject check of syringe after attack (mob/user) #define COMSIG_LIVING_TRY_SYRINGE "living_try_syringe" -///From living/Life(seconds_per_tick = SSMOBS_DT, times_fired). (deltatime, times_fired) -#define COMSIG_LIVING_LIFE "living_life" ///From living/set_resting(): (new_resting, silent, instant) #define COMSIG_LIVING_RESTING "living_resting" @@ -139,3 +137,7 @@ ///from mind/transfer_to. Sent after the mind has been transferred: (mob/previous_body) #define COMSIG_MIND_TRANSFERRED "mind_transferred" +///From living/Life(). (deltatime, times_fired) +#define COMSIG_LIVING_LIFE "living_life" + /// Block the Life() proc from proceeding... this should really only be done in some really wacky situations. + #define COMPONENT_LIVING_CANCEL_LIFE_PROCESSING (1<<0) diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob.dm index 14d26b0ac454..4423bfff8cc7 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob.dm @@ -80,9 +80,6 @@ #define COMSIG_MOB_SIGHT_CHANGE "mob_sight_changed" ///from base of mob/set_invis_see(): (new_invis, old_invis) #define COMSIG_MOB_SEE_INVIS_CHANGE "mob_see_invis_change" -///from base of mob/set_see_in_dark(): (new_range, old_range) -#define COMSIG_MOB_SEE_IN_DARK_CHANGE "mob_see_in_dark_change" - ///from base of /mob/living/proc/apply_damage(): (damage, damagetype, def_zone, blocked, wound_bonus, bare_wound_bonus, sharpness, attack_direction) #define COMSIG_MOB_APPLY_DAMAGE "mob_apply_damage" diff --git a/code/__DEFINES/dcs/signals/signals_turf.dm b/code/__DEFINES/dcs/signals/signals_turf.dm index a38162fa29c4..51e0040b4de8 100644 --- a/code/__DEFINES/dcs/signals/signals_turf.dm +++ b/code/__DEFINES/dcs/signals/signals_turf.dm @@ -16,3 +16,5 @@ #define FOOTSTEP_OVERRIDDEN (1<<0) ///from base of datum/thrownthing/finalize(): (turf/turf, atom/movable/thrownthing) when something is thrown and lands on us #define COMSIG_TURF_MOVABLE_THROW_LANDED "turf_movable_throw_landed" +///from base of turf/multiz_turf_del(): (turf/source, direction) +#define COMSIG_TURF_MULTIZ_DEL "turf_multiz_del" diff --git a/code/__DEFINES/directional.dm b/code/__DEFINES/directional.dm index 85d746a43882..bfde544b4471 100644 --- a/code/__DEFINES/directional.dm +++ b/code/__DEFINES/directional.dm @@ -4,7 +4,32 @@ // #define EAST 4 // #define WEST 8 +/// North direction as a string "[1]" #define TEXT_NORTH "[NORTH]" +/// South direction as a string "[2]" #define TEXT_SOUTH "[SOUTH]" +/// East direction as a string "[4]" #define TEXT_EAST "[EAST]" +/// West direction as a string "[8]" #define TEXT_WEST "[WEST]" + +/// Inverse direction, taking into account UP|DOWN if necessary. +#define REVERSE_DIR(dir) ( ((dir & 85) << 1) | ((dir & 170) >> 1) ) + +/// Create directional subtypes for a path to simplify mapping. +#define MAPPING_DIRECTIONAL_HELPERS(path, offset) ##path/directional/north {\ + dir = NORTH; \ + pixel_y = offset; \ +} \ +##path/directional/south {\ + dir = SOUTH; \ + pixel_y = -offset; \ +} \ +##path/directional/east {\ + dir = EAST; \ + pixel_x = offset; \ +} \ +##path/directional/west {\ + dir = WEST; \ + pixel_x = -offset; \ +} diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index 92c389063f8a..aa70ac67f0ea 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -211,3 +211,19 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define IGNORE_SLOWDOWNS (1<<4) #define IGNORE_ALL (IGNORE_USER_LOC_CHANGE|IGNORE_TARGET_LOC_CHANGE|IGNORE_HELD_ITEM|IGNORE_INCAPACITATED|IGNORE_SLOWDOWNS) + +//TURF FLAGS +/// If a turf cant be jaunted through. +#define NOJAUNT (1<<0) +/// If a turf is an usused reservation turf awaiting assignment +#define UNUSED_RESERVATION_TURF (1<<1) +/// If a turf is a reserved turf +#define RESERVATION_TURF (1<<2) +/// Blocks lava rivers being generated on the turf. +#define NO_LAVA_GEN (1<<3) +/// Blocks ruins spawning on the turf. +#define NO_RUINS (1<<4) +/// Is this turf is "solid". Space and lava aren't for instance +#define IS_SOLID (1<<6) +/// This turf will never be cleared away by other objects on Initialize. +#define NO_CLEARING (1<<7) diff --git a/code/__DEFINES/hud.dm b/code/__DEFINES/hud.dm index fdcc04a03b8f..900b77f28848 100644 --- a/code/__DEFINES/hud.dm +++ b/code/__DEFINES/hud.dm @@ -233,3 +233,8 @@ #define SCRN_OBJ_INSERT_FIRST "first" /// The filter name for the hover outline #define HOVER_OUTLINE_FILTER "hover_outline" +// Plane group keys, used to group swaths of plane masters that need to appear in subwindows +/// The primary group, holds everything on the main window +#define PLANE_GROUP_MAIN "main" +/// A secondary group, used when a client views a generic window +#define PLANE_GROUP_POPUP_WINDOW(screen) "popup-[REF(screen)]" diff --git a/code/__DEFINES/interaction_flags.dm b/code/__DEFINES/interaction_flags.dm index a85ffb8d7a09..25e31118fd17 100644 --- a/code/__DEFINES/interaction_flags.dm +++ b/code/__DEFINES/interaction_flags.dm @@ -36,3 +36,5 @@ /// This flag determines if a machine set_machine's the user when the user uses it, making updateUsrDialog make the user re-call interact() on it. /// THIS FLAG IS ON ALL MACHINES BY DEFAULT, NEEDS TO BE RE-EVALUATED LATER!! #define INTERACT_MACHINE_SET_MACHINE (1<<6) +/// the user must have vision to interact (blind people need not apply) +#define INTERACT_MACHINE_REQUIRES_SIGHT (1<<7) diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 128866d2f223..1f75466f98bc 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -240,3 +240,12 @@ GLOBAL_LIST_INIT(glass_sheet_types, typecacheof(list( #define isfugitive(M) (istype(M) && M.mind?.has_antag_datum(/datum/antagonist/fugitive)) #define isProbablyWallMounted(O) (O.pixel_x > 20 || O.pixel_x < -20 || O.pixel_y > 20 || O.pixel_y < -20) + +GLOBAL_LIST_INIT(turfs_openspace, typecacheof(list( + /turf/open/openspace, + /turf/open/space/openspace + ))) + +#define istransparentturf(A) (HAS_TRAIT(A, TURF_Z_TRANSPARENT_TRAIT)) + +#define isopenspaceturf(A) (is_type_in_typecache(A, GLOB.turfs_openspace)) diff --git a/code/__DEFINES/lag_switch.dm b/code/__DEFINES/lag_switch.dm new file mode 100644 index 000000000000..2115ce4a5dd4 --- /dev/null +++ b/code/__DEFINES/lag_switch.dm @@ -0,0 +1,20 @@ +// All of the possible Lag Switch lag mitigation measures +// If you add more do not forget to update MEASURES_AMOUNT accordingly +/// Stops ghosts flying around freely, they can still jump and orbit, staff exempted +#define DISABLE_DEAD_KEYLOOP 1 +/// Stops ghosts using zoom/t-ray verbs and resets their view if zoomed out, staff exempted +#define DISABLE_GHOST_ZOOM_TRAY 2 +/// Disable runechat and enable the bubbles, speaking mobs with TRAIT_BYPASS_MEASURES exempted +#define DISABLE_RUNECHAT 3 +/// Disable icon2html procs from verbs like examine, mobs calling with TRAIT_BYPASS_MEASURES exempted +#define DISABLE_USR_ICON2HTML 4 +/// Prevents anyone from joining the game as anything but observer +#define DISABLE_NON_OBSJOBS 5 +/// Limit IC/dchat spam to one message every x seconds per client, TRAIT_BYPASS_MEASURES exempted +#define SLOWMODE_SAY 6 +/// Disables parallax, as if everyone had disabled their preference, TRAIT_BYPASS_MEASURES exempted +#define DISABLE_PARALLAX 7 +/// Disables footsteps, TRAIT_BYPASS_MEASURES exempted +#define DISABLE_FOOTSTEPS 8 + +#define MEASURES_AMOUNT 8 // The total number of switches defined above diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index 3ce945e5eecc..b5abc1b25025 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -1,166 +1,332 @@ //Defines for atom layers and planes //KEEP THESE IN A NICE ACSCENDING ORDER, PLEASE -#define CLICKCATCHER_PLANE -99 +//NEVER HAVE ANYTHING BELOW THIS PLANE ADJUST IF YOU NEED MORE SPACE +#define LOWEST_EVER_PLANE -100 -#define PLANE_SPACE -95 -#define PLANE_SPACE_RENDER_TARGET "PLANE_SPACE" -#define PLANE_SPACE_PARALLAX -90 -#define PLANE_SPACE_PARALLAX_RENDER_TARGET "PLANE_SPACE_PARALLAX" +#define FIELD_OF_VISION_BLOCKER_PLANE -90 +#define FIELD_OF_VISION_BLOCKER_RENDER_TARGET "*FIELD_OF_VISION_BLOCKER_RENDER_TARGET" -#define SINGULARITY_EFFECT_PLANE -80 -#define SINGULARITY_RENDER_TARGET "*SINGULARITY_EFFECTS_PLANE" +#define CLICKCATCHER_PLANE -80 -#define FLOOR_PLANE -2 -#define FLOOR_PLANE_RENDER_TARGET "FLOOR_PLANE" -#define GAME_PLANE -1 -#define GAME_PLANE_RENDER_TARGET "GAME_PLANE" -#define BLACKNESS_PLANE 0 //To keep from conflicts with SEE_BLACKNESS internals -#define BLACKNESS_PLANE_RENDER_TARGET "BLACKNESS_PLANE" +#define PLANE_SPACE -25 +#define PLANE_SPACE_PARALLAX -20 +#define GRAVITY_PULSE_PLANE -13 +#define GRAVITY_PULSE_RENDER_TARGET "*GRAVPULSE_RENDER_TARGET" + +#define RENDER_PLANE_TRANSPARENT -12 //Transparent plane that shows openspace underneath the floor + +#define TRANSPARENT_FLOOR_PLANE -11 + +#define FLOOR_PLANE -10 + +#define WALL_PLANE -9 +#define GAME_PLANE -8 +#define GAME_PLANE_FOV_HIDDEN -7 +#define GAME_PLANE_UPPER -6 +#define WALL_PLANE_UPPER -5 +#define GAME_PLANE_UPPER_FOV_HIDDEN -4 + +///Slightly above the game plane but does not catch mouse clicks. Useful for certain visuals that should be clicked through, like seethrough trees +#define SEETHROUGH_PLANE -3 +#define ABOVE_GAME_PLANE -2 + +#define RENDER_PLANE_GAME_WORLD -1 + +#define DEFAULT_PLANE 0 //Marks out the default plane, even if we don't use it + +#define AREA_PLANE 2 +#define MASSIVE_OBJ_PLANE 3 +#define GHOST_PLANE 4 +#define POINT_PLANE 5 + +//---------- LIGHTING ------------- +///Normal 1 per turf dynamic lighting underlays +#define LIGHTING_PLANE 10 + +///Lighting objects that are "free floating" +#define O_LIGHTING_VISUAL_PLANE 11 +#define O_LIGHTING_VISUAL_RENDER_TARGET "O_LIGHT_VISUAL_PLANE" + +#define EMISSIVE_LAYER 13 +#define EMISSIVE_PLANE 13 +/// This plane masks out lighting to create an "emissive" effect, ie for glowing lights in otherwise dark areas. +#define EMISSIVE_RENDER_PLATE 14 +#define EMISSIVE_RENDER_TARGET "*EMISSIVE_PLANE" +// Ensures all the render targets that point at the emissive plate layer correctly +#define EMISSIVE_Z_BELOW_LAYER 1 +#define EMISSIVE_FLOOR_LAYER 2 +#define EMISSIVE_SPACE_LAYER 3 +#define EMISSIVE_WALL_LAYER 4 + +#define RENDER_PLANE_LIGHTING 15 + +/// Masks the lighting plane with turfs, so we never light up the void +/// Failing that, masks emissives and the overlay lighting plane +#define LIGHT_MASK_PLANE 16 +#define LIGHT_MASK_RENDER_TARGET "*LIGHT_MASK_PLANE" + +///Things that should render ignoring lighting +#define ABOVE_LIGHTING_PLANE 17 + +///---------------- MISC ----------------------- + +///Pipecrawling images +#define PIPECRAWL_IMAGES_PLANE 20 + +///AI Camera Static +#define CAMERA_STATIC_PLANE 40 +#define CAMERA_STATIC_LAYER 40 + +///Anything that wants to be part of the game plane, but also wants to draw above literally everything else +#define HIGH_GAME_PLANE 22 + +#define FULLSCREEN_PLANE 23 + +///--------------- FULLSCREEN RUNECHAT BUBBLES ------------ + +///Popup Chat Messages +#define RUNECHAT_PLANE 30 +/// Plane for balloon text (text that fades up) +#define BALLOON_CHAT_PLANE 31 + +//-------------------- HUD --------------------- +//HUD layer defines +#define HUD_PLANE 40 +#define ABOVE_HUD_PLANE 41 +#define ABOVE_HUD_LAYER 41 + +///Plane of the "splash" icon used that shows on the lobby screen +#define SPLASHSCREEN_PLANE 50 + +// The largest plane here must still be less than RENDER_PLANE_GAME + +//-------------------- Rendering --------------------- +#define RENDER_PLANE_GAME 100 +#define RENDER_PLANE_NON_GAME 101 + +// Only VERY special planes should be here, as they are above not just the game, but the UI planes as well. + +/// Plane related to the menu when pressing Escape. +/// Needed so that we can apply a blur effect to EVERYTHING, and guarantee we are above all UI. +#define ESCAPE_MENU_PLANE 105 + +#define RENDER_PLANE_MASTER 110 + +// Lummox I swear to god I will find you +// NOTE! You can only ever have planes greater then -10000, if you add too many with large offsets you will brick multiz +// Same can be said for large multiz maps. Tread carefully mappers +#define HIGHEST_EVER_PLANE RENDER_PLANE_MASTER +/// The range unique planes can be in +#define PLANE_RANGE (HIGHEST_EVER_PLANE - LOWEST_EVER_PLANE) + +// PLANE_SPACE layer(s) #define SPACE_LAYER 1.8 -//#define TURF_LAYER 2 //For easy recordkeeping; this is a byond define + +//#define TURF_LAYER 2 //For easy recordkeeping; this is a byond define. Most floors (FLOOR_PLANE) and walls (WALL_PLANE) use this. + +//FLOOR_PLANE layers +#define TURF_PLATING_DECAL_LAYER 2.001 +#define TURF_DECAL_LAYER 2.009 //Makes turf decals appear in DM how they will look inworld. +#define CULT_OVERLAY_LAYER 2.01 #define MID_TURF_LAYER 2.02 #define HIGH_TURF_LAYER 2.03 -#define TURF_PLATING_DECAL_LAYER 2.031 -#define TURF_DECAL_LAYER 2.039 //Makes turf decals appear in DM how they will look inworld. -#define ABOVE_OPEN_TURF_LAYER 2.04 +#define LATTICE_LAYER 2.04 +#define DISPOSAL_PIPE_LAYER 2.042 +#define WIRE_LAYER 2.044 +#define GLASS_FLOOR_LAYER 2.046 +#define TRAM_RAIL_LAYER 2.047 +#define ABOVE_OPEN_TURF_LAYER 2.049 + +//WALL_PLANE layers #define CLOSED_TURF_LAYER 2.05 + +// GAME_PLANE layers #define BULLET_HOLE_LAYER 2.06 #define ABOVE_NORMAL_TURF_LAYER 2.08 -#define LATTICE_LAYER 2.2 -#define DISPOSAL_PIPE_LAYER 2.25 #define GAS_PIPE_HIDDEN_LAYER 2.35 //layer = initial(layer) + piping_layer / 1000 in atmospherics/update_icon() to determine order of pipe overlap -#define WIRE_LAYER 2.4 +#define WIRE_BRIDGE_LAYER 2.44 #define WIRE_TERMINAL_LAYER 2.45 -#define UNDER_CATWALK 2.454 -#define CATWALK_LAYER 2.455 -#define GAS_SCRUBBER_LAYER 2.46 +#define GAS_SCRUBBER_LAYER 2.46 #define GAS_PIPE_VISIBLE_LAYER 2.47 //layer = initial(layer) + piping_layer / 1000 in atmospherics/update_icon() to determine order of pipe overlap #define GAS_FILTER_LAYER 2.48 #define GAS_PUMP_LAYER 2.49 +#define PLUMBING_PIPE_VISIBILE_LAYER 2.495//layer = initial(layer) + ducting_layer / 3333 in atmospherics/handle_layer() to determine order of duct overlap +#define BOT_PATH_LAYER 2.497 #define LOW_OBJ_LAYER 2.5 +///catwalk overlay of /turf/open/floor/plating/catwalk_floor +#define CATWALK_LAYER 2.51 #define LOW_SIGIL_LAYER 2.52 -#define SIGIL_LAYER 2.54 -#define HIGH_SIGIL_LAYER 2.56 +#define SIGIL_LAYER 2.53 +#define HIGH_SIGIL_LAYER 2.54 +#define HIGH_PIPE_LAYER 2.55 +// Anything above this layer is not "on" a turf for the purposes of washing +// I hate this life of ours +#define FLOOR_CLEAN_LAYER 2.56 +#define TRAM_STRUCTURE_LAYER 2.57 +#define TRAM_FLOOR_LAYER 2.58 +#define TRAM_WALL_LAYER 2.59 + #define BELOW_OPEN_DOOR_LAYER 2.6 -#define GAS_METER_LAYER 2.61 +///Anything below this layer is to be considered completely (visually) under water by the immerse layer. +#define WATER_LEVEL_LAYER 2.61 #define BLASTDOOR_LAYER 2.65 #define OPEN_DOOR_LAYER 2.7 -#define DOOR_HELPER_LAYER 2.71 //keep this above OPEN_DOOR_LAYER +#define DOOR_ACCESS_HELPER_LAYER 2.71 //keep this above OPEN_DOOR_LAYER, special layer used for /obj/effect/mapping_helpers/airlock/access +#define DOOR_HELPER_LAYER 2.72 //keep this above DOOR_ACCESS_HELPER_LAYER and OPEN_DOOR_LAYER since the others tend to have tiny sprites that tend to be covered up. #define PROJECTILE_HIT_THRESHHOLD_LAYER 2.75 //projectiles won't hit objects at or below this layer if possible #define TABLE_LAYER 2.8 +#define GATEWAY_UNDERLAY_LAYER 2.85 #define BELOW_OBJ_LAYER 2.9 #define LOW_ITEM_LAYER 2.95 //#define OBJ_LAYER 3 //For easy recordkeeping; this is a byond define -#define CLOSED_BLASTDOOR_LAYER 3.05 #define CLOSED_DOOR_LAYER 3.1 #define CLOSED_FIREDOOR_LAYER 3.11 -#define SHUTTER_LAYER 3.12 // HERE BE DRAGONS #define ABOVE_OBJ_LAYER 3.2 +#define CLOSED_BLASTDOOR_LAYER 3.3 // ABOVE WINDOWS AND DOORS +#define SHUTTER_LAYER 3.3 // HERE BE DRAGONS #define ABOVE_WINDOW_LAYER 3.3 #define SIGN_LAYER 3.4 +#define CORGI_ASS_PIN_LAYER 3.41 #define NOT_HIGH_OBJ_LAYER 3.5 #define HIGH_OBJ_LAYER 3.6 - #define BELOW_MOB_LAYER 3.7 + +// GAME_PLANE_FOV_HIDDEN layers +#define LOW_MOB_LAYER 3.75 #define LYING_MOB_LAYER 3.8 -#define SPACEPOD_LAYER 3.9 // yogs +#define VEHICLE_LAYER 3.9 +#define MOB_BELOW_PIGGYBACK_LAYER 3.94 //#define MOB_LAYER 4 //For easy recordkeeping; this is a byond define +#define MOB_SHIELD_LAYER 4.01 +#define MOB_ABOVE_PIGGYBACK_LAYER 4.06 +#define MOB_UPPER_LAYER 4.07 +#define HITSCAN_PROJECTILE_LAYER 4.09 //above all mob but still hidden by FoV + +// GAME_PLANE_UPPER layers #define ABOVE_MOB_LAYER 4.1 #define WALL_OBJ_LAYER 4.25 +#define TRAM_SIGNAL_LAYER 4.26 +// WALL_PLANE_UPPER layers #define EDGED_TURF_LAYER 4.3 #define ON_EDGED_TURF_LAYER 4.35 -#define LARGE_MOB_LAYER 4.4 -#define ABOVE_ALL_MOB_LAYER 4.5 -#define SPACEVINE_LAYER 4.8 -#define SPACEVINE_MOB_LAYER 4.9 +// GAME_PLANE_UPPER_FOV_HIDDEN layers +#define SPACEVINE_LAYER 4.4 +#define LARGE_MOB_LAYER 4.5 +#define SPACEVINE_MOB_LAYER 4.6 + +// Intermediate layer used by both GAME_PLANE_FOV_HIDDEN and ABOVE_GAME_PLANE +#define ABOVE_ALL_MOB_LAYER 4.7 + +// ABOVE_GAME_PLANE layers +#define NAVIGATION_EYE_LAYER 4.9 //#define FLY_LAYER 5 //For easy recordkeeping; this is a byond define #define GASFIRE_LAYER 5.05 #define RIPPLE_LAYER 5.1 -#define GHOST_LAYER 6 -#define LOW_LANDMARK_LAYER 9 -#define MID_LANDMARK_LAYER 9.1 -#define HIGH_LANDMARK_LAYER 9.2 -#define AREA_LAYER 10 -#define MASSIVE_OBJ_LAYER 11 -#define POINT_LAYER 12 +/** + * The layer of the visual overlay used in the submerge element. + * The vis overlay inherits the planes of the movables it's attached to (that also have KEEP_TOGETHER added) + * We just have to make sure the visual overlay is rendered above all the other overlays of those movables. + */ +#define WATER_VISUAL_OVERLAY_LAYER 1000 -#define EMISSIVE_BLOCKER_PLANE 12 -#define EMISSIVE_BLOCKER_LAYER 12 -#define EMISSIVE_BLOCKER_RENDER_TARGET "*EMISSIVE_BLOCKER_PLANE" +//---------- LIGHTING ------------- -#define CHAT_LAYER 12.0001 // Do not insert layers between these two values -#define CHAT_LAYER_MAX 12.9999 +// LIGHTING_PLANE layers +// The layer of turf underlays starts at 0.01 and goes up by 0.01 +// Based off the z level. No I do not remember why, should check that +/// Typically overlays, that "hide" portions of the turf underlay layer +/// I'm allotting 100 z levels before this breaks. That'll never happen +/// --Lemon +#define LIGHTING_MASK_LAYER 10 +/// Misc things that draw on the turf lighting plane +/// Space, solar beams, etc +#define LIGHTING_PRIMARY_LAYER 15 +/// Stuff that needs to draw above everything else on this plane +#define LIGHTING_ABOVE_ALL 20 -#define EMISSIVE_PLANE 13 -#define EMISSIVE_LAYER 13 -#define EMISSIVE_RENDER_TARGET "*EMISSIVE_PLANE" -#define EMISSIVE_UNBLOCKABLE_PLANE 14 -#define EMISSIVE_UNBLOCKABLE_LAYER 14 -#define EMISSIVE_UNBLOCKABLE_RENDER_TARGET "*EMISSIVE_UNBLOCKABLE_PLANE" +//---------- EMISSIVES ------------- +//Layering order of these is not particularly meaningful. +//Important part is the seperation of the planes for control via plane_master -#define LIGHTING_PLANE 15 -#define LIGHTING_LAYER 15 -#define LIGHTING_RENDER_TARGET "LIGHT_PLANE" +#define EMISSIVE_BLOCKER_PLANE 12 +#define EMISSIVE_BLOCKER_LAYER 12 +#define EMISSIVE_BLOCKER_RENDER_TARGET "*EMISSIVE_BLOCKER_PLANE" -#define RAD_TEXT_LAYER 15.1 +/// The layer you should use if you _really_ don't want an emissive overlay to be blocked. +#define EMISSIVE_LAYER_UNBLOCKABLE 9999 +///--------------- FULLSCREEN IMAGES ------------ -#define O_LIGHTING_VISUAL_PLANE 16 -#define O_LIGHTING_VISUAL_LAYER 16 -#define O_LIGHTING_VISUAL_RENDER_TARGET "O_LIGHT_VISUAL_PLANE" +#define FLASH_LAYER 1 +#define FULLSCREEN_LAYER 2 +#define UI_DAMAGE_LAYER 3 +#define BLIND_LAYER 4 +#define CRIT_LAYER 5 +#define CURSE_LAYER 6 +#define ECHO_LAYER 7 +#define PARRY_LAYER 8 -#define ABOVE_LIGHTING_PLANE 200 //things that should render ignoring lightning -#define ABOVE_LIGHTING_LAYER 17 -#define ABOVE_LIGHTING_RENDER_TARGET "ABOVE_LIGHTING_PLANE" +#define FOV_EFFECT_LAYER 100 -#define FLOOR_OPENSPACE_PLANE 17 -#define OPENSPACE_LAYER 17 +///--------------- FULLSCREEN RUNECHAT BUBBLES ------------ +/// Bubble for typing indicators +#define TYPING_LAYER 500 -#define BYOND_LIGHTING_PLANE 18 -#define BYOND_LIGHTING_LAYER 18 -#define BYOND_LIGHTING_RENDER_TARGET "BYOND_LIGHTING_PLANE" +#define RADIAL_BACKGROUND_LAYER 0 +///1000 is an unimportant number, it's just to normalize copied layers +#define RADIAL_CONTENT_LAYER 1000 -#define CAMERA_STATIC_PLANE 19 -#define CAMERA_STATIC_LAYER 19 -#define CAMERA_STATIC_RENDER_TARGET "CAMERA_STATIC_PLANE" +#define ADMIN_POPUP_LAYER 1 -#define RUNECHAT_PLANE 20 +///Layer for screentips +#define SCREENTIP_LAYER 4 -#define BALLOON_CHAT_PLANE 20.9 +/// Layer for tutorial instructions +#define TUTORIAL_INSTRUCTIONS_LAYER 5 -//HUD layer defines +/// Layer for light overlays +#define LIGHT_DEBUG_LAYER 6 -#define FULLSCREEN_PLANE 20 -#define FLASH_LAYER 20 -#define FULLSCREEN_LAYER 20.1 -#define UI_DAMAGE_LAYER 20.2 -#define BLIND_LAYER 20.3 -#define CRIT_LAYER 20.4 -#define CURSE_LAYER 20.5 -#define ECHO_LAYER 20.6 -#define FULLSCREEN_RENDER_TARGET "FULLSCREEN_PLANE" +#define LOBBY_BACKGROUND_LAYER 3 +#define LOBBY_BUTTON_LAYER 4 -#define HUD_PLANE 21 -#define HUD_LAYER 21 -#define HUD_RENDER_TARGET "HUD_PLANE" -#define ABOVE_HUD_PLANE 22 -#define ABOVE_HUD_LAYER 22 -#define ABOVE_HUD_RENDER_TARGET "ABOVE_HUD_PLANE" +///Layer for lobby menu collapse button +#define LOBBY_BELOW_MENU_LAYER 2 +///Layer for lobby menu background image and main buttons (Join/Ready, Observe, Charater Prefs) +#define LOBBY_MENU_LAYER 3 +///Layer for lobby menu shutter, which covers up the menu to collapse/expand it +#define LOBBY_SHUTTER_LAYER 4 +///Layer for lobby menu buttons that are hanging away from and lower than the main panel +#define LOBBY_BOTTOM_BUTTON_LAYER 5 -#define SPLASHSCREEN_LAYER 23 -#define SPLASHSCREEN_PLANE 23 -#define SPLASHSCREEN_RENDER_TARGET "SPLASHSCREEN_PLANE" +///cinematics are "below" the splash screen +#define CINEMATIC_LAYER -1 +///Plane master controller keys +#define PLANE_MASTERS_GAME "plane_masters_game" +#define PLANE_MASTERS_NON_MASTER "plane_masters_non_master" +#define PLANE_MASTERS_COLORBLIND "plane_masters_colorblind" -//-------------------- Rendering --------------------- -#define RENDER_PLANE_GAME 100 -#define RENDER_PLANE_NON_GAME 101 +//Plane master critical flags +//Describes how different plane masters behave when they are being culled for performance reasons +/// This plane master will not go away if its layer is culled. useful for preserving effects +#define PLANE_CRITICAL_DISPLAY (1<<0) +/// This plane master will temporarially remove relays to all other planes +/// Allows us to retain the effects of a plane while cutting off the changes it makes +#define PLANE_CRITICAL_NO_RELAY (1<<1) +/// We assume this plane master has a render target starting with *, it'll be removed, forcing it to render in place +#define PLANE_CRITICAL_CUT_RENDER (1<<2) +#define PLANE_CRITICAL_FUCKO_PARALLAX (PLANE_CRITICAL_DISPLAY|PLANE_CRITICAL_NO_RELAY|PLANE_CRITICAL_CUT_RENDER) -///1000 is an unimportant number, it's just to normalize copied layers -#define RADIAL_CONTENT_LAYER 1000 +/// A value of /datum/preference/numeric/multiz_performance that disables the option +#define MULTIZ_PERFORMANCE_DISABLE -1 +/// We expect at most 3 layers of multiz +/// Increment this define if you make a huge map. We unit test for it too just to make it easy for you +/// If you modify this, you'll need to modify the tsx file too +#define MAX_EXPECTED_Z_DEPTH 3 diff --git a/code/__DEFINES/lighting.dm b/code/__DEFINES/lighting.dm index 57271b6bcdee..2bfb2d8313bc 100644 --- a/code/__DEFINES/lighting.dm +++ b/code/__DEFINES/lighting.dm @@ -26,6 +26,10 @@ #define LIGHTING_FALLOFF 1 /// use lambertian shading for light sources #define LIGHTING_LAMBERTIAN 0 +/// light UNDER the floor. primarily used for starlight, shouldn't fuck with this +#define LIGHTING_HEIGHT_SPACE -0.5 +/// light ON the floor +#define LIGHTING_HEIGHT_FLOOR 0 /// height off the ground of light sources on the pseudo-z-axis, you should probably leave this alone #define LIGHTING_HEIGHT 1 /// 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. @@ -56,12 +60,16 @@ ///How many tiles standard fires glow. #define LIGHT_RANGE_FIRE 3 -#define LIGHTING_PLANE_ALPHA_VISIBLE 255 -#define LIGHTING_PLANE_ALPHA_NV_TRAIT 245 -#define LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE 192 -/// For lighting alpha, small amounts lead to big changes. even at 128 its hard to figure out what is dark and what is light, at 64 you almost can't even tell. -#define LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE 128 -#define LIGHTING_PLANE_ALPHA_INVISIBLE 0 +// Lighting cutoff defines +// These are a percentage of how much darkness to cut off (in rgb) +#define LIGHTING_CUTOFF_VISIBLE 0 +#define LIGHTING_CUTOFF_REAL_LOW 4.5 +#define LIGHTING_CUTOFF_MEDIUM 15 +#define LIGHTING_CUTOFF_HIGH 30 +#define LIGHTING_CUTOFF_FULLBRIGHT 100 + +/// What counts as being able to see in the dark +#define LIGHTING_NIGHTVISION_THRESHOLD 10 //lighting area defines /// dynamic lighting disabled (area stays at full brightness) @@ -86,10 +94,13 @@ #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 +#define EMISSIVE_BLOCK_GENERIC 0 /// 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 +#define EMISSIVE_BLOCK_UNIQUE 1 +/// Don't block any emissives. Useful for things like, pieces of paper? +#define EMISSIVE_BLOCK_NONE 2 /// Returns the red part of a #RRGGBB hex sequence as number #define GETREDPART(hexa) hex2num(copytext(hexa, 2, 4)) @@ -114,3 +125,26 @@ do { \ source.lum_b = 1; \ }; \ } while (FALSE) + + + + +#define _EMISSIVE_COLOR(val) list(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, val,val,val,0) +/// 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 _EMISSIVE_COLOR(1) +/// A globaly cached version of [EMISSIVE_COLOR] for quick access. +GLOBAL_LIST_INIT(emissive_color, EMISSIVE_COLOR) + +#define _EM_BLOCK_COLOR(val) list(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,val, 0,0,0,0) +/// 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 _EM_BLOCK_COLOR(1) +/// 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. +/// KEEP_APART to prevent parent hooking, KEEP_TOGETHER for children, and we reset the color and alpha of our parent so nothing gets overriden +#define EMISSIVE_APPEARANCE_FLAGS (KEEP_APART|KEEP_TOGETHER|RESET_COLOR|RESET_ALPHA) +/// 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) diff --git a/code/__DEFINES/map_switch.dm b/code/__DEFINES/map_switch.dm new file mode 100644 index 000000000000..dbb0059786ee --- /dev/null +++ b/code/__DEFINES/map_switch.dm @@ -0,0 +1,8 @@ +/// Uses the left operator when compiling, uses the right operator when not compiling. +// Currently uses the CBT macro, but if http://www.byond.com/forum/post/2831057 is ever added, +// or if map tools ever agree on a standard, this should switch to use that. +#ifdef CBT +#define MAP_SWITCH(compile_time, map_time) ##compile_time +#else +#define MAP_SWITCH(compile_time, map_time) ##map_time +#endif diff --git a/code/__DEFINES/mapping.dm b/code/__DEFINES/mapping.dm new file mode 100644 index 000000000000..3c08679e2a4b --- /dev/null +++ b/code/__DEFINES/mapping.dm @@ -0,0 +1,6 @@ +// Defines for SSmapping's multiz_levels +/// TRUE if we're ok with going up +#define Z_LEVEL_UP 1 +/// TRUE if we're ok with going down +#define Z_LEVEL_DOWN 2 +#define LARGEST_Z_LEVEL_INDEX Z_LEVEL_DOWN diff --git a/code/__DEFINES/maps.dm b/code/__DEFINES/maps.dm index 6bacc65b3ea1..635b395df8bc 100644 --- a/code/__DEFINES/maps.dm +++ b/code/__DEFINES/maps.dm @@ -140,3 +140,5 @@ require only minor tweaks. #define PLACE_LAVA_RUIN "lavaland" #define PLACE_ICE_RUIN "icesurface" #define PLACE_ICE_UNDERGROUND_RUIN "iceunderground" +///boolean - does this z disable parallax? +#define ZTRAIT_NOPARALLAX "No Parallax" diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index f83f70e94652..7b1121f491dc 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -422,8 +422,14 @@ GLOBAL_LIST_INIT(donor_pdas, list(PDA_COLOR_NORMAL, PDA_COLOR_TRANSPARENT, PDA_C // Used by PDA and cartridge code to reduce repetitiveness of spritesheets #define PDAIMG(what) {""} +#define NEGATIVE_GRAVITY -1 + #define STANDARD_GRAVITY 1 //Anything above this is high gravity, anything below no grav #define GRAVITY_DAMAGE_TRESHOLD 3 //Starting with this value gravity will start to damage mobs +/// The scaling factor for high gravity damage. +#define GRAVITY_DAMAGE_SCALING 3.5 +/// The maximum [BRUTE] damage a mob can take from high gravity per second. +#define GRAVITY_DAMAGE_MAXIMUM 4.5 #define CAMERA_NO_GHOSTS 0 #define CAMERA_SEE_GHOSTS_BASIC 1 @@ -480,3 +486,6 @@ GLOBAL_LIST_INIT(donor_pdas, list(PDA_COLOR_NORMAL, PDA_COLOR_TRANSPARENT, PDA_C #define ui_vamprank_display "WEST:6,CENTER-2:-5" /// 6 pixels to the right, zero tiles & 5 pixels DOWN. #define ui_sunlight_display "WEST:6,CENTER-0:0" + + + diff --git a/code/__DEFINES/movement.dm b/code/__DEFINES/movement.dm index c26cc24925b4..ecbd6e8fc6d6 100644 --- a/code/__DEFINES/movement.dm +++ b/code/__DEFINES/movement.dm @@ -14,3 +14,56 @@ GLOBAL_VAR_INIT(glide_size_multiplier, 1.0) /// The whole result is then clamped to within the range above. /// Not very readable but it works #define DELAY_TO_GLIDE_SIZE(delay) (clamp(((32 / max((delay) / world.tick_lag, 1)) * GLOB.glide_size_multiplier), MIN_GLIDE_SIZE, MAX_GLIDE_SIZE)) + + +/** + * currently_z_moving defines. Higher numbers mean higher priority. + * This one is for falling down open space from stuff such as deleted tile, pit grate... + */ +#define CURRENTLY_Z_FALLING 1 +/// currently_z_moving is set to this in zMove() if 0. +#define CURRENTLY_Z_MOVING_GENERIC 2 +/// This one is for falling down open space from movement. +#define CURRENTLY_Z_FALLING_FROM_MOVE 3 +/// This one is for going upstairs. +#define CURRENTLY_Z_ASCENDING 4 + +/// possible bitflag return values of [atom/proc/intercept_zImpact] calls +/// Stops the movable from falling further and crashing on the ground. Example: stairs. +#define FALL_INTERCEPTED (1<<0) +/// Suppresses the "[movable] falls through [old_turf]" message because it'd make little sense in certain contexts like climbing stairs. +#define FALL_NO_MESSAGE (1<<1) +/// Used when the whole intercept_zImpact forvar loop should be stopped. For example: when someone falls into the supermatter and becomes dust. +#define FALL_STOP_INTERCEPTING (1<<2) +/// Used when the grip on a pulled object shouldn't be broken. +#define FALL_RETAIN_PULL (1<<3) + +/// Runs check_pulling() by the end of [/atom/movable/proc/zMove] for every movable that's pulling something. Should be kept enabled unless you know what you are doing. +#define ZMOVE_CHECK_PULLING (1<<0) +/// Checks if pulledby is nearby. if not, stop being pulled. +#define ZMOVE_CHECK_PULLEDBY (1<<1) +/// flags for different checks done in [/atom/movable/proc/can_z_move]. Should be self-explainatory. +#define ZMOVE_FALL_CHECKS (1<<2) +#define ZMOVE_CAN_FLY_CHECKS (1<<3) +#define ZMOVE_INCAPACITATED_CHECKS (1<<4) +/// Doesn't call zPassIn() and zPassOut() +#define ZMOVE_IGNORE_OBSTACLES (1<<5) +/// Gives players chat feedbacks if they're unable to move through z levels. +#define ZMOVE_FEEDBACK (1<<6) +/// Whether we check the movable (if it exists) the living mob is buckled on or not. +#define ZMOVE_ALLOW_BUCKLED (1<<7) +/// If the movable is actually ventcrawling vertically. +#define ZMOVE_VENTCRAWLING (1<<8) +/// Includes movables that're either pulled by the source or mobs buckled to it in the list of moving movables. +#define ZMOVE_INCLUDE_PULLED (1<<9) +/// Skips check for whether the moving atom is anchored or not. +#define ZMOVE_ALLOW_ANCHORED (1<<10) + +#define ZMOVE_CHECK_PULLS (ZMOVE_CHECK_PULLING|ZMOVE_CHECK_PULLEDBY) + +/// Flags used in "Move Upwards" and "Move Downwards" verbs. +#define ZMOVE_FLIGHT_FLAGS (ZMOVE_CAN_FLY_CHECKS|ZMOVE_INCAPACITATED_CHECKS|ZMOVE_CHECK_PULLS|ZMOVE_ALLOW_BUCKLED) +/// Used when walking upstairs +#define ZMOVE_STAIRS_FLAGS (ZMOVE_CHECK_PULLEDBY|ZMOVE_ALLOW_BUCKLED) +/// Used for falling down open space. +#define ZMOVE_FALL_FLAGS (ZMOVE_FALL_CHECKS|ZMOVE_ALLOW_BUCKLED) diff --git a/code/__DEFINES/multiz.dm b/code/__DEFINES/multiz.dm new file mode 100644 index 000000000000..370eaa8ba459 --- /dev/null +++ b/code/__DEFINES/multiz.dm @@ -0,0 +1,8 @@ +/// Attempt to get the turf below the provided one according to Z traits +#define GET_TURF_BELOW(turf) ( \ + (turf.turf_flags & RESERVATION_TURF) ? SSmapping.get_reservation_from_turf(turf)?.get_turf_below(turf) : \ + (!(turf) || !length(SSmapping.multiz_levels) || !SSmapping.multiz_levels[(turf).z][Z_LEVEL_DOWN]) ? null : get_step((turf), DOWN)) +/// Attempt to get the turf above the provided one according to Z traits +#define GET_TURF_ABOVE(turf) ( \ + (turf.turf_flags & RESERVATION_TURF) ? SSmapping.get_reservation_from_turf(turf)?.get_turf_above(turf) : \ + (!(turf) || !length(SSmapping.multiz_levels) || !SSmapping.multiz_levels[(turf).z][Z_LEVEL_UP]) ? null : get_step((turf), UP)) diff --git a/code/__DEFINES/obj_flags.dm b/code/__DEFINES/obj_flags.dm index 4c5d348b082c..420a92373891 100644 --- a/code/__DEFINES/obj_flags.dm +++ b/code/__DEFINES/obj_flags.dm @@ -86,3 +86,8 @@ #define ADD_CLOTHING_TRAIT(mob, trait) ADD_TRAIT(mob, trait, "[CLOTHING_TRAIT]_[REF(src)]") /// Wrapper for removing clothing based traits #define REMOVE_CLOTHING_TRAIT(mob, trait) REMOVE_TRAIT(mob, trait, "[CLOTHING_TRAIT]_[REF(src)]") + +#define BLOCK_Z_OUT_DOWN (1<<5) // Should this object block z falling from loc? +#define BLOCK_Z_OUT_UP (1<<6) // Should this object block z uprise from loc? +#define BLOCK_Z_IN_DOWN (1<<7) // Should this object block z falling from above? +#define BLOCK_Z_IN_UP (1<<8) // Should this object block z uprise from below? diff --git a/code/__DEFINES/sight.dm b/code/__DEFINES/sight.dm index 862c851f187f..645e00941359 100644 --- a/code/__DEFINES/sight.dm +++ b/code/__DEFINES/sight.dm @@ -1,3 +1,5 @@ +#define INVISIBILITY_NONE 0 + #define SEE_INVISIBLE_MINIMUM 5 #define INVISIBILITY_LIGHTING 20 @@ -10,6 +12,8 @@ //#define SEE_INVISIBLE_LEVEL_TWO 45 //currently unused //#define INVISIBILITY_LEVEL_TWO 45 //currently unused +#define INVISIBILITY_REVENANT 50 + #define INVISIBILITY_OBSERVER 60 #define SEE_INVISIBLE_OBSERVER 60 @@ -17,16 +21,58 @@ #define INVISIBILITY_ABSTRACT 101 //only used for abstract objects (e.g. spacevine_controller), things that are not really there. -#define BORGMESON (1<<0) -#define BORGMESON_NIGHTVISION (1<<1) -#define BORGTHERM (1<<2) -#define BORGXRAY (1<<3) -#define BORGMATERIAL (1<<4) +#define BORGMESON (1<<0) +#define BORGTHERM (1<<1) +#define BORGXRAY (1<<2) +#define BORGMATERIAL (1<<3) //for clothing visor toggles, these determine which vars to toggle -#define VISOR_FLASHPROTECT (1<<0) -#define VISOR_TINT (1<<1) -#define VISOR_VISIONFLAGS (1<<2) //all following flags only matter for glasses -#define VISOR_DARKNESSVIEW (1<<3) -#define VISOR_INVISVIEW (1<<4) +#define VISOR_FLASHPROTECT (1<<0) +#define VISOR_TINT (1<<1) +#define VISOR_VISIONFLAGS (1<<2) //all following flags only matter for glasses +#define VISOR_INVISVIEW (1<<3) + +// BYOND internal values for the sight flags +// See [https://www.byond.com/docs/ref/#/mob/var/sight] +/// can't see anything +//#define BLIND (1<<0) +/// can see all mobs, no matter what +//#define SEE_MOBS (1<<2) +/// can see all objs, no matter what +//#define SEE_OBJS (1<<3) +// can see all turfs (and areas), no matter what +//#define SEE_TURFS (1<<4) +/// can see self, no matter what +//#define SEE_SELF (1<<5) +/// can see infra-red objects (different sort of luminosity, essentially a copy of it, one we do not use) +//#define SEE_INFRA (1<<6) +/// if an object is located on an unlit area, but some of its pixels are +/// in a lit area (via pixel_x,y or smooth movement), can see those pixels +//#define SEE_PIXELS (1<<8) +/// can see through opaque objects +//#define SEE_THRU (1<<9) +/// render dark tiles as blackness (Note, this basically means we draw dark tiles to plane 0) +/// we can then hijack that plane with a plane master, and start drawing it anywhere we want +/// NOTE: this does not function with the SIDE_MAP map format. So we can't. :( +//#define SEE_BLACKNESS (1<<10) + +/// Bitfield of sight flags that show THINGS but no lighting +/// Since lighting is an underlay on turfs, this is everything but that +#define SEE_AVOID_TURF_BLACKNESS (SEE_MOBS|SEE_OBJS) + +//------------------------ +// INVISIBILITY PRIORITIES + +#define INVISIBILITY_PRIORITY_ADMIN 100 +#define INVISIBILITY_PRIORITY_TURRET_COVER 20 +#define INVISIBILITY_PRIORITY_BASIC_ANTI_INVISIBILITY 10 +#define INVISIBILITY_PRIORITY_NONE 0 + +//------------------------ +// INVISIBILITY SOURCE IDS +// Though don't feel the need to add one here if you have a simple effect that +// gets added and/or removed in only one place near eachother in the code. + +#define INVISIBILITY_SOURCE_INVISIMIN "invisimin" +#define INVISIBILITY_SOURCE_STEALTHMODE "stealthmode" diff --git a/code/__DEFINES/space.dm b/code/__DEFINES/space.dm new file mode 100644 index 000000000000..eff2aebff3e7 --- /dev/null +++ b/code/__DEFINES/space.dm @@ -0,0 +1,5 @@ +#define SPACE_SIGNAL_GPSTAG "Distant Signal" + +/// Every mob in the game will have a screen object sitting inside them with a reference matching this +/// So you can render_source against it and never need to update it again +#define SPACE_OVERLAY_RENDER_TARGET(offset) "*space_overlay_target[offset]" diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 1229612db7fc..da03b270e497 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -491,6 +491,31 @@ #define TRAIT_BEING_BLADE_SHIELDED "being_blade_shielded" /// things with this trait are treated as having no access in /obj/proc/check_access(obj/item) #define TRAIT_ALWAYS_NO_ACCESS "alwaysnoaccess" +/// Gives us Night vision +#define TRAIT_TRUE_NIGHT_VISION "true_night_vision" +/// Gives us turf vision through walls and slight night vision +#define TRAIT_MESON_VISION "meson_vision" +/// Ignores darkness for hearing +#define TRAIT_HEAR_THROUGH_DARKNESS "hear_through_darkness" +/// "Magic" trait that blocks the mob from moving or interacting with anything. Used for transient stuff like mob transformations or incorporality in special cases. +/// Will block movement, `Life()` (!!!), and other stuff based on the mob. +#define TRAIT_NO_TRANSFORM "block_transformations" +/// Does nothing on its own, applied via status effect. +#define TRAIT_STASIS "in_stasis" +/* Traits for ventcrawling. + * Both give access to ventcrawling, but *_NUDE requires the user to be + * wearing no clothes and holding no items. If both present, *_ALWAYS + * takes precedence. + */ +#define TRAIT_VENTCRAWLER_ALWAYS "ventcrawler_always" +#define TRAIT_VENTCRAWLER_NUDE "ventcrawler_nude" +#define TRAIT_SPACEWALK "spacewalk" +///Turf trait for when a turf is transparent +#define TURF_Z_TRANSPARENT_TRAIT "turf_z_transparent" +/// This mob overrides certian SSlag_switch measures with this special trait +#define TRAIT_BYPASS_MEASURES "bypass_lagswitch_measures" +/// This movable atom has the explosive block element +#define TRAIT_BLOCKING_EXPLOSIVES "blocking_explosives" ///Trait for dryable items #define TRAIT_DRYABLE "trait_dryable" ///Trait for dried items diff --git a/code/__DEFINES/turfs.dm b/code/__DEFINES/turfs.dm index 549af806ee03..303384684171 100644 --- a/code/__DEFINES/turfs.dm +++ b/code/__DEFINES/turfs.dm @@ -10,3 +10,24 @@ #define ALL_TURFS(...) block(locate(1, 1, 1), locate(world.maxx, world.maxy, world.maxz)) #define IS_OPAQUE_TURF(turf) (turf.directional_opacity == ALL_CARDINALS) + +/// The pipes, disposals, and wires are hidden +#define UNDERFLOOR_HIDDEN 0 +/// The pipes, disposals, and wires are visible but cannot be interacted with +#define UNDERFLOOR_VISIBLE 1 +/// The pipes, disposals, and wires are visible and can be interacted with +#define UNDERFLOOR_INTERACTABLE 2 + +#define TURF_FROM_COORDS_LIST(List) (locate(List[1], List[2], List[3])) +/// Returns a list of turfs in the rectangle specified by BOTTOM LEFT corner and height/width, checks for being outside the world border for you +#define CORNER_BLOCK(corner, width, height) CORNER_BLOCK_OFFSET(corner, width, height, 0, 0) + +/// Returns a list of turfs similar to CORNER_BLOCK but with offsets +#define CORNER_BLOCK_OFFSET(corner, width, height, offset_x, offset_y) ((block(locate(corner.x + offset_x, corner.y + offset_y, corner.z), locate(min(corner.x + (width - 1) + offset_x, world.maxx), min(corner.y + (height - 1) + offset_y, world.maxy), corner.z)))) + +/// Returns an outline (neighboring turfs) of the given block +#define CORNER_OUTLINE(corner, width, height) ( \ + CORNER_BLOCK_OFFSET(corner, width + 2, 1, -1, -1) + \ + CORNER_BLOCK_OFFSET(corner, width + 2, 1, -1, height) + \ + CORNER_BLOCK_OFFSET(corner, 1, height, -1, 0) + \ + CORNER_BLOCK_OFFSET(corner, 1, height, width, 0)) diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index 0bb4f866b3cf..71deadd54f32 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -743,6 +743,31 @@ /proc/sort_list(list/list_to_sort, cmp=/proc/cmp_text_asc) return sortTim(list_to_sort.Copy(), cmp) +/** + * Picks a random element from a list based on a weighting system. + * For example, given the following list: + * A = 6, B = 3, C = 1, D = 0 + * A would have a 60% chance of being picked, + * B would have a 30% chance of being picked, + * C would have a 10% chance of being picked, + * and D would have a 0% chance of being picked. + * You should only pass integers in. + */ +/proc/pick_weight(list/list_to_pick) + var/total = 0 + var/item + for(item in list_to_pick) + if(!list_to_pick[item]) + list_to_pick[item] = 0 + total += list_to_pick[item] + + total = rand(1, total) + for(item in list_to_pick) + total -= list_to_pick[item] + if(total <= 0 && list_to_pick[item]) + return item + + return null /// ORs two lazylists together without inserting errant nulls, returning a new list and not modifying the existing lists. #define LAZY_LISTS_OR(left_list, right_list)\ diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm index d678e57e513d..0815635613f6 100644 --- a/code/__HELPERS/_logging.dm +++ b/code/__HELPERS/_logging.dm @@ -7,13 +7,8 @@ #define WRITE_FILE(file, text) DIRECT_OUTPUT(file, text) #define READ_FILE(file, text) DIRECT_INPUT(file, text) //This is an external call, "true" and "false" are how rust parses out booleans -#ifdef EXTOOLS_LOGGING -#define WRITE_LOG(log, text) extools_log_write(log, text, TRUE) -#define WRITE_LOG_NO_FORMAT(log, text) extools_log_write(log, text, FALSE) -#else #define WRITE_LOG(log, text) rustg_log_write(log, "\[[worldtime2text()]\] [text]", "true") #define WRITE_LOG_NO_FORMAT(log, text) rustg_log_write(log, text, "false") -#endif //print a warning message to world.log #define WARNING(MSG) warning("[MSG] in [__FILE__] at line [__LINE__] src: [UNLINT(src)] usr: [usr].") @@ -255,13 +250,7 @@ /* Close open log handles. This should be called as late as possible, and no logging should hapen after. */ /proc/shutdown_logging() -#ifdef EXTOOLS_LOGGING - extools_finalize_logging() -#else rustg_log_close_all() -#endif - - /* Helper procs for building detailed log lines */ /proc/key_name(whom, include_link = null, include_name = TRUE) diff --git a/code/__HELPERS/_planes.dm b/code/__HELPERS/_planes.dm new file mode 100644 index 000000000000..769c4283ea62 --- /dev/null +++ b/code/__HELPERS/_planes.dm @@ -0,0 +1,88 @@ +// This file contains helper macros for plane operations +// See the planes section of Visuals.md for more detail, but essentially +// When we render multiz, we do it by placing all atoms on lower levels on well, lower planes +// This is done with stacks of plane masters (things we use to apply effects to planes) +// These macros exist to facilitate working with this system, and other associated small bits + +/// Takes an atom to change the plane of, a new plane value, and something that can be used as a reference to a z level as input +/// Modifies the new value to match the plane we actually want. Note, if you pass in an already offset plane the offsets will add up +/// Use PLANE_TO_TRUE() to avoid this +#define SET_PLANE(thing, new_value, z_reference) (thing.plane = MUTATE_PLANE(new_value, z_reference)) + +/// Takes a plane and a z reference, and offsets the plane by the mutation +/// The SSmapping.max_plane_offset bit here is technically redundant, but saves a bit of work in the base case +/// And the base case is important to me. Non multiz shouldn't get hit too bad by this code +#define MUTATE_PLANE(new_value, z_reference) ((SSmapping.max_plane_offset) ? GET_NEW_PLANE(new_value, GET_TURF_PLANE_OFFSET(z_reference)) : (new_value)) + +/// Takes a z reference that we are unsure of, sanity checks it +/// Returns either its offset, or 0 if it's not a valid ref +/// Will return the reference's PLANE'S offset if we can't get anything out of the z level. We do our best +#define GET_TURF_PLANE_OFFSET(z_reference) ((SSmapping.max_plane_offset && isatom(z_reference)) ? (z_reference.z ? GET_Z_PLANE_OFFSET(z_reference.z) : PLANE_TO_OFFSET(z_reference.plane)) : 0) +/// Essentially just an unsafe version of GET_TURF_PLANE_OFFSET() +/// Takes a z value we returns its offset with a list lookup +/// Will runtime during parts of init. Be careful :) +#define GET_Z_PLANE_OFFSET(z) (SSmapping.z_level_to_plane_offset[z]) + +/// Takes a plane to offset, and the multiplier to use, and well, does the offsetting +/// Respects a blacklist we use to remove redundant plane masters, such as hud objects +#define GET_NEW_PLANE(new_value, multiplier) (SSmapping.plane_offset_blacklist?["[new_value]"] ? new_value : (new_value) - (PLANE_RANGE * (multiplier))) + +// Now for the more niche things + +/// Takes an object, new plane, and multipler, and offsets the plane +/// This is for cases where you have a multipler precalculated, and just want to use it +/// Often an optimization, sometimes a necessity +#define SET_PLANE_W_SCALAR(thing, new_value, multiplier) (thing.plane = GET_NEW_PLANE(new_value, multiplier)) + + +/// Implicit plane set. We take the turf from the object we're changing the plane of, and use ITS z as a spokesperson for our plane value +#define SET_PLANE_IMPLICIT(thing, new_value) SET_PLANE_EXPLICIT(thing, new_value, thing) + +// This is an unrolled and optimized version of SET_PLANE, for use anywhere where you are unsure of a source's "turfness" +// We do also try and guess at what the thing's z level is, even if it's not a z +// The plane is cached to allow for fancy stuff to be eval'd once, rather then often +#define SET_PLANE_EXPLICIT(thing, new_value, source) \ + do {\ + if(SSmapping.max_plane_offset) {\ + var/_cached_plane = new_value;\ + var/turf/_our_turf = get_turf(source);\ + if(_our_turf){\ + thing.plane = GET_NEW_PLANE(_cached_plane, GET_Z_PLANE_OFFSET(_our_turf.z));\ + }\ + else if(source) {\ + thing.plane = GET_NEW_PLANE(_cached_plane, PLANE_TO_OFFSET(source.plane));\ + }\ + else {\ + thing.plane = _cached_plane;\ + }\ + }\ + else {\ + thing.plane = new_value;\ + }\ + }\ + while (FALSE) + +// Now for macros that exist to get info from SSmapping +// Mostly about details of planes, or z levels + +/// Takes a z level, gets the lowest plane offset in its "stack" +#define GET_LOWEST_STACK_OFFSET(z) ((SSmapping.max_plane_offset) ? SSmapping.z_level_to_lowest_plane_offset[z] : 0) +/// Takes a plane, returns the canonical, unoffset plane it represents +#define PLANE_TO_TRUE(plane) ((SSmapping.plane_offset_to_true) ? SSmapping.plane_offset_to_true["[plane]"] : plane) +/// Takes a plane, returns the offset it uses +#define PLANE_TO_OFFSET(plane) ((SSmapping.plane_to_offset) ? SSmapping.plane_to_offset["[plane]"] : plane) +/// Takes a plane, returns TRUE if it is of critical priority, FALSE otherwise +#define PLANE_IS_CRITICAL(plane) ((SSmapping.plane_to_offset) ? !!SSmapping.critical_planes["[plane]"] : FALSE) +/// Takes a true plane, returns the offset planes that would canonically represent it +#define TRUE_PLANE_TO_OFFSETS(plane) ((SSmapping.true_to_offset_planes) ? SSmapping.true_to_offset_planes["[plane]"] : list(plane)) +/// Takes a render target and an offset, returns a canonical render target string for it +#define OFFSET_RENDER_TARGET(render_target, offset) (_OFFSET_RENDER_TARGET(render_target, SSmapping.render_offset_blacklist?["[render_target]"] ? 0 : offset)) +/// Helper macro for the above +/// Honestly just exists to make the pattern of render target strings more readable +#define _OFFSET_RENDER_TARGET(render_target, offset) ("[(render_target)] #[(offset)]") + +// Known issues: +// Potentially too much client load? Hard to tell due to not having a potato pc to hand. +// This is solvable with lowspec preferences, which would not be hard to implement +// Player popups will now render their effects, like overlay lights. this is fixable, but I've not gotten to it +// I think overlay lights can render on the wrong z layer. s fucked diff --git a/code/__HELPERS/cameras.dm b/code/__HELPERS/cameras.dm new file mode 100644 index 000000000000..9d74f3fe71b4 --- /dev/null +++ b/code/__HELPERS/cameras.dm @@ -0,0 +1,35 @@ +/** + * get_camera_list + * + * Builds a list of all available cameras that can be seen to networks_available + * Args: + * networks_available - List of networks that we use to see which cameras are visible to it. + */ +/proc/get_camera_list(list/networks_available) + var/list/all_camera_list = list() + for(var/obj/machinery/camera/camera as anything in GLOB.cameranet.cameras) + all_camera_list.Add(camera) + + camera_sort(all_camera_list) + + var/list/usable_camera_list = list() + + for(var/obj/machinery/camera/camera as anything in all_camera_list) + var/list/tempnetwork = camera.network & networks_available + if(length(tempnetwork)) + usable_camera_list["[camera.c_tag][camera.can_use() ? null : " (Deactivated)"]"] = camera + + return usable_camera_list + +///Sorts the list of cameras by their c_tag to display to players. +/proc/camera_sort(list/camera_list) + var/obj/machinery/camera/camera_comparing_a + var/obj/machinery/camera/camera_comparing_b + + for(var/i = length(camera_list), i > 0, i--) + for(var/j = 1 to i - 1) + camera_comparing_a = camera_list[j] + camera_comparing_b = camera_list[j + 1] + if(sorttext(camera_comparing_a.c_tag, camera_comparing_b.c_tag) < 0) + camera_list.Swap(j, j + 1) + return camera_list diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index 73118506fcf1..06bca876d4bb 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -150,3 +150,6 @@ GLOBAL_VAR_INIT(cmp_field, "name") rhs = ispath(B, /datum/reagent) ? 0 : 1 return lhs - rhs + +/proc/cmp_filter_data_priority(list/A, list/B) + return A["priority"] - B["priority"] diff --git a/code/__HELPERS/colors.dm b/code/__HELPERS/colors.dm index 4a15f36ef6ff..134091a88f88 100644 --- a/code/__HELPERS/colors.dm +++ b/code/__HELPERS/colors.dm @@ -16,3 +16,34 @@ final_color += copytext(color, digit, digit + 1) return final_color + +/// Blends together two colors (passed as 3 or 4 length lists) using the screen blend mode +/// Much like multiply, screen effects the brightness of the resulting color +/// Screen blend will always lighten the resulting color, since before multiplication we invert the colors +/// This makes our resulting output brighter instead of darker +/proc/blend_screen_color(list/first_color, list/second_color) + var/list/output = new /list(4) + + // max out any non existant alphas + if(length(first_color) < 4) + first_color[4] = 255 + if(length(second_color) < 4) + second_color[4] = 255 + + // time to do our blending + for(var/i in 1 to 4) + output[i] = (1 - (1 - first_color[i] / 255) * (1 - second_color[i] / 255)) * 255 + return output + +/// Used to blend together two different color cutoffs +/// Uses the screen blendmode under the hood, essentially just [/proc/blend_screen_color] +/// But paired down and modified to work for our color range +/// Accepts the color cutoffs as two 3 length list(0-100,...) arguments +/proc/blend_cutoff_colors(list/first_color, list/second_color) + var/list/output = new /list(3) + + // Invert the colors, multiply to "darken" (actually lights), then uninvert to get back to what we want + for(var/i in 1 to 3) + output[i] = (1 - (1 - first_color[i] / 100) * (1 - second_color[i] / 100)) * 100 + + return output diff --git a/code/__HELPERS/filters.dm b/code/__HELPERS/filters.dm index 5bc143695e2b..cd44409ddb23 100644 --- a/code/__HELPERS/filters.dm +++ b/code/__HELPERS/filters.dm @@ -1,3 +1,146 @@ +#define ICON_NOT_SET "Not Set" + +//This is stored as a nested list instead of datums or whatever because it json encodes nicely for usage in tgui +GLOBAL_LIST_INIT(master_filter_info, list( + "alpha" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "icon" = ICON_NOT_SET, + "render_source" = "", + "flags" = 0 + ), + "flags" = list( + "MASK_INVERSE" = MASK_INVERSE, + "MASK_SWAP" = MASK_SWAP + ) + ), + "angular_blur" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 1 + ) + ), + // Not implemented, but if this isn't uncommented some windows will just error + // Needs either a proper matrix editor, or just a hook to our existing one + // Issue is filterrific assumes variables will have the same value type if they share the same name, which this violates + // Gotta refactor this sometime + "color" = list( + "defaults" = list( + "color" = matrix(), + "space" = FILTER_COLOR_RGB + ) + ), + "displace" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = null, + "icon" = ICON_NOT_SET, + "render_source" = "" + ) + ), + "drop_shadow" = list( + "defaults" = list( + "x" = 1, + "y" = -1, + "size" = 1, + "offset" = 0, + "color" = COLOR_HALF_TRANSPARENT_BLACK + ) + ), + "blur" = list( + "defaults" = list( + "size" = 1 + ) + ), + "layer" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "icon" = ICON_NOT_SET, + "render_source" = "", + "flags" = FILTER_OVERLAY, + "color" = "", + "transform" = null, + "blend_mode" = BLEND_DEFAULT + ) + ), + "motion_blur" = list( + "defaults" = list( + "x" = 0, + "y" = 0 + ) + ), + "outline" = list( + "defaults" = list( + "size" = 0, + "color" = COLOR_BLACK, + "flags" = NONE + ), + "flags" = list( + "OUTLINE_SHARP" = OUTLINE_SHARP, + "OUTLINE_SQUARE" = OUTLINE_SQUARE + ) + ), + "radial_blur" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 0.01 + ) + ), + "rays" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 16, + "color" = COLOR_WHITE, + "offset" = 0, + "density" = 10, + "threshold" = 0.5, + "factor" = 0, + "flags" = FILTER_OVERLAY | FILTER_UNDERLAY + ), + "flags" = list( + "FILTER_OVERLAY" = FILTER_OVERLAY, + "FILTER_UNDERLAY" = FILTER_UNDERLAY + ) + ), + "ripple" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 1, + "repeat" = 2, + "radius" = 0, + "falloff" = 1, + "flags" = NONE + ), + "flags" = list( + "WAVE_BOUNDED" = WAVE_BOUNDED + ) + ), + "wave" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 1, + "offset" = 0, + "flags" = NONE + ), + "flags" = list( + "WAVE_SIDEWAYS" = WAVE_SIDEWAYS, + "WAVE_BOUNDED" = WAVE_BOUNDED + ) + ) +)) + +#undef ICON_NOT_SET + +//Helpers to generate lists for filter helpers +//This is the only practical way of writing these that actually produces sane lists /proc/alpha_mask_filter(x, y, icon/icon, render_source, flags) . = list("type" = "alpha") if(!isnull(x)) @@ -11,6 +154,78 @@ if(!isnull(flags)) .["flags"] = flags +/proc/angular_blur_filter(x, y, size) + . = list("type" = "angular_blur") + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(size)) + .["size"] = size + +/proc/color_matrix_filter(matrix/in_matrix, space) + . = list("type" = "color") + .["color"] = in_matrix + if(!isnull(space)) + .["space"] = space + +/proc/displacement_map_filter(icon, render_source, x, y, size = 32) + . = list("type" = "displace") + if(!isnull(icon)) + .["icon"] = icon + if(!isnull(render_source)) + .["render_source"] = render_source + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(size)) + .["size"] = size + +/proc/drop_shadow_filter(x, y, size, offset, color) + . = list("type" = "drop_shadow") + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(size)) + .["size"] = size + if(!isnull(offset)) + .["offset"] = offset + if(!isnull(color)) + .["color"] = color + +/proc/gauss_blur_filter(size) + . = list("type" = "blur") + if(!isnull(size)) + .["size"] = size + +/proc/layering_filter(icon, render_source, x, y, flags, color, transform, blend_mode) + . = list("type" = "layer") + if(!isnull(icon)) + .["icon"] = icon + if(!isnull(render_source)) + .["render_source"] = render_source + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(color)) + .["color"] = color + if(!isnull(flags)) + .["flags"] = flags + if(!isnull(transform)) + .["transform"] = transform + if(!isnull(blend_mode)) + .["blend_mode"] = blend_mode + +/proc/motion_blur_filter(x, y) + . = list("type" = "motion_blur") + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + /proc/outline_filter(size, color, flags) . = list("type" = "outline") if(!isnull(size)) @@ -19,3 +234,87 @@ .["color"] = color if(!isnull(flags)) .["flags"] = flags + +/proc/radial_blur_filter(size, x, y) + . = list("type" = "radial_blur") + if(!isnull(size)) + .["size"] = size + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + +/proc/rays_filter(size, color, offset, density, threshold, factor, x, y, flags) + . = list("type" = "rays") + if(!isnull(size)) + .["size"] = size + if(!isnull(color)) + .["color"] = color + if(!isnull(offset)) + .["offset"] = offset + if(!isnull(density)) + .["density"] = density + if(!isnull(threshold)) + .["threshold"] = threshold + if(!isnull(factor)) + .["factor"] = factor + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(flags)) + .["flags"] = flags + +/proc/ripple_filter(radius, size, falloff, repeat, x, y, flags) + . = list("type" = "ripple") + if(!isnull(radius)) + .["radius"] = radius + if(!isnull(size)) + .["size"] = size + if(!isnull(falloff)) + .["falloff"] = falloff + if(!isnull(repeat)) + .["repeat"] = repeat + if(!isnull(flags)) + .["flags"] = flags + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + +/proc/wave_filter(x, y, size, offset, flags) + . = list("type" = "wave") + if(!isnull(size)) + .["size"] = size + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(offset)) + .["offset"] = offset + if(!isnull(flags)) + .["flags"] = flags + +/proc/apply_wibbly_filters(atom/in_atom, length) + for(var/i in 1 to 7) + //This is a very baffling and strange way of doing this but I am just preserving old functionality + var/X + var/Y + var/rsq + do + X = 60*rand() - 30 + Y = 60*rand() - 30 + rsq = X*X + Y*Y + while(rsq<100 || rsq>900) // Yeah let's just loop infinitely due to bad luck what's the worst that could happen? + var/random_roll = rand() + in_atom.add_filter("wibbly-[i]", 5, wave_filter(x = X, y = Y, size = rand() * 2.5 + 0.5, offset = random_roll)) + var/filter = in_atom.get_filter("wibbly-[i]") + animate(filter, offset = random_roll, time = 0, loop = -1, flags = ANIMATION_PARALLEL) + animate(offset = random_roll - 1, time = rand() * 20 + 10) + +/proc/remove_wibbly_filters(atom/in_atom) + var/filter + for(var/i in 1 to 7) + filter = in_atom.get_filter("wibbly-[i]") + animate(filter) + in_atom.remove_filter("wibbly-[i]") diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 924c42e51f1b..ff01317a63bf 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -8,19 +8,6 @@ #define Z_TURFS(ZLEVEL) block(locate(1,1,ZLEVEL), locate(world.maxx, world.maxy, ZLEVEL)) #define CULT_POLL_WAIT 2400 -/// Returns a list of turfs similar to CORNER_BLOCK but with offsets -#define CORNER_BLOCK_OFFSET(corner, width, height, offset_x, offset_y) ( \ - (block(locate(corner.x + offset_x, corner.y + offset_y, corner.z), \ - locate(min(corner.x + (width - 1) + offset_x, world.maxx), \ - min(corner.y + (height - 1) + offset_y, world.maxy), corner.z)))) - -/// Returns an outline (neighboring turfs) of the given block -#define CORNER_OUTLINE(corner, width, height) ( \ - CORNER_BLOCK_OFFSET(corner, width + 2, 1, -1, -1) + \ - CORNER_BLOCK_OFFSET(corner, width + 2, 1, -1, height) + \ - CORNER_BLOCK_OFFSET(corner, 1, height, -1, 0) + \ - CORNER_BLOCK_OFFSET(corner, 1, height, width, 0)) - /proc/get_area_name(atom/X, format_text = FALSE, is_sensor = FALSE) var/area/A = isarea(X) ? X : get_area(X) if(!A) diff --git a/code/__HELPERS/icon_smoothing.dm b/code/__HELPERS/icon_smoothing.dm index 41b9d266c59e..2b00c7c4db39 100644 --- a/code/__HELPERS/icon_smoothing.dm +++ b/code/__HELPERS/icon_smoothing.dm @@ -156,13 +156,11 @@ /turf/closed/wall/diagonal_smooth(adjacencies) adjacencies = reverse_ndir(..()) if(adjacencies) - var/mutable_appearance/underlay_appearance = mutable_appearance(layer = TURF_LAYER, plane = FLOOR_PLANE) + var/mutable_appearance/underlay_appearance = mutable_appearance(layer = TURF_LAYER, offset_spokesman = src, plane = FLOOR_PLANE) var/list/U = list(underlay_appearance) if(fixed_underlay) if(fixed_underlay["space"]) - underlay_appearance.icon = 'icons/turf/space.dmi' - underlay_appearance.icon_state = SPACE_ICON_STATE - underlay_appearance.plane = PLANE_SPACE + generate_space_underlay(underlay_appearance, src) else underlay_appearance.icon = fixed_underlay["icon"] underlay_appearance.icon_state = fixed_underlay["icon_state"] diff --git a/code/__HELPERS/lighting.dm b/code/__HELPERS/lighting.dm new file mode 100644 index 000000000000..2bcaab5761bd --- /dev/null +++ b/code/__HELPERS/lighting.dm @@ -0,0 +1,73 @@ +/// Produces a mutable appearance glued to the [EMISSIVE_PLANE] dyed to be the [EMISSIVE_COLOR]. +/proc/emissive_appearance(icon, icon_state = "", atom/offset_spokesman, layer = FLOAT_LAYER, alpha = 255, appearance_flags = NONE, offset_const) + var/mutable_appearance/appearance = mutable_appearance(icon, icon_state, layer, offset_spokesman, EMISSIVE_PLANE, 255, appearance_flags | EMISSIVE_APPEARANCE_FLAGS, offset_const) + if(alpha == 255) + appearance.color = GLOB.emissive_color + else + var/alpha_ratio = alpha/255 + appearance.color = _EMISSIVE_COLOR(alpha_ratio) + + //Test to make sure emissives with broken or missing icon states are created + // if(PERFORM_ALL_TESTS(focus_only/invalid_emissives)) + // if(icon_state && !icon_exists(icon, icon_state, scream = FALSE)) //Scream set to False so we can have a custom stack_trace + // stack_trace("An emissive appearance was added with non-existant icon_state \"[icon_state]\" in [icon]!") -- fuck this too + + return appearance + +// This is a semi hot proc, so we micro it. saves maybe 150ms +// sorry :) +/proc/fast_emissive_blocker(atom/make_blocker) + var/mutable_appearance/blocker = new() + blocker.icon = make_blocker.icon + blocker.icon_state = make_blocker.icon_state + // blocker.layer = FLOAT_LAYER // Implied, FLOAT_LAYER is default for appearances + blocker.appearance_flags |= make_blocker.appearance_flags | EMISSIVE_APPEARANCE_FLAGS + blocker.dir = make_blocker.dir + if(make_blocker.alpha == 255) + blocker.color = GLOB.em_block_color + else + var/alpha_ratio = make_blocker.alpha/255 + blocker.color = _EM_BLOCK_COLOR(alpha_ratio) + + // Note, we are ok with null turfs, that's not an error condition we'll just default to 0, the error would be + // Not passing ANYTHING in, key difference + SET_PLANE_EXPLICIT(blocker, EMISSIVE_PLANE, make_blocker) + return blocker + +/// Produces a mutable appearance glued to the [EMISSIVE_PLANE] dyed to be the [EM_BLOCK_COLOR]. +/proc/emissive_blocker(icon, icon_state = "", atom/offset_spokesman, layer = FLOAT_LAYER, alpha = 255, appearance_flags = NONE, offset_const) + var/mutable_appearance/appearance = mutable_appearance(icon, icon_state, layer, offset_spokesman, EMISSIVE_PLANE, alpha, appearance_flags | EMISSIVE_APPEARANCE_FLAGS, offset_const) + if(alpha == 255) + appearance.color = GLOB.em_block_color + else + var/alpha_ratio = alpha/255 + appearance.color = _EM_BLOCK_COLOR(alpha_ratio) + return appearance + +/// Takes a non area atom and a threshold +/// Makes it block emissive with any pixels with more alpha then that threshold, with the rest allowing the light to pass +/// Returns a list of objects, automatically added to your vis_contents, that apply this effect +/// QDEL them when appropriate +/proc/partially_block_emissives(atom/make_blocker, alpha_to_leave) + var/static/uid = 0 + uid++ + if(!make_blocker.render_target) + make_blocker.render_target = "partial_emissive_block_[uid]" + + // First, we cut away a constant amount + var/cut_away = (alpha_to_leave - 1) / 255 + var/atom/movable/render_step/color/alpha_threshold_down = new(null, make_blocker, list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,-cut_away)) + alpha_threshold_down.render_target = "*emissive_block_alpha_down_[uid]" + // Then we multiply what remains by the amount we took away + var/atom/movable/render_step/color/alpha_threshold_up = new(null, alpha_threshold_down, list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,alpha_to_leave, 0,0,0,0)) + alpha_threshold_up.render_target = "*emissive_block_alpha_up_[uid]" + // Now we just feed that into an emissive blocker + var/atom/movable/render_step/emissive_blocker/em_block = new(null, alpha_threshold_up) + var/list/hand_back = list() + hand_back += alpha_threshold_down + hand_back += alpha_threshold_up + hand_back += em_block + // Cast to movable so we can use vis_contents. will work for turfs, but not for areas + var/atom/movable/vis_cast = make_blocker + vis_cast.vis_contents += hand_back + return hand_back diff --git a/code/__HELPERS/string_lists.dm b/code/__HELPERS/string_lists.dm new file mode 100644 index 000000000000..99ce28fba1df --- /dev/null +++ b/code/__HELPERS/string_lists.dm @@ -0,0 +1,31 @@ +GLOBAL_LIST_EMPTY(string_lists) + +/** + * Caches lists with non-numeric stringify-able values (text or typepath). + */ +/proc/string_list(list/values) + var/string_id = values.Join("-") + + . = GLOB.string_lists[string_id] + + if(.) + return . + + return GLOB.string_lists[string_id] = values + +///A wrapper for baseturf string lists, to offer support of non list values, and a stack_trace if we have major issues +/proc/baseturfs_string_list(list/values, turf/baseturf_holder) + if(!islist(values)) + return values //baseturf things + // return values + if(length(values) > 10) + stack_trace("The baseturfs list of [baseturf_holder] at [baseturf_holder.x], [baseturf_holder.y], [baseturf_holder.x] is [length(values)], it should never be this long, investigate. I've set baseturfs to a flashing wall as a visual queue") + baseturf_holder.ChangeTurf(/turf/closed/indestructible/baseturfs_ded, list(/turf/closed/indestructible/baseturfs_ded), flags = CHANGETURF_FORCEOP) + return string_list(list(/turf/closed/indestructible/baseturfs_ded)) //I want this reported god damn it + return string_list(values) + +/turf/closed/indestructible/baseturfs_ded + name = "Report this" + desc = "It looks like base turfs went to the fucking moon, TELL YOUR LOCAL CODER TODAY" + icon = 'icons/turf/debug.dmi' + icon_state = "fucked_baseturfs" diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index d346e7fae044..93e35e863139 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -41,9 +41,6 @@ else if(x<0) .+=360 -//Better performant than an artisanal proc and more reliable than Turn(). From TGMC. -#define REVERSE_DIR(dir) ( ((dir & 85) << 1) | ((dir & 170) >> 1) ) - //Returns location. Returns null if no location was found. /proc/get_teleport_loc(turf/location,mob/target,distance = 1, density = FALSE, errorx = 0, errory = 0, eoffsetx = 0, eoffsety = 0) /* @@ -1209,7 +1206,6 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) name = "INTERNAL DVIEW MOB" invisibility = 101 density = FALSE - see_in_dark = 1e6 move_resist = INFINITY var/ready_to_die = FALSE @@ -1289,11 +1285,13 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) temp = ((temp + (temp>>3))&29127) % 63 //070707 return temp -// \ref behaviour got changed in 512 so this is necesary to replicate old behaviour. -// If it ever becomes necesary to get a more performant REF(), this lies here in wait -// #define REF(thing) (thing && istype(thing, /datum) && (thing:datum_flags & DF_USE_TAG) && thing:tag ? "[thing:tag]" : "\ref[thing]") +/** + * \ref behaviour got changed in 512 so this is necesary to replicate old behaviour. + * If it ever becomes necesary to get a more performant REF(), this lies here in wait + * #define REF(thing) (thing && isdatum(thing) && (thing:datum_flags & DF_USE_TAG) && thing:tag ? "[thing:tag]" : text_ref(thing)) +**/ /proc/REF(input) - if(istype(input, /datum)) + if(isdatum(input)) var/datum/thing = input if(thing.datum_flags & DF_USE_TAG) if(!thing.tag) @@ -1301,7 +1299,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) thing.datum_flags &= ~DF_USE_TAG else return "\[[url_encode(thing.tag)]\]" - return "\ref[input]" + return text_ref(input) //returns a GUID like identifier (using a mostly made up record format) //guids are not on their own suitable for access or security tokens, as most of their bits are predictable. @@ -1486,4 +1484,3 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) set waitfor = FALSE return call(source, proctype)(arglist(arguments)) -#define TURF_FROM_COORDS_LIST(List) (locate(List[1], List[2], List[3])) diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 2123f6a32d20..b098b265c9c0 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -235,3 +235,12 @@ DEFINE_BITFIELD(vis_flags, list( "VIS_UNDERLAY" = VIS_UNDERLAY, "VIS_HIDE" = VIS_HIDE, )) +DEFINE_BITFIELD(turf_flags, list( + "NO_LAVA_GEN" = NO_LAVA_GEN, + "NO_RUINS" = NO_RUINS, + "NO_RUST" = NO_RUST, + "NOJAUNT" = NOJAUNT, + "IS_SOLID" = IS_SOLID, + "UNUSED_RESERVATION_TURF" = UNUSED_RESERVATION_TURF, + "RESERVATION_TURF" = RESERVATION_TURF, +)) diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index b47e4b6ef2d2..23d236dbb1d1 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -7,6 +7,7 @@ GLOBAL_LIST_EMPTY(stealthminID) //reference list with IDs that store ckeys, //This is for procs to replace all the goddamn 'in world's that are chilling around the code GLOBAL_LIST_EMPTY(player_list) //all mobs **with clients attached**. +GLOBAL_LIST_EMPTY(keyloop_list) //as above but can be limited to boost performance GLOBAL_LIST_EMPTY(mob_list) //all mobs, including clientless GLOBAL_LIST_EMPTY(mob_directory) //mob_id -> mob GLOBAL_LIST_EMPTY(alive_mob_list) //all alive mobs, including clientless. Excludes /mob/dead/new_player diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index f4bd68cd15e9..40a08fb1a167 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -474,6 +474,16 @@ M.Scale(px/sx, py/sy) transform = M +/atom/movable/screen/click_catcher/Initialize(mapload, datum/hud/hud_owner) + . = ..() + RegisterSignal(SSmapping, COMSIG_PLANE_OFFSET_INCREASE, PROC_REF(offset_increased)) + offset_increased(SSmapping, 0, SSmapping.max_plane_offset) + +// Draw to the lowest plane level offered +/atom/movable/screen/click_catcher/proc/offset_increased(datum/source, old_offset, new_offset) + SIGNAL_HANDLER + SET_PLANE_W_SCALAR(src, initial(plane), new_offset) + /atom/movable/screen/click_catcher/Click(location, control, params) var/list/modifiers = params2list(params) if(modifiers["middle"] && iscarbon(usr)) diff --git a/code/_onclick/hud/blob_overmind.dm b/code/_onclick/hud/blob_overmind.dm index 8a1c0a940299..d16cd8493823 100644 --- a/code/_onclick/hud/blob_overmind.dm +++ b/code/_onclick/hud/blob_overmind.dm @@ -131,8 +131,7 @@ blobpwrdisplay.icon_state = "block" blobpwrdisplay.screen_loc = ui_health blobpwrdisplay.mouse_opacity = MOUSE_OPACITY_TRANSPARENT - blobpwrdisplay.layer = ABOVE_HUD_LAYER - blobpwrdisplay.plane = ABOVE_HUD_PLANE + SET_PLANE_EXPLICIT(blobpwrdisplay, ABOVE_HUD_PLANE, owner) infodisplay += blobpwrdisplay healths = new /atom/movable/screen/healths/blob(src) diff --git a/code/_onclick/hud/credits.dm b/code/_onclick/hud/credits.dm index 68470eb14c0b..bb48756d3b0a 100644 --- a/code/_onclick/hud/credits.dm +++ b/code/_onclick/hud/credits.dm @@ -34,7 +34,7 @@ GLOBAL_LIST(end_titles) mouse_opacity = MOUSE_OPACITY_TRANSPARENT alpha = 0 screen_loc = "2,2" - layer = SPLASHSCREEN_LAYER + plane = SPLASHSCREEN_PLANE var/matrix/target /atom/movable/screen/credit/Initialize(mapload, credited) diff --git a/code/_onclick/hud/devil.dm b/code/_onclick/hud/devil.dm index 618aa89189f4..ddbcbd5818d9 100644 --- a/code/_onclick/hud/devil.dm +++ b/code/_onclick/hud/devil.dm @@ -24,7 +24,7 @@ using.icon = ui_style using.icon_state = "swap_1_m" using.screen_loc = ui_swaphand_position(owner,1) - using.layer = HUD_LAYER + SET_PLANE_EXPLICIT(using, ABOVE_HUD_PLANE, owner) using.plane = HUD_PLANE static_inventory += using @@ -33,7 +33,7 @@ using.icon = ui_style using.icon_state = "swap_2" using.screen_loc = ui_swaphand_position(owner,2) - using.layer = HUD_LAYER + SET_PLANE_EXPLICIT(using, ABOVE_HUD_PLANE, owner) using.plane = HUD_PLANE static_inventory += using diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/fullscreen.dm index 1162314ce15a..dea8b8806d2b 100644 --- a/code/_onclick/hud/fullscreen.dm +++ b/code/_onclick/hud/fullscreen.dm @@ -14,6 +14,9 @@ if (client && screen.should_show_to(src)) screen.update_for_view(client.view) client.screen += screen + + if(screen.needs_offsetting) + SET_PLANE_EXPLICIT(screen, PLANE_TO_TRUE(screen.plane), src) return screen @@ -67,6 +70,7 @@ var/view = 7 var/severity = 0 var/show_when_dead = FALSE + var/needs_offsetting = TRUE /atom/movable/screen/fullscreen/proc/update_for_view(client_view) if (screen_loc == "CENTER-7,CENTER-7" && view != client_view) @@ -173,28 +177,29 @@ screen_loc = "WEST,SOUTH to EAST,NORTH" icon_state = "flash" plane = SPLASHSCREEN_PLANE - layer = SPLASHSCREEN_LAYER - 1 + layer = CINEMATIC_LAYER color = "#000000" show_when_dead = TRUE /atom/movable/screen/fullscreen/cinematic_backdrop/Initialize(mapload) . = ..() - layer = SPLASHSCREEN_LAYER - 1 + layer = CINEMATIC_LAYER /atom/movable/screen/fullscreen/lighting_backdrop icon = 'icons/mob/screen_gen.dmi' icon_state = "flash" - transform = matrix(200, 0, 0, 0, 200, 0) + screen_loc = "WEST,SOUTH to EAST,NORTH" plane = LIGHTING_PLANE + layer = LIGHTING_ABOVE_ALL blend_mode = BLEND_OVERLAY show_when_dead = TRUE + needs_offsetting = FALSE //Provides darkness to the back of the lighting plane /atom/movable/screen/fullscreen/lighting_backdrop/lit invisibility = INVISIBILITY_LIGHTING layer = BACKGROUND_LAYER+21 color = "#000" - show_when_dead = TRUE //Provides whiteness in case you don't see lights so everything is still visible /atom/movable/screen/fullscreen/lighting_backdrop/unlit @@ -204,7 +209,7 @@ /atom/movable/screen/fullscreen/see_through_darkness icon_state = "nightvision" plane = LIGHTING_PLANE - layer = LIGHTING_LAYER + layer = LIGHTING_ABOVE_ALL blend_mode = BLEND_ADD show_when_dead = TRUE diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index f60a5fa86cdc..ea8ca758d39b 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -22,10 +22,10 @@ GLOBAL_LIST_INIT(available_ui_styles, list( /datum/hud var/mob/mymob - var/hud_shown = TRUE //Used for the HUD toggle (F12) - var/hud_version = HUD_STYLE_STANDARD //Current displayed version of the HUD - var/inventory_shown = FALSE //Equipped item inventory - var/hotkey_ui_hidden = FALSE //This is to hide the buttons that can be used via hotkeys. (hotkeybuttons list of buttons) + var/hud_shown = TRUE //Used for the HUD toggle (F12) + var/hud_version = HUD_STYLE_STANDARD //Current displayed version of the HUD + var/inventory_shown = FALSE //Equipped item inventory + var/hotkey_ui_hidden = FALSE //This is to hide the buttons that can be used via hotkeys. (hotkeybuttons list of buttons) var/atom/movable/screen/blobpwrdisplay @@ -33,6 +33,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list( var/atom/movable/screen/alien_queen_finder var/atom/movable/screen/devil/soul_counter/devilsouldisplay + var/atom/movable/screen/combo/combo_display var/atom/movable/screen/action_intent var/atom/movable/screen/zone_select @@ -47,11 +48,37 @@ GLOBAL_LIST_INIT(available_ui_styles, list( var/list/infodisplay = list() //the screen objects that display mob info (health, alien plasma, etc...) /// Screen objects that never exit view. var/list/always_visible_inventory = list() - var/list/screenoverlays = list() //the screen objects used as whole screen overlays (flash, damageoverlay, etc...) var/list/inv_slots[SLOTS_AMT] // /atom/movable/screen/inventory objects, ordered by their slot ID. var/list/hand_slots // /atom/movable/screen/inventory/hand objects, assoc list of "[held_index]" = object + + /// Assoc list of key => "plane master groups" + /// This is normally just the main window, but it'll occasionally contain things like spyglasses windows + var/list/datum/plane_master_group/master_groups = list() + ///Assoc list of controller groups, associated with key string group name with value of the plane master controller ref + var/list/atom/movable/plane_master_controller/plane_master_controllers = list() var/list/atom/movable/screen/plane_master/plane_masters = list() // see "appearance_flags" in the ref, assoc list of "[plane]" = object + /// Think of multiz as a stack of z levels. Each index in that stack has its own group of plane masters + /// This variable is the plane offset our mob/client is currently "on" + /// We use it to track what we should show/not show + /// Goes from 0 to the max (z level stack size - 1) + var/current_plane_offset = 0 + + ///UI for screentips that appear when you mouse over things + var/atom/movable/screen/screentip/screentip_text + + /// Whether to use text or images for click hints. + /// Same behavior as `screentips_enabled`--very hot, updated when the preference is updated. + var/screentip_images = TRUE + /// If this client is being shown atmos debug overlays or not + var/atmos_debug_overlays = FALSE + + /// The color to use for the screentips. + /// This is updated by the preference for cheaper reads than would be + /// had with a proc call, especially on one of the hottest procs in the + /// game (MouseEntered). + var/screentip_color + var/atom/movable/screen/button_palette/toggle_palette var/atom/movable/screen/palette_scroll/down/palette_down var/atom/movable/screen/palette_scroll/up/palette_up @@ -61,12 +88,17 @@ GLOBAL_LIST_INIT(available_ui_styles, list( var/list/floating_actions var/atom/movable/screen/healths - var/atom/movable/screen/healthdoll var/atom/movable/screen/stamina - + var/atom/movable/screen/healthdoll + var/atom/movable/screen/spacesuit // subtypes can override this to force a specific UI style var/ui_style + // List of weakrefs to objects that we add to our screen that we don't expect to DO anything + // They typically use * in their render target. They exist solely so we can reuse them, + // and avoid needing to make changes to all idk 300 consumers if we want to change the appearance + var/list/asset_refs_for_reuse = list() + /datum/hud/New(mob/owner) mymob = owner @@ -83,10 +115,87 @@ GLOBAL_LIST_INIT(available_ui_styles, list( hand_slots = list() - for(var/mytype in subtypesof(/atom/movable/screen/plane_master)) - var/atom/movable/screen/plane_master/instance = new mytype() - plane_masters["[instance.plane]"] = instance - instance.backdrop(mymob) + var/datum/plane_master_group/main/main_group = new(PLANE_GROUP_MAIN) + main_group.attach_to(src) + + for(var/mytype in subtypesof(/atom/movable/plane_master_controller)) + var/atom/movable/plane_master_controller/controller_instance = new mytype(null,src) + plane_master_controllers[controller_instance.name] = controller_instance + + owner.overlay_fullscreen("see_through_darkness", /atom/movable/screen/fullscreen/see_through_darkness) + + // Register onto the global spacelight appearances + // So they can be render targeted by anything in the world + for(var/obj/starlight_appearance/starlight as anything in GLOB.starlight_objects) + register_reuse(starlight) + + RegisterSignal(SSmapping, COMSIG_PLANE_OFFSET_INCREASE, PROC_REF(on_plane_increase)) + RegisterSignal(mymob, COMSIG_MOB_LOGIN, PROC_REF(client_refresh)) + RegisterSignal(mymob, COMSIG_MOB_LOGOUT, PROC_REF(clear_client)) + RegisterSignal(mymob, COMSIG_MOB_SIGHT_CHANGE, PROC_REF(update_sightflags)) + RegisterSignal(mymob, COMSIG_VIEWDATA_UPDATE, PROC_REF(on_viewdata_update)) + update_sightflags(mymob, mymob.sight, NONE) + +/datum/hud/proc/client_refresh(datum/source) + SIGNAL_HANDLER + RegisterSignal(mymob.canon_client, COMSIG_CLIENT_SET_EYE, PROC_REF(on_eye_change)) + on_eye_change(null, null, mymob.canon_client.eye) + +/datum/hud/proc/clear_client(datum/source) + SIGNAL_HANDLER + if(mymob.canon_client) + UnregisterSignal(mymob.canon_client, COMSIG_CLIENT_SET_EYE) + +/datum/hud/proc/on_viewdata_update(datum/source, view) + SIGNAL_HANDLER + + view_audit_buttons() + +/datum/hud/proc/on_eye_change(datum/source, atom/old_eye, atom/new_eye) + SIGNAL_HANDLER + SEND_SIGNAL(src, COMSIG_HUD_EYE_CHANGED, old_eye, new_eye) + + if(old_eye) + UnregisterSignal(old_eye, COMSIG_MOVABLE_Z_CHANGED) + if(new_eye) + // By the time logout runs, the client's eye has already changed + // There's just no log of the old eye, so we need to override + // :sadkirby: + RegisterSignal(new_eye, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(eye_z_changed), override = TRUE) + eye_z_changed(new_eye) + +/datum/hud/proc/update_sightflags(datum/source, new_sight, old_sight) + SIGNAL_HANDLER + // If neither the old and new flags can see turfs but not objects, don't transform the turfs + // This is to ensure parallax works when you can't see holder objects + if(should_sight_scale(new_sight) == should_sight_scale(old_sight)) + return + + for(var/group_key as anything in master_groups) + var/datum/plane_master_group/group = master_groups[group_key] + group.transform_lower_turfs(src, current_plane_offset) + +/datum/hud/proc/should_use_scale() + return should_sight_scale(mymob.sight) + +/datum/hud/proc/should_sight_scale(sight_flags) + return (sight_flags & (SEE_TURFS | SEE_OBJS)) != SEE_TURFS + +/datum/hud/proc/eye_z_changed(atom/eye) + SIGNAL_HANDLER + update_parallax_pref() // If your eye changes z level, so should your parallax prefs + var/turf/eye_turf = get_turf(eye) + var/new_offset = GET_TURF_PLANE_OFFSET(eye_turf) + if(current_plane_offset == new_offset) + return + var/old_offset = current_plane_offset + current_plane_offset = new_offset + + SEND_SIGNAL(src, COMSIG_HUD_OFFSET_CHANGED, old_offset, new_offset) + if(should_use_scale()) + for(var/group_key as anything in master_groups) + var/datum/plane_master_group/group = master_groups[group_key] + group.transform_lower_turfs(src, new_offset) /datum/hud/Destroy() if(mymob.hud_used == src) @@ -102,10 +211,13 @@ GLOBAL_LIST_INIT(available_ui_styles, list( QDEL_NULL(module_store_icon) QDEL_LIST(static_inventory) + // all already deleted by static inventory clear inv_slots.Cut() action_intent = null zone_select = null pull_icon = null + rest_icon = null + hand_slots.Cut() QDEL_LIST(toggleable_inventory) QDEL_LIST(hotkeybuttons) @@ -115,18 +227,53 @@ GLOBAL_LIST_INIT(available_ui_styles, list( healths = null stamina = null healthdoll = null - devilsouldisplay = null + spacesuit = null blobpwrdisplay = null alien_plasma_display = null alien_queen_finder = null - QDEL_LIST_ASSOC_VAL(plane_masters) - QDEL_LIST(screenoverlays) + QDEL_LIST_ASSOC_VAL(master_groups) + QDEL_LIST_ASSOC_VAL(plane_master_controllers) QDEL_LIST(always_visible_inventory) mymob = null + return ..() +/datum/hud/proc/on_plane_increase(datum/source, old_max_offset, new_max_offset) + SIGNAL_HANDLER + for(var/i in old_max_offset + 1 to new_max_offset) + register_reuse(GLOB.starlight_objects[i + 1]) + build_plane_groups(old_max_offset + 1, new_max_offset) + +/// Creates the required plane masters to fill out new z layers (because each "level" of multiz gets its own plane master set) +/datum/hud/proc/build_plane_groups(starting_offset, ending_offset) + for(var/group_key in master_groups) + var/datum/plane_master_group/group = master_groups[group_key] + group.build_plane_masters(starting_offset, ending_offset) + +/// Returns the plane master that matches the input plane from the passed in group +/datum/hud/proc/get_plane_master(plane, group_key = PLANE_GROUP_MAIN) + var/plane_key = "[plane]" + var/datum/plane_master_group/group = master_groups[group_key] + return group.plane_masters[plane_key] + +/// Returns a list of all plane masters that match the input true plane, drawn from the passed in group (ignores z layer offsets) +/datum/hud/proc/get_true_plane_masters(true_plane, group_key = PLANE_GROUP_MAIN) + var/list/atom/movable/screen/plane_master/masters = list() + for(var/plane in TRUE_PLANE_TO_OFFSETS(true_plane)) + masters += get_plane_master(plane, group_key) + return masters + +/// Returns all the planes belonging to the passed in group key +/datum/hud/proc/get_planes_from(group_key) + var/datum/plane_master_group/group = master_groups[group_key] + return group.plane_masters + +/// Returns the corresponding plane group datum if one exists +/datum/hud/proc/get_plane_group(key) + return master_groups[key] + /mob/proc/create_mob_hud() if(!client || hud_used) return @@ -154,7 +301,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list( // This code is the absolute fucking worst, I want it to go die in a fire // Seriously, why - screenmob.client.screen = list() + screenmob.client.clear_screen() screenmob.client.apply_clickcatcher() var/display_hud_version = version @@ -225,6 +372,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list( reorganize_alerts(screenmob) screenmob.reload_fullscreen() update_parallax_pref(screenmob) + update_reuse(screenmob) // ensure observers get an accurate and up-to-date view if (!viewmob) @@ -239,11 +387,10 @@ GLOBAL_LIST_INIT(available_ui_styles, list( return TRUE /datum/hud/proc/plane_masters_update() - // Plane masters are always shown to OUR mob, never to observers - for(var/thing in plane_masters) - var/atom/movable/screen/plane_master/PM = plane_masters[thing] - PM.backdrop(mymob) - mymob.client.screen += PM + for(var/group_key in master_groups) + var/datum/plane_master_group/group = master_groups[group_key] + // Plane masters are always shown to OUR mob, never to observers + group.refresh_hud() /datum/hud/human/show_hud(version = 0,mob/viewmob) . = ..() @@ -270,13 +417,29 @@ GLOBAL_LIST_INIT(available_ui_styles, list( if (initial(ui_style) || ui_style == new_ui_style) return - for(var/atom/item in static_inventory + toggleable_inventory + hotkeybuttons + infodisplay + screenoverlays + always_visible_inventory + inv_slots) + for(var/atom/item in static_inventory + toggleable_inventory + hotkeybuttons + infodisplay + always_visible_inventory + inv_slots) if (item.icon == ui_style) item.icon = new_ui_style ui_style = new_ui_style build_hand_slots() +/datum/hud/proc/register_reuse(atom/movable/screen/reuse) + asset_refs_for_reuse += WEAKREF(reuse) + mymob?.client?.screen += reuse + +/datum/hud/proc/unregister_reuse(atom/movable/screen/reuse) + asset_refs_for_reuse -= WEAKREF(reuse) + mymob?.client?.screen -= reuse + +/datum/hud/proc/update_reuse(mob/show_to) + for(var/datum/weakref/screen_ref as anything in asset_refs_for_reuse) + var/atom/movable/screen/reuse = screen_ref.resolve() + if(isnull(reuse)) + asset_refs_for_reuse -= screen_ref + continue + show_to.client?.screen += reuse + //Triggered when F12 is pressed (Unless someone changed something in the DMF) /mob/verb/button_pressed_F12() set name = "F12" @@ -284,9 +447,9 @@ GLOBAL_LIST_INIT(available_ui_styles, list( if(hud_used && client) hud_used.show_hud() //Shows the next hud preset - to_chat(usr, "Switched HUD mode. Press F12 to toggle.") + to_chat(usr, span_info("Switched HUD mode. Press F12 to toggle.")) else - to_chat(usr, "This mob type does not use a HUD.") + to_chat(usr, span_warning("This mob type does not use a HUD.")) //(re)builds the hand ui slots, throwing away old ones @@ -323,7 +486,6 @@ GLOBAL_LIST_INIT(available_ui_styles, list( /datum/hud/proc/update_locked_slots() return - /datum/hud/proc/position_action(atom/movable/screen/movable/action_button/button, position) // This is kinda a hack, I'm sorry. // Basically, FLOATING is never a valid position to pass into this proc. It exists as a generic marker for manually positioned buttons @@ -361,7 +523,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list( palette_actions.insert_action(button, palette_actions.index_of(relative_to)) if(SCRN_OBJ_FLOATING) // If we don't have it as a define, this is a screen_loc, and we should be floating floating_actions += button - var/client/our_client = mymob.client + var/client/our_client = mymob.canon_client if(!our_client) position_action(button, button.linked_action.default_button_position) return @@ -402,7 +564,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list( /// Ensures all of our buttons are properly within the bounds of our client's view, moves them if they're not /datum/hud/proc/view_audit_buttons() - var/our_view = mymob?.client?.view + var/our_view = mymob?.canon_client?.view if(!our_view) return listed_actions.check_against_view() @@ -518,7 +680,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list( return "WEST[coord_col]:[coord_col_offset],NORTH[coord_row]:-[pixel_north_offset]" /datum/action_group/proc/check_against_view() - var/owner_view = owner?.mymob?.client?.view + var/owner_view = owner?.mymob?.canon_client?.view if(!owner_view) return // Unlikey as it is, we may have been changed. Want to start from our target position and fail down diff --git a/code/_onclick/hud/map_popups.dm b/code/_onclick/hud/map_popups.dm index f070084f4770..a3b5699c8484 100644 --- a/code/_onclick/hud/map_popups.dm +++ b/code/_onclick/hud/map_popups.dm @@ -1,36 +1,3 @@ -/client - /** - * Assoc list with all the active maps - when a screen obj is added to - * a map, it's put in here as well. - * - * Format: list( = list(/atom/movable/screen)) - */ - var/list/screen_maps = list() - -/atom/movable/screen - /** - * Map name assigned to this object. - * Automatically set by /client/proc/add_obj_to_map. - */ - var/assigned_map - /** - * Mark this object as garbage-collectible after you clean the map - * it was registered on. - * - * This could probably be changed to be a proc, for conditional removal. - * But for now, this works. - */ - var/del_on_map_removal = TRUE - -/** - * A screen object, which acts as a container for turfs and other things - * you want to show on the map, which you usually attach to "vis_contents". - */ -/atom/movable/screen/map_view - // Map view has to be on the lowest plane to enable proper lighting - layer = GAME_PLANE - plane = GAME_PLANE - /** * A generic background object. * It is also implicitly used to allocate a rectangle on the map, which will @@ -77,23 +44,18 @@ screen_maps[screen_obj.assigned_map] = list() // NOTE: Possibly an expensive operation var/list/screen_map = screen_maps[screen_obj.assigned_map] - if(!screen_map.Find(screen_obj)) - screen_map += screen_obj - if(!screen.Find(screen_obj)) - screen += screen_obj + screen_map |= screen_obj + screen |= screen_obj /** * Clears the map of registered screen objects. - * - * Not really needed most of the time, as the client's screen list gets reset - * on relog. any of the buttons are going to get caught by garbage collection - * anyway. they're effectively qdel'd. */ /client/proc/clear_map(map_name) - if(!map_name || !(map_name in screen_maps)) + if(!map_name || !screen_maps[map_name]) return FALSE for(var/atom/movable/screen/screen_obj in screen_maps[map_name]) screen_maps[map_name] -= screen_obj + screen -= screen_obj if(screen_obj.del_on_map_removal) qdel(screen_obj) screen_maps -= map_name @@ -113,9 +75,10 @@ * * Returns a map name. */ -/client/proc/create_popup(name, ratiox = 100, ratioy = 100) +/client/proc/create_popup(name, title, ratiox = 100, ratioy = 100) winclone(src, "popupwindow", name) var/list/winparams = list() + winparams["title"] = title winparams["size"] = "[ratiox]x[ratioy]" winparams["on-close"] = "handle-popup-close [name]" winset(src, "[name]", list2params(winparams)) @@ -138,13 +101,13 @@ * Width and height are multiplied by 64 by default. */ /client/proc/setup_popup(popup_name, width = 9, height = 9, \ - tilesize = 2, bg_icon) + tilesize = 2, title, bg_icon) if(!popup_name) return clear_map("[popup_name]_map") var/x_value = world.icon_size * tilesize * width var/y_value = world.icon_size * tilesize * height - var/map_name = create_popup(popup_name, x_value, y_value) + var/map_name = create_popup(popup_name, title, x_value, y_value) var/atom/movable/screen/background/background = new background.assigned_map = map_name @@ -167,4 +130,5 @@ */ /client/verb/handle_popup_close(window_id as text) set hidden = TRUE - clear_map("[window_id]_map") \ No newline at end of file + clear_map("[window_id]_map") + SEND_SIGNAL(src, COMSIG_POPUP_CLEARED, window_id) diff --git a/code/_onclick/hud/map_view.dm b/code/_onclick/hud/map_view.dm new file mode 100644 index 000000000000..79e4d7ae10bc --- /dev/null +++ b/code/_onclick/hud/map_view.dm @@ -0,0 +1,71 @@ +/** + * A screen object, which acts as a container for turfs and other things + * you want to show on the map, which you usually attach to "vis_contents". + * Additionally manages the plane masters required to display said container contents + */ +INITIALIZE_IMMEDIATE(/atom/movable/screen/map_view) +/atom/movable/screen/map_view + name = "screen" + // Map view has to be on the lowest plane to enable proper lighting + layer = GAME_PLANE + plane = GAME_PLANE + del_on_map_removal = FALSE + + // Weakrefs of all our hud viewers -> a weakref to the hud datum they last used + var/list/datum/weakref/viewers_to_huds = list() + + var/datum/plane_master_group/popup/pop_planes + +/atom/movable/screen/map_view/Destroy() + for(var/datum/weakref/client_ref in viewers_to_huds) + var/client/our_client = client_ref.resolve() + if(!our_client) + continue + hide_from(our_client.mob) + + return ..() + +/atom/movable/screen/map_view/proc/generate_view(map_key) + // Map keys have to start and end with an A-Z character, + // and definitely NOT with a square bracket or even a number. + // I wasted 6 hours on this. :agony: + // -- Stylemistake + assigned_map = map_key + set_position(1, 1) + +/atom/movable/screen/map_view/proc/display_to(mob/show_to) + show_to.client.register_map_obj(src) + // We need to add planesmasters to the popup, otherwise + // blending fucks up massively. Any planesmaster on the main screen does + // NOT apply to map popups. If there's ever a way to make planesmasters + // omnipresent, then this wouldn't be needed. + // We lazy load this because there's no point creating all these if none's gonna see em + + // Store this info in a client -> hud pattern, so ghosts closing the window nukes the right group + var/datum/weakref/client_ref = WEAKREF(show_to.client) + + var/datum/weakref/hud_ref = viewers_to_huds[client_ref] + var/datum/hud/our_hud = hud_ref?.resolve() + if(our_hud) + return our_hud.get_plane_group(PLANE_GROUP_POPUP_WINDOW(src)) + + // Generate a new plane group for this case + pop_planes = new(PLANE_GROUP_POPUP_WINDOW(src), assigned_map) + viewers_to_huds[client_ref] = WEAKREF(show_to.hud_used) + pop_planes.attach_to(show_to.hud_used) + + return pop_planes + +/atom/movable/screen/map_view/proc/hide_from(mob/hide_from) + hide_from?.canon_client.clear_map(assigned_map) + var/client_ref = WEAKREF(hide_from?.canon_client) + + // Make sure we clear the *right* hud + var/datum/weakref/hud_ref = viewers_to_huds[client_ref] + viewers_to_huds -= client_ref + var/datum/hud/clear_from = hud_ref?.resolve() + if(!clear_from) + return + + var/datum/plane_master_group/popup/pop_planes = clear_from.get_plane_group(PLANE_GROUP_POPUP_WINDOW(src)) + qdel(pop_planes) diff --git a/code/_onclick/hud/parallax.dm b/code/_onclick/hud/parallax.dm index 73a2c5a160d8..c9e1f580399f 100755 --- a/code/_onclick/hud/parallax.dm +++ b/code/_onclick/hud/parallax.dm @@ -1,30 +1,23 @@ -/client - var/list/parallax_layers - var/list/parallax_layers_cached - var/atom/movable/movingmob - var/turf/previous_turf - var/dont_animate_parallax //world.time of when we can state animate()ing parallax again - var/last_parallax_shift //world.time of last update - var/parallax_throttle = 0 //ds between updates - var/parallax_movedir = 0 - var/parallax_layers_max = 4 - var/parallax_animate_timer - /datum/hud/proc/create_parallax(mob/viewmob) var/mob/screenmob = viewmob || mymob var/client/C = screenmob.client + if (!apply_parallax_pref(viewmob)) //don't want shit computers to crash when specing someone with insane parallax, so use the viewer's pref + for(var/atom/movable/screen/plane_master/parallax as anything in get_true_plane_masters(PLANE_SPACE_PARALLAX)) + parallax.hide_plane(screenmob) return + for(var/atom/movable/screen/plane_master/parallax as anything in get_true_plane_masters(PLANE_SPACE_PARALLAX)) + parallax.unhide_plane(screenmob) + if(!length(C.parallax_layers_cached)) C.parallax_layers_cached = list() - C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/layer_1(null, C.view) - C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/layer_2(null, C.view) - + C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/layer_1(null, src) + C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/layer_2(null, src) if(!GLOB.minetype) stack_trace("GLOB.minetype wasn't set when creating parallax! uh oh!!") - + if(GLOB.minetype == MINETYPE_LAVALAND) if(HAS_TRAIT(SSstation, STATION_TRAIT_MOONSCORCH)) C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/planet/moonscorch(null, C.view) @@ -32,10 +25,9 @@ C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/planet(null, C.view) if(GLOB.minetype == MINETYPE_JUNGLE) C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/planet/jungle(null, C.view) - if(SSparallax.random_layer) - C.parallax_layers_cached += new SSparallax.random_layer - C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/layer_3(null, C.view) + C.parallax_layers_cached += new SSparallax.random_layer.type(null, src, FALSE, SSparallax.random_layer) + C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/layer_3(null, src) C.parallax_layers = C.parallax_layers_cached.Copy() @@ -43,70 +35,88 @@ C.parallax_layers.len = C.parallax_layers_max C.screen |= (C.parallax_layers) - var/atom/movable/screen/plane_master/PM = screenmob.hud_used.plane_masters["[PLANE_SPACE]"] - if(screenmob != mymob) - C.screen -= locate(/atom/movable/screen/plane_master/parallax_white) in C.screen - C.screen += PM - PM.color = list( - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 1, 1, 1, 1, - 0, 0, 0, 0 - ) - + // We could do not do parallax for anything except the main plane group + // This could be changed, but it would require refactoring this whole thing + // And adding non client particular hooks for all the inputs, and I do not have the time I'm sorry :( + for(var/atom/movable/screen/plane_master/plane_master as anything in screenmob.hud_used.get_true_plane_masters(PLANE_SPACE)) + if(screenmob != mymob) + C.screen -= locate(/atom/movable/screen/plane_master/parallax_white) in C.screen + C.screen += plane_master + plane_master.color = list( + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 1, 1, 1, 1, + 0, 0, 0, 0 + ) /datum/hud/proc/remove_parallax(mob/viewmob) var/mob/screenmob = viewmob || mymob var/client/C = screenmob.client C.screen -= (C.parallax_layers_cached) - var/atom/movable/screen/plane_master/PM = screenmob.hud_used.plane_masters["[PLANE_SPACE]"] - if(screenmob != mymob) - C.screen -= locate(/atom/movable/screen/plane_master/parallax_white) in C.screen - C.screen += PM - PM.color = initial(PM.color) + for(var/atom/movable/screen/plane_master/plane_master as anything in screenmob.hud_used.get_true_plane_masters(PLANE_SPACE)) + if(screenmob != mymob) + C.screen -= locate(/atom/movable/screen/plane_master/parallax_white) in C.screen + C.screen += plane_master + plane_master.color = initial(plane_master.color) C.parallax_layers = null /datum/hud/proc/apply_parallax_pref(mob/viewmob) var/mob/screenmob = viewmob || mymob + var/turf/screen_location = get_turf(screenmob) + + if(SSmapping.level_trait(screen_location?.z, ZTRAIT_NOPARALLAX)) + for(var/atom/movable/screen/plane_master/white_space as anything in get_true_plane_masters(PLANE_SPACE)) + white_space.hide_plane(screenmob) + return FALSE + + for(var/atom/movable/screen/plane_master/white_space as anything in get_true_plane_masters(PLANE_SPACE)) + white_space.unhide_plane(screenmob) + + if (SSlag_switch.measures[DISABLE_PARALLAX] && !HAS_TRAIT(viewmob, TRAIT_BYPASS_MEASURES)) + return FALSE + var/client/C = screenmob.client - if(C.prefs) - var/pref = C.prefs.read_preference(/datum/preference/choiced/parallax) - if (isnull(pref)) - pref = PARALLAX_HIGH - switch(pref) - if (PARALLAX_INSANE) - C.parallax_throttle = FALSE - C.parallax_layers_max = 5 - return TRUE - - if (PARALLAX_MED) - C.parallax_throttle = PARALLAX_DELAY_MED - C.parallax_layers_max = 3 - return TRUE - - if (PARALLAX_LOW) - C.parallax_throttle = PARALLAX_DELAY_LOW - C.parallax_layers_max = 1 - return TRUE - - if (PARALLAX_DISABLE) - return FALSE - - //This is high parallax. - C.parallax_throttle = PARALLAX_DELAY_DEFAULT - C.parallax_layers_max = 4 - return TRUE + // Default to HIGH + var/parallax_selection = C?.prefs.read_preference(/datum/preference/choiced/parallax) || PARALLAX_HIGH + + switch(parallax_selection) + if (PARALLAX_INSANE) + C.parallax_layers_max = 5 + C.do_parallax_animations = TRUE + return TRUE + + if(PARALLAX_HIGH) + C.parallax_layers_max = 4 + C.do_parallax_animations = TRUE + return TRUE + + if (PARALLAX_MED) + C.parallax_layers_max = 3 + C.do_parallax_animations = TRUE + return TRUE + + if (PARALLAX_LOW) + C.parallax_layers_max = 1 + C.do_parallax_animations = FALSE + return TRUE + + if (PARALLAX_DISABLE) + return FALSE /datum/hud/proc/update_parallax_pref(mob/viewmob) - remove_parallax(viewmob) - create_parallax(viewmob) - update_parallax() + var/mob/screen_mob = viewmob || mymob + if(!screen_mob.client) + return + remove_parallax(screen_mob) + create_parallax(screen_mob) + update_parallax(screen_mob) // This sets which way the current shuttle is moving (returns true if the shuttle has stopped moving so the caller can append their animation) -/datum/hud/proc/set_parallax_movedir(new_parallax_movedir, skip_windups) +/datum/hud/proc/set_parallax_movedir(new_parallax_movedir = 0, skip_windups, mob/viewmob) . = FALSE - var/client/C = mymob.client + var/mob/screenmob = viewmob || mymob + var/client/C = screenmob.client if(new_parallax_movedir == C.parallax_movedir) return var/animatedir = new_parallax_movedir @@ -178,29 +188,25 @@ L.transform = newtransform - animate(L, transform = L.transform, time = 0 SECONDS, loop = -1, flags = ANIMATION_END_NOW) + animate(L, transform = L.transform, time = 0, loop = -1, flags = ANIMATION_END_NOW) animate(transform = matrix(), time = T) -/datum/hud/proc/update_parallax() - var/client/C = mymob.client - if(!C) - return - var/turf/posobj = get_turf_global(C.eye) // yogs - replace get_turf with get_turf_global +/datum/hud/proc/update_parallax(mob/viewmob) + var/mob/screenmob = viewmob || mymob + var/client/C = screenmob.client + var/turf/posobj = get_turf(C.eye) if(!posobj) return - var/area/areaobj = posobj.loc + var/area/areaobj = posobj.loc // Update the movement direction of the parallax if necessary (for shuttles) - set_parallax_movedir(areaobj.parallax_movedir, FALSE) + set_parallax_movedir(areaobj.parallax_movedir, FALSE, screenmob) - var/force + var/force = FALSE if(!C.previous_turf || (C.previous_turf.z != posobj.z)) C.previous_turf = posobj force = TRUE - if (!force && world.time < C.last_parallax_shift+C.parallax_throttle) - return - //Doing it this way prevents parallax layers from "jumping" when you change Z-Levels. var/offset_x = posobj.x - C.previous_turf.x var/offset_y = posobj.y - C.previous_turf.y @@ -208,83 +214,105 @@ if(!offset_x && !offset_y && !force) return - var/last_delay = world.time - C.last_parallax_shift - last_delay = min(last_delay, C.parallax_throttle) + var/glide_rate = round(world.icon_size / screenmob.glide_size * world.tick_lag, world.tick_lag) C.previous_turf = posobj - C.last_parallax_shift = world.time - for(var/thing in C.parallax_layers) - var/atom/movable/screen/parallax_layer/L = thing - L.update_status(mymob) - if (L.view_sized != C.view) - L.update_o(C.view) + var/largest_change = max(abs(offset_x), abs(offset_y)) + var/max_allowed_dist = (glide_rate / world.tick_lag) + 1 + // If we aren't already moving/don't allow parallax, have made some movement, and that movement was smaller then our "glide" size, animate + var/run_parralax = (C.do_parallax_animations && glide_rate && !areaobj.parallax_movedir && C.dont_animate_parallax <= world.time && largest_change <= max_allowed_dist) + for(var/atom/movable/screen/parallax_layer/parallax_layer as anything in C.parallax_layers) + var/our_speed = parallax_layer.speed var/change_x var/change_y - - if(L.absolute) - L.offset_x = -(posobj.x - SSparallax.planet_x_offset) * L.speed - L.offset_y = -(posobj.y - SSparallax.planet_y_offset) * L.speed + if(parallax_layer.absolute) + // We use change here so the typically large absolute objects (just lavaland for now) don't jitter so much + change_x = (posobj.x - SSparallax.planet_x_offset) * our_speed + parallax_layer.offset_x + change_y = (posobj.y - SSparallax.planet_y_offset) * our_speed + parallax_layer.offset_y else - change_x = offset_x * L.speed - L.offset_x -= change_x - change_y = offset_y * L.speed - L.offset_y -= change_y - - if(L.offset_x > 240) - L.offset_x -= 480 - if(L.offset_x < -240) - L.offset_x += 480 - if(L.offset_y > 240) - L.offset_y -= 480 - if(L.offset_y < -240) - L.offset_y += 480 - - - if(!areaobj.parallax_movedir && C.dont_animate_parallax <= world.time && (offset_x || offset_y) && abs(offset_x) <= max(C.parallax_throttle/world.tick_lag+1,1) && abs(offset_y) <= max(C.parallax_throttle/world.tick_lag+1,1) && (round(abs(change_x)) > 1 || round(abs(change_y)) > 1)) - L.transform = matrix(1, 0, offset_x*L.speed, 0, 1, offset_y*L.speed) - animate(L, transform=matrix(), time = last_delay) - - L.screen_loc = "CENTER-7:[round(L.offset_x,1)],CENTER-7:[round(L.offset_y,1)]" + change_x = offset_x * our_speed + change_y = offset_y * our_speed + + // This is how we tile parralax sprites + // It doesn't use change because we really don't want to animate this + if(parallax_layer.offset_x - change_x > 240) + parallax_layer.offset_x -= 480 + else if(parallax_layer.offset_x - change_x < -240) + parallax_layer.offset_x += 480 + if(parallax_layer.offset_y - change_y > 240) + parallax_layer.offset_y -= 480 + else if(parallax_layer.offset_y - change_y < -240) + parallax_layer.offset_y += 480 + + // Now that we have our offsets, let's do our positioning + parallax_layer.offset_x -= change_x + parallax_layer.offset_y -= change_y + + parallax_layer.screen_loc = "CENTER-7:[round(parallax_layer.offset_x, 1)],CENTER-7:[round(parallax_layer.offset_y, 1)]" + + // We're going to use a transform to "glide" that last movement out, so it looks nicer + // Don't do any animates if we're not actually moving enough distance yeah? thanks lad + if(run_parralax && (largest_change * our_speed > 1)) + parallax_layer.transform = matrix(1,0,change_x, 0,1,change_y) + animate(parallax_layer, transform=matrix(), time = glide_rate) /atom/movable/proc/update_parallax_contents() - if(length(client_mobs_in_contents)) - for(var/thing in client_mobs_in_contents) - var/mob/M = thing - if(M && M.client && M.hud_used && length(M.client.parallax_layers)) - M.hud_used.update_parallax() - -/mob/proc/update_parallax_teleport() //used for arrivals shuttle - if(client && client.eye && hud_used && length(client.parallax_layers)) + for(var/mob/client_mob as anything in client_mobs_in_contents) + if(length(client_mob?.client?.parallax_layers) && client_mob.hud_used) + client_mob.hud_used.update_parallax() + +/mob/proc/update_parallax_teleport() //used for arrivals shuttle + if(client?.eye && hud_used && length(client.parallax_layers)) var/area/areaobj = get_area(client.eye) hud_used.set_parallax_movedir(areaobj.parallax_movedir, TRUE) +// We need parallax to always pass its args down into initialize, so we immediate init it +INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_layer) /atom/movable/screen/parallax_layer icon = 'icons/effects/parallax.dmi' var/speed = 1 var/offset_x = 0 var/offset_y = 0 - var/view_sized var/absolute = FALSE blend_mode = BLEND_ADD plane = PLANE_SPACE_PARALLAX screen_loc = "CENTER-7,CENTER-7" mouse_opacity = MOUSE_OPACITY_TRANSPARENT - -/atom/movable/screen/parallax_layer/Initialize(mapload, view) +/atom/movable/screen/parallax_layer/Initialize(mapload, datum/hud/hud_owner, template = FALSE) . = ..() - if (!view) - view = world.view + // Parallax layers are independant of hud, they care about client + // Not doing this will just create a bunch of hard deletes + hud = null + + if(template) + return + + var/client/boss = hud_owner?.mymob?.canon_client + + if(!boss) // If this typepath all starts to harddel your culprit is likely this + return INITIALIZE_HINT_QDEL + + // I do not want to know bestie + var/view = boss.view || world.view update_o(view) + RegisterSignal(boss, COMSIG_VIEW_SET, PROC_REF(on_view_change)) + +/atom/movable/screen/parallax_layer/proc/on_view_change(datum/source, new_size) + SIGNAL_HANDLER + update_o(new_size) /atom/movable/screen/parallax_layer/proc/update_o(view) if (!view) view = world.view + var/static/parallax_scaler = world.icon_size / 480 + + // Turn the view size into a grid of correctly scaled overlays var/list/viewscales = getviewsize(view) - var/countx = CEILING((viewscales[1]/2)/(480/world.icon_size), 1)+1 - var/county = CEILING((viewscales[2]/2)/(480/world.icon_size), 1)+1 + var/countx = CEILING((viewscales[1] / 2) * parallax_scaler, 1) + 1 + var/county = CEILING((viewscales[2] / 2) * parallax_scaler, 1) + 1 var/list/new_overlays = new for(var/x in -countx to countx) for(var/y in -county to county) @@ -295,10 +323,6 @@ new_overlays += texture_overlay cut_overlays() add_overlay(new_overlays) - view_sized = view - -/atom/movable/screen/parallax_layer/proc/update_status(mob/M) - return /atom/movable/screen/parallax_layer/layer_1 icon_state = "layer1" @@ -315,21 +339,6 @@ speed = 1.4 layer = 3 -/atom/movable/screen/parallax_layer/random - blend_mode = BLEND_OVERLAY - speed = 3 - layer = 3 - -/atom/movable/screen/parallax_layer/random/space_gas - icon_state = "random_layer1" - -/atom/movable/screen/parallax_layer/random/space_gas/Initialize(mapload, view) - . = ..() - src.add_atom_colour(SSparallax.random_parallax_color, ADMIN_COLOUR_PRIORITY) - -/atom/movable/screen/parallax_layer/random/asteroids - icon_state = "random_layer2" - /atom/movable/screen/parallax_layer/planet icon_state = "planet" blend_mode = BLEND_OVERLAY @@ -337,18 +346,36 @@ speed = 3 layer = 30 +/atom/movable/screen/parallax_layer/planet/jungle + icon_state = "jungleland" + /atom/movable/screen/parallax_layer/planet/moonscorch icon_state = "rheus_moon" -/atom/movable/screen/parallax_layer/planet/update_status(mob/M) - var/turf/T = get_turf(M) - if(is_station_level(T.z)) - invisibility = 0 - else - invisibility = INVISIBILITY_ABSTRACT +/atom/movable/screen/parallax_layer/planet/Initialize(mapload, datum/hud/hud_owner) + . = ..() + var/client/boss = hud_owner?.mymob?.canon_client + if(!boss) + return + var/static/list/connections = list( + COMSIG_MOVABLE_Z_CHANGED = PROC_REF(on_z_change), + COMSIG_MOB_LOGOUT = PROC_REF(on_mob_logout), + ) + AddComponent(/datum/component/connect_mob_behalf, boss, connections) + on_z_change(hud_owner?.mymob) + +/atom/movable/screen/parallax_layer/planet/proc/on_mob_logout(mob/source) + SIGNAL_HANDLER + var/client/boss = source.canon_client + on_z_change(boss.mob) + +/atom/movable/screen/parallax_layer/planet/proc/on_z_change(mob/source) + SIGNAL_HANDLER + var/client/boss = source.client + var/turf/posobj = get_turf(boss?.eye) + if(!posobj) + return + SetInvisibility(is_station_level(posobj.z) ? INVISIBILITY_NONE : INVISIBILITY_ABSTRACT, id=type) /atom/movable/screen/parallax_layer/planet/update_o() - return //Shit wont move - -/atom/movable/screen/parallax_layer/planet/jungle - icon_state = "jungleland" + return //Shit won't move diff --git a/code/_onclick/hud/picture_in_picture.dm b/code/_onclick/hud/picture_in_picture.dm index 354a6ed54630..dbf4e0af5310 100644 --- a/code/_onclick/hud/picture_in_picture.dm +++ b/code/_onclick/hud/picture_in_picture.dm @@ -12,11 +12,16 @@ var/atom/movable/screen/component_button/button_shrink var/mutable_appearance/standard_background - var/const/max_dimensions = 10 -/atom/movable/screen/movable/pic_in_pic/Initialize(mapload) +/atom/movable/screen/movable/pic_in_pic/Initialize(mapload, datum/hud/hud_owner) . = ..() make_backgrounds() + RegisterSignal(SSmapping, COMSIG_PLANE_OFFSET_INCREASE, PROC_REF(multiz_offset_increase)) + multiz_offset_increase(SSmapping) + +/atom/movable/screen/movable/pic_in_pic/proc/multiz_offset_increase(datum/source) + SIGNAL_HANDLER + SET_PLANE_W_SCALAR(src, initial(plane), SSmapping.max_plane_offset) /atom/movable/screen/movable/pic_in_pic/Destroy() for(var/C in shown_to) @@ -101,9 +106,11 @@ standard_background.transform = M add_overlay(standard_background) +// maximum number of dimensions is 10 + /atom/movable/screen/movable/pic_in_pic/proc/set_view_size(width, height, do_refresh = TRUE) - width = clamp(width, 0, max_dimensions) - height = clamp(height, 0, max_dimensions) + width = clamp(width, 0, 10) + height = clamp(height, 0, 10) src.width = width src.height = height diff --git a/code/_onclick/hud/plane_master.dm b/code/_onclick/hud/plane_master.dm index 401a350fba1d..9cb3c06b71f1 100644 --- a/code/_onclick/hud/plane_master.dm +++ b/code/_onclick/hud/plane_master.dm @@ -1,205 +1,237 @@ +// I hate this place +INITIALIZE_IMMEDIATE(/atom/movable/screen/plane_master) + /atom/movable/screen/plane_master screen_loc = "CENTER" icon_state = "blank" - appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR + appearance_flags = PLANE_MASTER blend_mode = BLEND_OVERLAY - var/show_alpha = 255 - var/hide_alpha = 0 + plane = LOWEST_EVER_PLANE + /// Will be sent to the debug ui as a description for each plane + /// Also useful as a place to explain to coders how/why your plane works, and what it's meant to do + /// Plaintext and basic html are fine to use here. + /// I'll bonk you if I find you putting "lmao stuff" in here, make this useful. + var/documentation = "" + /// Our real alpha value, so alpha can persist through being hidden/shown + var/true_alpha = 255 + /// Tracks if we're using our true alpha, or being manipulated in some other way + var/alpha_enabled = TRUE + + /// The plane master group we're a member of, our "home" + var/datum/plane_master_group/home + /// If our plane master allows for offsetting /// Mostly used for planes that really don't need to be duplicated, like the hud planes var/allows_offsetting = TRUE + /// Our offset from our "true" plane, see below + var/offset + /// When rendering multiz, lower levels get their own set of plane masters + /// Real plane here represents the "true" plane value of something, ignoring the offset required to handle lower levels + var/real_plane + //--rendering relay vars-- /// list of planes we will relay this plane's render to var/list/render_relay_planes = list(RENDER_PLANE_GAME) - -/atom/movable/screen/plane_master/proc/Show(override) - alpha = override || show_alpha - -/atom/movable/screen/plane_master/proc/Hide(override) - alpha = override || hide_alpha - -//Why do plane masters need a backdrop sometimes? Read https://secure.byond.com/forum/?post=2141928 -//Trust me, you need one. Period. If you don't think you do, you're doing something extremely wrong. -/atom/movable/screen/plane_master/proc/backdrop(mob/mymob) - -///Things rendered on "openspace"; holes in multi-z -/atom/movable/screen/plane_master/openspace - name = "open space plane master" - plane = FLOOR_OPENSPACE_PLANE - appearance_flags = PLANE_MASTER - blend_mode = BLEND_MULTIPLY - alpha = 255 - -/atom/movable/screen/plane_master/openspace/backdrop(mob/mymob) - filters = list() - filters += filter(type="alpha", render_source = LIGHTING_RENDER_TARGET, flags = MASK_INVERSE) - filters += filter(type = "drop_shadow", color = "#04080FAA", size = -10) - filters += filter(type = "drop_shadow", color = "#04080FAA", size = -15) - filters += filter(type = "drop_shadow", color = "#04080FAA", size = -20) - -/atom/movable/screen/plane_master/proc/outline(_size, _color) - filters += filter(type = "outline", size = _size, color = _color) - -/atom/movable/screen/plane_master/proc/shadow(_size, _border, _offset = 0, _x = 0, _y = 0, _color = "#04080FAA") - filters += filter(type = "drop_shadow", x = _x, y = _y, color = _color, size = _size, offset = _offset) - -/atom/movable/screen/plane_master/proc/clear_filters() - filters = list() - -///Contains just the floor -/atom/movable/screen/plane_master/floor - name = "floor plane master" - plane = FLOOR_PLANE - appearance_flags = PLANE_MASTER - blend_mode = BLEND_OVERLAY - -/atom/movable/screen/plane_master/floor/backdrop(mob/mymob) - filters = list() - if(istype(mymob) && mymob.eye_blurry) - filters += GAUSSIAN_BLUR(clamp(mymob.eye_blurry*0.1,0.6,3)) - // Should be moved to the world render plate when render plates get ported in - filters += filter(type="displace", render_source = SINGULARITY_RENDER_TARGET, size=75) - -///Contains most things in the game world -/atom/movable/screen/plane_master/game_world - name = "game world plane master" - plane = GAME_PLANE - appearance_flags = PLANE_MASTER //should use client color - blend_mode = BLEND_OVERLAY - -/atom/movable/screen/plane_master/game_world/backdrop(mob/mymob) - filters = list() - if(istype(mymob) && mymob.client?.prefs?.read_preference(/datum/preference/toggle/ambient_occlusion)) - filters += AMBIENT_OCCLUSION - if(istype(mymob) && mymob.eye_blurry) - filters += GAUSSIAN_BLUR(clamp(mymob.eye_blurry*0.1,0.6,3)) - // Should be moved to the world render plate when render plates get ported in - filters += filter(type="displace", render_source = SINGULARITY_RENDER_TARGET, size=75) - - -///Contains all lighting objects -/atom/movable/screen/plane_master/lighting - name = "lighting plane master" - plane = LIGHTING_PLANE - blend_mode = BLEND_MULTIPLY - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - -/atom/movable/screen/plane_master/lighting/Initialize(mapload) - . = ..() - filters += filter(type="alpha", render_source = EMISSIVE_RENDER_TARGET, flags = MASK_INVERSE) - filters += filter(type="alpha", render_source = EMISSIVE_UNBLOCKABLE_RENDER_TARGET, flags = MASK_INVERSE) - filters += filter(type="alpha", render_source = O_LIGHTING_VISUAL_RENDER_TARGET, flags = MASK_INVERSE) - // Should be moved to the world render plate when render plates get ported in - filters += filter(type="displace", render_source = SINGULARITY_RENDER_TARGET, size=75) - -/** - * Things placed on this mask the lighting plane. Doesn't render directly. - * - * Gets masked by blocking plane. Use for things that you want blocked by - * mobs, items, etc. - */ -/atom/movable/screen/plane_master/emissive - name = "emissive plane master" - plane = EMISSIVE_PLANE - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - render_target = EMISSIVE_RENDER_TARGET - -/atom/movable/screen/plane_master/emissive/Initialize(mapload) + /// blend mode to apply to the render relay in case you dont want to use the plane_masters blend_mode + var/blend_mode_override + /// list of current relays this plane is utilizing to render + var/list/atom/movable/render_plane_relay/relays = list() + /// if render relays have already be generated + var/relays_generated = FALSE + + /// If this plane master should be hidden from the player at roundstart + /// We do this so PMs can opt into being temporary, to reduce load on clients + var/start_hidden = FALSE + /// If this plane master is being forced to hide. + /// Hidden PMs will dump ANYTHING relayed or drawn onto them. Be careful with this + /// Remember: a hidden plane master will dump anything drawn directly to it onto the output render. It does NOT hide its contents + /// Use alpha for that + var/force_hidden = FALSE + + /// If this plane should be scaled by multiz + /// Planes with this set should NEVER be relay'd into each other, as that will cause visual fuck + var/multiz_scaled = TRUE + + /// Bitfield that describes how this plane master will render if its z layer is being "optimized" + /// If a plane master is NOT critical, it will be completely dropped if we start to render outside a client's multiz boundary prefs + /// Of note: most of the time we will relay renders to non critical planes in this stage. so the plane master will end up drawing roughly "in order" with its friends + /// This is NOT done for parallax and other problem children, because the rules of BLEND_MULTIPLY appear to not behave as expected :( + /// This will also just make debugging harder, because we do fragile things in order to ensure things operate as epected. I'm sorry + /// Compile time + /// See [code\__DEFINES\layers.dm] for our bitflags + var/critical = NONE + + /// If this plane master is outside of our visual bounds right now + var/is_outside_bounds = FALSE + var/relay_loc_debug = "CENTER" + +/atom/movable/screen/plane_master/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset = 0, relay_loc_override = null) . = ..() - filters += filter(type="alpha", render_source=EMISSIVE_BLOCKER_RENDER_TARGET, flags=MASK_INVERSE) - // Should be moved to the world render plate when render plates get ported in - filters += filter(type="displace", render_source = SINGULARITY_RENDER_TARGET, size=75) - -/** - * Things placed on this always mask the lighting plane. Doesn't render directly. - * - * Always masks the light plane, isn't blocked by anything. Use for on mob glows, - * magic stuff, etc. - */ - -/atom/movable/screen/plane_master/emissive_unblockable - name = "unblockable emissive plane master" - plane = EMISSIVE_UNBLOCKABLE_PLANE - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - render_target = EMISSIVE_UNBLOCKABLE_RENDER_TARGET - -/** - * Things placed on this layer mask the emissive layer. Doesn't render directly - * - * You really shouldn't be directly using this, use atom helpers instead - */ -/atom/movable/screen/plane_master/emissive_blocker - name = "emissive blocker plane master" - plane = EMISSIVE_BLOCKER_PLANE - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - render_target = EMISSIVE_BLOCKER_RENDER_TARGET - -///Contains space parallax -/atom/movable/screen/plane_master/parallax - name = "parallax plane master" - plane = PLANE_SPACE_PARALLAX - blend_mode = BLEND_MULTIPLY - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - -/atom/movable/screen/plane_master/parallax/Initialize(mapload) + src.offset = offset + true_alpha = alpha + real_plane = plane + + if(!set_home(home)) + return INITIALIZE_HINT_QDEL + update_offset() + if(!documentation && !(istype(src, /atom/movable/screen/plane_master) || istype(src, /atom/movable/screen/plane_master/rendering_plate))) + stack_trace("Plane master created without a description. Document how your thing works so people will know in future, and we can display it in the debug menu") + if(start_hidden) + hide_plane(home.our_hud?.mymob) + generate_render_relays(relay_loc_override) + +/atom/movable/screen/plane_master/Destroy() + if(home) + // NOTE! We do not clear ourselves from client screens + // We relay on whoever qdel'd us to reset our hud, and properly purge us + home.plane_masters -= "[plane]" + home = null . = ..() - // Should be moved to the world render plate when render plates get ported in - filters += filter(type="displace", render_source = SINGULARITY_RENDER_TARGET, size=75) - -/atom/movable/screen/plane_master/parallax_white - name = "parallax whitifier plane master" - plane = PLANE_SPACE - -/atom/movable/screen/plane_master/lighting/backdrop(mob/mymob) - mymob.overlay_fullscreen("lighting_backdrop_lit", /atom/movable/screen/fullscreen/lighting_backdrop/lit) - mymob.overlay_fullscreen("lighting_backdrop_unlit", /atom/movable/screen/fullscreen/lighting_backdrop/unlit) - -/atom/movable/screen/plane_master/singularity_effect - name = "singularity plane master" - plane = SINGULARITY_EFFECT_PLANE - render_target = SINGULARITY_RENDER_TARGET - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - -/atom/movable/screen/plane_master/camera_static - name = "camera static plane master" - plane = CAMERA_STATIC_PLANE - appearance_flags = PLANE_MASTER - blend_mode = BLEND_OVERLAY - -/atom/movable/screen/plane_master/runechat - name = "runechat plane master" - plane = RUNECHAT_PLANE - appearance_flags = PLANE_MASTER - blend_mode = BLEND_OVERLAY - -/atom/movable/screen/plane_master/runechat/backdrop(mob/mymob) - filters = list() - if(istype(mymob) && mymob.client?.prefs?.read_preference(/datum/preference/toggle/ambient_occlusion)) - filters += AMBIENT_OCCLUSION - -/atom/movable/screen/plane_master/o_light_visual - name = "overlight light visual plane master" - layer = O_LIGHTING_VISUAL_LAYER - plane = O_LIGHTING_VISUAL_PLANE - render_target = O_LIGHTING_VISUAL_RENDER_TARGET - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - blend_mode = BLEND_MULTIPLY - -/** - * Render relay object assigned to a plane master to be able to relay it's render onto other planes that are not it's own - */ -/atom/movable/render_plane_relay - screen_loc = "CENTER" - layer = -1 - plane = 0 - appearance_flags = PASS_MOUSE | NO_CLIENT_COLOR | KEEP_TOGETHER - /// If we render into a critical plane master, or not - var/critical_target = FALSE - -/atom/movable/screen/plane_master/fullscreen - name = "Fullscreen" - plane = FULLSCREEN_PLANE - appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR - render_relay_planes = list(RENDER_PLANE_NON_GAME) - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - allows_offsetting = FALSE + QDEL_LIST(relays) + +/// Sets the plane group that owns us, it also determines what screen we render to +/// Returns FALSE if the set_home fails, TRUE otherwise +/atom/movable/screen/plane_master/proc/set_home(datum/plane_master_group/home) + if(!istype(home, /datum/plane_master_group)) + return FALSE + src.home = home + if(home.map) + screen_loc = "[home.map]:[screen_loc]" + assigned_map = home.map + return TRUE + +/// Updates our "offset", basically what layer of multiz we're meant to render +/// Top is 0, goes up as you go down +/// It's taken into account by render targets and relays, so we gotta make sure they're on the same page +/atom/movable/screen/plane_master/proc/update_offset() + name = "[initial(name)] #[offset]" + SET_PLANE_W_SCALAR(src, real_plane, offset) + for(var/i in 1 to length(render_relay_planes)) + render_relay_planes[i] = GET_NEW_PLANE(render_relay_planes[i], offset) + if(initial(render_target)) + render_target = OFFSET_RENDER_TARGET(initial(render_target), offset) + +/atom/movable/screen/plane_master/proc/set_alpha(new_alpha) + true_alpha = new_alpha + if(!alpha_enabled) + return + alpha = new_alpha + +/atom/movable/screen/plane_master/proc/disable_alpha() + alpha_enabled = FALSE + alpha = 0 + +/atom/movable/screen/plane_master/proc/enable_alpha() + alpha_enabled = TRUE + alpha = true_alpha + +/// Shows a plane master to the passed in mob +/// Override this to apply unique effects and such +/// Returns TRUE if the call is allowed, FALSE otherwise +/atom/movable/screen/plane_master/proc/show_to(mob/mymob) + SHOULD_CALL_PARENT(TRUE) + if(force_hidden) + return FALSE + + var/client/our_client = mymob?.canon_client + // Alright, let's get this out of the way + // Mobs can move z levels without their client. If this happens, we need to ensure critical display settings are respected + // This is done here. Mild to severe pain but it's nessesary + if(check_outside_bounds()) + if(!(critical & PLANE_CRITICAL_DISPLAY)) + return FALSE + if(!our_client) + return TRUE + our_client.screen += src + + if(!(critical & PLANE_CRITICAL_NO_RELAY)) + our_client.screen += relays + return TRUE + return TRUE + + if(!our_client) + return TRUE + + our_client.screen += src + our_client.screen += relays + return TRUE + +/// Hook to allow planes to work around is_outside_bounds +/// Return false to allow a show, true otherwise +/atom/movable/screen/plane_master/proc/check_outside_bounds() + return is_outside_bounds + +/// Hides a plane master from the passeed in mob +/// Do your effect cleanup here +/atom/movable/screen/plane_master/proc/hide_from(mob/oldmob) + SHOULD_CALL_PARENT(TRUE) + var/client/their_client = oldmob?.client + if(!their_client) + return + their_client.screen -= src + their_client.screen -= relays + + +/// Forces this plane master to hide, until unhide_plane is called +/// This allows us to disable unused PMs without breaking anything else +/atom/movable/screen/plane_master/proc/hide_plane(mob/cast_away) + force_hidden = TRUE + hide_from(cast_away) + +/// Disables any forced hiding, allows the plane master to be used as normal +/atom/movable/screen/plane_master/proc/unhide_plane(mob/enfold) + force_hidden = FALSE + show_to(enfold) + +/// Mirrors our force hidden state to the hidden state of the plane that came before, assuming it's valid +/// This allows us to mirror any hidden sets from before we were created, no matter how low that chance is +/atom/movable/screen/plane_master/proc/mirror_parent_hidden() + var/mob/our_mob = home?.our_hud?.mymob + var/atom/movable/screen/plane_master/true_plane = our_mob?.hud_used?.get_plane_master(plane) + if(true_plane == src || !true_plane) + return + + if(true_plane.force_hidden == force_hidden) + return + + // If one of us already exists and it's not hidden, unhide ourselves + if(true_plane.force_hidden) + hide_plane(our_mob) + else + unhide_plane(our_mob) + +/atom/movable/screen/plane_master/proc/outside_bounds(mob/relevant) + if(force_hidden || is_outside_bounds) + return + is_outside_bounds = TRUE + // If we're of critical importance, AND we're below the rendering layer + if(critical & PLANE_CRITICAL_DISPLAY) + // We here assume that your render target starts with * + if(critical & PLANE_CRITICAL_CUT_RENDER && render_target) + render_target = copytext_char(render_target, 2) + if(!(critical & PLANE_CRITICAL_NO_RELAY)) + return + var/client/our_client = relevant.client + if(our_client) + for(var/atom/movable/render_plane_relay/relay as anything in relays) + our_client.screen -= relay + + return + hide_from(relevant) + +/atom/movable/screen/plane_master/proc/inside_bounds(mob/relevant) + is_outside_bounds = FALSE + if(critical & PLANE_CRITICAL_DISPLAY) + // We here assume that your render target starts with * + if(critical & PLANE_CRITICAL_CUT_RENDER && render_target) + render_target = "*[render_target]" + + if(!(critical & PLANE_CRITICAL_NO_RELAY)) + return + var/client/our_client = relevant.client + if(our_client) + for(var/atom/movable/render_plane_relay/relay as anything in relays) + our_client.screen += relay + + return + show_to(relevant) diff --git a/code/_onclick/hud/plane_master_controller.dm b/code/_onclick/hud/plane_master_controller.dm new file mode 100644 index 000000000000..cae4822c6fec --- /dev/null +++ b/code/_onclick/hud/plane_master_controller.dm @@ -0,0 +1,103 @@ +///Atom that manages and controls multiple planes. It's an atom so we can hook into add_filter etc. Multiple controllers can control one plane. +///Of note: plane master controllers are currently not very extensively used, because render plates fill a semi similar niche +///This could well change someday, and I'd like to keep this stuff around, so we use it for a few cases just out of convenience +/atom/movable/plane_master_controller + ///List of planes as defines in this controllers control + var/list/controlled_planes = list() + ///hud that owns this controller + var/datum/hud/owner_hud + +INITIALIZE_IMMEDIATE(/atom/movable/plane_master_controller) + +///Ensures that all the planes are correctly in the controlled_planes list. +/atom/movable/plane_master_controller/Initialize(mapload, datum/hud/hud) + . = ..() + if(!istype(hud)) + return + owner_hud = hud + +/atom/movable/plane_master_controller/proc/get_planes() + var/returned_planes = list() + for(var/true_plane in controlled_planes) + returned_planes += get_true_plane(true_plane) + return returned_planes + +/atom/movable/plane_master_controller/proc/get_true_plane(true_plane) + var/list/returned_planes = owner_hud.get_true_plane_masters(true_plane) + if(!length(returned_planes)) //If we looked for a hud that isn't instanced, just keep going + stack_trace("[plane] isn't a valid plane master layer for [owner_hud.type], are you sure it exists in the first place?") + return + + return returned_planes + +///Full override so we can just use filterrific +/atom/movable/plane_master_controller/add_filter(name, priority, list/params) + . = ..() + for(var/atom/movable/screen/plane_master/pm_iterator as anything in get_planes()) + pm_iterator.add_filter(name, priority, params) + +///Full override so we can just use filterrific +/atom/movable/plane_master_controller/remove_filter(name_or_names) + . = ..() + for(var/atom/movable/screen/plane_master/pm_iterator as anything in get_planes()) + pm_iterator.remove_filter(name_or_names) + +/atom/movable/plane_master_controller/update_filters() + . = ..() + for(var/atom/movable/screen/plane_master/pm_iterator as anything in get_planes()) + pm_iterator.update_filters() + +///Gets all filters for this controllers plane masters +/atom/movable/plane_master_controller/proc/get_filters(name) + . = list() + for(var/atom/movable/screen/plane_master/pm_iterator as anything in get_planes()) + . += pm_iterator.get_filter(name) + +///Transitions all filters owned by this plane master controller +/atom/movable/plane_master_controller/transition_filter(name, time, list/new_params, easing, loop) + . = ..() + for(var/atom/movable/screen/plane_master/pm_iterator as anything in get_planes()) + pm_iterator.transition_filter(name, new_params, time, easing, loop) + +///Full override so we can just use filterrific +/atom/movable/plane_master_controller/add_atom_colour(coloration, colour_priority) + . = ..() + for(var/atom/movable/screen/plane_master/pm_iterator as anything in get_planes()) + pm_iterator.add_atom_colour(coloration, colour_priority) + + +///Removes an instance of colour_type from the atom's atom_colours list +/atom/movable/plane_master_controller/remove_atom_colour(colour_priority, coloration) + . = ..() + for(var/atom/movable/screen/plane_master/pm_iterator as anything in get_planes()) + pm_iterator.remove_atom_colour(colour_priority, coloration) + + +///Resets the atom's color to null, and then sets it to the highest priority colour available +/atom/movable/plane_master_controller/update_atom_colour() + for(var/atom/movable/screen/plane_master/pm_iterator as anything in get_planes()) + pm_iterator.update_atom_colour() + + +/// Exists for convienience when referencing all game render plates +/atom/movable/plane_master_controller/game + name = PLANE_MASTERS_GAME + controlled_planes = list( + RENDER_PLANE_GAME + ) + +/// Exists for convienience when referencing all non-master render plates. +/// This is the whole game and the UI, but not the escape menu. +/atom/movable/plane_master_controller/non_master + name = PLANE_MASTERS_NON_MASTER + controlled_planes = list( + RENDER_PLANE_GAME, + RENDER_PLANE_NON_GAME, + ) + +/// Exists for convienience when referencing all game render plates +/atom/movable/plane_master_controller/colorblind + name = PLANE_MASTERS_COLORBLIND + controlled_planes = list( + RENDER_PLANE_MASTER + ) diff --git a/code/_onclick/hud/plane_master_group.dm b/code/_onclick/hud/plane_master_group.dm new file mode 100644 index 000000000000..6a0e39322d30 --- /dev/null +++ b/code/_onclick/hud/plane_master_group.dm @@ -0,0 +1,201 @@ +/// Datum that represents one "group" of plane masters +/// So all the main window planes would be in one, all the spyglass planes in another +/// Etc +/datum/plane_master_group + /// Our key in the group list on /datum/hud + /// Should be unique for any group of plane masters in the world + var/key + /// Our parent hud + var/datum/hud/our_hud + /// List in the form "[plane]" = object, the plane masters we own + var/list/atom/movable/screen/plane_master/plane_masters = list() + /// The visual offset we are currently using + var/active_offset = 0 + /// What, if any, submap we render onto + var/map = "" + var/relay_loc_override = null + +/datum/plane_master_group/New(key, map = "") + . = ..() + src.key = key + src.map = map + build_plane_masters(0, SSmapping.max_plane_offset) + +/datum/plane_master_group/Destroy() + orphan_hud() + QDEL_LIST_ASSOC_VAL(plane_masters) + return ..() + +/// Display a plane master group to some viewer, so show all our planes to it +/datum/plane_master_group/proc/attach_to(datum/hud/viewing_hud) + if(viewing_hud.master_groups[key]) + stack_trace("Hey brother, our key [key] is already in use by a plane master group on the passed in hud, belonging to [viewing_hud.mymob]. Ya fucked up, why are there dupes") + return + + our_hud = viewing_hud + our_hud.master_groups[key] = src + show_hud() + // transform_lower_turfs(our_hud, active_offset) + +/// Hide the plane master from its current hud, fully clear it out +/datum/plane_master_group/proc/orphan_hud() + if(our_hud) + our_hud.master_groups -= key + hide_hud() + our_hud = null + +/// Well, refresh our group, mostly useful for plane specific updates +/datum/plane_master_group/proc/refresh_hud() + hide_hud() + show_hud() + +/// Fully regenerate our group, resetting our planes to their compile time values +/datum/plane_master_group/proc/rebuild_hud() + hide_hud() + QDEL_LIST_ASSOC_VAL(plane_masters) + build_plane_masters(0, SSmapping.max_plane_offset) + show_hud() + // transform_lower_turfs(our_hud, active_offset) + +/datum/plane_master_group/proc/hide_hud() + for(var/thing in plane_masters) + var/atom/movable/screen/plane_master/plane = plane_masters[thing] + plane.hide_from(our_hud.mymob) + +/datum/plane_master_group/proc/show_hud() + for(var/thing in plane_masters) + var/atom/movable/screen/plane_master/plane = plane_masters[thing] + show_plane(plane) + +/// This is mostly a proc so it can be overriden by popups, since they have unique behavior they want to do +/datum/plane_master_group/proc/show_plane(atom/movable/screen/plane_master/plane) + plane.show_to(our_hud.mymob) + +/// Nice wrapper for the "[]"ing +/datum/plane_master_group/proc/get_plane(plane) + return plane_masters["[plane]"] + +/// Returns a list of all the plane master types we want to create +/datum/plane_master_group/proc/get_plane_types() + return subtypesof(/atom/movable/screen/plane_master) - /atom/movable/screen/plane_master/rendering_plate + +/// Actually generate our plane masters, in some offset range (where offset is the z layers to render to, because each "layer" in a multiz stack gets its own plane master cube) +/datum/plane_master_group/proc/build_plane_masters(starting_offset, ending_offset) + for(var/atom/movable/screen/plane_master/mytype as anything in get_plane_types()) + for(var/plane_offset in starting_offset to ending_offset) + if(plane_offset != 0 && !initial(mytype.allows_offsetting)) + continue + var/atom/movable/screen/plane_master/instance = new mytype(null, null, src, plane_offset, relay_loc_override) + plane_masters["[instance.plane]"] = instance + prep_plane_instance(instance) + +/// Similarly, exists so subtypes can do unique behavior to planes on creation +/datum/plane_master_group/proc/prep_plane_instance(atom/movable/screen/plane_master/instance) + return + +// // It would be nice to setup parallaxing for stairs and things when doing this +// // So they look nicer. if you can't it's all good, if you think you can sanely look at monster's work +// // It's hard, and potentially expensive. be careful +/datum/plane_master_group/proc/transform_lower_turfs(datum/hud/source, new_offset, use_scale = TRUE) + // Check if this feature is disabled for the client, in which case don't use scale. + // var/mob/our_mob = our_hud?.mymob + // if(!our_mob?.client?.prefs?.read_preference(/datum/preference/toggle/multiz_parallax)) + // use_scale = FALSE + + // No offset? piss off + if(!SSmapping.max_plane_offset) + return + + active_offset = new_offset + + // Each time we go "down" a visual z level, we'll reduce the scale by this amount + // Chosen because mothblocks liked it, didn't cause motion sickness while also giving a sense of height + var/scale_by = 0.965 + if(!use_scale) + // This is a workaround for two things + // First of all, if a mob can see objects but not turfs, they will not be shown the holder objects we use for + // What I'd like to do is revert to images if this case throws, but image vis_contents is broken + // https://www.byond.com/forum/post/2821969 + // If that's ever fixed, please just use that. thanks :) + scale_by = 1 + + var/list/offsets = list() + // var/multiz_boundary = our_mob?.client?.prefs?.read_preference(/datum/preference/numeric/multiz_performance) + + // We accept negatives so going down "zooms" away the drop above as it goes + for(var/offset in -SSmapping.max_plane_offset to SSmapping.max_plane_offset) + // Multiz boundaries disable transforms + // if(multiz_boundary != MULTIZ_PERFORMANCE_DISABLE && (multiz_boundary < abs(offset))) + // offsets += null + // continue + + // No transformations if we're landing ON you + if(offset == 0) + offsets += null + continue + + var/scale = scale_by ** (offset) + var/matrix/multiz_shrink = matrix() + multiz_shrink.Scale(scale) + offsets += multiz_shrink + + // So we can talk in 1 -> max_offset * 2 + 1, rather then -max_offset -> max_offset + var/offset_offset = SSmapping.max_plane_offset + 1 + + for(var/plane_key in plane_masters) + var/atom/movable/screen/plane_master/plane = plane_masters[plane_key] + if(!plane.allows_offsetting) + continue + + var/visual_offset = plane.offset - new_offset + + // Basically uh, if we're showing something down X amount of levels, or up any amount of levels + // if(multiz_boundary != MULTIZ_PERFORMANCE_DISABLE && (visual_offset > multiz_boundary || visual_offset < 0)) + // plane.outside_bounds(our_mob) + // else if(plane.is_outside_bounds) + // plane.inside_bounds(our_mob) + + if(!plane.multiz_scaled) + continue + + if(plane.force_hidden || plane.is_outside_bounds || visual_offset < 0) + // We don't animate here because it should be invisble, but we do mark because it'll look nice + plane.transform = offsets[visual_offset + offset_offset] + continue + + animate(plane, transform = offsets[visual_offset + offset_offset], 0.05 SECONDS, easing = LINEAR_EASING) + +/// Holds plane masters for popups, like camera windows +/// Note: We do not scale this plane, even though we could +/// This is because it's annoying to get turfs to position inside it correctly +/// If you wanna try someday feel free, but I can't manage it +/datum/plane_master_group/popup + + relay_loc_override = "LEFT,TOP" +// /datum/plane_master_group/popup/transform_lower_turfs(datum/hud/source, new_offset, use_scale = TRUE) +// return ..(source, new_offset, FALSE) + +/// Holds the main plane master +/datum/plane_master_group/main + +// /datum/plane_master_group/main/transform_lower_turfs(datum/hud/source, new_offset, use_scale = TRUE) +// if(use_scale) +// return ..(source, new_offset, source.should_use_scale()) +// return ..() + +/// Hudless group. Exists for testing +/datum/plane_master_group/hudless + var/mob/our_mob + +/datum/plane_master_group/hudless/Destroy() + . = ..() + our_mob = null + +/datum/plane_master_group/hudless/hide_hud() + for(var/thing in plane_masters) + var/atom/movable/screen/plane_master/plane = plane_masters[thing] + plane.hide_from(our_mob) + +/// This is mostly a proc so it can be overriden by popups, since they have unique behavior they want to do +/datum/plane_master_group/hudless/show_plane(atom/movable/screen/plane_master/plane) + plane.show_to(our_mob) diff --git a/code/_onclick/hud/plane_master_subtypes.dm b/code/_onclick/hud/plane_master_subtypes.dm new file mode 100644 index 000000000000..8988b2f7f33c --- /dev/null +++ b/code/_onclick/hud/plane_master_subtypes.dm @@ -0,0 +1,447 @@ +/atom/movable/screen/plane_master/clickcatcher + name = "Click Catcher" + documentation = "Contains the screen object we use as a backdrop to catch clicks on portions of the screen that would otherwise contain nothing else. \ +
Will always be below almost everything else" + plane = CLICKCATCHER_PLANE + appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR + multiz_scaled = FALSE + critical = PLANE_CRITICAL_DISPLAY + +/atom/movable/screen/plane_master/clickcatcher/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset) + . = ..() + RegisterSignal(SSmapping, COMSIG_PLANE_OFFSET_INCREASE, PROC_REF(offset_increased)) + offset_increased(SSmapping, 0, SSmapping.max_plane_offset) + +/atom/movable/screen/plane_master/clickcatcher/proc/offset_increased(datum/source, old_off, new_off) + SIGNAL_HANDLER + // We only want need the lowest level + // If my system better supported changing PM plane values mid op I'd do that, but I do NOT so + if(new_off > offset) + hide_plane(home?.our_hud?.mymob) + +/atom/movable/screen/plane_master/parallax_white + name = "Parallax whitifier" + documentation = "Essentially a backdrop for the parallax plane. We're rendered just below it, so we'll be multiplied by its well, parallax.\ +
If you want something to look as if it has parallax on it, draw it to this plane." + plane = PLANE_SPACE + appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR + render_relay_planes = list(RENDER_PLANE_GAME, LIGHT_MASK_PLANE) + critical = PLANE_CRITICAL_FUCKO_PARALLAX // goes funny when touched. no idea why I don't trust byond + +/atom/movable/screen/plane_master/parallax_white/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset) + . = ..() + add_relay_to(GET_NEW_PLANE(EMISSIVE_RENDER_PLATE, offset), relay_layer = EMISSIVE_SPACE_LAYER) + +///Contains space parallax +/atom/movable/screen/plane_master/parallax + name = "Parallax" + documentation = "Contains parallax, or to be more exact the screen objects that hold parallax.\ +
Note the BLEND_MULTIPLY. The trick here is how low our plane value is. Because of that, we draw below almost everything in the game.\ +
We abuse this to ensure we multiply against the Parallax whitifier plane, or space's plane. It's set to full white, so when you do the multiply you just get parallax out where it well, makes sense to be.\ +
Also notice that the parent parallax plane is mirrored down to all children. We want to support viewing parallax across all z levels at once." + plane = PLANE_SPACE_PARALLAX + appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR + blend_mode = BLEND_MULTIPLY + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + multiz_scaled = FALSE + +/atom/movable/screen/plane_master/parallax/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset) + . = ..() + if(offset != 0) + // You aren't the source? don't change yourself + return + RegisterSignal(SSmapping, COMSIG_PLANE_OFFSET_INCREASE, PROC_REF(on_offset_increase)) + RegisterSignal(SSdcs, COMSIG_NARSIE_SUMMON_UPDATE, PROC_REF(narsie_modified)) + if(GLOB.narsie_summon_count >= 1) + narsie_start_midway(GLOB.narsie_effect_last_modified) // We assume we're on the start, so we can use this number + offset_increase(0, SSmapping.max_plane_offset) + +/atom/movable/screen/plane_master/parallax/proc/on_offset_increase(datum/source, old_offset, new_offset) + SIGNAL_HANDLER + offset_increase(old_offset, new_offset) + +/atom/movable/screen/plane_master/parallax/proc/offset_increase(old_offset, new_offset) + // Parallax will be mirrored down to any new planes that are added, so it will properly render across mirage borders + for(var/offset in old_offset to new_offset) + if(offset != 0) + // Overlay so we don't multiply twice, and thus fuck up our rendering + add_relay_to(GET_NEW_PLANE(plane, offset), BLEND_OVERLAY) + +// Hacky shit to ensure parallax works in perf mode +/atom/movable/screen/plane_master/parallax/outside_bounds(mob/relevant) + if(offset == 0) + remove_relay_from(GET_NEW_PLANE(RENDER_PLANE_GAME, 0)) + is_outside_bounds = TRUE // I'm sorry :( + return + // If we can't render, and we aren't the bottom layer, don't render us + // This way we only multiply against stuff that's not fullwhite space + var/atom/movable/screen/plane_master/parent_parallax = home.our_hud.get_plane_master(PLANE_SPACE_PARALLAX) + var/turf/viewing_turf = get_turf(relevant) + if(!viewing_turf || offset != GET_LOWEST_STACK_OFFSET(viewing_turf.z)) + parent_parallax.remove_relay_from(plane) + else + parent_parallax.add_relay_to(plane, BLEND_OVERLAY) + return ..() + +/atom/movable/screen/plane_master/parallax/inside_bounds(mob/relevant) + if(offset == 0) + add_relay_to(GET_NEW_PLANE(RENDER_PLANE_GAME, 0)) + is_outside_bounds = FALSE + return + // Always readd, just in case we lost it + var/atom/movable/screen/plane_master/parent_parallax = home.our_hud.get_plane_master(PLANE_SPACE_PARALLAX) + parent_parallax.add_relay_to(plane, BLEND_OVERLAY) + return ..() + +// Needs to handle rejoining on a lower z level, so we NEED to readd old planes +/atom/movable/screen/plane_master/parallax/check_outside_bounds() + // If we're outside bounds AND we're the 0th plane, we need to show cause parallax is hacked to hell + return offset != 0 && is_outside_bounds + +/// Starts the narsie animation midway, so we can catch up to everyone else quickly +/atom/movable/screen/plane_master/parallax/proc/narsie_start_midway(start_time) + var/time_elapsed = world.time - start_time + narsie_summoned_effect(max(16 SECONDS - time_elapsed, 0)) + +/// Starts the narsie animation, make us grey, then red +/atom/movable/screen/plane_master/parallax/proc/narsie_modified(datum/source, new_count) + SIGNAL_HANDLER + if(new_count >= 1) + narsie_summoned_effect(16 SECONDS) + else + narsie_unsummoned() + +/atom/movable/screen/plane_master/parallax/proc/narsie_summoned_effect(animate_time) + if(GLOB.narsie_summon_count >= 2) + var/static/list/nightmare_parallax = list(255,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, -130,0,0,0) + animate(src, color = nightmare_parallax, time = animate_time) + return + + var/static/list/grey_parallax = list(0.4,0.4,0.4,0, 0.4,0.4,0.4,0, 0.4,0.4,0.4,0, 0,0,0,1, -0.1,-0.1,-0.1,0) + // We're gonna animate ourselves grey + // Then, once it's done, about 40 seconds into the event itself, we're gonna start doin some shit. see below + animate(src, color = grey_parallax, time = animate_time) + +/atom/movable/screen/plane_master/parallax/proc/narsie_unsummoned() + animate(src, color = null, time = 8 SECONDS) + +/atom/movable/screen/plane_master/gravpulse + name = "Gravpulse" + documentation = "Ok so this one's fun. Basically, we want to be able to distort the game plane when a grav annom is around.\ +
So we draw the pattern we want to use to this plane, and it's then used as a render target by a distortion filter on the game plane.\ +
Note the blend mode and lack of relay targets. This plane exists only to distort, it's never rendered anywhere." + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + plane = GRAVITY_PULSE_PLANE + appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR + blend_mode = BLEND_ADD + render_target = GRAVITY_PULSE_RENDER_TARGET + render_relay_planes = list() + +///Contains just the floor +/atom/movable/screen/plane_master/floor + name = "Floor" + documentation = "The well, floor. This is mostly used as a sorting mechanism, but it also lets us create a \"border\" around the game world plane, so its drop shadow will actually work." + plane = FLOOR_PLANE + render_relay_planes = list(RENDER_PLANE_GAME, LIGHT_MASK_PLANE) + +/atom/movable/screen/plane_master/transparent_floor + name = "Transparent Floor" + documentation = "Really just openspace, stuff that is a turf but has no color or alpha whatsoever.\ +
We use this to draw to just the light mask plane, cause if it's not there we get holes of blackness over openspace" + plane = TRANSPARENT_FLOOR_PLANE + render_relay_planes = list(LIGHT_MASK_PLANE) + // Needs to be critical or it uh, it'll look white + critical = PLANE_CRITICAL_DISPLAY|PLANE_CRITICAL_NO_RELAY + +/atom/movable/screen/plane_master/floor/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset) + . = ..() + add_relay_to(GET_NEW_PLANE(EMISSIVE_RENDER_PLATE, offset), relay_layer = EMISSIVE_FLOOR_LAYER, relay_color = GLOB.em_block_color) + +/atom/movable/screen/plane_master/wall + name = "Wall" + documentation = "Holds all walls. We render this onto the game world. Separate so we can use this + space and floor planes as a guide for where byond blackness is NOT." + plane = WALL_PLANE + render_relay_planes = list(RENDER_PLANE_GAME_WORLD, LIGHT_MASK_PLANE) + +/atom/movable/screen/plane_master/wall/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset) + . = ..() + add_relay_to(GET_NEW_PLANE(EMISSIVE_RENDER_PLATE, offset), relay_layer = EMISSIVE_WALL_LAYER, relay_color = GLOB.em_block_color) + +/atom/movable/screen/plane_master/game + name = "Lower game world" + documentation = "Exists mostly because of FOV shit. Basically, if you've just got a normal not ABOVE fov thing, and you don't want it masked, stick it here yeah?" + plane = GAME_PLANE + render_relay_planes = list(RENDER_PLANE_GAME_WORLD) + +/atom/movable/screen/plane_master/game_world_fov_hidden + name = "lower game world fov hidden" + documentation = "If you want something to be hidden by fov, stick it on this plane. We're masked by the fov blocker plane, so the items on us can actually well, disappear." + plane = GAME_PLANE_FOV_HIDDEN + render_relay_planes = list(RENDER_PLANE_GAME_WORLD) + +/atom/movable/screen/plane_master/game_world_fov_hidden/Initialize(mapload, datum/hud/hud_owner) + . = ..() + add_filter("vision_cone", 1, alpha_mask_filter(render_source = OFFSET_RENDER_TARGET(FIELD_OF_VISION_BLOCKER_RENDER_TARGET, offset), flags = MASK_INVERSE)) + +/atom/movable/screen/plane_master/field_of_vision_blocker + name = "Field of vision blocker" + documentation = "This is one of those planes that's only used as a filter. It masks out things that want to be hidden by fov.\ +
Literally just contains FOV images, or masks." + plane = FIELD_OF_VISION_BLOCKER_PLANE + appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR + render_target = FIELD_OF_VISION_BLOCKER_RENDER_TARGET + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + render_relay_planes = list() + // We do NOT allow offsetting, because there's no case where you would want to block only one layer, at least currently + allows_offsetting = FALSE + start_hidden = TRUE + // We mark as multiz_scaled FALSE so transforms don't effect us, and we draw to the planes below us as if they were us. + // This is safe because we will ALWAYS be on the top z layer, so it DON'T MATTER + multiz_scaled = FALSE + +/atom/movable/screen/plane_master/field_of_vision_blocker/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset) + . = ..() + mirror_parent_hidden() + +/atom/movable/screen/plane_master/game_world_upper + name = "Upper game world" + documentation = "Ok so fov is kinda fucky, because planes in byond serve both as effect groupings and as rendering orderers. Since that's true, we need a plane that we can stick stuff that draws above fov blocked stuff on." + plane = GAME_PLANE_UPPER + render_relay_planes = list(RENDER_PLANE_GAME_WORLD) + +/atom/movable/screen/plane_master/wall_upper + name = "Upper wall" + documentation = "There are some walls that want to render above most things (mostly minerals since they shift over.\ +
We draw them to their own plane so we can hijack them for our emissive mask stuff" + plane = WALL_PLANE_UPPER + render_relay_planes = list(RENDER_PLANE_GAME_WORLD, LIGHT_MASK_PLANE) + +/atom/movable/screen/plane_master/wall_upper/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset) + . = ..() + add_relay_to(GET_NEW_PLANE(EMISSIVE_RENDER_PLATE, offset), relay_layer = EMISSIVE_WALL_LAYER, relay_color = GLOB.em_block_color) + +/atom/movable/screen/plane_master/game_world_upper_fov_hidden + name = "Upper game world fov hidden" + documentation = "Just as we need a place to draw things \"above\" the hidden fov plane, we also need to be able to hide stuff that draws over the upper game plane." + plane = GAME_PLANE_UPPER_FOV_HIDDEN + render_relay_planes = list(RENDER_PLANE_GAME_WORLD) + +/atom/movable/screen/plane_master/game_world_upper_fov_hidden/Initialize(mapload, datum/hud/hud_owner) + . = ..() + // Dupe of the other hidden plane + add_filter("vision_cone", 1, alpha_mask_filter(render_source = OFFSET_RENDER_TARGET(FIELD_OF_VISION_BLOCKER_RENDER_TARGET, offset), flags = MASK_INVERSE)) + +/atom/movable/screen/plane_master/seethrough + name = "Seethrough" + documentation = "Holds the seethrough versions (done using image overrides) of large objects. Mouse transparent, so you can click through them." + plane = SEETHROUGH_PLANE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + render_relay_planes = list(RENDER_PLANE_GAME_WORLD) + start_hidden = TRUE + +/atom/movable/screen/plane_master/game_world_above + name = "Above game world" + documentation = "We need a place that's unmasked by fov that also draws above the upper game world fov hidden plane. I told you fov was hacky man." + plane = ABOVE_GAME_PLANE + render_relay_planes = list(RENDER_PLANE_GAME_WORLD) + +/** + * Plane master that byond will by default draw to + * Shouldn't be used, exists to prevent people using plane 0 + * NOTE: If we used SEE_BLACKNESS on a map format that wasn't SIDE_MAP, this is where its darkness would land + * This would allow us to control it and do fun things. But we can't because side map doesn't support it, so this is just a stub + */ +/atom/movable/screen/plane_master/default + name = "Default" + documentation = "This is quite fiddly, so bear with me. By default (in byond) everything in the game is rendered onto plane 0. It's the default plane. \ +
But, because we've moved everything we control off plane 0, all that's left is stuff byond internally renders. \ +
What I'd like to do with this is capture byond blackness by giving mobs the SEE_BLACKNESS sight flag. \ +
But we CAN'T because SEE_BLACKNESS does not work with our rendering format. So I just eat it I guess" + plane = DEFAULT_PLANE + multiz_scaled = FALSE + start_hidden = TRUE // Doesn't DO anything, exists to hold this place + +/atom/movable/screen/plane_master/area + name = "Area" + documentation = "Holds the areas themselves, which ends up meaning it holds any overlays/effects we apply to areas. NOT snow or rad storms, those go on above lighting" + plane = AREA_PLANE + +/atom/movable/screen/plane_master/massive_obj + name = "Massive object" + documentation = "Huge objects need to render above everything else on the game plane, otherwise they'd well, get clipped and look not that huge. This does that." + plane = MASSIVE_OBJ_PLANE + +/atom/movable/screen/plane_master/point + name = "Point" + documentation = "I mean like, what do you want me to say? Points draw over pretty much everything else, so they get their own plane. Remember we layer render relays to draw planes in their proper order on render plates." + plane = POINT_PLANE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +///Contains all turf lighting +/atom/movable/screen/plane_master/turf_lighting + name = "Turf Lighting" + documentation = "Contains all lighting drawn to turfs. Not so complex, draws directly onto the lighting plate." + plane = LIGHTING_PLANE + appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR + render_relay_planes = list(RENDER_PLANE_LIGHTING) + blend_mode_override = BLEND_ADD + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + critical = PLANE_CRITICAL_DISPLAY + +/// This will not work through multiz, because of a byond bug with BLEND_MULTIPLY +/// Bug report is up, waiting on a fix +/atom/movable/screen/plane_master/o_light_visual + name = "Overlight light visual" + documentation = "Holds overlay lighting objects, or the sort of lighting that's a well, overlay stuck to something.\ +
Exists because lighting updating is really slow, and movement needs to feel smooth.\ +
We draw to the game plane, and mask out space for ourselves on the lighting plane so any color we have has the chance to display." + plane = O_LIGHTING_VISUAL_PLANE + appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR + render_target = O_LIGHTING_VISUAL_RENDER_TARGET + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + blend_mode = BLEND_MULTIPLY + critical = PLANE_CRITICAL_DISPLAY + +/atom/movable/screen/plane_master/above_lighting + name = "Above lighting" + plane = ABOVE_LIGHTING_PLANE + documentation = "Anything on the game plane that needs a space to draw on that will be above the lighting plane.\ +
Mostly little alerts and effects, also sometimes contains things that are meant to look as if they glow." + +/** + * Handles emissive overlays and emissive blockers. + */ +/atom/movable/screen/plane_master/emissive + name = "Emissive" + documentation = "Holds things that will be used to mask the lighting plane later on. Masked by the Emissive Mask plane to ensure we don't emiss out under a wall.\ +
Relayed onto the Emissive render plane to do the actual masking of lighting, since we need to be transformed and other emissive stuff needs to be transformed too.\ +
Don't want to double scale now." + plane = EMISSIVE_PLANE + appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + render_relay_planes = list(EMISSIVE_RENDER_PLATE) + critical = PLANE_CRITICAL_DISPLAY + +/atom/movable/screen/plane_master/pipecrawl + name = "Pipecrawl" + documentation = "Holds pipecrawl images generated during well, pipecrawling.\ +
Has a few effects and a funky color matrix designed to make things a bit more visually readable." + plane = PIPECRAWL_IMAGES_PLANE + start_hidden = TRUE + +/atom/movable/screen/plane_master/pipecrawl/Initialize(mapload, datum/hud/hud_owner) + . = ..() + // Makes everything on this plane slightly brighter + // Has a nice effect, makes thing stand out + color = list(1.2,0,0,0, 0,1.2,0,0, 0,0,1.2,0, 0,0,0,1, 0,0,0,0) + // This serves a similar purpose, I want the pipes to pop + add_filter("pipe_dropshadow", 1, drop_shadow_filter(x = -1, y= -1, size = 1, color = "#0000007A")) + mirror_parent_hidden() + +/atom/movable/screen/plane_master/camera_static + name = "Camera static" + documentation = "Holds camera static images. Usually only visible to people who can well, see static.\ +
We use images rather then vis contents because they're lighter on maptick, and maptick sucks butt." + plane = CAMERA_STATIC_PLANE + +/atom/movable/screen/plane_master/camera_static/show_to(mob/mymob) + . = ..() + if(!.) + return + var/datum/hud/our_hud = home.our_hud + if(isnull(our_hud)) + return + + // We'll hide the slate if we're not seeing through a camera eye + // This can call on a cycle cause we don't clear in hide_from + // Yes this is the best way of hooking into the hud, I hate myself too + RegisterSignal(our_hud, COMSIG_HUD_EYE_CHANGED, PROC_REF(eye_changed), override = TRUE) + eye_changed(our_hud, null, our_hud.mymob?.canon_client?.eye) + +/atom/movable/screen/plane_master/camera_static/proc/eye_changed(datum/hud/source, atom/old_eye, atom/new_eye) + SIGNAL_HANDLER + + if(!isaicamera(new_eye)) + if(!force_hidden) + hide_plane(source.mymob) + return + + if(force_hidden) + unhide_plane(source.mymob) + +/atom/movable/screen/plane_master/high_game + name = "High Game" + documentation = "Holds anything that wants to be displayed above the rest of the game plane, and doesn't want to be clickable. \ +
This includes atmos debug overlays, blind sound images, and mining scanners. \ +
Really only exists for its layering potential, we don't use this for any vfx" + plane = HIGH_GAME_PLANE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +/atom/movable/screen/plane_master/ghost + name = "Ghost" + documentation = "Ghosts draw here, so they don't get mixed up in the visuals of the game world. Note, this is not not how we HIDE ghosts from people, that's done with invisible and see_invisible." + plane = GHOST_PLANE + render_relay_planes = list(RENDER_PLANE_NON_GAME) + +/atom/movable/screen/plane_master/fullscreen + name = "Fullscreen" + documentation = "Holds anything that applies to or above the full screen. \ +
Note, it's still rendered underneath hud objects, but this lets us control the order that things like death/damage effects render in." + plane = FULLSCREEN_PLANE + appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR + render_relay_planes = list(RENDER_PLANE_NON_GAME) + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + allows_offsetting = FALSE + +/atom/movable/screen/plane_master/runechat + name = "Runechat" + documentation = "Holds runechat images, that text that pops up when someone say something. Uses a dropshadow to well, look nice." + plane = RUNECHAT_PLANE + render_relay_planes = list(RENDER_PLANE_NON_GAME) + +/atom/movable/screen/plane_master/runechat/show_to(mob/mymob) + . = ..() + if(!.) + return + remove_filter("AO") + if(istype(mymob) && mymob.canon_client?.prefs?.read_preference(/datum/preference/toggle/ambient_occlusion)) + add_filter("AO", 1, drop_shadow_filter(x = 0, y = -2, size = 4, color = "#04080FAA")) + +/atom/movable/screen/plane_master/balloon_chat + name = "Balloon chat" + documentation = "Holds ballon chat images, those little text bars that pop up for a second when you do some things. NOT runechat." + plane = BALLOON_CHAT_PLANE + appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR + render_relay_planes = list(RENDER_PLANE_NON_GAME) + +/atom/movable/screen/plane_master/hud + name = "HUD" + documentation = "Contains anything that want to be rendered on the hud. Typically is just screen elements." + plane = HUD_PLANE + appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR + render_relay_planes = list(RENDER_PLANE_NON_GAME) + allows_offsetting = FALSE + +/atom/movable/screen/plane_master/above_hud + name = "Above HUD" + documentation = "Anything that wants to be drawn ABOVE the rest of the hud. Typically close buttons and other elements that need to be always visible. Think preventing draggable action button memes." + plane = ABOVE_HUD_PLANE + appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR + render_relay_planes = list(RENDER_PLANE_NON_GAME) + allows_offsetting = FALSE + +/atom/movable/screen/plane_master/splashscreen + name = "Splashscreen" + documentation = "Cinematics and the splash screen." + plane = SPLASHSCREEN_PLANE + appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR + render_relay_planes = list(RENDER_PLANE_NON_GAME) + allows_offsetting = FALSE + +/atom/movable/screen/plane_master/escape_menu + name = "Escape Menu" + documentation = "Anything relating to the escape menu." + plane = ESCAPE_MENU_PLANE + appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR + render_relay_planes = list(RENDER_PLANE_MASTER) + allows_offsetting = FALSE diff --git a/code/_onclick/hud/radial.dm b/code/_onclick/hud/radial.dm index 4b8ca93d56f5..f994cac81afc 100644 --- a/code/_onclick/hud/radial.dm +++ b/code/_onclick/hud/radial.dm @@ -326,8 +326,8 @@ GLOBAL_LIST_EMPTY(radial_menus) return current_user = M.client //Blank - menu_holder = image(icon='icons/effects/effects.dmi',loc=anchor,icon_state="nothing",layer = ABOVE_HUD_LAYER) - menu_holder.plane = ABOVE_HUD_PLANE + menu_holder = image(icon='icons/effects/effects.dmi',loc=anchor,icon_state="nothing",layer = RADIAL_BACKGROUND_LAYER) + SET_PLANE_EXPLICIT(menu_holder, ABOVE_HUD_PLANE, M) menu_holder.appearance_flags |= KEEP_APART menu_holder.vis_contents += elements + close_button current_user.images += menu_holder diff --git a/code/_onclick/hud/random_layer.dm b/code/_onclick/hud/random_layer.dm new file mode 100644 index 000000000000..2a20889dd0ae --- /dev/null +++ b/code/_onclick/hud/random_layer.dm @@ -0,0 +1,52 @@ +/// Parallax layers that vary between rounds. Has some code to make sure we all have the same one +/atom/movable/screen/parallax_layer/random + blend_mode = BLEND_OVERLAY + speed = 2 + layer = 3 + +/atom/movable/screen/parallax_layer/random/Initialize(mapload, datum/hud/hud_owner, template, atom/movable/screen/parallax_layer/random/twin) + . = ..() + + if(twin) + copy_parallax(twin) + +/// Make this layer unique, with color or position or something +/atom/movable/screen/parallax_layer/random/proc/get_random_look() + return + +/// Copy a parallax instance to ensure parity between everyones parallax +/atom/movable/screen/parallax_layer/random/proc/copy_parallax(atom/movable/screen/parallax_layer/random/twin) + return + +/// For applying minor effects related to parallax. If you want big stuff, put it in a station trait or something +/atom/movable/screen/parallax_layer/random/proc/apply_global_effects() + return + +/// Gassy background with a few random colors +/atom/movable/screen/parallax_layer/random/space_gas + icon_state = "random_layer1" + + /// The colors we can be + var/possible_colors = list(COLOR_TEAL, COLOR_GREEN, COLOR_SILVER, COLOR_YELLOW, COLOR_CYAN, COLOR_ORANGE, COLOR_PURPLE) + /// The color we are + var/parallax_color + +/atom/movable/screen/parallax_layer/random/space_gas/get_random_look() + parallax_color = parallax_color || pick(possible_colors) + +/atom/movable/screen/parallax_layer/random/space_gas/copy_parallax(atom/movable/screen/parallax_layer/random/space_gas/twin) + parallax_color = twin.parallax_color + add_atom_colour(parallax_color, ADMIN_COLOUR_PRIORITY) + +/// Space gas but green for the radioactive nebula station trait +/atom/movable/screen/parallax_layer/random/space_gas/radioactive + parallax_color = list(0,0,0,0, 0,2,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) //very vibrant green + +/atom/movable/screen/parallax_layer/random/space_gas/radioactive/apply_global_effects() + . = ..() + set_base_starlight("#189156") + +/// Big asteroid rocks appear in the background +/atom/movable/screen/parallax_layer/random/asteroids + icon_state = "random_layer2" + layer = 4 diff --git a/code/_onclick/hud/render_plate.dm b/code/_onclick/hud/render_plate.dm new file mode 100644 index 000000000000..a8fac41d07c6 --- /dev/null +++ b/code/_onclick/hud/render_plate.dm @@ -0,0 +1,392 @@ +/*! + * Custom rendering solution to allow for advanced effects + * We (ab)use plane masters and render source/target to cheaply render 2+ planes as 1 + * if you want to read more read the _render_readme.md + */ + + +/** + * Render relay object assigned to a plane master to be able to relay it's render onto other planes that are not it's own + */ +/atom/movable/render_plane_relay + screen_loc = "CENTER" + layer = -1 + plane = 0 + appearance_flags = PASS_MOUSE | NO_CLIENT_COLOR | KEEP_TOGETHER + /// If we render into a critical plane master, or not + var/critical_target = FALSE + +/** + * ## Rendering plate + * + * Acts like a plane master, but for plane masters + * Renders other planes onto this plane, through the use of render objects + * Any effects applied onto this plane will act on the unified plane + * IE a bulge filter will apply as if the world was one object + * remember that once planes are unified on a render plate you cant change the layering of them! + */ +/atom/movable/screen/plane_master/rendering_plate + name = "Default rendering plate" + multiz_scaled = FALSE + +///this plate renders the final screen to show to the player +/atom/movable/screen/plane_master/rendering_plate/master + name = "Master rendering plate" + documentation = "The endpoint of all plane masters, you can think of this as the final \"view\" we draw.\ +
If offset is not 0 this will be drawn to the transparent plane of the floor above, but otherwise this is drawn to nothing, or shown to the player." + plane = RENDER_PLANE_MASTER + render_relay_planes = list() + +/atom/movable/screen/plane_master/rendering_plate/master/show_to(mob/mymob) + . = ..() + if(!.) + return + if(offset == 0) + return + // Non 0 offset render plates will relay up to the transparent plane above them, assuming they're not on the same z level as their target of course + var/datum/hud/hud = home.our_hud + // show_to can be called twice successfully with no hide_from call. Ensure no runtimes off the registers from this + if(hud) + RegisterSignal(hud, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(on_offset_change), override = TRUE) + offset_change(hud?.current_plane_offset || 0) + +/atom/movable/screen/plane_master/rendering_plate/master/hide_from(mob/oldmob) + . = ..() + if(offset == 0) + return + var/datum/hud/hud = home.our_hud + if(hud) + UnregisterSignal(hud, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(on_offset_change)) + +/atom/movable/screen/plane_master/rendering_plate/master/proc/on_offset_change(datum/source, old_offset, new_offset) + SIGNAL_HANDLER + offset_change(new_offset) + +/atom/movable/screen/plane_master/rendering_plate/master/proc/offset_change(new_offset) + if(new_offset == offset) // If we're on our own z layer, relay to nothing, just draw + remove_relay_from(GET_NEW_PLANE(RENDER_PLANE_TRANSPARENT, offset - 1)) + else // Otherwise, regenerate the relay + add_relay_to(GET_NEW_PLANE(RENDER_PLANE_TRANSPARENT, offset - 1)) + +///renders general in charachter game objects +/atom/movable/screen/plane_master/rendering_plate/game_plate + name = "Game rendering plate" + documentation = "Holds all objects that are ahhh, in character? is maybe the best way to describe it.\ +
We apply a displacement effect from the gravity pulse plane too, so we can warp the game world." + plane = RENDER_PLANE_GAME + render_relay_planes = list(RENDER_PLANE_MASTER) + +/atom/movable/screen/plane_master/rendering_plate/game_plate/Initialize(mapload, datum/hud/hud_owner) + . = ..() + add_filter("displacer", 1, displacement_map_filter(render_source = OFFSET_RENDER_TARGET(GRAVITY_PULSE_RENDER_TARGET, offset), size = 75)) + +// Blackness renders weird when you view down openspace, because of transforms and borders and such +// This is a consequence of not using lummy's grouped transparency, but I couldn't get that to work without totally fucking up +// Sight flags, and shooting vis_contents usage to the moon. So we're doin it different. +// If image vis contents worked (it should in 515), and we were ok with a maptick cost (wait for threaded maptick) this could be fixed +/atom/movable/screen/plane_master/rendering_plate/transparent + name = "Transparent plate" + documentation = "The master rendering plate from the offset below ours will be mirrored onto this plane. That way we achive a \"stack\" effect.\ +
This plane exists to uplayer the master rendering plate to the correct spot in our z layer's rendering order" + plane = RENDER_PLANE_TRANSPARENT + appearance_flags = PLANE_MASTER + +/atom/movable/screen/plane_master/rendering_plate/transparent/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset) + . = ..() + // Don't display us if we're below everything else yeah? + AddComponent(/datum/component/plane_hide_highest_offset) + color = list(0.9,0,0,0, 0,0.9,0,0, 0,0,0.9,0, 0,0,0,1, 0,0,0,0) + +///Contains most things in the game world +/atom/movable/screen/plane_master/rendering_plate/game_world + name = "Game world plate" + documentation = "Contains most of the objects in the world. Mobs, machines, etc. Note the drop shadow, it gives a very nice depth effect." + plane = RENDER_PLANE_GAME_WORLD + appearance_flags = PLANE_MASTER //should use client color + blend_mode = BLEND_OVERLAY + +/atom/movable/screen/plane_master/rendering_plate/game_world/show_to(mob/mymob) + . = ..() + if(!.) + return + remove_filter("AO") + if(istype(mymob) && mymob.canon_client?.prefs?.read_preference(/datum/preference/toggle/ambient_occlusion)) + add_filter("AO", 1, drop_shadow_filter(x = 0, y = -2, size = 4, color = "#04080FAA")) + +///Contains all lighting objects +/atom/movable/screen/plane_master/rendering_plate/lighting + name = "Lighting plate" + documentation = "Anything on this plane will be multiplied with the plane it's rendered onto (typically the game plane).\ +
That's how lighting functions at base. Because it uses BLEND_MULTIPLY and occasionally color matrixes, it needs a backdrop of blackness.\ +
See This byond post\ +
Lemme see uh, we're masked by the emissive plane so it can actually function (IE: make things glow in the dark).\ +
We're also masked by the overlay lighting plane, which contains all the movable lights in the game. It draws to us and also the game plane.\ +
Masks us out so it has the breathing room to apply its effect.\ +
Oh and we quite often have our alpha changed to achive night vision effects, or things of that sort." + plane = RENDER_PLANE_LIGHTING + blend_mode_override = BLEND_MULTIPLY + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + critical = PLANE_CRITICAL_DISPLAY + /// A list of light cutoffs we're actively using, (mass, r, g, b) to avoid filter churn + var/list/light_cutoffs + +/*! + * 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. + */ +/atom/movable/screen/plane_master/rendering_plate/lighting/Initialize(mapload, datum/hud/hud_owner) + . = ..() + add_filter("emissives", 1, alpha_mask_filter(render_source = OFFSET_RENDER_TARGET(EMISSIVE_RENDER_TARGET, offset), flags = MASK_INVERSE)) + add_filter("object_lighting", 2, alpha_mask_filter(render_source = OFFSET_RENDER_TARGET(O_LIGHTING_VISUAL_RENDER_TARGET, offset), flags = MASK_INVERSE)) + set_light_cutoff(10) + +/atom/movable/screen/plane_master/rendering_plate/lighting/show_to(mob/mymob) + . = ..() + if(!.) + return + // This applies a backdrop to our lighting plane + // Why do plane masters need a backdrop sometimes? Read https://secure.byond.com/forum/?post=2141928 + // Basically, we need something to brighten + // unlit is perhaps less needed rn, it exists to provide a fullbright for things that can't see the lighting plane + // but we don't actually use invisibility to hide the lighting plane anymore, so it's pointless + var/atom/movable/screen/backdrop = mymob.overlay_fullscreen("lighting_backdrop_lit_[home.key]#[offset]", /atom/movable/screen/fullscreen/lighting_backdrop/lit) + // Need to make sure they're on our plane, ALL the time. We always need a backdrop + SET_PLANE_EXPLICIT(backdrop, PLANE_TO_TRUE(backdrop.plane), src) + backdrop = mymob.overlay_fullscreen("lighting_backdrop_unlit_[home.key]#[offset]", /atom/movable/screen/fullscreen/lighting_backdrop/unlit) + SET_PLANE_EXPLICIT(backdrop, PLANE_TO_TRUE(backdrop.plane), src) + + // Sorry, this is a bit annoying + // Basically, we only want the lighting plane we can actually see to attempt to render + // If we don't our lower plane gets totally overriden by the black void of the upper plane + var/datum/hud/hud = home.our_hud + // show_to can be called twice successfully with no hide_from call. Ensure no runtimes off the registers from this + if(hud) + RegisterSignal(hud, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(on_offset_change), override = TRUE) + offset_change(hud?.current_plane_offset || 0) + set_light_cutoff(mymob.lighting_cutoff, mymob.lighting_color_cutoffs) + + +/atom/movable/screen/plane_master/rendering_plate/lighting/hide_from(mob/oldmob) + . = ..() + oldmob.clear_fullscreen("lighting_backdrop_lit_[home.key]#[offset]") + oldmob.clear_fullscreen("lighting_backdrop_unlit_[home.key]#[offset]") + var/datum/hud/hud = home.our_hud + if(hud) + UnregisterSignal(hud, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(on_offset_change)) + +/atom/movable/screen/plane_master/rendering_plate/lighting/proc/on_offset_change(datum/source, old_offset, new_offset) + SIGNAL_HANDLER + offset_change(new_offset) + +/atom/movable/screen/plane_master/rendering_plate/lighting/proc/offset_change(mob_offset) + // Offsets stack down remember. This implies that we're above the mob's view plane, and shouldn't render + if(offset < mob_offset) + disable_alpha() + else + enable_alpha() + +/atom/movable/screen/plane_master/rendering_plate/lighting/proc/set_light_cutoff(light_cutoff, list/color_cutoffs) + var/list/new_cutoffs = list(light_cutoff) + new_cutoffs += color_cutoffs + if(new_cutoffs ~= light_cutoffs) + return + + remove_filter(list("light_cutdown", "light_cutup")) + + var/ratio = light_cutoff/100 + if(!color_cutoffs) + color_cutoffs = list(0, 0, 0) + + var/red = color_cutoffs[1] / 100 + var/green = color_cutoffs[2] / 100 + var/blue = color_cutoffs[3] / 100 + add_filter("light_cutdown", 3, color_matrix_filter(list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, -(ratio + red),-(ratio+green),-(ratio+blue),0))) + add_filter("light_cutup", 4, color_matrix_filter(list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, ratio+red,ratio+green,ratio+blue,0))) + +/atom/movable/screen/plane_master/rendering_plate/emissive_slate + name = "Emissive Plate" + documentation = "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 (This one).\ +
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." + plane = EMISSIVE_RENDER_PLATE + appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + render_target = EMISSIVE_RENDER_TARGET + render_relay_planes = list() + critical = PLANE_CRITICAL_DISPLAY + +/atom/movable/screen/plane_master/rendering_plate/emissive_slate/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset) + . = ..() + add_filter("em_block_masking", 2, color_matrix_filter(GLOB.em_mask_matrix)) + if(offset != 0) + add_relay_to(GET_NEW_PLANE(EMISSIVE_RENDER_PLATE, offset - 1), relay_layer = EMISSIVE_Z_BELOW_LAYER) + +/atom/movable/screen/plane_master/rendering_plate/light_mask + name = "Light Mask" + documentation = "Any part of this plane that is transparent will be black below it on the game rendering plate.\ +
This is done to ensure emissives and overlay lights don't light things up \"through\" the darkness that normally sits at the bottom of the lighting plane.\ +
We relay copies of the space, floor and wall planes to it, so we can use them as masks. Then we just boost any existing alpha to 100% and we're done.\ +
If we ever switch to a sight setup that shows say, mobs but not floors, we instead mask just overlay lighting and emissives.\ +
This avoids dumb seethrough without breaking stuff like thermals." + plane = LIGHT_MASK_PLANE + appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR + // Fullwhite where there's anything, no color otherwise + color = list(255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255, 0,0,0,0) + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + render_target = LIGHT_MASK_RENDER_TARGET + // We blend against the game plane, so she's gotta multiply! + blend_mode = BLEND_MULTIPLY + render_relay_planes = list(RENDER_PLANE_GAME) + +/atom/movable/screen/plane_master/rendering_plate/light_mask/show_to(mob/mymob) + . = ..() + if(!.) + return + + RegisterSignal(mymob, COMSIG_MOB_SIGHT_CHANGE, PROC_REF(handle_sight)) + handle_sight(mymob, mymob.sight, NONE) + +/atom/movable/screen/plane_master/rendering_plate/light_mask/hide_from(mob/oldmob) + . = ..() + var/atom/movable/screen/plane_master/overlay_lights = home.get_plane(GET_NEW_PLANE(O_LIGHTING_VISUAL_PLANE, offset)) + overlay_lights.remove_filter("lighting_mask") + var/atom/movable/screen/plane_master/emissive = home.get_plane(GET_NEW_PLANE(EMISSIVE_RENDER_PLATE, offset)) + emissive.remove_filter("lighting_mask") + remove_relay_from(GET_NEW_PLANE(RENDER_PLANE_GAME, offset)) + UnregisterSignal(oldmob, COMSIG_MOB_SIGHT_CHANGE) + +/atom/movable/screen/plane_master/rendering_plate/light_mask/proc/handle_sight(datum/source, new_sight, old_sight) + // If we can see something that shows "through" blackness, and we can't see turfs, disable our draw to the game plane + // And instead mask JUST the overlay lighting plane, since that will look fuckin wrong + var/atom/movable/screen/plane_master/overlay_lights = home.get_plane(GET_NEW_PLANE(O_LIGHTING_VISUAL_PLANE, offset)) + var/atom/movable/screen/plane_master/emissive = home.get_plane(GET_NEW_PLANE(EMISSIVE_RENDER_PLATE, offset)) + if(new_sight & SEE_AVOID_TURF_BLACKNESS && !(new_sight & SEE_TURFS)) + remove_relay_from(GET_NEW_PLANE(RENDER_PLANE_GAME, offset)) + overlay_lights.add_filter("lighting_mask", 1, alpha_mask_filter(render_source = OFFSET_RENDER_TARGET(LIGHT_MASK_RENDER_TARGET, offset))) + emissive.add_filter("lighting_mask", 1, alpha_mask_filter(render_source = OFFSET_RENDER_TARGET(LIGHT_MASK_RENDER_TARGET, offset))) + // If we CAN'T see through the black, then draw er down brother! + else + overlay_lights.remove_filter("lighting_mask") + emissive.remove_filter("lighting_mask") + // We max alpha here, so our darkness is actually.. dark + // Can't do it before cause it fucks with the filter + add_relay_to(GET_NEW_PLANE(RENDER_PLANE_GAME, offset), relay_color = list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,1)) + +///render plate for OOC stuff like ghosts, hud-screen effects, etc +/atom/movable/screen/plane_master/rendering_plate/non_game + name = "Non-Game rendering plate" + documentation = "Renders anything that's out of character. Mostly useful as a converse to the game rendering plate." + plane = RENDER_PLANE_NON_GAME + render_relay_planes = list(RENDER_PLANE_MASTER) + +/** + * Plane master proc called in Initialize() that creates relay objects, and sets them uo as needed + * Sets: + * * layer from plane to avoid z-fighting + * * planes to relay the render to + * * render_source so that the plane will render on these objects + * * mouse opacity to ensure proper mouse hit tracking + * * name for debugging purposes + * Other vars such as alpha will automatically be applied with the render source + */ +/atom/movable/screen/plane_master/proc/generate_render_relays(relay_loc_override) + var/relay_loc = relay_loc_override ? relay_loc_override : "CENTER" + // If we're using a submap (say for a popup window) make sure we draw onto it + if(home?.map) + relay_loc = "[home.map]:[relay_loc]" + + var/list/generated_planes = list() + for(var/atom/movable/render_plane_relay/relay as anything in relays) + generated_planes += relay.plane + + for(var/relay_plane in (render_relay_planes - generated_planes)) + generate_relay_to(relay_plane, relay_loc) + + if(blend_mode != BLEND_MULTIPLY) + blend_mode = BLEND_DEFAULT + relays_generated = TRUE + +/// Creates a connection between this plane master and the passed in plane +/// Helper for out of system code, shouldn't be used in this file +/// Build system to differenchiate between generated and non generated render relays +/atom/movable/screen/plane_master/proc/add_relay_to(target_plane, blend_override, relay_layer, relay_color, relay_loc) + if(get_relay_to(target_plane)) + return + render_relay_planes += target_plane + var/client/display_lad = home?.our_hud?.mymob?.canon_client + var/atom/movable/render_plane_relay/relay = generate_relay_to(target_plane, relay_loc = relay_loc, show_to = display_lad, blend_override = blend_override, relay_layer = relay_layer) + relay.color = relay_color + +/proc/get_plane_master_render_base(name) + return "*[name]: AUTOGENERATED RENDER TGT" + +/atom/movable/screen/plane_master/proc/generate_relay_to(target_plane, relay_loc, client/show_to, blend_override, relay_layer) + if(!length(relays) && !initial(render_target)) + render_target = OFFSET_RENDER_TARGET(get_plane_master_render_base(name), offset) + if(!relay_loc) + relay_loc = "CENTER" + // If we're using a submap (say for a popup window) make sure we draw onto it + if(home?.map) + relay_loc = "[home.map]:[relay_loc]" + var/blend_to_use = blend_override + if(isnull(blend_to_use)) + blend_to_use = blend_mode_override || initial(blend_mode) + + var/atom/movable/render_plane_relay/relay = new() + relay.render_source = render_target + relay.plane = target_plane + relay.screen_loc = relay_loc + // There are two rules here + // 1: layer needs to be positive (negative layers are treated as float layers) + // 2: lower planes (including offset ones) need to be layered below higher ones (because otherwise they'll render fucky) + // By multiplying LOWEST_EVER_PLANE by 30, we give 30 offsets worth of room to planes before they start going negative + // Bet + // We allow for manuel override if requested. careful with this + relay.layer = relay_layer || (plane + abs(LOWEST_EVER_PLANE * 30)) //layer must be positive but can be a decimal + relay.blend_mode = blend_to_use + relay.mouse_opacity = mouse_opacity + relay.name = render_target + relay.critical_target = PLANE_IS_CRITICAL(target_plane) + relays += relay + // Relays are sometimes generated early, before huds have a mob to display stuff to + // That's what this is for + if(show_to) + show_to.screen += relay + return relay + +/// Breaks a connection between this plane master, and the passed in place +/atom/movable/screen/plane_master/proc/remove_relay_from(target_plane) + render_relay_planes -= target_plane + var/atom/movable/render_plane_relay/existing_relay = get_relay_to(target_plane) + if(!existing_relay) + return + relays -= existing_relay + if(!length(relays) && !initial(render_target)) + render_target = null + var/client/lad = home?.our_hud?.mymob?.canon_client + if(lad) + lad.screen -= existing_relay + +/// Gets the relay atom we're using to connect to the target plane, if one exists +/atom/movable/screen/plane_master/proc/get_relay_to(target_plane) + for(var/atom/movable/render_plane_relay/relay in relays) + if(relay.plane == target_plane) + return relay + + return null + +/atom/movable/screen/plane_master/proc/remove_all_relays() + for(var/atom/movable/render_plane_relay/relay in relays) + remove_relay_from(relay.plane) + + relays_generated = FALSE diff --git a/code/_onclick/hud/robot.dm b/code/_onclick/hud/robot.dm index 37acb22e59e2..271097598806 100644 --- a/code/_onclick/hud/robot.dm +++ b/code/_onclick/hud/robot.dm @@ -224,8 +224,7 @@ A.screen_loc = "CENTER[x]:16,SOUTH+[y]:7" else A.screen_loc = "CENTER+[x]:16,SOUTH+[y]:7" - A.layer = ABOVE_HUD_LAYER - A.plane = ABOVE_HUD_PLANE + SET_PLANE_IMPLICIT(A, ABOVE_HUD_PLANE) x++ if(x == 4) diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index c6d23ad8b245..a4f735a9d2a5 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -9,17 +9,32 @@ /atom/movable/screen name = "" icon = 'icons/mob/screen_gen.dmi' - layer = HUD_LAYER plane = HUD_PLANE animate_movement = SLIDE_STEPS speech_span = SPAN_ROBOT - vis_flags = VIS_INHERIT_PLANE + //vis_flags = VIS_INHERIT_PLANE appearance_flags = APPEARANCE_UI /// A reference to the object in the slot. Grabs or items, generally. var/obj/master = null /// A reference to the owner HUD, if any. VAR_PRIVATE/datum/hud/hud = null + /// If FALSE, this will not be cleared when calling /client/clear_screen() + var/clear_with_screen = TRUE + /** + * Map name assigned to this object. + * Automatically set by /client/proc/add_obj_to_map. + */ + var/assigned_map + /** + * Mark this object as garbage-collectible after you clean the map + * it was registered on. + * + * This could probably be changed to be a proc, for conditional removal. + * But for now, this works. + */ + var/del_on_map_removal = TRUE + /atom/movable/screen/New(datum/hud/new_hud) . = ..() if(istype(new_hud)) @@ -48,7 +63,6 @@ maptext_width = 480 /atom/movable/screen/swap_hand - layer = HUD_LAYER plane = HUD_PLANE name = "swap hand" @@ -156,7 +170,6 @@ var/icon_empty // Icon when empty. For now used only by humans. var/icon_full // Icon when contains an item. For now used only by humans. var/list/object_overlays = list() - layer = HUD_LAYER plane = HUD_PLANE /atom/movable/screen/inventory/Click(location, control, params) @@ -264,7 +277,6 @@ /atom/movable/screen/close name = "close" - layer = ABOVE_HUD_LAYER plane = ABOVE_HUD_PLANE icon_state = "backpack_close" @@ -281,7 +293,6 @@ name = "drop" icon = 'icons/mob/screen_midnight.dmi' icon_state = "act_drop" - layer = HUD_LAYER plane = HUD_PLANE /atom/movable/screen/drop/Click() @@ -362,7 +373,6 @@ name = "resist" icon = 'icons/mob/screen_midnight.dmi' icon_state = "act_resist" - layer = HUD_LAYER plane = HUD_PLANE /atom/movable/screen/resist/Click() @@ -374,7 +384,6 @@ name = "rest" icon = 'icons/mob/screen_midnight.dmi' icon_state = "act_rest" - layer = HUD_LAYER plane = HUD_PLANE /atom/movable/screen/rest/Click() @@ -393,7 +402,6 @@ name = "storage" icon_state = "block" screen_loc = "7,7 to 10,8" - layer = HUD_LAYER plane = HUD_PLANE /atom/movable/screen/storage/Initialize(mapload, new_master) @@ -474,7 +482,6 @@ mouse_opacity = MOUSE_OPACITY_TRANSPARENT alpha = 128 anchored = TRUE - layer = ABOVE_HUD_LAYER plane = ABOVE_HUD_PLANE /atom/movable/screen/zone_sel/MouseExited(location, control, params) @@ -657,7 +664,6 @@ icon = 'icons/blanks/blank_title.png' icon_state = "" screen_loc = "1,1" - layer = SPLASHSCREEN_LAYER plane = SPLASHSCREEN_PLANE var/client/holder diff --git a/code/_onclick/telekinesis.dm b/code/_onclick/telekinesis.dm index 26489fe5537f..25fe8a0ac051 100644 --- a/code/_onclick/telekinesis.dm +++ b/code/_onclick/telekinesis.dm @@ -76,7 +76,6 @@ item_flags = NOBLUDGEON | ABSTRACT | DROPDEL //item_state = null w_class = WEIGHT_CLASS_GIGANTIC - layer = ABOVE_HUD_LAYER plane = ABOVE_HUD_PLANE var/atom/movable/focus = null diff --git a/code/controllers/admin.dm b/code/controllers/admin.dm index 524e568b8f56..2c01e55f2626 100644 --- a/code/controllers/admin.dm +++ b/code/controllers/admin.dm @@ -1,7 +1,7 @@ // Clickable stat() button. /obj/effect/statclick name = "Initializing..." - blocks_emissive = NONE + blocks_emissive = EMISSIVE_BLOCK_NONE var/target INITIALIZE_IMMEDIATE(/obj/effect/statclick) diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index 2252e5ea54de..d221400577f3 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -308,6 +308,10 @@ max_val = 1 integer = FALSE +/datum/config_entry/number/auto_lag_switch_pop //Number of clients at which drastic lag mitigation measures kick in + config_entry_value = null + min_val = 0 + /datum/config_entry/number/soft_popcap config_entry_value = null min_val = 0 diff --git a/code/controllers/lag_switch.dm b/code/controllers/lag_switch.dm new file mode 100644 index 000000000000..c79db0518601 --- /dev/null +++ b/code/controllers/lag_switch.dm @@ -0,0 +1,146 @@ +/// The subsystem for controlling drastic performance enhancements aimed at reducing server load for a smoother albeit slightly duller gaming experience +SUBSYSTEM_DEF(lag_switch) + name = "Lag Switch" + flags = SS_NO_FIRE + + /// If the lag switch measures should attempt to trigger automatically, TRUE if a config value exists + var/auto_switch = FALSE + /// Amount of connected clients at which the Lag Switch should engage, set via config or admin panel + var/trigger_pop = INFINITY - 1337 + /// List of bools corresponding to code/__DEFINES/lag_switch.dm + var/static/list/measures[MEASURES_AMOUNT] + /// List of measures that toggle automatically + var/list/auto_measures = list(DISABLE_GHOST_ZOOM_TRAY, DISABLE_RUNECHAT, DISABLE_USR_ICON2HTML, DISABLE_PARALLAX, DISABLE_FOOTSTEPS) + /// Timer ID for the automatic veto period + var/veto_timer_id + /// Cooldown between say verb uses when slowmode is enabled + var/slowmode_cooldown = 3 SECONDS + +/datum/controller/subsystem/lag_switch/Initialize() + for(var/i in 1 to measures.len) + measures[i] = FALSE + var/auto_switch_pop = CONFIG_GET(number/auto_lag_switch_pop) + if(auto_switch_pop) + auto_switch = TRUE + trigger_pop = auto_switch_pop + RegisterSignal(SSdcs, COMSIG_GLOB_CLIENT_CONNECT, PROC_REF(client_connected)) + return SS_INIT_SUCCESS + +/datum/controller/subsystem/lag_switch/proc/client_connected(datum/source, client/connected) + SIGNAL_HANDLER + if(TGS_CLIENT_COUNT < trigger_pop) + return + + auto_switch = FALSE + UnregisterSignal(SSdcs, COMSIG_GLOB_CLIENT_CONNECT) + veto_timer_id = addtimer(CALLBACK(src, PROC_REF(set_all_measures), TRUE, TRUE), 20 SECONDS, TIMER_STOPPABLE) + message_admins("Lag Switch population threshold reached. Automatic activation of lag mitigation measures occuring in 20 seconds. (CANCEL)") + log_admin("Lag Switch population threshold reached. Automatic activation of lag mitigation measures occuring in 20 seconds.") + +/// (En/Dis)able automatic triggering of switches based on client count +/datum/controller/subsystem/lag_switch/proc/toggle_auto_enable() + auto_switch = !auto_switch + if(auto_switch) + RegisterSignal(SSdcs, COMSIG_GLOB_CLIENT_CONNECT, PROC_REF(client_connected)) + else + UnregisterSignal(SSdcs, COMSIG_GLOB_CLIENT_CONNECT) + +/// Called from an admin chat link +/datum/controller/subsystem/lag_switch/proc/cancel_auto_enable_in_progress() + if(!veto_timer_id) + return FALSE + + deltimer(veto_timer_id) + veto_timer_id = null + return TRUE + +/// Update the slowmode timer length and clear existing ones if reduced +/datum/controller/subsystem/lag_switch/proc/change_slowmode_cooldown(length) + if(!length) + return FALSE + + var/length_secs = length SECONDS + if(length_secs <= 0) + length_secs = 1 // one tick because cooldowns do not like 0 + + if(length_secs < slowmode_cooldown) + for(var/client/C as anything in GLOB.clients) + COOLDOWN_RESET(C, say_slowmode) + + slowmode_cooldown = length_secs + if(measures[SLOWMODE_SAY]) + to_chat(world, span_boldannounce("Slowmode timer has been changed to [length] seconds by an admin.")) + return TRUE + +/// Handle the state change for individual measures +/datum/controller/subsystem/lag_switch/proc/set_measure(measure_key, state) + if(isnull(measure_key) || isnull(state)) + stack_trace("SSlag_switch.set_measure() was called with a null arg") + return FALSE + if(isnull(LAZYACCESS(measures, measure_key))) + stack_trace("SSlag_switch.set_measure() was called with a measure_key not in the list of measures") + return FALSE + if(measures[measure_key] == state) + return TRUE + + measures[measure_key] = state + + switch(measure_key) + if(DISABLE_DEAD_KEYLOOP) + if(state) + for(var/mob/user as anything in GLOB.player_list) + if(user.stat == DEAD && !user.client?.holder) + GLOB.keyloop_list -= user + deadchat_broadcast(span_big("To increase performance Observer freelook is now disabled. Please use Orbit, Teleport, and Jump to look around."), message_type = DEADCHAT_ANNOUNCEMENT) + else + GLOB.keyloop_list |= GLOB.player_list + deadchat_broadcast("Observer freelook has been re-enabled. Enjoy your wooshing.", message_type = DEADCHAT_ANNOUNCEMENT) + if(DISABLE_GHOST_ZOOM_TRAY) + if(state) // if enabling make sure current ghosts are updated + for(var/mob/dead/observer/ghost in GLOB.dead_mob_list) + if(!ghost.client) + continue + if(!ghost.client.holder && ghost.client.view_size.getView() != ghost.client.view_size.default) + ghost.client.view_size.resetToDefault() + if(SLOWMODE_SAY) + if(state) + to_chat(world, span_boldannounce("Slowmode for IC/dead chat has been enabled with [slowmode_cooldown/10] seconds between messages.")) + else + for(var/client/C as anything in GLOB.clients) + COOLDOWN_RESET(C, say_slowmode) + to_chat(world, span_boldannounce("Slowmode for IC/dead chat has been disabled by an admin.")) + if(DISABLE_NON_OBSJOBS) + world.update_status() + if(DISABLE_PARALLAX) + if (state) + to_chat(world, span_boldannounce("Parallax has been disabled for performance concerns.")) + else + to_chat(world, span_boldannounce("Parallax has been re-enabled.")) + + for (var/mob/mob as anything in GLOB.mob_list) + mob.hud_used?.update_parallax_pref() + if (DISABLE_FOOTSTEPS) + if (state) + to_chat(world, span_boldannounce("Footstep sounds have been disabled for performance concerns.")) + else + to_chat(world, span_boldannounce("Footstep sounds have been re-enabled.")) + + return TRUE + +/// Helper to loop over all measures for mass changes +/datum/controller/subsystem/lag_switch/proc/set_all_measures(state, automatic = FALSE) + if(isnull(state)) + stack_trace("SSlag_switch.set_all_measures() was called with a null state arg") + return FALSE + + if(automatic) + message_admins("Lag Switch enabling automatic measures now.") + log_admin("Lag Switch enabling automatic measures now.") + veto_timer_id = null + for(var/i in 1 to auto_measures.len) + set_measure(auto_measures[i], state) + return TRUE + + for(var/i in 1 to measures.len) + set_measure(i, state) + return TRUE diff --git a/code/controllers/subsystem/demo.dm b/code/controllers/subsystem/demo.dm index 5296f21dd20c..0833575471c8 100644 --- a/code/controllers/subsystem/demo.dm +++ b/code/controllers/subsystem/demo.dm @@ -444,7 +444,7 @@ SUBSYSTEM_DEF(demo) return if(!isobj(M) && !ismob(M)) return - if(M.gc_destroyed) + if(M.gc_destroyed || QDELETED(M)) return marked_new[M] = TRUE if(marked_dirty[M]) diff --git a/code/controllers/subsystem/events.dm b/code/controllers/subsystem/events.dm index 3d0e1295dc5b..bc7305876274 100644 --- a/code/controllers/subsystem/events.dm +++ b/code/controllers/subsystem/events.dm @@ -185,3 +185,4 @@ SUBSYSTEM_DEF(events) /datum/controller/subsystem/events/proc/resetFrequency() frequency_lower = initial(frequency_lower) frequency_upper = initial(frequency_upper) + diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index b8895d1aa060..080b65b9f570 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -33,8 +33,38 @@ SUBSYSTEM_DEF(mapping) var/list/station_minimaps = list() + /// The largest plane offset we've generated so far + var/max_plane_offset = 0 + + /// List of z level (as number) -> The lowest plane offset in that z stack + var/list/z_level_to_lowest_plane_offset = list() + + /// Assoc list of string plane to the plane's offset value + var/list/plane_to_offset + + /// Used to maintain the plane cube + var/list/z_level_to_plane_offset = list() + + /// List of planes that do not allow for offsetting + var/list/plane_offset_blacklist + + /// Assoc list of true string plane values to a list of all potential offset planess + var/list/true_to_offset_planes + + /// List of render targets that do not allow for offsetting + var/list/render_offset_blacklist + + /// List of plane masters that are of critical priority + var/list/critical_planes + + /// Assoc list of string plane values to their true, non offset representation + var/list/plane_offset_to_true + var/list/areas_in_z = list() + ///list of lists, inner lists are of the form: list("up or down link direction" = TRUE) + var/list/multiz_levels = list() + var/loading_ruins = FALSE var/list/turf/unused_turfs = list() //Not actually unused turfs they're unused but reserved for use for whatever requests them. "[zlevel_of_turf]" = list(turfs) var/list/datum/turf_reservations //list of turf reservations @@ -701,6 +731,10 @@ GLOBAL_LIST_EMPTY(the_station_areas) var/area/our_area = to_contain.loc our_area.contained_turfs += to_contain +/datum/controller/subsystem/mapping/proc/get_reservation_from_turf(turf/T) + RETURN_TYPE(/datum/turf_reservation) + return used_turfs[T] + /datum/controller/subsystem/mapping/proc/get_map_weights() diff --git a/code/controllers/subsystem/parallax.dm b/code/controllers/subsystem/parallax.dm index 9f2f57d48c60..d412900c9433 100644 --- a/code/controllers/subsystem/parallax.dm +++ b/code/controllers/subsystem/parallax.dm @@ -1,24 +1,34 @@ +/// Define for the pickweight value where you get no parallax +#define PARALLAX_NONE "parallax_none" + SUBSYSTEM_DEF(parallax) name = "Parallax" wait = 2 - flags = SS_POST_FIRE_TIMING | SS_BACKGROUND + flags = SS_POST_FIRE_TIMING | SS_BACKGROUND | SS_NO_INIT priority = FIRE_PRIORITY_PARALLAX runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT var/list/currentrun var/planet_x_offset = 128 var/planet_y_offset = 128 - var/random_layer - var/random_parallax_color + /// A random parallax layer that we sent to every player + var/atom/movable/screen/parallax_layer/random/random_layer + /// Weighted list with the parallax layers we could spawn + var/random_parallax_weights = list( + /atom/movable/screen/parallax_layer/random/space_gas = 35, + ///atom/movable/screen/parallax_layer/random/asteroids = 35, + PARALLAX_NONE = 30, + ) + +//These are cached per client so needs to be done asap so people joining at roundstart do not miss these. +/datum/controller/subsystem/parallax/PreInit() + . = ..() + + set_random_parallax_layer(pick_weight(random_parallax_weights)) -/datum/controller/subsystem/parallax/Initialize(timeofday) - if(prob(70)) //70% chance to pick a special extra layer - random_layer = pick(/atom/movable/screen/parallax_layer/random/space_gas, /atom/movable/screen/parallax_layer/random/asteroids) - random_parallax_color = pick(COLOR_TEAL, COLOR_GREEN, COLOR_SILVER, COLOR_YELLOW, COLOR_CYAN, COLOR_ORANGE, COLOR_PURPLE)//Special color for random_layer1. Has to be done here so everyone sees the same color. planet_y_offset = rand(100, 160) planet_x_offset = rand(100, 160) - return SS_INIT_SUCCESS -/datum/controller/subsystem/parallax/fire(resumed = 0) +/datum/controller/subsystem/parallax/fire(resumed = FALSE) if (!resumed) src.currentrun = GLOB.clients.Copy() @@ -26,24 +36,65 @@ SUBSYSTEM_DEF(parallax) var/list/currentrun = src.currentrun while(length(currentrun)) - var/client/C = currentrun[currentrun.len] + var/client/processing_client = currentrun[currentrun.len] currentrun.len-- - if (!C || !C.eye) + if (QDELETED(processing_client) || !processing_client.eye) if (MC_TICK_CHECK) return continue - var/atom/movable/A = C.eye - if(!istype(A)) + + var/atom/movable/movable_eye = processing_client.eye + if(!istype(movable_eye)) + continue + + while(isloc(movable_eye.loc) && !isturf(movable_eye.loc)) + movable_eye = movable_eye.loc + //get the last movable holding the mobs eye + + if(movable_eye == processing_client.movingmob) + if (MC_TICK_CHECK) + return continue - for (A; isloc(A.loc) && !isturf(A.loc); A = A.loc); - - if(A != C.movingmob) - if(C.movingmob != null) - C.movingmob.client_mobs_in_contents -= C.mob - UNSETEMPTY(C.movingmob.client_mobs_in_contents) - LAZYINITLIST(A.client_mobs_in_contents) - A.client_mobs_in_contents += C.mob - C.movingmob = A + + //eye and the last recorded eye are different, and the last recorded eye isnt just the clients mob + if(!isnull(processing_client.movingmob)) + LAZYREMOVE(processing_client.movingmob.client_mobs_in_contents, processing_client.mob) + LAZYADD(movable_eye.client_mobs_in_contents, processing_client.mob) + + processing_client.movingmob = movable_eye if (MC_TICK_CHECK) return currentrun = null + +/// Generate a random layer for parallax +/datum/controller/subsystem/parallax/proc/set_random_parallax_layer(picked_parallax) + if(picked_parallax == PARALLAX_NONE) + return + + random_layer = new picked_parallax(null, /* hud_owner = */ null, /* template = */ TRUE) + RegisterSignal(random_layer, COMSIG_PARENT_QDELETING, PROC_REF(clear_references)) + random_layer.get_random_look() + +/// Change the random parallax layer after it's already been set. update_player_huds = TRUE will also replace them in the players client images, if it was set +/datum/controller/subsystem/parallax/proc/swap_out_random_parallax_layer(atom/movable/screen/parallax_layer/new_type, update_player_huds = TRUE) + set_random_parallax_layer(new_type) + + if(!update_player_huds) + return + + //Parallax is one of the first things to be set (during client join), so rarely is anything fast enough to swap it out + //That's why we need to swap the layers out for fast joining clients :/ + for(var/client/client as anything in GLOB.clients) + client.parallax_layers_cached?.Cut() + client.mob?.hud_used?.update_parallax_pref(client.mob) + +/datum/controller/subsystem/parallax/proc/clear_references() + SIGNAL_HANDLER + + random_layer = null + +/// Called at the end of SSstation setup, in-case we want to run some code that would otherwise be too early to run (like GLOB. stuff) +/datum/controller/subsystem/parallax/proc/post_station_setup() + random_layer?.apply_global_effects() + +#undef PARALLAX_NONE diff --git a/code/datums/actions/mobs/adjust_vision.dm b/code/datums/actions/mobs/adjust_vision.dm new file mode 100644 index 000000000000..906ef33fa73e --- /dev/null +++ b/code/datums/actions/mobs/adjust_vision.dm @@ -0,0 +1,73 @@ +#define VISION_ACTION_LIGHT_OFF 0 +#define VISION_ACTION_LIGHT_LOW 1 +#define VISION_ACTION_LIGHT_MID 2 +#define VISION_ACTION_LIGHT_HIG 3 + +/datum/action/adjust_vision + name = "Adjust Vision" + desc = "See better in the dark. Or don't. Your advanced vision allows either." + button_icon = 'icons/mob/actions/actions_animal.dmi' + button_icon_state = "adjust_vision" + background_icon_state = "bg_default" + overlay_icon_state = "bg_default_border" + + + // These lists are used as the color cutoff for the action + // They need to be filled out for subtypes + var/list/low_light_cutoff + var/list/medium_light_cutoff + var/list/high_light_cutoff + var/light_level = VISION_ACTION_LIGHT_OFF + +/datum/action/adjust_vision/Grant(mob/living/grant_to) + . = ..() + set_light_level(VISION_ACTION_LIGHT_LOW) + RegisterSignal(grant_to, COMSIG_MOB_UPDATE_SIGHT, PROC_REF(on_update_sight)) + +/datum/action/adjust_vision/Remove(mob/living/remove_from) + set_light_level(VISION_ACTION_LIGHT_OFF) + UnregisterSignal(remove_from, COMSIG_MOB_UPDATE_SIGHT) + . = ..() + +/datum/action/adjust_vision/Trigger(trigger_flags) + . = ..() + if(!.) + return + + switch(light_level) + if (VISION_ACTION_LIGHT_OFF) + set_light_level(VISION_ACTION_LIGHT_LOW) + if (VISION_ACTION_LIGHT_LOW) + set_light_level(VISION_ACTION_LIGHT_MID) + if (VISION_ACTION_LIGHT_MID) + set_light_level(VISION_ACTION_LIGHT_HIG) + else + set_light_level(VISION_ACTION_LIGHT_OFF) + +/datum/action/adjust_vision/proc/set_light_level(new_level) + light_level = new_level + owner.update_sight() + +/datum/action/adjust_vision/proc/on_update_sight(datum/source) + SIGNAL_HANDLER + var/list/color_from + switch(light_level) + if (VISION_ACTION_LIGHT_LOW) + color_from = low_light_cutoff + if (VISION_ACTION_LIGHT_MID) + color_from = medium_light_cutoff + if (VISION_ACTION_LIGHT_HIG) + color_from = high_light_cutoff + else // just in case + color_from = list(0, 0, 0) + owner.lighting_color_cutoffs = blend_cutoff_colors(owner.lighting_color_cutoffs, color_from.Copy()) + +/datum/action/adjust_vision/bileworm + low_light_cutoff = list(18, 12, 0) + medium_light_cutoff = list(30, 20, 5) + high_light_cutoff = list(45, 30, 10) + +#undef VISION_ACTION_LIGHT_OFF +#undef VISION_ACTION_LIGHT_LOW +#undef VISION_ACTION_LIGHT_MID +#undef VISION_ACTION_LIGHT_HIG diff --git a/code/datums/brain_damage/creepy_trauma.dm b/code/datums/brain_damage/creepy_trauma.dm index a8c87ab78a83..ad3cac664e2b 100644 --- a/code/datums/brain_damage/creepy_trauma.dm +++ b/code/datums/brain_damage/creepy_trauma.dm @@ -91,7 +91,7 @@ switch(rand(1, 100)) if(1 to 40) INVOKE_ASYNC(owner, TYPE_PROC_REF(/mob, emote), pick("blink", "blink_r")) - owner.blur_eyes(10) + owner.adjust_eye_blur(10) to_chat(owner, span_userdanger("You sweat profusely and have a hard time focusing...")) if(41 to 80) INVOKE_ASYNC(owner, TYPE_PROC_REF(/mob, emote), "pale") diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index 7d5685af7c03..5a74bb46721e 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -58,8 +58,6 @@ real_name = "imaginary friend" move_on_shuttle = TRUE desc = "A wonderful yet fake friend." - see_in_dark = 0 - lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE sight = NONE mouse_opacity = MOUSE_OPACITY_OPAQUE see_invisible = SEE_INVISIBLE_LIVING diff --git a/code/datums/brain_damage/mild.dm b/code/datums/brain_damage/mild.dm index 9eccabd5d59a..cf8b54ba9c48 100644 --- a/code/datums/brain_damage/mild.dm +++ b/code/datums/brain_damage/mild.dm @@ -117,7 +117,7 @@ owner.adjust_dizzy(20 SECONDS) if(4,5) owner.adjust_confusion(10 SECONDS) - owner.blur_eyes(10) + owner.adjust_eye_blur(10) if(6 to 9) owner.adjust_slurring(1 MINUTES) if(10) diff --git a/code/datums/chatmessage.dm b/code/datums/chatmessage.dm index 5694bfa0fd08..f8d158943a29 100644 --- a/code/datums/chatmessage.dm +++ b/code/datums/chatmessage.dm @@ -20,6 +20,10 @@ #define CHAT_LAYER_MAX_Z (CHAT_LAYER_MAX - CHAT_LAYER) / CHAT_LAYER_Z_STEP /// The dimensions of the chat message icons #define CHAT_MESSAGE_ICON_SIZE 9 +///Base layer of chat elements +#define CHAT_LAYER 1 +///Highest possible layer of chat elements +#define CHAT_LAYER_MAX 2 /** * # Chat Message Overlay diff --git a/code/datums/cinematic.dm b/code/datums/cinematic.dm index 22a76a7c2d0a..29ba4e960219 100644 --- a/code/datums/cinematic.dm +++ b/code/datums/cinematic.dm @@ -23,7 +23,6 @@ icon_state = "station_intact" plane = SPLASHSCREEN_PLANE mouse_opacity = MOUSE_OPACITY_TRANSPARENT - layer = SPLASHSCREEN_LAYER screen_loc = "BOTTOM,LEFT+50%" appearance_flags = APPEARANCE_UI | TILE_BOUND diff --git a/code/datums/components/connect_mob_behalf.dm b/code/datums/components/connect_mob_behalf.dm new file mode 100644 index 000000000000..1c1a8a652342 --- /dev/null +++ b/code/datums/components/connect_mob_behalf.dm @@ -0,0 +1,59 @@ +/// This component behaves similar to connect_loc_behalf, but working off clients and mobs instead of loc +/// To be clear, we hook into a signal on a tracked client's mob +/// We retain the ability to react to that signal on a seperate listener, which makes this quite powerful +/datum/component/connect_mob_behalf + dupe_mode = COMPONENT_DUPE_UNIQUE + + /// An assoc list of signal -> procpath to register to the mob our client "owns" + var/list/connections + /// The master client we're working with + var/client/tracked + /// The mob we're currently tracking + var/mob/tracked_mob + +/datum/component/connect_mob_behalf/Initialize(client/tracked, list/connections) + . = ..() + if (!istype(tracked)) + return COMPONENT_INCOMPATIBLE + src.connections = connections + src.tracked = tracked + +/datum/component/connect_mob_behalf/RegisterWithParent() + RegisterSignal(tracked, COMSIG_PARENT_QDELETING, PROC_REF(handle_tracked_qdel)) + update_signals() + +/datum/component/connect_mob_behalf/UnregisterFromParent() + unregister_signals() + UnregisterSignal(tracked, COMSIG_PARENT_QDELETING) + + tracked = null + tracked_mob = null + +/datum/component/connect_mob_behalf/proc/handle_tracked_qdel() + SIGNAL_HANDLER + qdel(src) + +/datum/component/connect_mob_behalf/proc/update_signals() + unregister_signals() + // Yes this is a runtime silencer + // We could be in a position where logout is sent to two things, one thing intercepts it, then deletes the client's new mob + // It's rare, and the same check in connect_loc_behalf is more fruitful, but it's still worth doing + if(QDELETED(tracked?.mob)) + return + tracked_mob = tracked.mob + RegisterSignal(tracked_mob, COMSIG_MOB_LOGOUT, PROC_REF(on_logout)) + for (var/signal in connections) + parent.RegisterSignal(tracked_mob, signal, connections[signal]) + +/datum/component/connect_mob_behalf/proc/unregister_signals() + if(isnull(tracked_mob)) + return + + parent.UnregisterSignal(tracked_mob, connections) + UnregisterSignal(tracked_mob, COMSIG_MOB_LOGOUT) + + tracked_mob = null + +/datum/component/connect_mob_behalf/proc/on_logout(mob/source) + SIGNAL_HANDLER + update_signals() diff --git a/code/datums/components/hide_highest_offset.dm b/code/datums/components/hide_highest_offset.dm new file mode 100644 index 000000000000..eb5b5660db0f --- /dev/null +++ b/code/datums/components/hide_highest_offset.dm @@ -0,0 +1,24 @@ +/// Component that takes a plane master, and will hide it if it's the highest offset of its kind +/// This allows us to not show PMs to clients if they're not actively doing anything +/datum/component/plane_hide_highest_offset + +/datum/component/plane_hide_highest_offset/Initialize() + if(!istype(parent, /atom/movable/screen/plane_master)) + return + RegisterSignal(SSmapping, COMSIG_PLANE_OFFSET_INCREASE, PROC_REF(on_offset_increase)) + offset_increase(SSmapping.max_plane_offset) + +/datum/component/plane_hide_highest_offset/proc/on_offset_increase(datum/source, old_offset, new_offset) + SIGNAL_HANDLER + offset_increase(new_offset) + +/datum/component/plane_hide_highest_offset/proc/offset_increase(new_offset) + var/atom/movable/screen/plane_master/plane_parent = parent + var/mob/our_mob = plane_parent.home?.our_hud?.mymob + var/our_offset = plane_parent.offset + if(!our_mob) + return + if(our_offset == new_offset) + plane_parent.hide_plane(our_mob) + else if(plane_parent.force_hidden) + plane_parent.unhide_plane(our_mob) diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm index bd557a8bd347..40cc6aa80321 100644 --- a/code/datums/components/storage/storage.dm +++ b/code/datums/components/storage/storage.dm @@ -357,7 +357,6 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) ND.sample_object.mouse_opacity = MOUSE_OPACITY_OPAQUE ND.sample_object.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]" ND.sample_object.maptext = "[(ND.number > 1)? "[ND.number]" : ""]" - ND.sample_object.layer = ABOVE_HUD_LAYER ND.sample_object.plane = ABOVE_HUD_PLANE cx++ if(cx - screen_start_x >= cols) @@ -373,7 +372,6 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) O.mouse_opacity = MOUSE_OPACITY_OPAQUE //This is here so storage items that spawn with contents correctly have the "click around item to equip" O.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]" O.maptext = "" - O.layer = ABOVE_HUD_LAYER O.plane = ABOVE_HUD_PLANE cx++ if(cx - screen_start_x >= cols) @@ -439,7 +437,6 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) if(QDELETED(O)) continue O.screen_loc = "[cx],[cy]" - O.layer = ABOVE_HUD_LAYER O.plane = ABOVE_HUD_PLANE cx++ if(cx > mx) diff --git a/code/datums/components/wall_mounted.dm b/code/datums/components/wall_mounted.dm new file mode 100644 index 000000000000..335f306f4f7c --- /dev/null +++ b/code/datums/components/wall_mounted.dm @@ -0,0 +1,59 @@ +// This element should be applied to wall-mounted machines/structures, so that if the wall it's "hanging" from is broken or deconstructed, the wall-hung structure will deconstruct. +/datum/component/wall_mounted + dupe_mode = COMPONENT_DUPE_ALLOWED + /// The wall our object is currently linked to. + var/turf/hanging_wall_turf + /// Callback to the parent's proc to call on the linked object when the wall disappear's or changes. + var/datum/callback/on_drop + +/datum/component/wall_mounted/Initialize(target_wall, on_drop_callback) + . = ..() + if(!isobj(parent)) + return COMPONENT_INCOMPATIBLE + if(!isturf(target_wall)) + return COMPONENT_INCOMPATIBLE + hanging_wall_turf = target_wall + on_drop = on_drop_callback + +/datum/component/wall_mounted/RegisterWithParent() + RegisterSignal(hanging_wall_turf, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine)) + RegisterSignal(parent, COMSIG_PARENT_QDELETING, PROC_REF(on_linked_destroyed)) + +/datum/component/wall_mounted/UnregisterFromParent() + UnregisterSignal(hanging_wall_turf, list(COMSIG_PARENT_EXAMINE, COMSIG_TURF_CHANGE)) + UnregisterSignal(parent, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_MOVED)) + hanging_wall_turf = null + +/** + * Basic reference handling if the hanging/linked object is destroyed first. + */ +/datum/component/wall_mounted/proc/on_linked_destroyed() + SIGNAL_HANDLER + if(!QDELING(src)) + qdel(src) + +/** + * When the wall is examined, explains that it's supporting the linked object. + */ +/datum/component/wall_mounted/proc/on_examine(datum/source, mob/user, list/examine_list) + SIGNAL_HANDLER + examine_list += span_notice("\The [hanging_wall_turf] is currently supporting [span_bold("[parent]")]. Deconstruction or excessive damage would cause it to [span_bold("fall to the ground")].") + +/** + * Checks object direction and then verifies if there's a wall in that direction. Finally, applies a wall_mounted component to the object. + * + * @param directional If TRUE, will use the direction of the object to determine the wall to attach to. If FALSE, will use the object's loc. + * @param custom_drop_callback If set, will use this callback instead of the default deconstruct callback. + */ +/obj/proc/find_and_hang_on_wall(directional = TRUE, custom_drop_callback) + if(istype(get_area(src), /area/shuttle)) + return FALSE //For now, we're going to keep the component off of shuttles to avoid the turf changing issue. We'll hit that later really; + var/turf/attachable_wall + if(directional) + attachable_wall = get_step(src, dir) + else + attachable_wall = loc ///Pull from the curent object loc + if(!iswallturf(attachable_wall)) + return FALSE//Nothing to latch onto, or not the right thing. + src.AddComponent(/datum/component/wall_mounted, attachable_wall, custom_drop_callback) + return TRUE diff --git a/code/datums/datum.dm b/code/datums/datum.dm index a2d370e2ee68..884218199d8f 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -46,6 +46,8 @@ * add_timer() returns the truthy value of -1 when not stoppable, and else a truthy numeric index */ var/list/cooldowns + /// List for handling persistent filters. + var/list/filter_data #ifdef TESTING var/running_find_references @@ -250,3 +252,100 @@ return SEND_SIGNAL(source, COMSIG_CD_RESET(index), S_TIMER_COOLDOWN_TIMELEFT(source, index)) TIMER_COOLDOWN_END(source, index) + +/** Update a filter's parameter and animate this change. If the filter doesnt exist we won't do anything. + * Basically a [datum/proc/modify_filter] call but with animations. Unmodified filter parameters are kept. + * + * Arguments: + * * name - Filter name + * * new_params - New parameters of the filter + * * time - time arg of the BYOND animate() proc. + * * easing - easing arg of the BYOND animate() proc. + * * loop - loop arg of the BYOND animate() proc. + */ +/datum/proc/transition_filter(name, list/new_params, time, easing, loop) + var/filter = get_filter(name) + if(!filter) + return + // This can get injected by the filter procs, we want to support them so bye byeeeee + new_params -= "type" + animate(filter, new_params, time = time, easing = easing, loop = loop) + modify_filter(name, new_params) + +/// Returns the filter associated with the passed key +/datum/proc/get_filter(name) + ASSERT(isatom(src) || istype(src, /image)) + if(filter_data && filter_data[name]) + var/atom/atom_cast = src // filters only work with images or atoms. + return atom_cast.filters[filter_data.Find(name)] + +/** Update a filter's parameter to the new one. If the filter doesnt exist we won't do anything. + * + * Arguments: + * * name - Filter name + * * new_params - New parameters of the filter + * * overwrite - TRUE means we replace the parameter list completely. FALSE means we only replace the things on new_params. + */ +/datum/proc/modify_filter(name, list/new_params, overwrite = FALSE) + var/filter = get_filter(name) + if(!filter) + return + if(overwrite) + filter_data[name] = new_params + else + for(var/thing in new_params) + filter_data[name][thing] = new_params[thing] + update_filters() + +/// Reapplies all the filters. +/datum/proc/update_filters() + ASSERT(isatom(src) || istype(src, /image)) + var/atom/atom_cast = src // filters only work with images or atoms. + atom_cast.filters = null + filter_data = sortTim(filter_data, GLOBAL_PROC_REF(cmp_filter_data_priority), TRUE) + for(var/filter_raw in filter_data) + var/list/data = filter_data[filter_raw] + var/list/arguments = data.Copy() + arguments -= "priority" + atom_cast.filters += filter(arglist(arguments)) + UNSETEMPTY(filter_data) + +/obj/item/update_filters() + . = ..() + update_item_action_buttons() + +/datum/proc/add_filter(name, priority, list/params) + LAZYINITLIST(filter_data) + var/list/copied_parameters = params.Copy() + copied_parameters["priority"] = priority + filter_data[name] = copied_parameters + update_filters() + +/// Returns the indice in filters of the given filter name. +/// If it is not found, returns null. +/datum/proc/get_filter_index(name) + return filter_data?.Find(name) + +/// Removes the passed filter, or multiple filters, if supplied with a list. +/datum/proc/remove_filter(name_or_names) + if(!filter_data) + return + + var/list/names = islist(name_or_names) ? name_or_names : list(name_or_names) + + for(var/name in names) + if(filter_data[name]) + filter_data -= name + update_filters() + +/datum/proc/clear_filters() + ASSERT(isatom(src) || istype(src, /image)) + var/atom/atom_cast = src // filters only work with images or atoms. + filter_data = null + atom_cast.filters = null + +/// Return text from this proc to provide extra context to hard deletes that happen to it +/// Optional, you should use this for cases where replication is difficult and extra context is required +/// Can be called more then once per object, use harddel_deets_dumped to avoid duplicate calls (I am so sorry) +/datum/proc/dump_harddel_info() + return diff --git a/code/datums/diseases/advance/symptoms/sensory.dm b/code/datums/diseases/advance/symptoms/sensory.dm index 410a215652c7..bd4682516c3d 100644 --- a/code/datums/diseases/advance/symptoms/sensory.dm +++ b/code/datums/diseases/advance/symptoms/sensory.dm @@ -98,16 +98,16 @@ to_chat(M, span_notice("Your vision slowly returns...")) M.cure_blind(EYE_DAMAGE) M.cure_nearsighted(EYE_DAMAGE) - M.blur_eyes(35) + M.adjust_eye_blur(35) else if(HAS_TRAIT_FROM(M, TRAIT_NEARSIGHT, EYE_DAMAGE)) to_chat(M, span_notice("You can finally focus your eyes on distant objects.")) M.cure_nearsighted(EYE_DAMAGE) - M.blur_eyes(10) + M.adjust_eye_blur(10) else if(M.eye_blind || M.eye_blurry) M.set_blindness(0) - M.set_blurriness(0) + M.set_eye_blur(0) else if(eyes.damage > 0) eyes.applyOrganDamage(-1) else diff --git a/code/datums/diseases/advance/symptoms/vision.dm b/code/datums/diseases/advance/symptoms/vision.dm index 7d428afa0f53..c860a9f5a25d 100644 --- a/code/datums/diseases/advance/symptoms/vision.dm +++ b/code/datums/diseases/advance/symptoms/vision.dm @@ -57,10 +57,10 @@ Bonus to_chat(M, span_warning("Your eyes itch.")) if(3, 4) to_chat(M, span_warning("Your eyes burn!")) - M.blur_eyes(10) + M.adjust_eye_blur(10) eyes.applyOrganDamage(1) else - M.blur_eyes(20) + M.adjust_eye_blur(20) eyes.applyOrganDamage(5) if(eyes.damage >= 10) M.become_nearsighted(EYE_DAMAGE) diff --git a/code/datums/diseases/sleepy.dm b/code/datums/diseases/sleepy.dm index 0ca951fa8a04..209a4ef207c3 100644 --- a/code/datums/diseases/sleepy.dm +++ b/code/datums/diseases/sleepy.dm @@ -50,10 +50,10 @@ affected_mob.adjustStaminaLoss(10) if(prob(3)) to_chat(affected_mob, span_danger("Your eyes feel strained.")) - affected_mob.blur_eyes(6) + affected_mob.adjust_eye_blur(6) if(prob(3)) to_chat(affected_mob, span_warning("[pick("So tired...","You feel very sleepy.","You have a hard time keeping your eyes open.","You try to stay awake.")]")) - affected_mob.blur_eyes(6) + affected_mob.adjust_eye_blur(6) affected_mob.set_confusion_if_lower(4 SECONDS) affected_mob.adjustStaminaLoss(15) if(prob(5)) diff --git a/code/datums/elements/turf_transparency.dm b/code/datums/elements/turf_transparency.dm new file mode 100644 index 000000000000..e8312aa4dd2d --- /dev/null +++ b/code/datums/elements/turf_transparency.dm @@ -0,0 +1,280 @@ +/// List of z pillars (datums placed in the bottom left of XbyX squares that control transparency in that space) +/// The pillars are stored in triple depth lists indexed by (world_size % pillar_size) + 1 +/// They are created at transparent turf request, and deleted when no turfs remain +GLOBAL_LIST_EMPTY(pillars_by_z) +#define Z_PILLAR_RADIUS 20 +// Takes a position, transforms it into a z pillar key +#define Z_PILLAR_TRANSFORM(pos) (ROUND_UP(pos / Z_PILLAR_RADIUS)) +// Takes a z pillar key, hands back the actual posiiton it represents +// A key of 1 becomes 1, a key of 2 becomes Z_PILLAR_RADIUS + 1, etc. +#define Z_KEY_TO_POSITION(key) (((key - 1) * Z_PILLAR_RADIUS) + 1) + +/// Returns a z pillar to insert turfs into +/proc/request_z_pillar(x, y, z) + var/list/pillars_by_z = GLOB.pillars_by_z + if(length(pillars_by_z) < z) + pillars_by_z.len = z + var/list/our_z = pillars_by_z[z] + if(!our_z) + our_z = list() + pillars_by_z[z] = our_z + + //Now that we've got the z layer sorted, we're gonna check the X line + var/x_key = Z_PILLAR_TRANSFORM(x) + if(length(our_z) < x_key) + our_z.len = x_key + var/list/our_x = our_z[x_key] + if(!our_x) + our_x = list() + our_z[x_key] = our_x + + //And now the y layer + var/y_key = Z_PILLAR_TRANSFORM(y) + if(length(our_x) < y_key) + our_x.len = y_key + var/datum/z_pillar/our_lad = our_x[y_key] + if(!our_lad) + our_lad = new(x_key, y_key, z) + our_x[y_key] = our_lad + return our_lad + +/// Exists to be placed on the turf of walls and such to hold the vis_contents of the tile below +/// Otherwise the lower turf might get shifted around, which is dumb. do this instead. +/obj/effect/abstract/z_holder + var/datum/z_pillar/pillar + var/turf/show_for + appearance_flags = PIXEL_SCALE + plane = HUD_PLANE + anchored = TRUE + move_resist = INFINITY + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +/obj/effect/abstract/z_holder/Destroy() + if(pillar) + pillar.drawing_object -= show_for + pillar = null + show_for = null + return ..() + +/obj/effect/abstract/z_holder/proc/display(turf/display, datum/z_pillar/behalf_of) + if(pillar) + CRASH("We attempted to use a z holder to display when it was already in use, what'd you do") + + pillar = behalf_of + show_for = display + vis_contents += display + behalf_of.drawing_object[display] = src + +/// Grouping datum that manages transparency for a block of space +/// Setup to ease debugging, and to make add/remove operations cheaper +/datum/z_pillar + var/x_pos + var/y_pos + var/z_pos + /// Assoc list in the form displayed turf -> list of sources + var/list/turf_sources = list() + /// Assoc list of turfs using z holders in the form displayed turf -> z holder + var/list/drawing_object = list() + +/datum/z_pillar/New(x_pos, y_pos, z_pos) + . = ..() + src.x_pos = x_pos + src.y_pos = y_pos + src.z_pos = z_pos + +/datum/z_pillar/Destroy() + GLOB.pillars_by_z[z_pos][x_pos][y_pos] = null + // Just to be totally clear, this is code that exists to + // A: make sure cleanup is actually possible for this datum, just in case someone goes insane + // B: allow for easier debugging and making sure everything behaves as expected when fully removed + // It is not meant to be relied on, please don't actually it's not very fast + for(var/turf/displaying in turf_sources) + for(var/turf/displaying_for in turf_sources[displaying]) + hide_turf(displaying, displaying_for) + return ..() + +/// Displays a turf from the z level below us on our level +/datum/z_pillar/proc/display_turf(turf/to_display, turf/source) + var/list/sources = turf_sources[to_display] + + if(sources) // If we aren't the first to request this turf, return + sources |= source + var/obj/effect/abstract/z_holder/holding = drawing_object[to_display] + if(!holding) + return + + var/turf/visual_target = GET_TURF_ABOVE(to_display) + /// Basically, if we used to be under a non transparent turf, but are no longer in that position + /// Then we add to the transparent turf we're now under, and nuke the old object + if(!istransparentturf(visual_target)) + return + + holding.vis_contents -= to_display + qdel(holding) + drawing_object -= to_display + visual_target.vis_contents += to_display + return + + // Otherwise, we need to create a new set of sources. let's do that yeah? + sources = list() + turf_sources[to_display] = sources + sources |= source + + var/turf/visual_target = GET_TURF_ABOVE(to_display) + if(istransparentturf(visual_target) || isopenspaceturf(visual_target)) + visual_target.vis_contents += to_display + else + var/obj/effect/abstract/z_holder/hold_this = new(visual_target) + hold_this.display(to_display, src) + +/// Hides an existing turf from our vis_contents, or the vis_contents of the source if applicable +/datum/z_pillar/proc/hide_turf(turf/to_hide, turf/source) + var/list/sources = turf_sources[to_hide] + if(!sources) + return + sources -= source + // More sources remain + if(length(sources)) + return + + turf_sources -= to_hide + var/obj/effect/abstract/z_holder/holding = drawing_object[to_hide] + if(holding) + qdel(holding) + else + var/turf/visual_target = GET_TURF_ABOVE(to_hide) + visual_target.vis_contents -= to_hide + + if(!length(turf_sources) && !QDELETED(src)) + qdel(src) + +/// Called when a transparent turf is cleared. We wait a tick, then check to see what +/// Kind of turf replaced our former holder, and resetup our visuals as desired +/// We do not need to do this for non transparent holders, because they will have their abstract object cleared +/// When a transparent holder comes back. +/datum/z_pillar/proc/parent_cleared(turf/visual, turf/current_holder) + addtimer(CALLBACK(src, PROC_REF(refresh_orphan), visual, current_holder)) + +/// Runs the actual refresh of some formerly orphaned via vis_loc deletiong turf +/// We'll only reup if we either have no souece, or if the source is a transparent turf +/datum/z_pillar/proc/refresh_orphan(turf/orphan, turf/parent) + var/list/sources = turf_sources[orphan] + if(!length(sources)) + return + + var/obj/effect/abstract/z_holder/holding = drawing_object[orphan] + if(holding) + return + + if(istransparentturf(parent) || isopenspaceturf(parent)) + parent.vis_contents += orphan + else + var/obj/effect/abstract/z_holder/hold_this = new(parent) + hold_this.display(orphan, src) + +/datum/element/turf_z_transparency + element_flags = ELEMENT_DETACH_ON_HOST_DESTROY + +///This proc sets up the signals to handle updating viscontents when turfs above/below update. Handle plane and layer here too so that they don't cover other obs/turfs in Dream Maker +/datum/element/turf_z_transparency/Attach(datum/target, mapload) + . = ..() + if(!isturf(target)) + return ELEMENT_INCOMPATIBLE + + var/turf/our_turf = target + + RegisterSignal(target, COMSIG_TURF_MULTIZ_DEL, PROC_REF(on_multiz_turf_del)) + RegisterSignal(target, COMSIG_TURF_MULTIZ_NEW, PROC_REF(on_multiz_turf_new)) + + ADD_TRAIT(our_turf, TURF_Z_TRANSPARENT_TRAIT, ELEMENT_TRAIT(type)) + + if(!mapload) + update_multi_z(our_turf) + +/datum/element/turf_z_transparency/Detach(datum/source) + . = ..() + var/turf/our_turf = source + clear_multiz(our_turf) + + UnregisterSignal(our_turf, list(COMSIG_TURF_MULTIZ_NEW, COMSIG_TURF_MULTIZ_DEL)) + REMOVE_TRAIT(our_turf, TURF_Z_TRANSPARENT_TRAIT, ELEMENT_TRAIT(type)) + +///Updates the viscontents or underlays below this tile. +/datum/element/turf_z_transparency/proc/update_multi_z(turf/our_turf) + var/turf/below_turf = GET_TURF_BELOW(our_turf) + if(below_turf) // If we actually have something below us, display it. + for(var/turf/partner in range(1, below_turf)) + // We use our z here to ensure the pillar is actually on our level + var/datum/z_pillar/z_boss = request_z_pillar(partner.x, partner.y, our_turf.z) + z_boss.display_turf(partner, our_turf) + else + our_turf.underlays += get_baseturf_underlay(our_turf) + + // This shit is stupid + // z transparency is for making something SHOW WHAT'S BENEATH it, or if nothing is, show + // the appropriate underlay + // IT IS NOT FOR MAKING YOUR CLOSED TURF SEETHROUGH + // these are different concerns, and should not be HANDLED TOGETHER + // similarly, if you rip this out, rework diagonal closed turfs to work with this system + // it will make them look significantly nicer, and should let you tie into their logic more easily + // Just please don't break behavior yeah? thanks, I love you <3 + if(isclosedturf(our_turf)) //Show girders below closed turfs + var/mutable_appearance/girder_underlay = mutable_appearance('icons/obj/structures.dmi', "girder", layer = TURF_LAYER-0.01) + girder_underlay.appearance_flags = RESET_ALPHA | RESET_COLOR + our_turf.underlays += girder_underlay + var/mutable_appearance/plating_underlay = mutable_appearance('icons/turf/floors.dmi', "plating", layer = TURF_LAYER-0.02) + plating_underlay.appearance_flags = RESET_ALPHA | RESET_COLOR + our_turf.underlays += plating_underlay + return TRUE + +/datum/element/turf_z_transparency/proc/clear_multiz(turf/our_turf) + var/turf/below_turf = GET_TURF_BELOW(our_turf) + if(below_turf) // If we actually have something below us, we need to clear ourselves from it + for(var/turf/partner in range(1, below_turf)) + // We use our z here to ensure the pillar is actually on our level + var/datum/z_pillar/z_boss = request_z_pillar(partner.x, partner.y, our_turf.z) + z_boss.hide_turf(partner, our_turf) + if(partner == below_turf) + z_boss.parent_cleared(below_turf, our_turf) + else + our_turf.underlays -= get_baseturf_underlay(our_turf) + + if(isclosedturf(our_turf)) //Show girders below closed turfs + var/mutable_appearance/girder_underlay = mutable_appearance('icons/obj/structures.dmi', "girder", layer = TURF_LAYER-0.01) + girder_underlay.appearance_flags = RESET_ALPHA | RESET_COLOR + our_turf.underlays -= girder_underlay + var/mutable_appearance/plating_underlay = mutable_appearance('icons/turf/floors.dmi', "plating", layer = TURF_LAYER-0.02) + plating_underlay.appearance_flags = RESET_ALPHA | RESET_COLOR + our_turf.underlays -= plating_underlay + +/datum/element/turf_z_transparency/proc/on_multiz_turf_del(turf/our_turf, turf/below_turf, dir) + SIGNAL_HANDLER + + if(dir != DOWN) + return + + update_multi_z(our_turf) + +/datum/element/turf_z_transparency/proc/on_multiz_turf_new(turf/our_turf, turf/below_turf, dir) + SIGNAL_HANDLER + + if(dir != DOWN) + return + + update_multi_z(our_turf) + +///Called when there is no real turf below this turf +/datum/element/turf_z_transparency/proc/get_baseturf_underlay(turf/our_turf) + var/turf/path = SSmapping.level_trait(our_turf.z, ZTRAIT_BASETURF) || /turf/open/space + if(!ispath(path)) + path = text2path(path) + if(!ispath(path)) + warning("Z-level [our_turf.z] has invalid baseturf '[SSmapping.level_trait(our_turf.z, ZTRAIT_BASETURF)]'") + path = /turf/open/space + var/mutable_appearance/underlay_appearance = mutable_appearance(initial(path.icon), initial(path.icon_state), layer = TURF_LAYER-0.02, plane = PLANE_SPACE) + underlay_appearance.appearance_flags = RESET_ALPHA | RESET_COLOR + return underlay_appearance + +#undef Z_PILLAR_RADIUS +#undef Z_PILLAR_TRANSFORM +#undef Z_KEY_TO_POSITION diff --git a/code/datums/martial/flying_fang.dm b/code/datums/martial/flying_fang.dm index 95e8eeef2bf2..a50a66ebb534 100644 --- a/code/datums/martial/flying_fang.dm +++ b/code/datums/martial/flying_fang.dm @@ -132,7 +132,7 @@ playsound(D, 'sound/weapons/genhit1.ogg', 50, TRUE, -1) D.apply_damage(disarm_damage, STAMINA, BODY_ZONE_HEAD, armor_block) D.apply_damage(disarm_damage, A.dna.species.attack_type, BODY_ZONE_HEAD, armor_block) - D.blur_eyes(4) + D.adjust_eye_blur(4) if(!istype(D.head, /obj/item/clothing/head/helmet)) D.dna.species.aiminginaccuracy += 25 addtimer(CALLBACK(src, PROC_REF(remove_bonk), D), 10 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE) diff --git a/code/datums/mutable_appearance.dm b/code/datums/mutable_appearance.dm index f248da2a7323..aa69c510d8d0 100644 --- a/code/datums/mutable_appearance.dm +++ b/code/datums/mutable_appearance.dm @@ -4,18 +4,45 @@ // Mutable appearances are children of images, just so you know. -/mutable_appearance/New() +// Mutable appearances erase template vars on new, because they accept an appearance to copy as an arg +// If we have nothin to copy, we set the float plane +/mutable_appearance/New(mutable_appearance/to_copy) ..() - plane = FLOAT_PLANE // No clue why this is 0 by default yet images are on FLOAT_PLANE - // And yes this does have to be in the constructor, BYOND ignores it if you set it as a normal var + if(!to_copy) + plane = FLOAT_PLANE -// Helper similar to image() -/proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER, plane = FLOAT_PLANE, alpha = 255, appearance_flags = NONE) - var/mutable_appearance/MA = new() - MA.icon = icon - MA.icon_state = icon_state - MA.layer = layer - MA.plane = plane - MA.alpha = alpha - MA.appearance_flags |= appearance_flags - return MA +/** Helper similar to image() + * + * icon - Our appearance's icon + * icon_state - Our appearance's icon state + * layer - Our appearance's layer + * atom/offset_spokesman - An atom to use as reference for the z position of this appearance. + * Only required if a plane is passed in. If this is not passed in we accept offset_const as a substitute + * plane - The plane to use for the appearance. If this is not FLOAT_PLANE we require context for the offset to use + * alpha - Our appearance's alpha + * appearance_flags - Our appearance's appearance_flags + * offset_const - A constant to offset our plane by, so it renders on the right "z layer" +**/ +/proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER, atom/offset_spokesman, plane = FLOAT_PLANE, alpha = 255, appearance_flags = NONE, offset_const) + var/mutable_appearance/appearance = new() + appearance.icon = icon + appearance.icon_state = icon_state + appearance.layer = layer + appearance.alpha = alpha + appearance.appearance_flags |= appearance_flags + if(plane != FLOAT_PLANE) + // You need to pass in some non null object to reference + if(isatom(offset_spokesman)) + // Note, we are ok with null turfs, that's not an error condition we'll just default to 0, the error would be + // Not passing ANYTHING in, key difference + SET_PLANE_EXPLICIT(appearance, plane, offset_spokesman) + // That or I'll let you pass in a static offset. Don't be stupid now + else if(!isnull(offset_const)) + SET_PLANE_W_SCALAR(appearance, plane, offset_const) + // otherwise if you're setting plane you better have the guts to back it up + // else + // stack_trace("No plane offset passed in as context for a non floating mutable appearance, things are gonna go to hell on multiz maps") + // else if(!isnull(offset_spokesman) && !isatom(offset_spokesman)) + // stack_trace("Why did you pass in offset_spokesman as [offset_spokesman]? We need an atom to properly offset planes") + + return appearance diff --git a/code/datums/mutations/olfaction.dm b/code/datums/mutations/olfaction.dm index 3b9e3817b073..4bf2b3a0afd7 100644 --- a/code/datums/mutations/olfaction.dm +++ b/code/datums/mutations/olfaction.dm @@ -60,8 +60,7 @@ trail.Flip(dir) trail.Turn(180) - img = image(trail, loc = src, layer = HUD_LAYER) - img.layer = HUD_LAYER + img = image(trail, loc = src) img.plane = HUD_PLANE img.appearance_flags = NO_CLIENT_COLOR img.alpha = 0 diff --git a/code/datums/progressbar.dm b/code/datums/progressbar.dm index 23b916604dfe..225fa2e363ea 100644 --- a/code/datums/progressbar.dm +++ b/code/datums/progressbar.dm @@ -35,8 +35,7 @@ goal = goal_number bar_loc = target bar = image('icons/effects/progessbar.dmi', bar_loc, "prog_bar_0") - bar.plane = ABOVE_HUD_PLANE + 1 //yogs change, increased so it draws ontop of ventcrawling overlays - //SET_PLANE_EXPLICIT(bar, ABOVE_HUD_PLANE, User) //comment in when we port TG planes + SET_PLANE_EXPLICIT(bar, ABOVE_HUD_PLANE, User) //yogs change, increased so it draws ontop of ventcrawling overlays bar.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA user = User diff --git a/code/datums/status_effects/debuffs/debuffs.dm b/code/datums/status_effects/debuffs/debuffs.dm index 13601c2c4123..dbc2f2c6bff0 100644 --- a/code/datums/status_effects/debuffs/debuffs.dm +++ b/code/datums/status_effects/debuffs/debuffs.dm @@ -493,7 +493,6 @@ /datum/status_effect/cultghost/on_apply() owner.see_invisible = SEE_INVISIBLE_OBSERVER - owner.see_in_dark = 2 /datum/status_effect/cultghost/tick() if(owner.reagents) diff --git a/code/datums/status_effects/debuffs/drowsiness.dm b/code/datums/status_effects/debuffs/drowsiness.dm index d2707049a8c2..c719573772e3 100644 --- a/code/datums/status_effects/debuffs/drowsiness.dm +++ b/code/datums/status_effects/debuffs/drowsiness.dm @@ -37,6 +37,6 @@ if(owner.resting && remove_duration(2 * initial(tick_interval))) return - owner.blur_eyes(4 SECONDS) + owner.adjust_eye_blur(4 SECONDS) if(prob(5)) owner.AdjustSleeping(10 SECONDS) diff --git a/code/datums/status_effects/debuffs/drunk.dm b/code/datums/status_effects/debuffs/drunk.dm index 469d5dafd2d6..86481fcae6a2 100644 --- a/code/datums/status_effects/debuffs/drunk.dm +++ b/code/datums/status_effects/debuffs/drunk.dm @@ -169,7 +169,7 @@ // Over 71, we will constantly have blurry eyes if(drunk_value >= 71) - owner.blur_eyes(drunk_value * 2 - 140) + owner.adjust_eye_blur(drunk_value * 2 - 140) // Over 81, we will gain constant toxloss and experienced drunks will now begin to slur if(drunk_value >= 81) diff --git a/code/datums/status_effects/debuffs/screen_blur.dm b/code/datums/status_effects/debuffs/screen_blur.dm new file mode 100644 index 000000000000..abdd07d3cd59 --- /dev/null +++ b/code/datums/status_effects/debuffs/screen_blur.dm @@ -0,0 +1,52 @@ +/// This number is multiplied by the duration remaining (IN SECONDS, NOT DECISECONDS) +/// of the eye blur status effect to determine the intensity of the blur on the user +#define BLUR_DURATION_TO_INTENSITY 0.05 + +/// Applies a blur to the user's screen, increasing in strength depending on duration remaining. +/datum/status_effect/eye_blur + id = "eye_blur" + tick_interval = 1 SECONDS + alert_type = null + remove_on_fullheal = TRUE + +/datum/status_effect/eye_blur/on_creation(mob/living/new_owner, duration = 10 SECONDS) + src.duration = duration + return ..() + +/datum/status_effect/eye_blur/on_apply() + if(owner.mob_biotypes & (MOB_ROBOTIC|MOB_SPIRIT|MOB_SPECIAL)) + return FALSE + + // Refresh the blur when a client jumps into the mob, in case we get put on a clientless mob with no hud + RegisterSignal(owner, COMSIG_MOB_LOGIN, PROC_REF(update_blur)) + // Apply initial blur + update_blur() + return TRUE + +/datum/status_effect/eye_blur/on_remove() + UnregisterSignal(owner, COMSIG_MOB_LOGIN) + if(!owner.hud_used) + return + + var/atom/movable/plane_master_controller/game_plane_master_controller = owner.hud_used.plane_master_controllers[PLANE_MASTERS_GAME] + game_plane_master_controller.remove_filter("eye_blur") + +/datum/status_effect/eye_blur/tick(seconds_between_ticks) + // Blur lessens the closer we are to expiring, so we update per tick. + update_blur() + +/// Updates the blur of the owner of the status effect. +/// Also a signal proc for [COMSIG_MOB_LOGIN], to trigger then when the mob gets a client. +/datum/status_effect/eye_blur/proc/update_blur(datum/source) + SIGNAL_HANDLER + + if(!owner.hud_used) + return + + var/time_left_in_seconds = (duration - world.time) / (1 SECONDS) + var/amount_of_blur = clamp(time_left_in_seconds * BLUR_DURATION_TO_INTENSITY, 0.6, 3) + + var/atom/movable/plane_master_controller/game_plane_master_controller = owner.hud_used.plane_master_controllers[PLANE_MASTERS_GAME] + game_plane_master_controller.add_filter("eye_blur", 1, gauss_blur_filter(amount_of_blur)) + +#undef BLUR_DURATION_TO_INTENSITY diff --git a/code/datums/traits/good.dm b/code/datums/traits/good.dm index 6c9e33ccef9d..f9844d1661af 100644 --- a/code/datums/traits/good.dm +++ b/code/datums/traits/good.dm @@ -165,7 +165,7 @@ /datum/quirk/night_vision/on_spawn() var/mob/living/carbon/human/H = quirk_holder var/obj/item/organ/eyes/eyes = H.getorgan(/obj/item/organ/eyes) - if(!eyes || eyes.lighting_alpha) + if(!eyes || eyes.lighting_cutoff) return eyes.Insert(H) //refresh their eyesight and vision diff --git a/code/datums/weather/weather.dm b/code/datums/weather/weather.dm index 1a8d9800b424..f9c6d963ebba 100644 --- a/code/datums/weather/weather.dm +++ b/code/datums/weather/weather.dm @@ -29,7 +29,7 @@ var/impacted_z_levels // The list of z-levels that this weather is actively affecting var/overlay_layer = AREA_LAYER //Since it's above everything else, this is the layer used by default. TURF_LAYER is below mobs and walls if you need to use that. - var/overlay_plane = BLACKNESS_PLANE + var/overlay_plane = DEFAULT_PLANE var/aesthetic = FALSE //If the weather has no purpose other than looks var/immunity_type = WEATHER_STORM //Used by mobs to prevent them from being affected by the weather diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 42a45f528fbc..3ebb1f8455f5 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -10,7 +10,7 @@ icon_state = "unknown" layer = AREA_LAYER //Keeping this on the default plane, GAME_PLANE, will make area overlays fail to render on FLOOR_PLANE. - plane = BLACKNESS_PLANE + plane = DEFAULT_PLANE mouse_opacity = MOUSE_OPACITY_TRANSPARENT invisibility = INVISIBILITY_LIGHTING @@ -624,7 +624,7 @@ GLOBAL_LIST_EMPTY(teleportlocs) /area/proc/power_change() for(var/obj/machinery/M in src) // for each machine in the area M.power_change() // reverify power status (to update icons etc.) - update_appearance(UPDATE_ICON) + update_appearance() /** * Return the usage of power per channel diff --git a/code/game/atom_invisibility.dm b/code/game/atom_invisibility.dm new file mode 100644 index 000000000000..53fbe895817c --- /dev/null +++ b/code/game/atom_invisibility.dm @@ -0,0 +1,72 @@ +// Index defines +#define INVISIBILITY_VALUE 1 +#define INVISIBILITY_PRIORITY 2 + +/atom + VAR_PRIVATE/list/invisibility_sources + VAR_PRIVATE/current_invisibility_priority = -INFINITY + +/atom/proc/RecalculateInvisibility() + PRIVATE_PROC(TRUE) + + if(!invisibility_sources) + current_invisibility_priority = -INFINITY + invisibility = initial(invisibility) + return + + var/highest_priority + var/list/highest_priority_invisibility_data + for(var/entry in invisibility_sources) + var/list/priority_data + if(islist(entry)) + priority_data = entry + else + priority_data = invisibility_sources[entry] + + var/priority = priority_data[INVISIBILITY_PRIORITY] + if(highest_priority > priority) // In the case of equal priorities, we use the last thing in the list so that more recent changes apply first + continue + + highest_priority = priority + highest_priority_invisibility_data = priority_data + + current_invisibility_priority = highest_priority + invisibility = highest_priority_invisibility_data[INVISIBILITY_VALUE] + +/** + * Sets invisibility according to priority. + * If you want to be able to undo the value you set back to what it would be otherwise, + * you should provide an id here and remove it using RemoveInvisibility(id) + */ +/atom/proc/SetInvisibility(desired_value, id, priority=0) + if(!invisibility_sources) + invisibility_sources = list() + + if(id) + invisibility_sources[id] = list(desired_value, priority) + else + invisibility_sources += list(list(desired_value, priority)) + + if(current_invisibility_priority > priority) + return + + RecalculateInvisibility() + +/// Removes the specified invisibility source from the tracker +/atom/proc/RemoveInvisibility(id) + if(!invisibility_sources) + return + + var/list/priority_data = invisibility_sources[id] + invisibility_sources -= id + + if(length(invisibility_sources) == 0) + invisibility_sources = null + + if(current_invisibility_priority > priority_data[INVISIBILITY_PRIORITY]) + return + + RecalculateInvisibility() + +#undef INVISIBILITY_VALUE +#undef INVISIBILITY_PRIORITY diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 9135e2ad8909..1fe810856a3e 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -12,6 +12,9 @@ ///If non-null, overrides a/an/some in all cases var/article + /// How many tiles "up" this light is. 1 is typical, should only really change this if it's a floor light + var/light_height = LIGHTING_HEIGHT + ///First atom flags var var/flags_1 = NONE ///Intearaction flags @@ -85,8 +88,6 @@ ///Last fingerprints to touch this atom var/fingerprintslast - var/list/filter_data //For handling persistent filters - ///Economy cost of item var/custom_price ///Economy cost of item in premium vendor @@ -144,7 +145,6 @@ if(SSatoms.InitAtom(src, args)) //we were deleted return - SSdemo.mark_new(src) /** * The primary method that objects are setup in SS13 with @@ -204,6 +204,8 @@ custom_materials = null //Null the list to prepare for applying the materials properly set_custom_materials(temp_list) + + SSdemo.mark_new(src) return INITIALIZE_HINT_NORMAL @@ -1509,22 +1511,7 @@ victim.log_message(message, LOG_ATTACK, color="blue") -/atom/movable/proc/add_filter(name,priority,list/params) - if(!filter_data) - filter_data = list() - var/list/p = params.Copy() - p["priority"] = priority - filter_data[name] = p - update_filters() -/atom/movable/proc/update_filters() - filters = null - sortTim(filter_data,associative = TRUE) - for(var/f in filter_data) - var/list/data = filter_data[f] - var/list/arguments = data.Copy() - arguments -= "priority" - filters += filter(arglist(arguments)) /obj/item/update_filters() . = ..() @@ -1532,21 +1519,6 @@ var/datum/action/A = X A.build_all_button_icons() -/atom/movable/proc/get_filter(name) - if(filter_data && filter_data[name]) - return filters[filter_data.Find(name)] - -/// Returns the indice in filters of the given filter name. -/// If it is not found, returns null. -/atom/proc/get_filter_index(name) - return filter_data?.Find(name) - -/atom/movable/proc/remove_filter(name) - if(filter_data && filter_data[name]) - filter_data -= name - update_filters() - - /atom/proc/intercept_zImpact(atom/movable/AM, levels = 1) return FALSE diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 99dc8e01778b..e15a0bba1ea8 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -1,5 +1,8 @@ /atom/movable layer = OBJ_LAYER + glide_size = 8 + appearance_flags = TILE_BOUND|PIXEL_SCALE|LONG_GLIDE + var/last_move = null var/last_move_time = 0 var/anchored = FALSE @@ -31,8 +34,7 @@ var/atom/movable/moving_from_pull //attempt to resume grab after moving instead of before. var/list/client_mobs_in_contents // This contains all the client mobs within this container var/list/acted_explosions //for explosion dodging - glide_size = 8 - appearance_flags = TILE_BOUND|PIXEL_SCALE|LONG_GLIDE + var/datum/forced_movement/force_moving = null //handled soley by forced_movement.dm var/movement_type = GROUND //Incase you have multiple types, you automatically use the most useful one. IE: Skating on ice, flippers on water, flying over chasm/space, etc. var/atom/movable/pulling @@ -45,10 +47,14 @@ var/zfalling = FALSE + ///is the mob currently ascending or descending through z levels? + var/currently_z_moving + + /// Either FALSE, [EMISSIVE_BLOCK_GENERIC], or [EMISSIVE_BLOCK_UNIQUE] - var/blocks_emissive = FALSE + var/blocks_emissive = EMISSIVE_BLOCK_NONE ///Internal holder for emissive blocker object, do not use directly use blocks_emissive - var/atom/movable/emissive_blocker/em_block + var/atom/movable/render_step/emissive_blocker/em_block /// The degree of thermal insulation that mobs in list/contents have from the external environment, between 0 and 1 var/contents_thermal_insulation = 0 @@ -60,20 +66,68 @@ ///Highest-intensity light affecting us, which determines our visibility. var/affecting_dynamic_lumi = 0 +/mutable_appearance/emissive_blocker + +/mutable_appearance/emissive_blocker/New() + . = ..() + // Need to do this here because it's overriden by the parent call + color = EM_BLOCK_COLOR + plane = EMISSIVE_PLANE + appearance_flags = EMISSIVE_APPEARANCE_FLAGS /atom/movable/Initialize(mapload, ...) . = ..() - switch(blocks_emissive) - if(EMISSIVE_BLOCK_GENERIC) - update_emissive_block() - if(EMISSIVE_BLOCK_UNIQUE) +// #ifdef UNIT_TESTS +// if(explosion_block && !HAS_TRAIT(src, TRAIT_BLOCKING_EXPLOSIVES)) +// stack_trace("[type] blocks explosives, but does not have the managing element applied") +// #endif -- who cares + +#if EMISSIVE_BLOCK_GENERIC != 0 + #error EMISSIVE_BLOCK_GENERIC is expected to be 0 to faciliate a weird optimization hack where we rely on it being the most common. + #error Read the comment in code/game/atoms_movable.dm for details. +#endif + + // This one is incredible. + // `if (x) else { /* code */ }` is surprisingly fast, and it's faster than a switch, which is seemingly not a jump table. + // From what I can tell, a switch case checks every single branch individually, although sane, is slow in a hot proc like this. + // So, we make the most common `blocks_emissive` value, EMISSIVE_BLOCK_GENERIC, 0, getting to the fast else branch quickly. + // If it fails, then we can check over every value it can be (here, EMISSIVE_BLOCK_UNIQUE is the only one that matters). + // This saves several hundred milliseconds of init time. + if (blocks_emissive) + if (blocks_emissive == EMISSIVE_BLOCK_UNIQUE) render_target = ref(src) - em_block = new(src, render_target) - vis_contents += em_block + em_block = new(null, src) + overlays += em_block + if(managed_overlays) + if(islist(managed_overlays)) + managed_overlays += em_block + else + managed_overlays = list(managed_overlays, em_block) + else + managed_overlays = em_block + else + var/static/mutable_appearance/emissive_blocker/blocker = new() + blocker.icon = icon + blocker.icon_state = icon_state + blocker.dir = dir + blocker.appearance_flags |= appearance_flags + //blocker.plane = GET_NEW_PLANE(EMISSIVE_PLANE, PLANE_TO_OFFSET(plane)) + // Ok so this is really cursed, but I want to set with this blocker cheaply while + // Still allowing it to be removed from the overlays list later + // So I'm gonna flatten it, then insert the flattened overlay into overlays AND the managed overlays list, directly + // I'm sorry + var/mutable_appearance/flat = blocker.appearance + overlays += flat + if(managed_overlays) + if(islist(managed_overlays)) + managed_overlays += flat + else + managed_overlays = list(managed_overlays, flat) + else + managed_overlays = flat if(opacity) AddElement(/datum/element/light_blocking) - switch(light_system) if(MOVABLE_LIGHT) AddComponent(/datum/component/overlay_lighting) @@ -126,15 +180,83 @@ vis_contents.Cut() /atom/movable/proc/update_emissive_block() - if(blocks_emissive != EMISSIVE_BLOCK_GENERIC) - return - if(length(managed_vis_overlays)) - for(var/a in managed_vis_overlays) - var/obj/effect/overlay/vis/vs - if(vs.plane == EMISSIVE_BLOCKER_PLANE) - SSvis_overlays.remove_vis_overlay(src, list(vs)) - break - SSvis_overlays.add_vis_overlay(src, icon, icon_state, EMISSIVE_BLOCKER_LAYER, EMISSIVE_BLOCKER_PLANE, dir) + // This one is incredible. + // `if (x) else { /* code */ }` is surprisingly fast, and it's faster than a switch, which is seemingly not a jump table. + // From what I can tell, a switch case checks every single branch individually, although sane, is slow in a hot proc like this. + // So, we make the most common `blocks_emissive` value, EMISSIVE_BLOCK_GENERIC, 0, getting to the fast else branch quickly. + // If it fails, then we can check over every value it can be (here, EMISSIVE_BLOCK_UNIQUE is the only one that matters). + // This saves several hundred milliseconds of init time. + if (blocks_emissive) + if (blocks_emissive == EMISSIVE_BLOCK_UNIQUE) + if(em_block) + SET_PLANE(em_block, EMISSIVE_PLANE, src) + else if(!QDELETED(src)) + render_target = ref(src) + em_block = new(null, src) + return em_block + // Implied else if (blocks_emissive == EMISSIVE_BLOCK_NONE) -> return + // EMISSIVE_BLOCK_GENERIC == 0 + else + return fast_emissive_blocker(src) + +/// Generates a space underlay for a turf +/// This provides proper lighting support alongside just looking nice +/// Accepts the appearance to make "spaceish", and the turf we're doing this for +/proc/generate_space_underlay(mutable_appearance/underlay_appearance, turf/generate_for) + underlay_appearance.icon = 'icons/turf/space.dmi' + underlay_appearance.icon_state = "0" + SET_PLANE(underlay_appearance, PLANE_SPACE, generate_for) + if(!generate_for.render_target) + generate_for.render_target = ref(generate_for) + var/atom/movable/render_step/emissive_blocker/em_block = new(null, generate_for) + underlay_appearance.overlays += em_block + // We used it because it's convienient and easy, but it's gotta go now or it'll hang refs + QDEL_NULL(em_block) + // We're gonna build a light, and mask it with the base turf's appearance + // grab a 32x32 square of it + // I would like to use GLOB.starbright_overlays here + // But that breaks down for... some? reason. I think recieving a render relay breaks keep_together or something + // So we're just gonna accept that this'll break with starlight color changing. hardly matters since this is really only for offset stuff, but I'd love to fix it someday + var/mutable_appearance/light = new(GLOB.starlight_objects[GET_TURF_PLANE_OFFSET(generate_for) + 1]) + light.render_target = "" + light.appearance_flags |= KEEP_TOGETHER + // Now apply a copy of the turf, set to multiply + // This will multiply against our light, so we only light up the bits that aren't "on" the wall + var/mutable_appearance/mask = new(generate_for.appearance) + mask.blend_mode = BLEND_MULTIPLY + mask.render_target = "" + mask.pixel_x = 0 + mask.pixel_y = 0 + mask.pixel_w = 0 + mask.pixel_z = 0 + mask.transform = null + mask.underlays = list() // Begone foul lighting overlay + SET_PLANE(mask, FLOAT_PLANE, generate_for) + mask.layer = FLOAT_LAYER + + // Bump the opacity to full, will this work? + mask.color = list(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,255, 0,0,0,0) + light.overlays += mask + underlay_appearance.overlays += light + + // Now, we're going to make a copy of the mask. Instead of using it to multiply against our light + // We're going to use it to multiply against the turf lighting plane. Going to mask away the turf light + // And rely on LIGHTING_MASK_LAYER to ensure we mask ONLY that bit + var/mutable_appearance/turf_mask = new(mask.appearance) + SET_PLANE(turf_mask, LIGHTING_PLANE, generate_for) + turf_mask.layer = LIGHTING_MASK_LAYER + /// Any color becomes white. Anything else is black, and it's fully opaque + /// Ought to work + turf_mask.color = list(255,255,255,0, 255,255,255,0, 255,255,255,0, 0,0,0,0, 0,0,0,255) + underlay_appearance.overlays += turf_mask + +/atom/movable/update_overlays() + var/list/overlays = ..() + var/emissive_block = update_emissive_block() + if(emissive_block) + // Emissive block should always go at the beginning of the list + overlays.Insert(1, emissive_block) + return overlays /atom/movable/proc/can_zFall(turf/source, levels = 1, turf/target, direction) if(!direction) @@ -1044,3 +1166,101 @@ if(. <= GRAB_AGGRESSIVE) ADD_TRAIT(pulling, TRAIT_FLOORED, CHOKEHOLD_TRAIT) ADD_TRAIT(pulling, TRAIT_HANDS_BLOCKED, CHOKEHOLD_TRAIT) +/* + * The core multi-z movement proc. Used to move a movable through z levels. + * If target is null, it'll be determined by the can_z_move proc, which can potentially return null if + * conditions aren't met (see z_move_flags defines in __DEFINES/movement.dm for info) or if dir isn't set. + * Bear in mind you don't need to set both target and dir when calling this proc, but at least one or two. + * This will set the currently_z_moving to CURRENTLY_Z_MOVING_GENERIC if unset, and then clear it after + * Forcemove(). + * + * + * Args: + * * dir: the direction to go, UP or DOWN, only relevant if target is null. + * * target: The target turf to move the src to. Set by can_z_move() if null. + * * z_move_flags: bitflags used for various checks in both this proc and can_z_move(). See __DEFINES/movement.dm. + */ +/atom/movable/proc/zMove(dir, turf/target, z_move_flags = ZMOVE_FLIGHT_FLAGS) + if(!target) + target = can_z_move(dir, get_turf(src), null, z_move_flags) + if(!target) + set_currently_z_moving(FALSE, TRUE) + return FALSE + + var/list/moving_movs = get_z_move_affected(z_move_flags) + + for(var/atom/movable/movable as anything in moving_movs) + movable.currently_z_moving = currently_z_moving || CURRENTLY_Z_MOVING_GENERIC + movable.forceMove(target) + movable.set_currently_z_moving(FALSE, TRUE) + // This is run after ALL movables have been moved, so pulls don't get broken unless they are actually out of range. + if(z_move_flags & ZMOVE_CHECK_PULLS) + for(var/atom/movable/moved_mov as anything in moving_movs) + if(z_move_flags & ZMOVE_CHECK_PULLEDBY && moved_mov.pulledby && (moved_mov.z != moved_mov.pulledby.z || get_dist(moved_mov, moved_mov.pulledby) > 1)) + moved_mov.pulledby.stop_pulling() + if(z_move_flags & ZMOVE_CHECK_PULLING) + moved_mov.check_pulling(TRUE) + return TRUE + +/// Returns a list of movables that should also be affected when src moves through zlevels, and src. +/atom/movable/proc/get_z_move_affected(z_move_flags) + . = list(src) + if(buckled_mobs) + . |= buckled_mobs + if(!(z_move_flags & ZMOVE_INCLUDE_PULLED)) + return + for(var/mob/living/buckled as anything in buckled_mobs) + if(buckled.pulling) + . |= buckled.pulling + if(pulling) + . |= pulling + +/** + * Checks if the destination turf is elegible for z movement from the start turf to a given direction and returns it if so. + * Args: + * * direction: the direction to go, UP or DOWN, only relevant if target is null. + * * start: Each destination has a starting point on the other end. This is it. Most of the times the location of the source. + * * z_move_flags: bitflags used for various checks. See __DEFINES/movement.dm. + * * rider: A living mob in control of the movable. Only non-null when a mob is riding a vehicle through z-levels. + */ +/atom/movable/proc/can_z_move(direction, turf/start, turf/destination, z_move_flags = ZMOVE_FLIGHT_FLAGS, mob/living/rider) + if(!start) + start = get_turf(src) + if(!start) + return FALSE + if(!direction) + if(!destination) + return FALSE + direction = get_dir_multiz(start, destination) + if(direction != UP && direction != DOWN) + return FALSE + if(!destination) + destination = get_step_multiz(start, direction) + if(!destination) + if(z_move_flags & ZMOVE_FEEDBACK) + to_chat(rider || src, span_warning("There's nowhere to go in that direction!")) + return FALSE + if(z_move_flags & ZMOVE_FALL_CHECKS && (throwing || (movement_type & (FLYING|FLOATING)) || !has_gravity(start))) + return FALSE + if(z_move_flags & ZMOVE_CAN_FLY_CHECKS && !(movement_type & (FLYING|FLOATING)) && has_gravity(start)) + if(z_move_flags & ZMOVE_FEEDBACK) + if(rider) + to_chat(rider, span_warning("[src] is is not capable of flight.")) + else + to_chat(src, span_warning("You are not Superman.")) + return FALSE + if((!(z_move_flags & ZMOVE_IGNORE_OBSTACLES) && !(start.zPassOut(direction) && destination.zPassIn(direction))) || (!(z_move_flags & ZMOVE_ALLOW_ANCHORED) && anchored)) + if(z_move_flags & ZMOVE_FEEDBACK) + to_chat(rider || src, span_warning("You couldn't move there!")) + return FALSE + return destination //used by some child types checks and zMove() + +/// Sets the currently_z_moving variable to a new value. Used to allow some zMovement sources to have precedence over others. +/atom/movable/proc/set_currently_z_moving(new_z_moving_value, forced = FALSE) + if(forced) + currently_z_moving = new_z_moving_value + return TRUE + var/old_z_moving_value = currently_z_moving + currently_z_moving = max(currently_z_moving, new_z_moving_value) + return currently_z_moving > old_z_moving_value + diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index ded2d319c4ef..323ce12ea174 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -95,6 +95,7 @@ Class Procs: anchored = TRUE interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT + blocks_emissive = EMISSIVE_BLOCK_GENERIC var/stat = 0 var/use_power = IDLE_POWER_USE diff --git a/code/game/machinery/airlock_cycle_control.dm b/code/game/machinery/airlock_cycle_control.dm index 7a142582f6f5..4c203c05e25f 100644 --- a/code/game/machinery/airlock_cycle_control.dm +++ b/code/game/machinery/airlock_cycle_control.dm @@ -182,7 +182,7 @@ var/matrix/TR = new TR.Translate(0, 16) TR.Multiply(new /matrix(s_dx, f_dx, 0, s_dy, f_dy, 0)) - var/mutable_appearance/M = mutable_appearance(icon, "hologram-line", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE) + var/mutable_appearance/M = mutable_appearance(icon, "hologram-line", ABOVE_LIGHTING_PLANE) M.transform = TR add_overlay(M) diff --git a/code/game/machinery/barsigns.dm b/code/game/machinery/barsigns.dm new file mode 100644 index 000000000000..f65c784142ee --- /dev/null +++ b/code/game/machinery/barsigns.dm @@ -0,0 +1,501 @@ +/obj/machinery/barsign // All Signs are 64 by 32 pixels, they take two tiles + name = "bar sign" + desc = "A bar sign which has not been initialized, somehow. Complain at a coder!" + icon = 'icons/obj/barsigns.dmi' + icon_state = "empty" + req_access = list(ACCESS_BAR) + max_integrity = 500 + integrity_failure = 0.5 + /// Selected barsign being used + var/datum/barsign/chosen_sign + /// Do we attempt to rename the area we occupy when the chosen sign is changed? + var/change_area_name = FALSE + /// What kind of sign do we drop upon being disassembled? + var/disassemble_result = /obj/item/wallframe/barsign + light_color = LIGHT_COLOR_BLUE + /// If barsign has a lighting mask + var/light_mask = FALSE + +/datum/armor/sign_barsign + melee = 20 + bullet = 20 + laser = 20 + energy = 100 + fire = 50 + acid = 50 + +MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/barsign, 32) + +/obj/machinery/barsign/Initialize(mapload) + . = ..() + //Roundstart/map specific barsigns "belong" in their area and should be renaming it, signs created from wallmounts will not. + change_area_name = mapload + set_sign(new /datum/barsign/hiddensigns/signoff) + find_and_hang_on_wall() + +/obj/machinery/barsign/proc/set_sign(datum/barsign/sign) + if(!istype(sign)) + return + + if(change_area_name && sign.rename_area) + rename_area(src, sign.name) + + chosen_sign = sign + update_appearance() + +/obj/machinery/barsign/update_icon_state() + if(!(stat & BROKEN) && (!(stat & NOPOWER) || stat & EMPED) && chosen_sign && chosen_sign.icon_state) + icon_state = chosen_sign.icon_state + else + icon_state = "empty" + + return ..() + +/obj/machinery/barsign/update_desc() + . = ..() + + if(chosen_sign && chosen_sign.desc) + desc = chosen_sign.desc + +/obj/machinery/barsign/update_name() + . = ..() + if(chosen_sign && chosen_sign.rename_area) + name = "[initial(name)] ([chosen_sign.name])" + else + name = "[initial(name)]" + +/obj/machinery/barsign/update_overlays() + . = ..() + + . += emissive_appearance(icon, "[chosen_sign.icon_state]-light-mask", src) + if(((stat & NOPOWER) && !(stat & EMPED)) || (stat & BROKEN)) + return + + if(chosen_sign && chosen_sign.light_mask) + . += emissive_appearance(icon, "[chosen_sign.icon_state]-light-mask", src) + +/obj/machinery/barsign/update_appearance(updates=ALL) + . = ..() + if(stat & (NOPOWER|BROKEN)) + set_light(0) + return + if(chosen_sign && chosen_sign.neon_color) + set_light(MINIMUM_USEFUL_LIGHT_RANGE, 0.7, chosen_sign.neon_color) + +/obj/machinery/barsign/proc/set_sign_by_name(sign_name) + for(var/datum/barsign/sign as anything in subtypesof(/datum/barsign)) + if(initial(sign.name) == sign_name) + var/new_sign = new sign + set_sign(new_sign) + +/obj/machinery/barsign/deconstruct(disassembled = TRUE) + if(!(flags_1 & NODECONSTRUCT_1)) + if(disassembled) + new disassemble_result(drop_location()) + else + new /obj/item/stack/sheet/metal(drop_location(), 2) + new /obj/item/stack/cable_coil(drop_location(), 2) + + qdel(src) + +/obj/machinery/barsign/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + playsound(src.loc, 'sound/effects/glasshit.ogg', 75, TRUE) + if(BURN) + playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) + +/obj/machinery/barsign/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/barsign/attack_hand(mob/user, list/modifiers) + . = ..() + if(.) + return + if(!allowed(user)) + balloon_alert(user, "access denied!") + return + if(stat & (NOPOWER|BROKEN|EMPED)) + balloon_alert(user, "controls are unresponsive!") + return + pick_sign(user) + +/obj/machinery/barsign/screwdriver_act(mob/living/user, obj/item/tool) + tool.play_tool_sound(src) + panel_open = !panel_open + if(panel_open) + balloon_alert(user, "panel opened") + set_sign(new /datum/barsign/hiddensigns/signoff) + return TOOL_ACT_TOOLTYPE_SUCCESS + + balloon_alert(user, "panel closed") + + if(stat & (NOPOWER|BROKEN) || !chosen_sign) + set_sign(new /datum/barsign/hiddensigns/signoff) + else + set_sign(chosen_sign) + + return TOOL_ACT_TOOLTYPE_SUCCESS + +/obj/machinery/barsign/wrench_act(mob/living/user, obj/item/tool) + . = ..() + if(!panel_open) + balloon_alert(user, "open the panel first!") + return FALSE + + tool.play_tool_sound(src) + if(!do_after(user, (10 SECONDS), target = src)) + return FALSE + + tool.play_tool_sound(src) + deconstruct(disassembled = TRUE) + return TOOL_ACT_TOOLTYPE_SUCCESS + + +/obj/machinery/barsign/attackby(obj/item/attacking_item, mob/user) + + if(istype(attacking_item, /obj/item/areaeditor/blueprints) && !change_area_name) + if(!panel_open) + balloon_alert(user, "open the panel first!") + return TRUE + + change_area_name = TRUE + balloon_alert(user, "sign registered") + return TRUE + + return ..() + + +/obj/machinery/barsign/emp_act(severity) + . = ..() + if(. & EMP_PROTECT_SELF) + return + set_sign(new /datum/barsign/hiddensigns/empbarsign) + +/obj/machinery/barsign/emag_act(mob/user, obj/item/card/emag/emag_card) + if(stat & (NOPOWER|BROKEN|EMPED)) + balloon_alert(user, "controls are unresponsive!") + return FALSE + + balloon_alert(user, "illegal barsign loaded") + addtimer(CALLBACK(src, PROC_REF(finish_emag_act)), 10 SECONDS) + return TRUE + +/// Timer proc, called after ~10 seconds after [emag_act], since [emag_act] returns a value and cannot sleep +/obj/machinery/barsign/proc/finish_emag_act() + set_sign(new /datum/barsign/hiddensigns/syndibarsign) + +/obj/machinery/barsign/proc/pick_sign(mob/user) + var/picked_name = tgui_input_list(user, "Available Signage", "Bar Sign", sort_list(get_bar_names())) + if(isnull(picked_name)) + return + set_sign_by_name(picked_name) + SSblackbox.record_feedback("tally", "barsign_picked", 1, chosen_sign.type) + +/proc/get_bar_names() + var/list/names = list() + for(var/d in subtypesof(/datum/barsign)) + var/datum/barsign/D = d + if(!initial(D.hidden)) + names += initial(D.name) + . = names + +/datum/barsign + /// User-visible name of the sign. + var/name + /// Icon state associated with this sign + var/icon_state + /// Description shown in the sign's examine text. + var/desc + /// Hidden from list of selectable options. + var/hidden = FALSE + /// Rename the area when this sign is selected. + var/rename_area = TRUE + /// If a barsign has a light mask for emission effects + var/light_mask = TRUE + /// The emission color of the neon light + var/neon_color + +/datum/barsign/New() + if(!desc) + desc = "It displays \"[name]\"." + +// Specific bar signs. + +/datum/barsign/maltesefalcon + name = "Maltese Falcon" + icon_state = "maltesefalcon" + desc = "The Maltese Falcon, Space Bar and Grill." + neon_color = "#5E8EAC" + +/datum/barsign/thebark + name = "The Bark" + icon_state = "thebark" + desc = "Ian's bar of choice." + neon_color = "#f7a604" + +/datum/barsign/harmbaton + name = "The Harmbaton" + icon_state = "theharmbaton" + desc = "A great dining experience for both security members and assistants." + neon_color = "#ff7a4d" + +/datum/barsign/thesingulo + name = "The Singulo" + icon_state = "thesingulo" + desc = "Where people go that'd rather not be called by their name." + neon_color = "#E600DB" + +/datum/barsign/thedrunkcarp + name = "The Drunk Carp" + icon_state = "thedrunkcarp" + desc = "Don't drink and swim." + neon_color = "#a82196" + +/datum/barsign/scotchservinwill + name = "Scotch Servin Willy's" + icon_state = "scotchservinwill" + desc = "Willy sure moved up in the world from clown to bartender." + neon_color = "#fee4bf" + +/datum/barsign/officerbeersky + name = "Officer Beersky's" + icon_state = "officerbeersky" + desc = "Man eat a dong, these drinks are great." + neon_color = "#16C76B" + +/datum/barsign/thecavern + name = "The Cavern" + icon_state = "thecavern" + desc = "Fine drinks while listening to some fine tunes." + neon_color = "#0fe500" + +/datum/barsign/theouterspess + name = "The Outer Spess" + icon_state = "theouterspess" + desc = "This bar isn't actually located in outer space." + neon_color = "#30f3cc" + +/datum/barsign/slipperyshots + name = "Slippery Shots" + icon_state = "slipperyshots" + desc = "Slippery slope to drunkeness with our shots!" + neon_color = "#70DF00" + +/datum/barsign/thegreytide + name = "The Grey Tide" + icon_state = "thegreytide" + desc = "Abandon your toolboxing ways and enjoy a lazy beer!" + neon_color = "#00F4D6" + +/datum/barsign/honkednloaded + name = "Honked 'n' Loaded" + icon_state = "honkednloaded" + desc = "Honk." + neon_color = "#FF998A" + +/datum/barsign/le_cafe_silencieux + name = "Le Café Silencieux" + icon_state = "le_cafe_silencieux" + desc = "..." + neon_color = "#ffffff" + +/datum/barsign/thenest + name = "The Nest" + icon_state = "thenest" + desc = "A good place to retire for a drink after a long night of crime fighting." + neon_color = "#4d6796" + +/datum/barsign/thecoderbus + name = "The Coderbus" + icon_state = "thecoderbus" + desc = "A very controversial bar known for its wide variety of constantly-changing drinks." + neon_color = "#ffffff" + +/datum/barsign/theadminbus + name = "The Adminbus" + icon_state = "theadminbus" + desc = "An establishment visited mainly by space-judges. It isn't bombed nearly as much as court hearings." + neon_color = "#ffffff" + +/datum/barsign/oldcockinn + name = "The Old Cock Inn" + icon_state = "oldcockinn" + desc = "Something about this sign fills you with despair." + neon_color = "#a4352b" + +/datum/barsign/thewretchedhive + name = "The Wretched Hive" + icon_state = "thewretchedhive" + desc = "Legally obligated to instruct you to check your drinks for acid before consumption." + neon_color = "#26b000" + +/datum/barsign/robustacafe + name = "The Robusta Cafe" + icon_state = "robustacafe" + desc = "Holder of the 'Most Lethal Barfights' record 5 years uncontested." + neon_color = "#c45f7a" + +/datum/barsign/emergencyrumparty + name = "The Emergency Rum Party" + icon_state = "emergencyrumparty" + desc = "Recently relicensed after a long closure." + neon_color = "#f90011" + +/datum/barsign/combocafe + name = "The Combo Cafe" + icon_state = "combocafe" + desc = "Renowned system-wide for their utterly uncreative drink combinations." + neon_color = "#33ca40" + +/datum/barsign/vladssaladbar + name = "Vlad's Salad Bar" + icon_state = "vladssaladbar" + desc = "Under new management. Vlad was always a bit too trigger happy with that shotgun." + neon_color = "#306900" + +/datum/barsign/theshaken + name = "The Shaken" + icon_state = "theshaken" + desc = "This establishment does not serve stirred drinks." + neon_color = "#dcd884" + +/datum/barsign/thealenath + name = "The Ale' Nath" + icon_state = "thealenath" + desc = "All right, buddy. I think you've had EI NATH. Time to get a cab." + neon_color = "#ed0000" + +/datum/barsign/thealohasnackbar + name = "The Aloha Snackbar" + icon_state = "alohasnackbar" + desc = "A tasteful, inoffensive tiki bar sign." + neon_color = "" + +/datum/barsign/thenet + name = "The Net" + icon_state = "thenet" + desc = "You just seem to get caught up in it for hours." + neon_color = "#0e8a00" + +/datum/barsign/maidcafe + name = "Maid Cafe" + icon_state = "maidcafe" + desc = "Welcome back, master!" + neon_color = "#ff0051" + +/datum/barsign/the_lightbulb + name = "The Lightbulb" + icon_state = "the_lightbulb" + desc = "A cafe popular among moths and moffs. Once shut down for a week after the bartender used mothballs to protect her spare uniforms." + neon_color = "#faff82" + +/datum/barsign/goose + name = "The Loose Goose" + icon_state = "goose" + desc = "Drink till you puke and/or break the laws of reality!" + neon_color = "#00cc33" + +/datum/barsign/maltroach + name = "Maltroach" + icon_state = "maltroach" + desc = "Mothroaches politely greet you into the bar, or are they greeting eachother?" + neon_color = "#649e8a" + +/datum/barsign/rock_bottom + name = "Rock Bottom" + icon_state = "rock-bottom" + desc = "When it feels like you're stuck in a pit, might as well have a drink." + neon_color = "#aa2811" + +/datum/barsign/tearoom + name = "Little Treats Tea Room" + icon_state = "little_treats" + desc = "A delightfully relaxing tearoom for all the fancy lads in the cosmos." + neon_color = LIGHT_COLOR_ORANGE + +/datum/barsign/assembly_line + name = "The Assembly Line" + icon_state = "the-assembly-line" + desc = "Where every drink is masterfully crafted with industrial efficiency!" + neon_color = "#ffffff" + +/datum/barsign/bargonia + name = "Bargonia" + icon_state = "bargonia" + desc = "The warehouse yearns for a higher calling... so Supply has declared BARGONIA!" + neon_color = COLOR_WHITE + +/datum/barsign/cult_cove + name = "Cult Cove" + icon_state = "cult-cove" + desc = "Nar'Sie's favourite retreat" + neon_color = COLOR_RED + +/datum/barsign/neon_flamingo + name = "Neon Flamingo" + icon_state = "neon-flamingo" + desc = "A bus for all but the flamboyantly challenged." + neon_color = COLOR_PINK + +/datum/barsign/slowdive + name = "Slowdive" + icon_state = "slowdive" + desc = "First stop out of hell, last stop before heaven." + neon_color = COLOR_RED + +// Hidden signs list below this point + +/datum/barsign/hiddensigns + hidden = TRUE + +/datum/barsign/hiddensigns/empbarsign + name = "EMP'd" + icon_state = "empbarsign" + desc = "Something has gone very wrong." + rename_area = FALSE + +/datum/barsign/hiddensigns/syndibarsign + name = "Syndi Cat" + icon_state = "syndibarsign" + desc = "Syndicate or die." + neon_color = "#ff0000" + +/datum/barsign/hiddensigns/signoff + name = "Off" + icon_state = "empty" + desc = "This sign doesn't seem to be on." + rename_area = FALSE + light_mask = FALSE + +// For other locations that aren't in the main bar +/obj/machinery/barsign/all_access + req_access = null + disassemble_result = /obj/item/wallframe/barsign/all_access + +MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/barsign/all_access, 32) + +/obj/item/wallframe/barsign + name = "bar sign frame" + desc = "Used to help draw the rabble into your bar. Some assembly required." + icon = 'icons/obj/wallmounts.dmi' + icon_state = "barsign" + result_path = /obj/machinery/barsign + pixel_shift = 32 + +/obj/item/wallframe/barsign/Initialize(mapload) + . = ..() + desc += " Can be registered with a set of [span_bold("station blueprints")] to associate the sign with the area it occupies." + +/obj/item/wallframe/barsign/try_build(turf/on_wall, mob/user) + . = ..() + if(!.) + return . + + if(isopenturf(get_step(on_wall, EAST))) //This takes up 2 tiles so we want to make sure we have two tiles to hang it from. + balloon_alert(user, "needs more support!") + return FALSE + +/obj/item/wallframe/barsign/all_access + desc = "Used to help draw the rabble into your bar. Some assembly required. This one doesn't have an access lock." + result_path = /obj/machinery/barsign/all_access diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index a36956f5459f..f7ee0cce9b84 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -11,6 +11,7 @@ idle_power_usage = 5 active_power_usage = 10 layer = WALL_OBJ_LAYER + plane = GAME_PLANE_UPPER resistance_flags = FIRE_PROOF armor = list(MELEE = 50, BULLET = 20, LASER = 20, ENERGY = 20, BOMB = 0, BIO = 0, RAD = 0, FIRE = 90, ACID = 50) @@ -95,7 +96,7 @@ if(mapload && is_station_level(z) && prob(3) && !start_active) toggle_cam() else //this is handled by toggle_camera, so no need to update it twice. - update_appearance(UPDATE_ICON) + update_appearance() /obj/machinery/camera/Destroy() if(can_use()) @@ -145,14 +146,14 @@ return if(!(. & EMP_PROTECT_SELF)) if(prob(15 * severity)) - update_appearance(UPDATE_ICON) + update_appearance() var/list/previous_network = network network = list() GLOB.cameranet.removeCamera(src) stat |= EMPED set_light(0) emped = emped+1 //Increase the number of consecutive EMP's - update_appearance(UPDATE_ICON) + update_appearance() var/thisemp = emped //Take note of which EMP this proc is for spawn(900) if(loc) //qdel limbo @@ -160,7 +161,7 @@ if(emped == thisemp) //Only fix it if the camera hasn't been EMP'd again network = previous_network stat &= ~EMPED - update_appearance(UPDATE_ICON) + update_appearance() if(can_use()) GLOB.cameranet.addCamera(src) emped = 0 //Resets the consecutive EMP count @@ -364,7 +365,7 @@ else icon_state = "[xray_module][default_camera_icon][in_use_lights ? "_in_use" : ""]" -/obj/machinery/camera/proc/toggle_cam(mob/user, displaymessage = 1) +/obj/machinery/camera/proc/toggle_cam(mob/user, displaymessage = TRUE) status = !status if(can_use()) GLOB.cameranet.addCamera(src) @@ -378,12 +379,15 @@ GLOB.cameranet.removeCamera(src) if (isarea(myarea)) LAZYREMOVE(myarea.cameras, src) - GLOB.cameranet.updateChunk(x, y, z) + // We are not guarenteed that the camera will be on a turf. account for that + var/turf/our_turf = get_turf(src) + GLOB.cameranet.updateChunk(our_turf.x, our_turf.y, our_turf.z) var/change_msg = "deactivates" if(status) change_msg = "reactivates" triggerCameraAlarm() - addtimer(CALLBACK(src, PROC_REF(cancelCameraAlarm)), 100) + if(!QDELETED(src)) //We'll be doing it anyway in destroy + addtimer(CALLBACK(src, PROC_REF(cancelCameraAlarm)), 100) if(displaymessage) if(user) visible_message(span_danger("[user] [change_msg] [src]!")) @@ -392,16 +396,16 @@ visible_message(span_danger("\The [src] [change_msg]!")) playsound(src, 'sound/items/wirecutter.ogg', 100, TRUE) - update_appearance(UPDATE_ICON) //update Initialize(mapload) if you remove this. + update_appearance() //update Initialize() if you remove this. // now disconnect anyone using the camera //Apparently, this will disconnect anyone even if the camera was re-activated. //I guess that doesn't matter since they can't use it anyway? for(var/mob/O in GLOB.player_list) - if (O.client && O.client.eye == src) + if (O.client?.eye == src) O.unset_machine() O.reset_perspective(null) - to_chat(O, "The screen bursts into static.") + to_chat(O, span_warning("The screen bursts into static!")) /obj/machinery/camera/proc/triggerCameraAlarm() alarm_on = TRUE @@ -423,10 +427,32 @@ /obj/machinery/camera/proc/can_see() var/list/see = null var/turf/pos = get_turf(src) + var/turf/directly_above = GET_TURF_ABOVE(pos) + var/check_lower = pos != get_lowest_turf(pos) + var/check_higher = directly_above && istransparentturf(directly_above) && (pos != get_highest_turf(pos)) + if(isXRay()) see = range(view_range, pos) else see = get_hear(view_range, pos) + if(check_lower || check_higher) + // Haha datum var access KILL ME + for(var/turf/seen in see) + if(check_lower) + var/turf/visible = seen + while(visible && istransparentturf(visible)) + var/turf/below = GET_TURF_BELOW(visible) + for(var/turf/adjacent in range(1, below)) + see += adjacent + see += adjacent.contents + visible = below + if(check_higher) + var/turf/above = GET_TURF_ABOVE(seen) + while(above && istransparentturf(above)) + for(var/turf/adjacent in range(1, above)) + see += adjacent + see += adjacent.contents + above = GET_TURF_ABOVE(above) return see /atom/proc/auto_turn() @@ -468,11 +494,9 @@ user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 2) /obj/machinery/camera/update_remote_sight(mob/living/user) - user.see_invisible = SEE_INVISIBLE_LIVING //can't see ghosts through cameras + user.set_invis_see(SEE_INVISIBLE_LIVING) //can't see ghosts through cameras if(isXRay()) - user.sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS) - user.see_in_dark = max(user.see_in_dark, 8) + user.add_sight(SEE_TURFS|SEE_MOBS|SEE_OBJS) else - user.sight = 0 - user.see_in_dark = 2 + user.clear_sight(SEE_TURFS|SEE_MOBS|SEE_OBJS) return 1 diff --git a/code/game/machinery/camera/tracking.dm b/code/game/machinery/camera/tracking.dm index 0976aba838e4..0c3ed0cc6b17 100644 --- a/code/game/machinery/camera/tracking.dm +++ b/code/game/machinery/camera/tracking.dm @@ -1,22 +1,10 @@ -/mob/living/silicon/ai/proc/get_camera_list() - var/list/L = list() - for (var/obj/machinery/camera/C in GLOB.cameranet.cameras) - L.Add(C) - - camera_sort(L) - - var/list/T = list() - - for (var/obj/machinery/camera/C in L) - var/list/tempnetwork = C.network&src.network - if (tempnetwork.len) - T[text("[][]", C.c_tag, (C.can_use() ? null : " (Deactivated)"))] = C - - return T - /mob/living/silicon/ai/proc/show_camera_list() - var/list/cameras = get_camera_list() - var/camera = input(src, "Choose which camera you want to view", "Cameras") as null|anything in cameras + var/list/cameras = get_camera_list(network) + var/camera = tgui_input_list(src, "Choose which camera you want to view", "Cameras", cameras) + if(isnull(camera)) + return + if(isnull(cameras[camera])) + return switchCamera(cameras[camera]) /datum/trackable @@ -138,14 +126,3 @@ return user.switchCamera(src) -/proc/camera_sort(list/L) - var/obj/machinery/camera/a - var/obj/machinery/camera/b - - for (var/i = L.len, i > 0, i--) - for (var/j = 1 to i - 1) - a = L[j] - b = L[j + 1] - if (sorttext(a.c_tag, b.c_tag) < 0) - L.Swap(j, j + 1) - return L diff --git a/code/game/machinery/computer/_computer.dm b/code/game/machinery/computer/_computer.dm index c0077e4fa619..d4ad71aa8e3a 100644 --- a/code/game/machinery/computer/_computer.dm +++ b/code/game/machinery/computer/_computer.dm @@ -10,16 +10,21 @@ integrity_failure = 100 armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 40, ACID = 20) clicksound = "keyboard" - light_system = STATIC_LIGHT - light_range = 2 - light_power = 1 - light_on = TRUE + /// How bright we are when turned on. + var/brightness_on = 1 + /// Icon_state of the keyboard overlay. var/icon_keyboard = "generic_key" + /// Should we render an unique icon for the keyboard when off? + var/keyboard_change_icon = TRUE + /// Icon_state of the emissive screen overlay. var/icon_screen = "generic" - var/clockwork = FALSE - var/time_to_scewdrive = 20 + /// Time it takes to deconstruct with a screwdriver. + var/time_to_unscrew = 2 SECONDS + /// Are we authenticated to use this? Used by things like comms console, security and medical data, and apc controller. var/authenticated = FALSE + var/clockwork = FALSE + /obj/machinery/computer/Initialize(mapload, obj/item/circuitboard/C) . = ..() if(mapload) @@ -86,17 +91,23 @@ /obj/machinery/computer/update_overlays() . = ..() - if(stat & NOPOWER) - . += "[icon_keyboard]_off" - return - . += icon_keyboard + if(icon_keyboard) + if(keyboard_change_icon && (stat & NOPOWER)) + . += "[icon_keyboard]_off" + else + . += icon_keyboard - // This whole block lets screens ignore lighting and be visible even in the darkest room - var/overlay_state = icon_screen if(stat & BROKEN) - overlay_state = "[icon_state]_broken" - . += mutable_appearance(icon, overlay_state) - . += mutable_appearance(icon, overlay_state, layer, EMISSIVE_PLANE) + . += mutable_appearance(icon, "[icon_state]_broken") + return // If we don't do this broken computers glow in the dark. + + if(stat & NOPOWER) // Your screen can't be on if you've got no damn charge + return + + // This lets screens ignore lighting and be visible even in the darkest room + if(icon_screen) + . += mutable_appearance(icon, icon_screen) + . += emissive_appearance(icon, icon_screen, src) /obj/machinery/computer/power_change() . = ..() @@ -105,14 +116,14 @@ if(stat & NOPOWER) set_light_on(FALSE) else - set_light_on(TRUE) + set_light_on(brightness_on) /obj/machinery/computer/screwdriver_act(mob/living/user, obj/item/I) if(..()) return TRUE if(circuit && !(flags_1&NODECONSTRUCT_1)) to_chat(user, span_notice("You start to disconnect the monitor...")) - if(I.use_tool(src, user, time_to_scewdrive, volume=50)) + if(I.use_tool(src, user, time_to_unscrew, volume=50)) deconstruct(TRUE, user) return TRUE @@ -173,3 +184,9 @@ . = ..() if(istype(mover) && (mover.pass_flags & PASSCOMPUTER)) return TRUE + + +/obj/machinery/computer/ui_interact(mob/user, datum/tgui/ui) + //SHOULD_CALL_PARENT(TRUE) + . = ..() + // update_use_power(ACTIVE_POWER_USE) diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm index 43f22a4e2754..ae5c81f9f8ba 100644 --- a/code/game/machinery/computer/camera.dm +++ b/code/game/machinery/computer/camera.dm @@ -1,64 +1,78 @@ +#define DEFAULT_MAP_SIZE 15 + /obj/machinery/computer/security name = "security camera console" desc = "Used to access the various cameras on the station." icon_screen = "cameras" icon_keyboard = "security_key" circuit = /obj/item/circuitboard/computer/security - light_color = LIGHT_COLOR_RED + light_color = COLOR_SOFT_RED var/list/network = list("ss13") + var/obj/machinery/camera/active_camera + /// The turf where the camera was last updated. + var/turf/last_camera_turf var/list/concurrent_users = list() // Stuff needed to render the map - var/map_name - var/const/default_map_size = 15 - var/atom/movable/screen/cam_screen + var/atom/movable/screen/map_view/cam_screen /// All the plane masters that need to be applied. - var/list/cam_plane_masters var/atom/movable/screen/background/cam_background - var/obj/machinery/camera/active_camera + var/datum/plane_master_group/popup/pop_planes + + var/list/cam_plane_masters - var/processing = FALSE + var/size_x = 0 + var/size_y = 0 + + interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON|INTERACT_MACHINE_REQUIRES_SIGHT /obj/machinery/computer/security/Initialize(mapload) . = ..() // Map name has to start and end with an A-Z character, // and definitely NOT with a square bracket or even a number. // I wasted 6 hours on this. :agony: - map_name = "camera_console_[REF(src)]_map" + var/map_name = "camera_console_[REF(src)]_map" // Convert networks to lowercase for(var/i in network) network -= i network += lowertext(i) // Initialize map objects cam_screen = new - cam_screen.name = "screen" - cam_screen.assigned_map = map_name - cam_screen.del_on_map_removal = FALSE - cam_screen.screen_loc = "[map_name]:1,1" + cam_screen.generate_view(map_name) + pop_planes = cam_screen.pop_planes cam_plane_masters = list() - for(var/plane in subtypesof(/atom/movable/screen/plane_master)) - var/atom/movable/screen/instance = new plane() - instance.assigned_map = map_name - instance.del_on_map_removal = FALSE - instance.screen_loc = "[map_name]:CENTER" - cam_plane_masters += instance + //subtypesof(/atom/movable/screen/plane_master) - /atom/movable/screen/plane_master/rendering_plate + // for(var/plane in subtypesof(/atom/movable/screen/plane_master) - /atom/movable/screen/plane_master/rendering_plate) + // var/atom/movable/screen/plane_master/instance = new plane() + // instance.assigned_map = map_name + // instance.del_on_map_removal = FALSE + // instance.screen_loc = "[map_name]:CENTER" + // cam_plane_masters += instance + cam_background = new cam_background.assigned_map = map_name cam_background.del_on_map_removal = FALSE + + /obj/machinery/computer/security/Destroy() - qdel(cam_screen) + QDEL_NULL(cam_screen) QDEL_LIST(cam_plane_masters) - qdel(cam_background) + QDEL_NULL(cam_background) return ..() /obj/machinery/computer/security/ui_interact(mob/user, datum/tgui/ui) + . = ..() + if(!user.canUseTopic(src, no_dextery = TRUE)) //prevents monkeys from using camera consoles + return + // Update UI ui = SStgui.try_update_ui(user, src, ui) - // Show static if can't use the camera - if(!active_camera?.can_use()) - show_camera_static() + + // Update the camera, showing static if necessary and updating data if the location has moved. + update_active_camera_screen() + if(!ui) var/user_ref = REF(user) var/is_living = isliving(user) @@ -71,137 +85,118 @@ playsound(src, 'sound/machines/terminal_on.ogg', 25, FALSE) use_power(active_power_usage) // Register map objects - user.client.register_map_obj(cam_screen) - for(var/plane in cam_plane_masters) - user.client.register_map_obj(plane) + cam_screen.display_to(user) + // user.client.register_map_obj(cam_screen) + // for(var/atom/movable/screen/plane_master/instance in cam_plane_masters) + // instance.show_to(user) user.client.register_map_obj(cam_background) // Open UI ui = new(user, src, "CameraConsole", name) ui.open() +/obj/machinery/computer/security/ui_status(mob/user) + . = ..() + if(. == UI_DISABLED) + return UI_CLOSE + return . + /obj/machinery/computer/security/ui_data() var/list/data = list() - data["network"] = network data["activeCamera"] = null if(active_camera) data["activeCamera"] = list( name = active_camera.c_tag, + ref = REF(active_camera), status = active_camera.status, ) return data /obj/machinery/computer/security/ui_static_data() var/list/data = list() - data["mapRef"] = map_name - var/list/cameras = get_available_cameras() + data["network"] = network + data["mapRef"] = cam_screen.assigned_map + var/list/cameras = get_camera_list(network) data["cameras"] = list() for(var/i in cameras) var/obj/machinery/camera/C = cameras[i] data["cameras"] += list(list( name = C.c_tag, + ref = REF(C), )) - return data -/obj/machinery/computer/security/process() - if(active_camera && active_camera.built_in) - if(!active_camera?.can_use()) - show_camera_static() - return TRUE - update_camera(active_camera) - else - STOP_PROCESSING(SSfastprocess, src) - processing = FALSE - ..() + return data /obj/machinery/computer/security/ui_act(action, params) . = ..() if(.) return - + if(action == "switch_camera") - var/c_tag = params["name"] - var/list/cameras = get_available_cameras() - var/obj/machinery/camera/C = cameras[c_tag] - active_camera = C + var/obj/machinery/camera/selected_camera = locate(params["camera"]) in GLOB.cameranet.cameras + active_camera = selected_camera playsound(src, get_sfx("terminal_type"), 25, FALSE) - // Show static if can't use the camera - if(!active_camera?.can_use()) - show_camera_static() + if(isnull(active_camera)) return TRUE - - //Assume it's a moving camera. - if(C.built_in) - if(!processing) - START_PROCESSING(SSfastprocess, src) - processing = TRUE - else - STOP_PROCESSING(SSfastprocess, src) - processing = FALSE - - update_camera(C) + update_active_camera_screen() return TRUE -/obj/machinery/computer/security/proc/update_camera(obj/machinery/camera/C) - var/originator = C - if(C.built_in) - originator = get_turf(C.built_in) +/obj/machinery/computer/security/proc/update_active_camera_screen() + // Show static if can't use the camera + if(!active_camera?.can_use()) + show_camera_static() + return var/list/visible_turfs = list() - for(var/turf/T in (C.isXRay() \ - ? range(C.view_range, originator) \ - : view(C.view_range, originator))) - visible_turfs += T + // Get the camera's turf to correctly gather what's visible from it's turf, in case it's located in a moving object (borgs / mechs) + var/new_cam_turf = get_turf(active_camera) + + // If we're not forcing an update for some reason and the cameras are in the same location, + // we don't need to update anything. + // Most security cameras will end here as they're not moving. + if(last_camera_turf == new_cam_turf) + return + + // Cameras that get here are moving, and are likely attached to some moving atom such as cyborgs. + last_camera_turf = new_cam_turf + + //Here we gather what's visible from the camera's POV based on its view_range and xray modifier if present + var/list/visible_things = active_camera.isXRay(ignore_malf_upgrades = TRUE) ? range(active_camera.view_range, new_cam_turf) : view(active_camera.view_range, new_cam_turf) + + for(var/turf/visible_turf in visible_things) + visible_turfs += visible_turf + + //Get coordinates for a rectangle area that contains the turfs we see so we can then clear away the static in the resulting rectangle area var/list/bbox = get_bbox_of_atoms(visible_turfs) - var/size_x = bbox[3] - bbox[1] + 1 - var/size_y = bbox[4] - bbox[2] + 1 + size_x = bbox[3] - bbox[1] + 1 + size_y = bbox[4] - bbox[2] + 1 cam_screen.vis_contents = visible_turfs cam_background.icon_state = "clear" cam_background.fill_rect(1, 1, size_x, size_y) /obj/machinery/computer/security/ui_close(mob/user) + . = ..() var/user_ref = REF(user) var/is_living = isliving(user) // Living creature or not, we remove you anyway. concurrent_users -= user_ref // Unregister map objects - user.client.clear_map(map_name) + cam_screen.hide_from(user) // Turn off the console if(length(concurrent_users) == 0 && is_living) active_camera = null + last_camera_turf = null playsound(src, 'sound/machines/terminal_off.ogg', 25, FALSE) use_power(0) /obj/machinery/computer/security/proc/show_camera_static() cam_screen.vis_contents.Cut() cam_background.icon_state = "scanline2" - cam_background.fill_rect(1, 1, default_map_size, default_map_size) - -//returns the list of cameras accessible from this computer -/obj/machinery/computer/security/proc/get_available_cameras() - var/list/L = list() - for (var/obj/machinery/camera/C in GLOB.cameranet.cameras) - if((is_away_level(z) || is_away_level(C.z)) && (C.z != z))//if on away mission, can only receive feed from same z_level cameras - continue - L.Add(C) - - var/list/D = list() - - for(var/obj/machinery/camera/C in L) - if(!C.network) - stack_trace("Camera in a cameranet has no camera network") - continue - if(!(islist(C.network))) - stack_trace("Camera in a cameranet has a non-list camera network") - continue - var/list/tempnetwork = C.network & network - if(tempnetwork.len) - D["[C.c_tag]"] = C - return D + cam_background.fill_rect(1, 1, DEFAULT_MAP_SIZE, DEFAULT_MAP_SIZE) // SECURITY MONITORS @@ -242,12 +237,11 @@ /obj/machinery/computer/security/qm name = "\improper Quartermaster's camera console" - desc = "A console with access to the mining, auxillary base and vault camera networks." + desc = "A console with access to the mining, auxiliary base and vault camera networks." network = list("mine", "auxbase", "vault") circuit = /obj/item/circuitboard/computer/security/qm // TELESCREENS - /obj/machinery/computer/security/telescreen name = "\improper Telescreen" desc = "Used for watching an empty arena." @@ -259,13 +253,11 @@ circuit = null clockwork = TRUE //it'd look very weird light_power = 0 - /obj/machinery/computer/security/telescreen/update_icon_state() . = ..() icon_state = initial(icon_state) if(stat & BROKEN) icon_state += "b" - /obj/machinery/computer/security/telescreen/entertainment name = "entertainment monitor" desc = "Damn, they better have the Yogs Channel on these things... Nope, just the /tg/ channel! Aww..." @@ -361,3 +353,5 @@ name = "\improper AI upload monitor" desc = "A telescreen that connects to the AI upload's camera network." network = list("aiupload") + +#undef DEFAULT_MAP_SIZE diff --git a/code/game/machinery/computer/camera_advanced.dm b/code/game/machinery/computer/camera_advanced.dm index 3788a9b0e816..0ddf5f2005c8 100644 --- a/code/game/machinery/computer/camera_advanced.dm +++ b/code/game/machinery/computer/camera_advanced.dm @@ -174,7 +174,6 @@ /mob/camera/aiEye/remote/update_remote_sight(mob/living/user) user.see_invisible = SEE_INVISIBLE_LIVING //can't see ghosts through cameras user.sight = SEE_TURFS | SEE_BLACKNESS - user.see_in_dark = 2 return TRUE /mob/camera/aiEye/remote/Destroy() diff --git a/code/game/machinery/computer/law.dm b/code/game/machinery/computer/law.dm index 1d94d7aaa442..c2457531d61b 100644 --- a/code/game/machinery/computer/law.dm +++ b/code/game/machinery/computer/law.dm @@ -5,7 +5,7 @@ icon_screen = "command" var/obj/item/gps/internal/ai_upload/embedded_gps var/obj/item/gps/internal/ai_upload/embedded_gps_type = /obj/item/gps/internal/ai_upload - time_to_scewdrive = 60 + time_to_unscrew = 6 SECONDS /obj/item/gps/internal/ai_upload icon_state = null diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index ec8b87cbeae2..f7720655fa93 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -454,7 +454,7 @@ GLOBAL_VAR_INIT(cryopods_enabled, FALSE) if(prob(90)) sleepyhead.adjust_drowsiness(rand(3 SECONDS, 10 SECONDS)) if(prob(75)) - sleepyhead.blur_eyes(rand(3, 6)) + sleepyhead.adjust_eye_blur(rand(3, 6)) //so much worse if(prob(66)) sleepyhead.adjust_disgust(rand(25,35)) diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index fcb931fa2f4a..345c410881b4 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -70,7 +70,8 @@ ) interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON | INTERACT_MACHINE_REQUIRES_SILICON | INTERACT_MACHINE_OPEN - + blocks_emissive = EMISSIVE_BLOCK_NONE + /// How much are wires secured var/security_level = 0 /// If 1, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in. diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 4d298a074a71..171f0c510fc7 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -15,6 +15,7 @@ damage_deflection = 10 interaction_flags_atom = INTERACT_ATOM_UI_INTERACT + blocks_emissive = EMISSIVE_BLOCK_UNIQUE /// TRUE means density will be set as soon as the door begins to close var/air_tight = FALSE diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm index 99b5a4987ec9..d946d60f8ffc 100644 --- a/code/game/machinery/firealarm.dm +++ b/code/game/machinery/firealarm.dm @@ -26,9 +26,11 @@ power_channel = AREA_USAGE_ENVIRON resistance_flags = FIRE_PROOF - light_power = 0 - light_range = 7 + light_power = 1 + light_range = 1.6 light_color = "#ff3232" + //Trick to get the glowing overlay visible from a distance + luminosity = 1 /// 1 = will auto detect fire, 0 = no auto var/detecting = 1 @@ -54,7 +56,7 @@ panel_open = TRUE pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24) pixel_y = (dir & 3)? (dir ==1 ? -24 : 24) : 0 - update_appearance(UPDATE_ICON) + update_appearance() myarea = get_area(src) LAZYADD(myarea.firealarms, src) radio = new(src) @@ -82,36 +84,41 @@ /obj/machinery/firealarm/update_overlays() . = ..() - SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays) if(stat & (NOPOWER|BROKEN)) return - . += "fire_overlay" - if(is_station_level(z)) - . += "fire_[GLOB.security_level]" - SSvis_overlays.add_vis_overlay(src, icon, "fire_[GLOB.security_level]", layer, plane, dir) - SSvis_overlays.add_vis_overlay(src, icon, "fire_[GLOB.security_level]", layer, EMISSIVE_PLANE, dir) + var/current_level = GLOB.security_level + . += mutable_appearance(icon, "fire_[GLOB.security_level]") + . += emissive_appearance(icon, "fire_[GLOB.security_level]", src) + switch(current_level) + if(SEC_LEVEL_GREEN) + set_light(l_color = LIGHT_COLOR_BLUEGREEN) + if(SEC_LEVEL_BLUE) + set_light(l_color = LIGHT_COLOR_ELECTRIC_CYAN) + if(SEC_LEVEL_RED) + set_light(l_color = LIGHT_COLOR_FLARE) + if(SEC_LEVEL_DELTA) + set_light(l_color = LIGHT_COLOR_INTENSE_RED) else - . += "fire_[SEC_LEVEL_GREEN]" - SSvis_overlays.add_vis_overlay(src, icon, "fire_[SEC_LEVEL_GREEN]", layer, plane, dir) - SSvis_overlays.add_vis_overlay(src, icon, "fire_[SEC_LEVEL_GREEN]", layer, EMISSIVE_PLANE, dir) + . += mutable_appearance(icon, "fire_[SEC_LEVEL_GREEN]") + . += emissive_appearance(icon, "fire_[SEC_LEVEL_GREEN]", src) + set_light(l_color = LIGHT_COLOR_BLUE) var/area/A = src.loc A = A.loc if(!A.fire && !A.delta_light) - . += "fire_off" - SSvis_overlays.add_vis_overlay(src, icon, "fire_off", layer, plane, dir) - SSvis_overlays.add_vis_overlay(src, icon, "fire_off", layer, EMISSIVE_PLANE, dir) + . += mutable_appearance(icon, "fire_off") + . += emissive_appearance(icon, "fire_off", src) else if(obj_flags & EMAGGED) - . += "fire_emagged" - SSvis_overlays.add_vis_overlay(src, icon, "fire_emagged", layer, plane, dir) - SSvis_overlays.add_vis_overlay(src, icon, "fire_emagged", layer, EMISSIVE_PLANE, dir) + . += mutable_appearance(icon, "fire_emagged") + . += emissive_appearance(icon, "fire_emagged", src) + set_light(l_color = LIGHT_COLOR_RED) else - . += "fire_on" - SSvis_overlays.add_vis_overlay(src, icon, "fire_on", layer, plane, dir) - SSvis_overlays.add_vis_overlay(src, icon, "fire_on", layer, EMISSIVE_PLANE, dir) + . += mutable_appearance(icon, "fire_on") + . += emissive_appearance(icon, "fire_on", src) + set_light(l_color = LIGHT_COLOR_RED) /obj/machinery/firealarm/emp_act(severity) . = ..() @@ -126,7 +133,7 @@ if(obj_flags & EMAGGED) return FALSE obj_flags |= EMAGGED - update_appearance(UPDATE_ICON) + update_appearance() if(user) user.visible_message(span_warning("Sparks fly out of [src]!"), span_notice("You emag [src], disabling its thermal sensors.")) @@ -205,7 +212,7 @@ W.play_tool_sound(src) panel_open = !panel_open to_chat(user, span_notice("The wires have been [panel_open ? "exposed" : "unexposed"].")) - update_appearance(UPDATE_ICON) + update_appearance() return if(panel_open) @@ -238,7 +245,7 @@ W.play_tool_sound(src) new /obj/item/stack/cable_coil(user.loc, 5) to_chat(user, span_notice("You cut the wires from \the [src].")) - update_appearance(UPDATE_ICON) + update_appearance() return else if(W.force) //hit and turn it on @@ -256,7 +263,7 @@ coil.use(5) buildstage = 2 to_chat(user, span_notice("You wire \the [src].")) - update_appearance(UPDATE_ICON) + update_appearance() return else if(W.tool_behaviour == TOOL_CROWBAR) @@ -271,14 +278,14 @@ to_chat(user, span_notice("You pry out the circuit.")) new /obj/item/electronics/firealarm(user.loc) buildstage = 0 - update_appearance(UPDATE_ICON) + update_appearance() return if(0) if(istype(W, /obj/item/electronics/firealarm)) to_chat(user, span_notice("You insert the circuit.")) qdel(W) buildstage = 1 - update_appearance(UPDATE_ICON) + update_appearance() return else if(istype(W, /obj/item/electroadaptive_pseudocircuit)) @@ -288,7 +295,7 @@ user.visible_message(span_notice("[user] fabricates a circuit and places it into [src]."), \ span_notice("You adapt a fire alarm circuit and slot it into the assembly.")) buildstage = 1 - update_appearance(UPDATE_ICON) + update_appearance() return else if(W.tool_behaviour == TOOL_WRENCH) @@ -314,7 +321,7 @@ user.visible_message(span_notice("[user] fabricates a circuit and places it into [src]."), \ span_notice("You adapt a fire alarm circuit and slot it into the assembly.")) buildstage = 1 - update_appearance(UPDATE_ICON) + update_appearance() return TRUE return FALSE @@ -353,11 +360,11 @@ if(fire == !!light_power) return // do nothing if we're already active if(fire) - set_light(l_power = 0.8) - update_appearance(UPDATE_ICON) + set_light(l_power = 3) + update_appearance() else - set_light(l_power = 0) - update_appearance(UPDATE_ICON) + set_light(l_power = 1) + update_appearance() /* * Return of the Return of the Party button diff --git a/code/game/machinery/lightswitch.dm b/code/game/machinery/lightswitch.dm index 2c93e617313a..3830ecb17ec3 100644 --- a/code/game/machinery/lightswitch.dm +++ b/code/game/machinery/lightswitch.dm @@ -62,8 +62,8 @@ desc = "Make dark." power_channel = AREA_USAGE_LIGHT - light_power = 0 - light_range = 7 + ///Range of the light emitted when powered, but off + var/light_on_range = 1 /// Set this to a string, path, or area instance to control that area /// instead of the switch's location. @@ -86,7 +86,7 @@ if(mapload) construction_state = LIGHT_CONSTRUCTED - update_appearance(UPDATE_ICON) + update_appearance() if(mapload) return INITIALIZE_HINT_LATELOAD return INITIALIZE_HINT_NORMAL @@ -99,25 +99,39 @@ return turn_off() +/obj/machinery/light_switch/update_appearance(updates=ALL) + . = ..() + luminosity = (stat & NOPOWER) ? 0 : 1 + +/obj/machinery/light_switch/update_icon_state() + set_light(area.lightswitch ? 0 : light_on_range) + switch(construction_state) + if(LIGHT_BARE) + icon_state = "light-b" + if(LIGHT_WIRE) + icon_state = "light-w" + if(LIGHT_CONSTRUCTED) + icon_state = "light-c" + + return ..() + /obj/machinery/light_switch/update_overlays() . = ..() if(stat & (NOPOWER|BROKEN)) return if(construction_state != LIGHT_CONSTRUCTED) return - if(area.lightswitch) - . += "light1" - else - . += "light0" + . += mutable_appearance(icon, "[area.lightswitch ? "light1" : "light0"]") + . += emissive_appearance(icon, "[area.lightswitch ? "light1" : "light0"]", src) /obj/machinery/light_switch/proc/turn_off() if(!area.lightswitch) return area.lightswitch = FALSE - area.update_icon() + area.update_appearance() for(var/obj/machinery/light_switch/L in area) - L.update_icon() + L.update_appearance() area.power_change() @@ -125,10 +139,10 @@ if(area.lightswitch) return area.lightswitch = TRUE - area.update_icon() + area.update_appearance() for(var/obj/machinery/light_switch/L in area) - L.update_icon() + L.update_appearance() area.power_change() @@ -146,11 +160,10 @@ . = ..() area.lightswitch = !area.lightswitch - area.update_appearance(UPDATE_ICON) play_click_sound("button") for(var/obj/machinery/light_switch/L in area) - L.update_appearance(UPDATE_ICON) + L.update_appearance() area.power_change() @@ -161,7 +174,7 @@ if(istype(c) && c.use(1)) to_chat(user, span_notice("You insert wiring into [src].")) construction_state = LIGHT_WIRE - update_appearance(UPDATE_ICON) + update_appearance() return if(W.tool_behaviour == TOOL_WRENCH) W.play_tool_sound(src) @@ -174,7 +187,7 @@ to_chat(user, span_notice("You cut the wires out.")) new /obj/item/stack/cable_coil(loc, 1) construction_state = LIGHT_BARE - update_appearance(UPDATE_ICON) + update_appearance() return if(panel_open && is_wire_tool(W)) wires.interact(user) @@ -184,7 +197,7 @@ to_chat(user, span_notice("You screw the cover on.")) panel_open = FALSE construction_state = LIGHT_CONSTRUCTED - update_appearance(UPDATE_ICON) + update_appearance() return if(LIGHT_CONSTRUCTED) if(W.tool_behaviour == TOOL_SCREWDRIVER) @@ -192,21 +205,10 @@ to_chat(user, span_notice("You take the cover off.")) panel_open = TRUE construction_state = LIGHT_WIRE - update_appearance(UPDATE_ICON) + update_appearance() return return ..() -/obj/machinery/light_switch/update_icon_state() - . = ..() - switch(construction_state) - if(LIGHT_BARE) - icon_state = "light-b" - if(LIGHT_WIRE) - icon_state = "light-w" - if(LIGHT_CONSTRUCTED) - icon_state = "light-c" - - /obj/machinery/light_switch/power_change() if(area == get_area(src)) return ..() diff --git a/code/game/machinery/mindmachine.dm b/code/game/machinery/mindmachine.dm index 80ccfd1f7ba6..b1b578674af1 100644 --- a/code/game/machinery/mindmachine.dm +++ b/code/game/machinery/mindmachine.dm @@ -105,7 +105,7 @@ if(stat & BROKEN) overlay_state = "[icon_state]_broken" . += mutable_appearance(icon, overlay_state) - . += mutable_appearance(icon, overlay_state, layer, EMISSIVE_PLANE) + . += emissive_appearance(icon, icon_screen, src) /obj/machinery/mindmachine_hub/RefreshParts() // 2 matter bins. Reduce failure chance by 5 per tier. Results in 30 (tier 1) to 0 (tier 4). diff --git a/code/game/machinery/navbeacon.dm b/code/game/machinery/navbeacon.dm index 6e14252d83cd..07fc5c3a3a63 100644 --- a/code/game/machinery/navbeacon.dm +++ b/code/game/machinery/navbeacon.dm @@ -8,7 +8,7 @@ name = "navigation beacon" desc = "A radio beacon used for bot navigation." level = 1 // underfloor - layer = UNDER_CATWALK + layer = LOW_OBJ_LAYER max_integrity = 500 armor = list(MELEE = 70, BULLET = 70, LASER = 70, ENERGY = 70, BOMB = 0, BIO = 0, RAD = 0, FIRE = 80, ACID = 80) diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm index 75e316392d43..466411473c78 100644 --- a/code/game/machinery/porta_turret/portable_turret.dm +++ b/code/game/machinery/porta_turret/portable_turret.dm @@ -9,6 +9,7 @@ icon = 'icons/obj/turrets.dmi' icon_state = "turretCover" base_icon_state = "standard" + blocks_emissive = EMISSIVE_BLOCK_UNIQUE layer = OBJ_LAYER invisibility = INVISIBILITY_OBSERVER //the turret is invisible if it's inside its cover density = TRUE diff --git a/code/game/machinery/slotmachine.dm b/code/game/machinery/slotmachine.dm index 8ff7540611dd..96207b5acde1 100644 --- a/code/game/machinery/slotmachine.dm +++ b/code/game/machinery/slotmachine.dm @@ -289,7 +289,7 @@ if(did_player_win) add_filter("jackpot_rays", 3, ray_filter) animate(get_filter("jackpot_rays"), offset = 10, time = 3 SECONDS, loop = -1) - addtimer(CALLBACK(src, TYPE_PROC_REF(/atom/movable, remove_filter), "jackpot_rays"), 3 SECONDS) + addtimer(CALLBACK(src, TYPE_PROC_REF(/datum, remove_filter), "jackpot_rays"), 3 SECONDS) playsound(src, 'sound/machines/roulettejackpot.ogg', 50, TRUE) /obj/machinery/computer/slot_machine/proc/get_lines() diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 6b552efaee81..13e965436a3a 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -92,6 +92,9 @@ var/silicon_pilot = FALSE //set to true if an AI or MMI is piloting. + ///Camera installed into the mech + var/obj/machinery/camera/exosuit/chassis_camera + var/enter_delay = 40 //Time taken to enter the mech var/exit_delay = 20 //Time to exit mech var/destruction_sleep_duration = 20 //Time that mech pilot is put to sleep for if mech is destroyed diff --git a/code/game/objects/effects/countdown.dm b/code/game/objects/effects/countdown.dm index 7f5933b4c125..f9a73eba6e5d 100644 --- a/code/game/objects/effects/countdown.dm +++ b/code/game/objects/effects/countdown.dm @@ -7,7 +7,7 @@ invisibility = INVISIBILITY_OBSERVER anchored = TRUE - layer = GHOST_LAYER + plane = GHOST_PLANE color = "#ff0000" // text color var/text_size = 3 // larger values clip when the displayed text is larger than 2 digits. var/started = FALSE @@ -107,7 +107,7 @@ name = "gateway countdown" text_size = 1 color = "#BE8700" - layer = POINT_LAYER + plane = POINT_PLANE /obj/effect/countdown/clockworkgate/get_value() var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = attached_to diff --git a/code/game/objects/effects/effects.dm b/code/game/objects/effects/effects.dm index 04ed1950154f..338fa9dabf2e 100644 --- a/code/game/objects/effects/effects.dm +++ b/code/game/objects/effects/effects.dm @@ -6,6 +6,7 @@ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF move_resist = INFINITY obj_flags = 0 + blocks_emissive = EMISSIVE_BLOCK_GENERIC /obj/effect/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = TRUE, attack_dir, armour_penetration = 0) return diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm index 12efb829cb06..86c543b3a69d 100644 --- a/code/game/objects/effects/landmarks.dm +++ b/code/game/objects/effects/landmarks.dm @@ -3,7 +3,8 @@ icon = 'icons/effects/landmarks_static.dmi' icon_state = "x2" anchored = TRUE - layer = MID_LANDMARK_LAYER + layer = OBJ_LAYER + plane = GAME_PLANE invisibility = INVISIBILITY_ABSTRACT resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF @@ -415,7 +416,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/start/new_player) /obj/effect/landmark/event_spawn name = "generic event spawn" icon_state = "generic_event" - layer = HIGH_LANDMARK_LAYER + layer = OBJ_LAYER /obj/effect/landmark/event_spawn/New() ..() @@ -428,7 +429,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/start/new_player) /obj/effect/landmark/brazil name = "brazilian reception marker" icon_state = "x" - layer = HIGH_LANDMARK_LAYER + layer = OBJ_LAYER /obj/effect/landmark/brazil/New() ..() @@ -465,9 +466,9 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/start/new_player) /obj/effect/landmark/centcom name = "centcomspawn" icon_state = "x" - layer = HIGH_LANDMARK_LAYER + layer = OBJ_LAYER /obj/effect/landmark/wiki name = "wiki sprite room" icon_state = "x" - layer = HIGH_LANDMARK_LAYER + layer = OBJ_LAYER diff --git a/code/game/objects/effects/misc.dm b/code/game/objects/effects/misc.dm index c7de6a2597cc..0086228f3d94 100644 --- a/code/game/objects/effects/misc.dm +++ b/code/game/objects/effects/misc.dm @@ -48,7 +48,7 @@ icon = 'icons/effects/alphacolors.dmi' icon_state = "white" plane = LIGHTING_PLANE - layer = LIGHTING_LAYER + layer = LIGHTING_ABOVE_ALL blend_mode = BLEND_ADD /obj/effect/abstract/marker @@ -77,6 +77,7 @@ light_color = "#FFFFFF" light_system = MOVABLE_LIGHT light_range = MINIMUM_USEFUL_LIGHT_RANGE + blocks_emissive = EMISSIVE_BLOCK_NONE mouse_opacity = MOUSE_OPACITY_TRANSPARENT /obj/effect/dummy/lighting_obj/Initialize(mapload, range, power, color, duration) diff --git a/code/game/objects/effects/overlays.dm b/code/game/objects/effects/overlays.dm index dfd44008cdbb..bff32277a9e5 100644 --- a/code/game/objects/effects/overlays.dm +++ b/code/game/objects/effects/overlays.dm @@ -82,7 +82,6 @@ name = "" icon = 'icons/effects/light_overlays/light_32.dmi' icon_state = "light" - layer = O_LIGHTING_VISUAL_LAYER plane = O_LIGHTING_VISUAL_PLANE appearance_flags = RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM mouse_opacity = MOUSE_OPACITY_TRANSPARENT diff --git a/code/game/objects/effects/temporary_visuals/clockcult.dm b/code/game/objects/effects/temporary_visuals/clockcult.dm index 16d10fd03483..dc7e982f8698 100644 --- a/code/game/objects/effects/temporary_visuals/clockcult.dm +++ b/code/game/objects/effects/temporary_visuals/clockcult.dm @@ -262,7 +262,7 @@ icon_state = "eminence" mouse_opacity = MOUSE_OPACITY_TRANSPARENT resistance_flags = INDESTRUCTIBLE - layer = MASSIVE_OBJ_LAYER + plane = MASSIVE_OBJ_PLANE duration = 30 SECONDS /obj/effect/temp_visual/ratvar/command_point/Initialize(mapload, appearance) diff --git a/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm b/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm index 5dc526f1436e..495af7510ec4 100644 --- a/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm +++ b/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm @@ -61,7 +61,6 @@ /obj/effect/projectile/tracer/tracer/aiming icon_state = "pixelbeam_greyscale" - layer = ABOVE_LIGHTING_LAYER plane = ABOVE_LIGHTING_PLANE /obj/effect/projectile/tracer/wormhole diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index ba92ac39eac9..bf62ac9ce872 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -641,10 +641,10 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) var/obj/item/organ/eyes/eyes = M.getorganslot(ORGAN_SLOT_EYES) if (!eyes) return - M.adjust_blurriness(3) + M.adjust_eye_blur(3) eyes.applyOrganDamage(rand(2,4)) if(eyes.damage >= 10) - M.adjust_blurriness(15) + M.adjust_eye_blur(15) if(M.stat != DEAD) to_chat(M, span_danger("Your eyes start to bleed profusely!")) if(!(HAS_TRAIT(M, TRAIT_BLIND) || HAS_TRAIT(M, TRAIT_NEARSIGHT))) @@ -654,7 +654,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) if(M.stat != DEAD) if(M.drop_all_held_items()) to_chat(M, span_danger("You drop what you're holding and clutch at your eyes!")) - M.adjust_blurriness(10) + M.adjust_eye_blur(10) M.Unconscious(20) M.Paralyze(40) if (prob(eyes.damage - 10 + 1)) @@ -1141,3 +1141,14 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) if(item_heal_robotic(H, user, brute_heal, burn_heal)) return heal_robo_limb(I, H, user, brute_heal, burn_heal, amount, volume) return TRUE + +/** + * Updates all action buttons associated with this item + * + * Arguments: + * * update_flags - Which flags of the action should we update + * * force - Force buttons update even if the given button icon state has not changed + */ +/obj/item/proc/update_item_action_buttons(update_flags = ALL, force = FALSE) + for(var/datum/action/current_action as anything in actions) + current_action.build_all_button_icons(update_flags, force) diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index 13c7c7a5fee3..ea46fd2a4d7c 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -672,7 +672,7 @@ to_chat(target, span_userdanger("[user] sprays [src] into your face!")) if(C.client) - C.blur_eyes(3) + C.adjust_eye_blur(3) C.blind_eyes(1) if(C.get_eye_protection() <= 0) // no eye protection? ARGH IT BURNS. C.adjust_confusion(3) diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm index c7baf90124e5..80b7b4f843a4 100644 --- a/code/game/objects/items/robot/robot_items.dm +++ b/code/game/objects/items/robot/robot_items.dm @@ -754,7 +754,7 @@ name = "\proper night vision meson vision" icon = 'icons/obj/clothing/glasses.dmi' icon_state = "nvgmeson" - sight_mode = BORGMESON_NIGHTVISION + sight_mode = BORGMESON /obj/item/borg/sight/material name = "\proper material vision" diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index 9dd4ff9c89a1..0366d3874365 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -4,6 +4,7 @@ max_integrity = 300 interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT layer = BELOW_OBJ_LAYER + blocks_emissive = EMISSIVE_BLOCK_GENERIC var/broken = 0 //similar to machinery's stat BROKEN diff --git a/code/game/objects/structures/barsigns.dm b/code/game/objects/structures/barsigns.dm deleted file mode 100644 index 36e45204be9f..000000000000 --- a/code/game/objects/structures/barsigns.dm +++ /dev/null @@ -1,363 +0,0 @@ -/obj/structure/sign/barsign // All Signs are 64 by 32 pixels, they take two tiles - name = "bar sign" - desc = "A bar sign which has not been initialized, somehow. Complain at a coder!" - icon = 'icons/obj/barsigns.dmi' - icon_state = "empty" - req_access = list(ACCESS_BAR) - max_integrity = 500 - integrity_failure = 250 - armor = list(MELEE = 20, BULLET = 20, LASER = 20, ENERGY = 100, BOMB = 0, BIO = 0, RAD = 0, FIRE = 50, ACID = 50) - buildable_sign = 0 - - var/panel_open = FALSE - var/datum/barsign/chosen_sign - -/obj/structure/sign/barsign/Initialize(mapload) - . = ..() - set_sign(new /datum/barsign/hiddensigns/signoff) - -/obj/structure/sign/barsign/proc/set_sign(datum/barsign/sign) - if(!istype(sign)) - return - - icon_state = sign.icon - - if(sign.name) - name = "[initial(name)] ([sign.name])" - else - name = "[initial(name)]" - - if(sign.desc) - desc = sign.desc - - if(sign.rename_area && sign.name) - rename_area(src, sign.name) - - return sign - -/obj/structure/sign/barsign/proc/set_sign_by_name(sign_name) - for(var/d in subtypesof(/datum/barsign)) - var/datum/barsign/D = d - if(initial(D.name) == sign_name) - var/new_sign = new D - return set_sign(new_sign) - -/obj/structure/sign/barsign/obj_break(damage_flag) - if(!broken && !(flags_1 & NODECONSTRUCT_1)) - broken = TRUE - -/obj/structure/sign/barsign/deconstruct(disassembled = TRUE) - new /obj/item/stack/sheet/metal(drop_location(), 2) - new /obj/item/stack/cable_coil(drop_location(), 2) - qdel(src) - -/obj/structure/sign/barsign/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - playsound(src.loc, 'sound/effects/glasshit.ogg', 75, 1) - if(BURN) - playsound(src.loc, 'sound/items/welder.ogg', 100, 1) - -/obj/structure/sign/barsign/attack_ai(mob/user) - return attack_hand(user) - -/obj/structure/sign/barsign/attack_hand(mob/user) - . = ..() - if(.) - return - if(!allowed(user)) - to_chat(user, span_info("Access denied.")) - return - if(broken) - to_chat(user, "The controls seem unresponsive.") - return - pick_sign(user) - -/obj/structure/sign/barsign/attackby(obj/item/I, mob/user) - if(I.tool_behaviour == TOOL_SCREWDRIVER) - if(!panel_open) - to_chat(user, span_notice("You open the maintenance panel.")) - set_sign(new /datum/barsign/hiddensigns/signoff) - panel_open = TRUE - else - to_chat(user, span_notice("You close the maintenance panel.")) - if(!broken) - if(!chosen_sign) - set_sign(new /datum/barsign/hiddensigns/signoff) - else - set_sign(chosen_sign) - else - set_sign(new /datum/barsign/hiddensigns/empbarsign) - panel_open = FALSE - - else if(istype(I, /obj/item/stack/cable_coil) && panel_open) - var/obj/item/stack/cable_coil/C = I - if(!broken) - to_chat(user, span_warning("This sign is functioning properly!")) - return - - if(C.use(2)) - to_chat(user, span_notice("You replace the burnt wiring.")) - broken = FALSE - else - to_chat(user, span_warning("You need at least two lengths of cable!")) - else - return ..() - - -/obj/structure/sign/barsign/emp_act(severity) - . = ..() - if(. & EMP_PROTECT_SELF) - return - set_sign(new /datum/barsign/hiddensigns/empbarsign) - broken = TRUE - -/obj/structure/sign/barsign/emag_act(mob/user, obj/item/card/emag/emag_card) - if(broken) - to_chat(user, span_warning("Nothing interesting happens!")) - return FALSE - to_chat(user, span_notice("You load an illegal barsign into the memory buffer...")) - addtimer(CALLBACK(src, PROC_REF(finish_emag_act)), 10 SECONDS) - return TRUE - -/obj/structure/sign/barsign/proc/finish_emag_act() - if(QDELETED(src)) - return - chosen_sign = set_sign(new /datum/barsign/hiddensigns/syndibarsign) - -/obj/structure/sign/barsign/proc/pick_sign(mob/user) - var/picked_name = input(user, "Available Signage", "Bar Sign", name) as null|anything in get_bar_names() - if(!picked_name) - return - chosen_sign = set_sign_by_name(picked_name) - SSblackbox.record_feedback("tally", "barsign_picked", 1, chosen_sign.type) - -/proc/get_bar_names() - var/list/names = list() - for(var/d in subtypesof(/datum/barsign)) - var/datum/barsign/D = d - if(initial(D.name) && !initial(D.hidden)) - names += initial(D.name) - . = names - -/datum/barsign - var/name = "Name" - var/icon = "Icon" - var/desc = "desc" - var/hidden = FALSE - var/rename_area = TRUE - -/datum/barsign/New() - if(!desc) - desc = "It displays \"[name]\"." - -// Specific bar signs. - -/datum/barsign/maltesefalcon - name = "Maltese Falcon" - icon = "maltesefalcon" - desc = "The Maltese Falcon, Space Bar and Grill." - -/datum/barsign/thebark - name = "The Bark" - icon = "thebark" - desc = "Ian's bar of choice." - -/datum/barsign/harmbaton - name = "The Harmbaton" - icon = "theharmbaton" - desc = "A great dining experience for both security members and assistants." - -/datum/barsign/thesingulo - name = "The Singulo" - icon = "thesingulo" - desc = "Where people go that'd rather not be called by their name." - -/datum/barsign/thedrunkcarp - name = "The Drunk Carp" - icon = "thedrunkcarp" - desc = "Don't drink and swim." - -/datum/barsign/scotchservinwill - name = "Scotch Servin Willy's" - icon = "scotchservinwill" - desc = "Willy sure moved up in the world from clown to bartender." - -/datum/barsign/officerbeersky - name = "Officer Beersky's" - icon = "officerbeersky" - desc = "Man eat a dong, these drinks are great." - -/datum/barsign/thecavern - name = "The Cavern" - icon = "thecavern" - desc = "Fine drinks while listening to some fine tunes." - -/datum/barsign/theouterspess - name = "The Outer Spess" - icon = "theouterspess" - desc = "This bar isn't actually located in outer space." - -/datum/barsign/slipperyshots - name = "Slippery Shots" - icon = "slipperyshots" - desc = "Slippery slope to drunkenness with our shots!" - -/datum/barsign/thegreytide - name = "The Grey Tide" - icon = "thegreytide" - desc = "Abandon your toolboxing ways and enjoy a lazy beer!" - -/datum/barsign/honkednloaded - name = "Honked 'n' Loaded" - icon = "honkednloaded" - desc = "Honk." - -/datum/barsign/thenest - name = "The Nest" - icon = "thenest" - desc = "A good place to retire for a drink after a long night of crime fighting." - -/datum/barsign/thecoderbus - name = "The Coderbus" - icon = "thecoderbus" - desc = "A very controversial bar known for its wide variety of constantly-changing drinks." - -/datum/barsign/theadminbus - name = "The Adminbus" - icon = "theadminbus" - desc = "An establishment visited mainly by space-judges. It isn't bombed nearly as much as court hearings." - -/datum/barsign/oldcockinn - name = "The Old Cock Inn" - icon = "oldcockinn" - desc = "Something about this sign fills you with despair." - -/datum/barsign/thewretchedhive - name = "The Wretched Hive" - icon = "thewretchedhive" - desc = "Legally obligated to instruct you to check your drinks for acid before consumption." - -/datum/barsign/robustacafe - name = "The Robusta Cafe" - icon = "robustacafe" - desc = "Holder of the 'Most Lethal Barfights' record 5 years uncontested." - -/datum/barsign/emergencyrumparty - name = "The Emergency Rum Party" - icon = "emergencyrumparty" - desc = "Recently relicensed after a long closure." - -/datum/barsign/combocafe - name = "The Combo Cafe" - icon = "combocafe" - desc = "Renowned system-wide for their utterly uncreative drink combinations." - -/datum/barsign/vladssaladbar - name = "Vlad's Salad Bar" - icon = "vladssaladbar" - desc = "Under new management. Vlad was always a bit too trigger happy with that shotgun." - -/datum/barsign/theshaken - name = "The Shaken" - icon = "theshaken" - desc = "This establishment does not serve stirred drinks." - -/datum/barsign/thealenath - name = "The Ale' Nath" - icon = "thealenath" - desc = "All right, buddy. I think you've had EI NATH. Time to get a cab." - -/datum/barsign/thealohasnackbar - name = "The Aloha Snackbar" - icon = "alohasnackbar" - desc = "A tasteful, inoffensive tiki bar sign." - -/datum/barsign/thenet - name = "The Net" - icon = "thenet" - desc = "You just seem to get caught up in it for hours." - -/datum/barsign/maidcafe - name = "Maid Cafe" - icon = "maidcafe" - desc = "Welcome back, master!" - -/datum/barsign/the_lightbulb - name = "The Lightbulb" - icon = "the_lightbulb" - desc = "A cafe popular among moths and moffs. Once shut down for a week after the bartender used mothballs to protect her spare uniforms." - -/datum/barsign/thegoose - name = "The Goose" - icon = "thegoose" - desc = "A nice place to hang loose and relax, while enjoying some electrifying drinks." - -/datum/barsign/tearoom - name = "Little Treats Tea Room" - icon = "little_treats" - desc = "A delightfully relaxing tearoom for all the fancy lads in the cosmos." - -/datum/barsign/le_cafe_silencieux - name = "Le Café Silencieux" - icon = "le_cafe_silencieux" - desc = "..." - -/datum/barsign/maltroach - name = "Maltroach" - icon = "maltroach" - desc = "Mothroaches politely greet you into the bar, or are they greeting eachother?" - -/datum/barsign/rock_bottom - name = "Rock Bottom" - icon = "rock-bottom" - desc = "When it feels like you're stuck in a pit, might as well have a drink." - -/datum/barsign/assembly_line - name = "The Assembly Line" - icon = "the-assembly-line" - desc = "Where every drink is masterfully crafted with industrial efficiency!" - -/datum/barsign/bargonia - name = "Bargonia" - icon = "bargonia" - desc = "The warehouse yearns for a higher calling... so Supply has declared BARGONIA!" - -/datum/barsign/cult_cove - name = "Cult Cove" - icon = "cult-cove" - desc = "Nar'Sie's favourite retreat" - -/datum/barsign/neon_flamingo - name = "Neon Flamingo" - icon = "neon-flamingo" - desc = "A bus for all but the flamboyantly challenged." - -/datum/barsign/slowdive - name = "Slowdive" - icon = "slowdive" - desc = "First stop out of hell, last stop before heaven." - -/datum/barsign/hiddensigns - hidden = TRUE - -//Hidden signs list below this point - - - -/datum/barsign/hiddensigns/empbarsign - name = null - icon = "empbarsign" - desc = "Something has gone very wrong." - rename_area = FALSE - -/datum/barsign/hiddensigns/syndibarsign - name = "Syndi Cat" - icon = "syndibarsign" - desc = "Syndicate or die." - -/datum/barsign/hiddensigns/signoff - name = null - icon = "empty" - desc = "This sign doesn't seem to be on." - rename_area = FALSE diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 66d75d7e3125..75136c9095e9 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -17,6 +17,7 @@ GLOBAL_LIST_EMPTY(lockers) max_integrity = 200 integrity_failure = 50 armor = list(MELEE = 20, BULLET = 10, LASER = 10, ENERGY = 0, BOMB = 10, BIO = 0, RAD = 0, FIRE = 70, ACID = 60) + blocks_emissive = EMISSIVE_BLOCK_GENERIC var/breakout_time = 1200 var/message_cooldown var/can_weld_shut = TRUE @@ -33,6 +34,7 @@ GLOBAL_LIST_EMPTY(lockers) var/delivery_icon = "deliverycloset" //which icon to use when packagewrapped. null to be unwrappable. var/anchorable = TRUE var/icon_welded = "welded" + var/icon_broken = "sparking" /// Protection against weather that being inside of it provides. var/list/weather_protection = null /// How close being inside of the thing provides complete pressure safety. Must be between 0 and 1! @@ -56,7 +58,7 @@ GLOBAL_LIST_EMPTY(lockers) if(mapload && !opened) // if closed, any item at the crate's loc is put in the contents . = INITIALIZE_HINT_LATELOAD - update_appearance(UPDATE_ICON) + update_appearance() PopulateContents() var/static/list/loc_connections = list( COMSIG_ATOM_MAGICALLY_UNLOCKED = PROC_REF(on_magic_unlock), @@ -84,8 +86,16 @@ GLOBAL_LIST_EMPTY(lockers) update_appearance() . = TRUE +/obj/structure/closet/update_appearance(updates=ALL) + . = ..() + if(opened || broken || !secure) + luminosity = 0 + return + luminosity = 1 + /obj/structure/closet/update_overlays() . = ..() + //SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays) if(opened) layer = BELOW_OBJ_LAYER if(is_animating_door) @@ -104,11 +114,17 @@ GLOBAL_LIST_EMPTY(lockers) . += "[icon_state]_door" if(welded) . += icon_welded - if(secure && !broken) - if(locked) - . += "locked" - else - . += "unlocked" + if(broken && secure) + . += mutable_appearance(icon, icon_broken, alpha = alpha) + . += emissive_appearance(icon, icon_broken, src, alpha = alpha) + return + if(broken || !secure) + return + //Overlay is similar enough for both that we can use the same mask for both + //luminosity = 1 + //SSvis_overlays.add_vis_overlay(src, icon, "locked", EMISSIVE_LAYER, EMISSIVE_PLANE, dir, alpha) + . += emissive_appearance(icon, "locked", src, alpha = src.alpha) + . += locked ? "locked" : "unlocked" /obj/structure/closet/proc/animate_door(closing = FALSE) if(!door_anim_time) @@ -138,7 +154,7 @@ GLOBAL_LIST_EMPTY(lockers) /obj/structure/closet/proc/end_door_animation() is_animating_door = FALSE vis_contents -= door_obj - update_appearance(UPDATE_ICON) + update_appearance() /obj/structure/closet/proc/get_door_transform(angle) var/matrix/M = matrix() @@ -217,7 +233,7 @@ GLOBAL_LIST_EMPTY(lockers) density = FALSE dump_contents() animate_door(FALSE) - update_appearance(UPDATE_ICON) + update_appearance() update_airtightness() return 1 @@ -270,7 +286,7 @@ GLOBAL_LIST_EMPTY(lockers) opened = FALSE density = TRUE animate_door(TRUE) - update_appearance(UPDATE_ICON) + update_appearance() update_airtightness() close_storage(user) return TRUE @@ -344,7 +360,7 @@ GLOBAL_LIST_EMPTY(lockers) user.visible_message(span_notice("[user] [welded ? "welds shut" : "unwelded"] \the [src]."), span_notice("You [welded ? "weld" : "unwelded"] \the [src] with \the [W]."), span_italics("You hear welding.")) - update_appearance(UPDATE_ICON) + update_appearance() else if(W.tool_behaviour == TOOL_WRENCH && anchorable) if(isinspace() && !anchored) return @@ -520,7 +536,7 @@ GLOBAL_LIST_EMPTY(lockers) locked = !locked user.visible_message(span_notice("[user] [locked ? null : "un"]locks [src]."), span_notice("You [locked ? null : "un"]lock [src].")) - update_appearance(UPDATE_ICON) + update_appearance() else if(!silent) to_chat(user, span_notice("Access Denied")) else if(secure && broken) @@ -535,7 +551,7 @@ GLOBAL_LIST_EMPTY(lockers) playsound(src, "sparks", 50, 1) broken = TRUE locked = FALSE - update_appearance(UPDATE_ICON) + update_appearance() return TRUE /obj/structure/closet/get_remote_view_fullscreens(mob/user) @@ -552,7 +568,7 @@ GLOBAL_LIST_EMPTY(lockers) if(secure && !broken && !(. & EMP_PROTECT_SELF)) if(prob(5 * severity)) locked = !locked - update_appearance(UPDATE_ICON) + update_appearance() if(prob(2 * severity) && !opened) if(!locked) open() diff --git a/code/game/objects/structures/ladders.dm b/code/game/objects/structures/ladders.dm index 27ee9e973cb2..6c8863004e56 100644 --- a/code/game/objects/structures/ladders.dm +++ b/code/game/objects/structures/ladders.dm @@ -32,13 +32,13 @@ var/obj/structure/ladder/L if (!down) - L = locate() in SSmapping.get_turf_below(T) + L = locate() in GET_TURF_BELOW(T) if (L) down = L L.up = src // Don't waste effort looping the other way L.update_appearance(UPDATE_ICON) if (!up) - L = locate() in SSmapping.get_turf_above(T) + L = locate() in GET_TURF_ABOVE(T) if (L) up = L L.down = src // Don't waste effort looping the other way diff --git a/code/game/objects/structures/statues.dm b/code/game/objects/structures/statues.dm index 30862146bfb1..7ae13941e176 100644 --- a/code/game/objects/structures/statues.dm +++ b/code/game/objects/structures/statues.dm @@ -15,6 +15,7 @@ var/material_drop_type = /obj/item/stack/sheet/metal var/impressiveness = 15 CanAtmosPass = ATMOS_PASS_DENSITY + blocks_emissive = EMISSIVE_BLOCK_UNIQUE /obj/structure/statue/Initialize(mapload) . = ..() diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index c1050969b625..424517cfbfbe 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -387,26 +387,34 @@ opacity = FALSE density = FALSE var/open = TRUE + /// if it can be seen through when closed + var/opaque_closed = FALSE + +/obj/structure/curtain/Initialize(mapload) + // see-through curtains should let emissives shine through + if(!opaque_closed) + blocks_emissive = EMISSIVE_BLOCK_NONE + return ..() /obj/structure/curtain/proc/toggle() open = !open - update_appearance(UPDATE_ICON) - -/obj/structure/curtain/update_icon_state() - . = ..() - if(!open) - icon_state = "closed" - layer = WALL_OBJ_LAYER - density = TRUE - open = FALSE - set_opacity(TRUE) - - else - icon_state = "open" + if(open) layer = SIGN_LAYER - density = FALSE - open = TRUE + SET_PLANE_IMPLICIT(src, GAME_PLANE) + set_density(FALSE) set_opacity(FALSE) + else + layer = WALL_OBJ_LAYER + SET_PLANE_IMPLICIT(src, GAME_PLANE_UPPER) + set_density(TRUE) + if(opaque_closed) + set_opacity(TRUE) + + update_appearance() + +/obj/structure/curtain/update_icon_state() + icon_state = "[open ? "open" : "closed"]" + return ..() /obj/structure/curtain/attackby(obj/item/W, mob/user) if (istype(W, /obj/item/toy/crayon)) diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index d4a01fd2a175..6726248bf79d 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -325,7 +325,7 @@ //This proc is used to update the icons of nearby windows. /obj/structure/window/proc/update_nearby_icons() - update_appearance(UPDATE_ICON) + update_appearance() if(smooth) queue_smooth_neighbors(src) diff --git a/code/game/objects/structures/wire_splicing.dm b/code/game/objects/structures/wire_splicing.dm index 5536249aa223..ea9e8780fb4e 100644 --- a/code/game/objects/structures/wire_splicing.dm +++ b/code/game/objects/structures/wire_splicing.dm @@ -6,7 +6,7 @@ density = FALSE anchored = TRUE flags_1 = CONDUCT_1 - layer = UNDER_CATWALK + layer = CATWALK_LAYER var/messiness = 0 // How bad the splicing was, determines the chance of shock /obj/structure/wire_splicing/Initialize(mapload) diff --git a/code/game/turfs/baseturfs/baseturfs.dm b/code/game/turfs/baseturfs/baseturfs.dm new file mode 100644 index 000000000000..195b52de1106 --- /dev/null +++ b/code/game/turfs/baseturfs/baseturfs.dm @@ -0,0 +1,127 @@ +/// Places the given turf on the bottom of the turf stack. +/turf/proc/place_on_bottom(turf/bottom_turf) + baseturfs = baseturfs_string_list( + list(initial(bottom_turf.baseturfs), bottom_turf) + baseturfs, + src + ) + +/// Places a turf at the top of the stack +/turf/proc/place_on_top(turf/added_layer, flags) + var/list/turf/new_baseturfs = list() + + new_baseturfs.Add(baseturfs) + if(isopenturf(src)) + new_baseturfs.Add(type) + + return ChangeTurf(added_layer, new_baseturfs, flags) + +/// Places a turf on top - for map loading +/turf/proc/load_on_top(turf/added_layer, flags) + var/area/our_area = get_area(src) + flags = our_area.PlaceOnTopReact(list(baseturfs), added_layer, flags) + + if(flags & CHANGETURF_SKIP) // We haven't been initialized + if(flags_1 & INITIALIZED_1) + stack_trace("CHANGETURF_SKIP was used in a PlaceOnTop call for a turf that's initialized. This is a mistake. [src]([type])") + assemble_baseturfs() + + var/turf/new_turf + if(!length(baseturfs)) + baseturfs = list(baseturfs) + + var/list/old_baseturfs = baseturfs.Copy() + if(!isclosedturf(src)) + old_baseturfs += type + + new_turf = ChangeTurf(added_layer, null, flags) + new_turf.assemble_baseturfs(initial(added_layer.baseturfs)) // The baseturfs list is created like roundstart + if(!length(new_turf.baseturfs)) + new_turf.baseturfs = list(baseturfs) + + // The old baseturfs are put underneath, and we sort out the unwanted ones + new_turf.baseturfs = baseturfs_string_list(old_baseturfs + (new_turf.baseturfs - GLOB.blacklisted_automated_baseturfs), new_turf) + return new_turf + +/// Tries to find the given type in baseturfs. +/// If found, returns how deep it is for use in other baseturf procs, or null if it cannot be found. +/// For example, this number can be passed into ScrapeAway to scrape everything until that point. +/turf/proc/depth_to_find_baseturf(baseturf_type) + if(!islist(baseturfs)) + return baseturfs == baseturf_type ? 1 : null + var/index = baseturfs.Find(baseturf_type) + if (index == 0) + return null + return baseturfs.len - index + 1 + +/// Returns the baseturf at the given depth. +/// For example, baseturf_at_depth(1) will give the baseturf that would show up when scraping once. +/turf/proc/baseturf_at_depth(index) + TEST_ONLY_ASSERT(isnum(index), "baseturf_at_depth must be given a number, received [index]") + if (islist(baseturfs)) + return LAZYACCESS(baseturfs, baseturfs.len - index + 1) + else if (index == 1) + return baseturfs + else + return null + +/// Replaces all instances of needle_type in baseturfs with replacement_type +/turf/proc/replace_baseturf(needle_type, replacement_type) + if (islist(baseturfs)) + var/list/new_baseturfs + + while (TRUE) + var/found_index = baseturfs.Find(needle_type) + if (found_index == 0) + break + + new_baseturfs ||= baseturfs.Copy() + new_baseturfs[found_index] = replacement_type + + if (!isnull(new_baseturfs)) + baseturfs = baseturfs_string_list(new_baseturfs, src) + else if (baseturfs == needle_type) + baseturfs = replacement_type + +/// Removes all baseturfs that are found in the given typecache. +/turf/proc/remove_baseturfs_from_typecache(list/typecache) + if (islist(baseturfs)) + var/list/new_baseturfs + + for (var/baseturf in baseturfs) + if (!typecache[baseturf]) + continue + + new_baseturfs ||= baseturfs.Copy() + new_baseturfs -= baseturf + + if (!isnull(new_baseturfs)) + baseturfs = baseturfs_string_list(new_baseturfs, src) + else if (typecache[baseturfs]) + baseturfs = /turf/baseturf_bottom + +/// Returns the total number of baseturfs +/turf/proc/count_baseturfs() + return islist(baseturfs) ? length(baseturfs) : 1 + +/// Inserts a baseturf at the given level. +/// "Level" here doesn't mean depth. +/// For example, `insert_baseturf(2, /turf/open/floor/plating)` will make it so +/// the 2nd to last turf in the list is plating. +/// This is different from *depth*, since depth is the level from the top. +/turf/proc/insert_baseturf(level, turf_type) + if (!islist(baseturfs)) + assemble_baseturfs() + if(!islist(baseturfs)) + baseturfs = list(baseturfs) + + var/list/baseturfs_copy = baseturfs.Copy() + baseturfs_copy.Insert(level, turf_type) + baseturfs = baseturfs_string_list(baseturfs_copy, src) + +/// Places a baseturf ontop of a searched for baseturf. +/turf/proc/stack_ontop_of_baseturf(floor, roof) + if (!islist(baseturfs)) + baseturfs = list(baseturfs) + var/floor_position = baseturfs.Find(floor) + if(floor_position != 0) + insert_baseturf(floor_position + 1, roof) diff --git a/code/game/turfs/closed.dm b/code/game/turfs/closed.dm index 114bcd368b51..07e3ca8f1869 100644 --- a/code/game/turfs/closed.dm +++ b/code/game/turfs/closed.dm @@ -1,5 +1,6 @@ /turf/closed layer = CLOSED_TURF_LAYER + plane = WALL_PLANE opacity = TRUE density = TRUE blocks_air = TRUE diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm index 82203455e9de..3fb38a41353f 100644 --- a/code/game/turfs/open.dm +++ b/code/game/turfs/open.dm @@ -36,6 +36,19 @@ /turf/open/zAirOut(direction, turf/source) return (direction == UP) +/** + * Replace an open turf with another open turf while avoiding the pitfall of replacing plating with a floor tile, leaving a hole underneath. + * This replaces the current turf if it is plating and is passed plating, is tile and is passed tile. + * It places the new turf on top of itself if it is plating and is passed a tile. + * It also replaces the turf if it is tile and is passed plating, essentially destroying the over turf. + * Flags argument is passed directly to ChangeTurf or PlaceOnTop + */ +/turf/open/proc/replace_floor(turf/open/new_floor_path, flags) + if (!overfloor_placed && initial(new_floor_path.overfloor_placed)) + place_on_top(new_floor_path, flags = flags) + return + ChangeTurf(new_floor_path, flags = flags) + /turf/open/indestructible name = "floor" icon = 'icons/turf/floors.dmi' @@ -609,3 +622,54 @@ flammability = initial(flammability) return flammability = new_flammability + +/// Builds with rods. This doesn't exist to be overriden, just to remove duplicate logic for turfs that want +/// To support floor tile creation +/// I'd make it a component, but one of these things is space. So no. +/turf/open/proc/build_with_rods(obj/item/stack/rods/used_rods, mob/user) + var/obj/structure/lattice/catwalk_bait = locate(/obj/structure/lattice, src) + var/obj/structure/lattice/catwalk/existing_catwalk = locate(/obj/structure/lattice/catwalk, src) + if(existing_catwalk) + to_chat(user, span_warning("There is already a catwalk here!")) + return + + if(catwalk_bait) + if(used_rods.use(1)) + qdel(catwalk_bait) + to_chat(user, span_notice("You construct a catwalk.")) + playsound(src, 'sound/weapons/genhit.ogg', 50, TRUE) + new /obj/structure/lattice/catwalk(src) + else + to_chat(user, span_warning("You need two rods to build a catwalk!")) + return + + if(used_rods.use(1)) + to_chat(user, span_notice("You construct a lattice.")) + playsound(src, 'sound/weapons/genhit.ogg', 50, TRUE) + new /obj/structure/lattice(src) + else + to_chat(user, span_warning("You need one rod to build a lattice.")) + +/// Very similar to build_with_rods, this exists to allow consistent behavior between different types in terms of how +/// Building floors works +/turf/open/proc/build_with_floor_tiles(obj/item/stack/tile/plasteel/used_tiles, user) + var/obj/structure/lattice/lattice = locate(/obj/structure/lattice, src) + if(!has_valid_support() && !lattice) + balloon_alert(user, "needs support, place rods!") + return + if(!used_tiles.use(1)) + balloon_alert(user, "need a floor tile to build!") + return + + playsound(src, 'sound/weapons/genhit.ogg', 50, TRUE) + var/turf/open/floor/plating/new_plating = place_on_top(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) + if(lattice) + qdel(lattice) + else + new_plating.lattice_underneath = FALSE + +/turf/open/proc/has_valid_support() + for (var/direction in GLOB.cardinals) + if(istype(get_step(src, direction), /turf/open/floor)) + return TRUE + return FALSE diff --git a/code/game/turfs/openspace/openspace.dm b/code/game/turfs/openspace/openspace.dm index 5a06c4bc2234..154cccf79383 100644 --- a/code/game/turfs/openspace/openspace.dm +++ b/code/game/turfs/openspace/openspace.dm @@ -5,6 +5,7 @@ baseturfs = /turf/open/openspace CanAtmosPassVertical = ATMOS_PASS_YES flags_1 = NO_RUST + plane = TRANSPARENT_FLOOR_PLANE //mouse_opacity = MOUSE_OPACITY_TRANSPARENT var/can_cover_up = TRUE var/can_build_on = TRUE @@ -15,9 +16,25 @@ /turf/open/openspace/Initialize(mapload) // handle plane and layer here so that they don't cover other obs/turfs in Dream Maker . = ..() - plane = FLOOR_OPENSPACE_PLANE - layer = OPENSPACE_LAYER + + RegisterSignal(src, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE, PROC_REF(on_atom_created)) + var/area/our_area = loc + if(istype(our_area, /area/space)) + force_no_gravity = TRUE return INITIALIZE_HINT_LATELOAD +/** + * Drops movables spawned on this turf after they are successfully initialized. + * so that spawned movables that should fall to gravity, will fall. + */ +/turf/open/openspace/proc/on_atom_created(datum/source, atom/created_atom) + SIGNAL_HANDLER + if(ismovable(created_atom)) + zfall_if_on_turf(created_atom) + +/turf/open/openspace/proc/zfall_if_on_turf(atom/movable/movable) + if(QDELETED(movable) || movable.loc != src) + return + zFall(movable) /turf/open/openspace/LateInitialize() update_multiz(TRUE, TRUE) diff --git a/code/game/turfs/simulated/chasm.dm b/code/game/turfs/simulated/chasm.dm index 482a7a9a7a7e..1c38a9af75a8 100644 --- a/code/game/turfs/simulated/chasm.dm +++ b/code/game/turfs/simulated/chasm.dm @@ -12,7 +12,7 @@ /turf/open/chasm/Initialize(mapload) . = ..() - AddComponent(/datum/component/chasm, SSmapping.get_turf_below(src)) + apply_components(mapload) /turf/open/chasm/proc/set_target(turf/target) var/datum/component/chasm/chasm_component = GetComponent(/datum/component/chasm) @@ -126,3 +126,7 @@ var/turf/T = pick(get_area_turfs(/area/fabric_of_reality)) if(T) set_target(T) + +/// Handles adding the chasm component to the turf (So stuff falls into it!) +/turf/open/chasm/proc/apply_components(mapload) + AddComponent(/datum/component/chasm, GET_TURF_BELOW(src), mapload) diff --git a/code/game/turfs/simulated/floor/plating.dm b/code/game/turfs/simulated/floor/plating.dm index 5fdd9fa14468..cfffea5f0cf6 100644 --- a/code/game/turfs/simulated/floor/plating.dm +++ b/code/game/turfs/simulated/floor/plating.dm @@ -188,3 +188,8 @@ if(locate(/obj/structure/lattice/catwalk, src)) return FALSE return TRUE +///not an actual turf its used just for rcd ui purposes +/turf/open/floor/plating/rcd + name = "Floor/Wall" + icon = 'icons/mob/radial.dmi' + icon_state = "wallfloor" diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index 253cf793e805..d9dac63995ee 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -1,8 +1,53 @@ +///The base color of light space emits +GLOBAL_VAR_INIT(base_starlight_color, default_starlight_color()) +///The color of light space is currently emitting +GLOBAL_VAR_INIT(starlight_color, default_starlight_color()) +/proc/default_starlight_color() + var/turf/open/space/read_from = /turf/open/space + return initial(read_from.light_color) + +///The range of the light space is displaying +GLOBAL_VAR_INIT(starlight_range, default_starlight_range()) +/proc/default_starlight_range() + var/turf/open/space/read_from = /turf/open/space + return initial(read_from.light_range) + +///The power of the light space is throwin out +GLOBAL_VAR_INIT(starlight_power, default_starlight_power()) +/proc/default_starlight_power() + var/turf/open/space/read_from = /turf/open/space + return initial(read_from.light_power) + +/proc/set_base_starlight(star_color = null, range = null, power = null) + GLOB.base_starlight_color = star_color + set_starlight(star_color, range, power) + +/proc/set_starlight(star_color = null, range = null, power = null) + if(isnull(star_color)) + star_color = GLOB.starlight_color + var/old_star_color = GLOB.starlight_color + GLOB.starlight_color = star_color + // set light color on all lit turfs + for(var/turf/open/space/spess as anything in GLOB.starlight) + spess.set_light(l_range = range, l_power = power, l_color = star_color) + + if(star_color == old_star_color) + return + + // Update the base overlays + for(var/obj/light as anything in GLOB.starlight_objects) + light.color = star_color + // Send some signals that'll update everything that uses the color + SEND_GLOBAL_SIGNAL(COMSIG_STARLIGHT_COLOR_CHANGED, old_star_color, star_color) + +GLOBAL_LIST_EMPTY(starlight) + /turf/open/space icon = 'icons/turf/space.dmi' icon_state = "0" name = "\proper space" - intact = 0 + overfloor_placed = FALSE + underfloor_accessibility = UNDERFLOOR_INTERACTABLE initial_temperature = TCMB thermal_conductivity = 0 @@ -12,6 +57,8 @@ pipe_astar_cost = 3\ ) + var/starlight_source_count = 0 + var/destination_z var/destination_x var/destination_y @@ -19,11 +66,20 @@ var/global/datum/gas_mixture/immutable/space/space_gas plane = PLANE_SPACE layer = SPACE_LAYER - light_power = 0.25 + light_power = 1 dynamic_lighting = DYNAMIC_LIGHTING_DISABLED + light_range = 2 + light_color = COLOR_STARLIGHT + light_height = LIGHTING_HEIGHT_SPACE + light_on = FALSE + space_lit = TRUE bullet_bounce_sound = null + vis_flags = VIS_INHERIT_ID //when this be added to vis_contents of something it be associated with something on clicking, important for visualisation of turf in openspace and interraction with openspace that show you turf. + + force_no_gravity = TRUE -/turf/open/space/basic/New() //Do not convert to Initialize +/turf/open/space/basic/New() //Do not convert to Initialize + SHOULD_CALL_PARENT(FALSE) //This is used to optimize the map loader return @@ -42,8 +98,8 @@ stack_trace("Warning: [src]([type]) initialized multiple times!") flags_1 |= INITIALIZED_1 - var/area/A = loc - if(!IS_DYNAMIC_LIGHTING(src) && IS_DYNAMIC_LIGHTING(A)) + var/area/our_area = loc + if(!IS_DYNAMIC_LIGHTING(src) && IS_DYNAMIC_LIGHTING(our_area)) add_overlay(/obj/effect/fullbright) if (light_system == STATIC_LIGHT && light_power && light_range) @@ -54,6 +110,11 @@ return INITIALIZE_HINT_NORMAL + +/turf/open/space/Destroy() + GLOB.starlight -= src + return ..() + //ATTACK GHOST IGNORING PARENT RETURN VALUE /turf/open/space/attack_ghost(mob/dead/observer/user) if(destination_z) @@ -86,20 +147,32 @@ /turf/open/space/remove_air(amount) return null +/// Updates starlight. Called when we're unsure of a turf's starlight state +/// Returns TRUE if we succeed, FALSE otherwise /turf/open/space/proc/update_starlight() - if(CONFIG_GET(flag/starlight)) - for(var/t in RANGE_TURFS(1,src)) //RANGE_TURFS is in code\__HELPERS\game.dm - if(isspaceturf(t)) - //let's NOT update this that much pls - continue - set_light(2) - return - set_light(0) - -/turf/open/space/attack_paw(mob/user) - return attack_hand(user) + for(var/t in RANGE_TURFS(1, src)) //RANGE_TURFS is in code\__HELPERS\game.dm + // I've got a lot of cordons near spaceturfs, be good kids + if(isspaceturf(t) || istype(t, /turf/cordon)) + //let's NOT update this that much pls + continue + enable_starlight() + return TRUE + GLOB.starlight -= src + set_light(l_on = FALSE) + return FALSE + +/// Turns on the stars, if they aren't already +/turf/open/space/proc/enable_starlight() + if(!light_on) + set_light(l_on = TRUE, l_range = GLOB.starlight_range, l_power = GLOB.starlight_power, l_color = GLOB.starlight_color) + GLOB.starlight += src + +/turf/open/space/attack_paw(mob/user, list/modifiers) + return attack_hand(user, modifiers) /turf/open/space/proc/CanBuildHere() + if(destination_z) + return FALSE return TRUE /turf/open/space/handle_slip() @@ -110,54 +183,24 @@ if(!CanBuildHere()) return if(istype(C, /obj/item/stack/rods)) - var/obj/item/stack/rods/R = C - var/obj/structure/lattice/L = locate(/obj/structure/lattice, src) - var/obj/structure/lattice/catwalk/W = locate(/obj/structure/lattice/catwalk, src) - if(W) - to_chat(user, span_warning("There is already a catwalk here!")) - return - if(L) - if(R.use(1)) - to_chat(user, span_notice("You construct a catwalk.")) - playsound(src, 'sound/weapons/genhit.ogg', 50, 1) - new/obj/structure/lattice/catwalk(src) - else - to_chat(user, span_warning("You need one rod to build a catwalk!")) - return - if(R.use(1)) - to_chat(user, span_notice("You construct a lattice.")) - playsound(src, 'sound/weapons/genhit.ogg', 50, 1) - ReplaceWithLattice() - else - to_chat(user, span_warning("You need one rod to build a lattice.")) - return - if(istype(C, /obj/item/stack/tile/plasteel)) - var/obj/structure/lattice/L = locate(/obj/structure/lattice, src) - if(L) - var/obj/item/stack/tile/plasteel/S = C - if(S.use(1)) - qdel(L) - playsound(src, 'sound/weapons/genhit.ogg', 50, 1) - to_chat(user, span_notice("You build a floor.")) - PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) - else - to_chat(user, span_warning("You need one floor tile to build a floor!")) - else - to_chat(user, span_warning("The plating is going to need some support! Place metal rods first.")) + build_with_rods(C, user) + else if(istype(C, /obj/item/stack/tile/plasteel)) + build_with_floor_tiles(C, user) -/turf/open/space/Entered(atom/movable/A) - ..() - if ((!(A) || src != A.loc)) + +/turf/open/space/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) + . = ..() + if(!arrived || src != arrived.loc) return - if(destination_z && destination_x && destination_y && !(A.pulledby || !A.can_be_z_moved)) + if(destination_z && destination_x && destination_y && !arrived.pulledby && !arrived.currently_z_moving) var/tx = destination_x var/ty = destination_y var/turf/DT = locate(tx, ty, destination_z) var/itercount = 0 while(DT.density || istype(DT.loc,/area/shuttle)) // Extend towards the center of the map, trying to look for a better place to arrive if (itercount++ >= 100) - log_game("SPACE Z-TRANSIT ERROR: Could not find a safe place to land [A] within 100 iterations.") + log_game("SPACE Z-TRANSIT ERROR: Could not find a safe place to land [arrived] within 100 iterations.") break if (tx < 128) tx++ @@ -169,18 +212,13 @@ ty-- DT = locate(tx, ty, destination_z) - var/atom/movable/AM = A.pulling - A.forceMove(DT) - if(AM) - var/turf/T = get_step(A.loc,turn(A.dir, 180)) - AM.can_be_z_moved = FALSE - AM.forceMove(T) - A.start_pulling(AM) - AM.can_be_z_moved = TRUE + arrived.zMove(null, DT, ZMOVE_ALLOW_BUCKLED) - //now we're on the new z_level, proceed the space drifting - stoplag()//Let a diagonal move finish, if necessary - A.newtonian_move(A.inertia_dir) + var/atom/movable/current_pull = arrived.pulling + while (current_pull) + var/turf/target_turf = get_step(current_pull.pulledby.loc, REVERSE_DIR(current_pull.pulledby.dir)) || current_pull.pulledby.loc + current_pull.zMove(null, target_turf, ZMOVE_ALLOW_BUCKLED) + current_pull = current_pull.pulling /turf/open/space/MakeSlippery(wet_setting, min_wet_time, wet_time_to_add, max_wet_time, permanent) @@ -191,46 +229,25 @@ /turf/open/space/can_have_cabling() if(locate(/obj/structure/lattice/catwalk, src)) - return 1 - return 0 + return TRUE + return FALSE /turf/open/space/is_transition_turf() if(destination_x || destination_y || destination_z) - return 1 + return TRUE /turf/open/space/acid_act(acidpwr, acid_volume) - return 0 + return FALSE /turf/open/space/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir) - underlay_appearance.icon = 'icons/turf/space.dmi' - underlay_appearance.icon_state = SPACE_ICON_STATE - underlay_appearance.plane = PLANE_SPACE + generate_space_underlay(underlay_appearance, asking_turf) return TRUE - -/turf/open/space/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd) - if(!CanBuildHere()) - return FALSE - - switch(the_rcd.mode) - if(RCD_FLOORWALL) - var/obj/structure/lattice/L = locate(/obj/structure/lattice, src) - if(L) - return list("mode" = RCD_FLOORWALL, "delay" = 0, "cost" = 1) - else - return list("mode" = RCD_FLOORWALL, "delay" = 0, "cost" = 3) +/turf/open/space/rust_heretic_act() return FALSE -/turf/open/space/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode) - switch(passed_mode) - if(RCD_FLOORWALL) - to_chat(user, span_notice("You build a floor.")) - PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) - return TRUE - return FALSE - -/turf/open/space/ReplaceWithLattice() +/turf/open/space/attempt_lattice_replacement() var/dest_x = destination_x var/dest_y = destination_y var/dest_z = destination_z @@ -238,3 +255,96 @@ destination_x = dest_x destination_y = dest_y destination_z = dest_z + +/turf/open/space/can_cross_safely(atom/movable/crossing) + return HAS_TRAIT(crossing, TRAIT_SPACEWALK) + +/turf/open/space/openspace + icon = 'icons/turf/floors.dmi' + icon_state = MAP_SWITCH("pure_white", "transparent") + plane = TRANSPARENT_FLOOR_PLANE + +/turf/open/space/openspace/Initialize(mapload) // handle plane and layer here so that they don't cover other obs/turfs in Dream Maker + . = ..() + icon_state = "pure_white" + // We make the assumption that the space plane will never be blacklisted, as an optimization + if(SSmapping.max_plane_offset) + plane = TRANSPARENT_FLOOR_PLANE - (PLANE_RANGE * SSmapping.z_level_to_plane_offset[z]) + return INITIALIZE_HINT_LATELOAD + +/turf/open/space/openspace/LateInitialize() + . = ..() + AddElement(/datum/element/turf_z_transparency) + +/turf/open/space/openspace/Destroy() + // Signals persist through destroy, GO HOME + var/turf/below = GET_TURF_BELOW(src) + if(below) + UnregisterSignal(below, COMSIG_TURF_CHANGE) + return ..() + +/turf/open/space/openspace/zAirIn() + return TRUE + +/turf/open/space/openspace/zAirOut() + return TRUE + +/turf/open/space/openspace/zPassIn(direction) + if(direction == DOWN) + for(var/obj/contained_object in contents) + if(contained_object.obj_flags & BLOCK_Z_IN_DOWN) + return FALSE + return TRUE + if(direction == UP) + for(var/obj/contained_object in contents) + if(contained_object.obj_flags & BLOCK_Z_IN_UP) + return FALSE + return TRUE + return FALSE + +/turf/open/space/openspace/zPassOut(direction) + if(direction == DOWN) + for(var/obj/contained_object in contents) + if(contained_object.obj_flags & BLOCK_Z_OUT_DOWN) + return FALSE + return TRUE + if(direction == UP) + for(var/obj/contained_object in contents) + if(contained_object.obj_flags & BLOCK_Z_OUT_UP) + return FALSE + return TRUE + return FALSE + +/turf/open/space/openspace/enable_starlight() + var/turf/below = GET_TURF_BELOW(src) + // Override = TRUE beacuse we could have our starlight updated many times without a failure, which'd trigger this + RegisterSignal(below, COMSIG_TURF_CHANGE, PROC_REF(on_below_change), override = TRUE) + if(!isspaceturf(below) || light_on) + return + set_light(l_on = TRUE, l_range = GLOB.starlight_range, l_power = GLOB.starlight_power, l_color = GLOB.starlight_color) + GLOB.starlight += src + +/turf/open/space/openspace/update_starlight() + . = ..() + if(.) + return + // If we're here, the starlight is not to be + var/turf/below = GET_TURF_BELOW(src) + UnregisterSignal(below, COMSIG_TURF_CHANGE) + +/turf/open/space/openspace/proc/on_below_change(turf/source, path, list/new_baseturfs, flags, list/post_change_callbacks) + SIGNAL_HANDLER + if(isspaceturf(source) && !ispath(path, /turf/open/space)) + GLOB.starlight += src + set_light(l_on = TRUE, l_range = GLOB.starlight_range, l_power = GLOB.starlight_power, l_color = GLOB.starlight_color) + else if(!isspaceturf(source) && ispath(path, /turf/open/space)) + GLOB.starlight -= src + set_light(l_on = FALSE) + +/turf/open/space/replace_floor(turf/open/new_floor_path, flags) + if (!initial(new_floor_path.overfloor_placed)) + ChangeTurf(new_floor_path, flags = flags) + return + // Create plating under tiled floor we try to create directly onto space + place_on_top(/turf/open/floor/plating, flags = flags) + place_on_top(new_floor_path, flags = flags) diff --git a/code/game/turfs/space/transit.dm b/code/game/turfs/space/transit.dm index 39d0c255272a..3ab965eb643e 100644 --- a/code/game/turfs/space/transit.dm +++ b/code/game/turfs/space/transit.dm @@ -8,7 +8,7 @@ /turf/open/space/transit/Initialize(mapload) . = ..() - update_appearance(UPDATE_ICON) + update_appearance() RegisterSignal(src, COMSIG_TURF_RESERVATION_RELEASED, PROC_REF(launch_contents)) /turf/open/space/transit/Destroy() diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm old mode 100755 new mode 100644 index c36b6dcadbbb..cd9f08781cc3 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -2,6 +2,7 @@ GLOBAL_LIST_EMPTY(station_turfs) /turf icon = 'icons/turf/floors.dmi' + vis_flags = VIS_INHERIT_ID // Important for interaction with and visualization of openspace. level = 1 luminosity = 1 @@ -19,10 +20,20 @@ GLOBAL_LIST_EMPTY(station_turfs) var/initial_temperature = T20C var/to_be_destroyed = 0 //Used for fire, if a melting temperature was reached, it will be destroyed var/max_fire_temperature_sustained = 0 //The max temperature of the fire which it was subjected to - + /// If there's a tile over a basic floor that can be ripped out + var/overfloor_placed = FALSE + /// How accessible underfloor pieces such as wires, pipes, etc are on this turf. Can be HIDDEN, VISIBLE, or INTERACTABLE. + var/underfloor_accessibility = UNDERFLOOR_HIDDEN var/blocks_air = FALSE - + ///Bool, whether this turf will always be illuminated no matter what area it is in + ///Makes it look blue, be warned + var/space_lit = FALSE + var/force_no_gravity = TRUE flags_1 = CAN_BE_DIRTY_1 + /// If there is a lattice underneat this turf. Used for the attempt_lattice_replacement proc to determine if it should place lattice. + var/lattice_underneath = TRUE + /// Turf bitflags, see code/__DEFINES/flags.dm + var/turf_flags = NONE var/list/image/blueprint_data //for the station blueprints, images of objects eg: pipes @@ -82,8 +93,8 @@ GLOBAL_LIST_EMPTY(station_turfs) for(var/atom/movable/AM in src) Entered(AM) - var/area/A = loc - if(!IS_DYNAMIC_LIGHTING(src) && IS_DYNAMIC_LIGHTING(A)) + var/area/our_area = loc + if(!IS_DYNAMIC_LIGHTING(src) && IS_DYNAMIC_LIGHTING(our_area)) add_overlay(/obj/effect/fullbright) if(requires_activation) @@ -94,11 +105,11 @@ GLOBAL_LIST_EMPTY(station_turfs) if (light_system == STATIC_LIGHT && light_power && light_range) update_light() - var/turf/T = SSmapping.get_turf_above(src) + var/turf/T = GET_TURF_ABOVE(src) if(T) T.multiz_turf_new(src, DOWN) SEND_SIGNAL(T, COMSIG_TURF_MULTIZ_NEW, src, DOWN) - T = SSmapping.get_turf_below(src) + T = GET_TURF_BELOW(src) if(T) T.multiz_turf_new(src, UP) SEND_SIGNAL(T, COMSIG_TURF_MULTIZ_NEW, src, UP) @@ -133,10 +144,10 @@ GLOBAL_LIST_EMPTY(station_turfs) if(!changing_turf) stack_trace("Incorrect turf deletion") changing_turf = FALSE - var/turf/T = SSmapping.get_turf_above(src) + var/turf/T = GET_TURF_ABOVE(src) if(T) T.multiz_turf_del(src, DOWN) - T = SSmapping.get_turf_below(src) + T = GET_TURF_BELOW(src) if(T) T.multiz_turf_del(src, UP) if(force) @@ -662,6 +673,10 @@ GLOBAL_LIST_EMPTY(station_turfs) /// Called when attempting to set fire to a turf /turf/proc/IgniteTurf(power, fire_color="red") return +/// Returns whether it is safe for an atom to move across this turf +/turf/proc/can_cross_safely(atom/movable/crossing) + return TRUE + /turf/proc/on_turf_saved() // This is all we can do. I'm sorry mappers, but there's no way to get any more details. diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index 9ae6194dfd44..3ac0b351c7e2 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -23,6 +23,8 @@ GLOBAL_PROTECT(href_token) var/deadmined + var/datum/plane_master_debug/plane_debug + var/ip_cache var/cid_cache diff --git a/code/modules/antagonists/blob/blob_mobs.dm b/code/modules/antagonists/blob/blob_mobs.dm index 3c9b7fa82a2e..3b9100bf680f 100644 --- a/code/modules/antagonists/blob/blob_mobs.dm +++ b/code/modules/antagonists/blob/blob_mobs.dm @@ -15,8 +15,10 @@ maxbodytemp = 360 unique_name = 1 a_intent = INTENT_HARM - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + // ... Blob colored lighting + lighting_cutoff_red = 20 + lighting_cutoff_green = 40 + lighting_cutoff_blue = 30 var/mob/camera/blob/overmind = null var/obj/structure/blob/factory/factory = null var/independent = FALSE diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm index dacff037b249..3c79ce8c687e 100644 --- a/code/modules/antagonists/blob/overmind.dm +++ b/code/modules/antagonists/blob/overmind.dm @@ -13,13 +13,15 @@ GLOBAL_LIST_EMPTY(blob_nodes) icon_state = "marker" mouse_opacity = MOUSE_OPACITY_ICON move_on_shuttle = 1 - see_in_dark = 8 invisibility = INVISIBILITY_OBSERVER layer = FLY_LAYER + // Vivid blue green, would be cool to make this change with strain + lighting_cutoff_red = 0 + lighting_cutoff_green = 35 + lighting_cutoff_blue = 20 pass_flags = PASSBLOB faction = list(ROLE_BLOB) - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE hud_type = /datum/hud/blob_overmind var/obj/structure/blob/core/blob_core = null // The blob overmind's core var/blob_points = 0 diff --git a/code/modules/antagonists/bloodsuckers/bloodsuckers.dm b/code/modules/antagonists/bloodsuckers/bloodsuckers.dm index 58826b125266..4797081ec734 100644 --- a/code/modules/antagonists/bloodsuckers/bloodsuckers.dm +++ b/code/modules/antagonists/bloodsuckers/bloodsuckers.dm @@ -564,8 +564,9 @@ if(user_eyes) user_eyes.flash_protect = initial(user_eyes.flash_protect) user_eyes.sight_flags = initial(user_eyes.sight_flags) - user_eyes.see_in_dark = initial(user_eyes.see_in_dark) - user_eyes.lighting_alpha = initial(user_eyes.lighting_alpha) + user.lighting_cutoff_red += 5 + user.lighting_cutoff_green += 15 + user.lighting_cutoff_blue += 5 user.update_sight() /datum/antagonist/bloodsucker/proc/give_masquerade_infraction() diff --git a/code/modules/antagonists/bloodsuckers/structures/bloodsucker_life.dm b/code/modules/antagonists/bloodsuckers/structures/bloodsucker_life.dm index 7925aa933ad6..d019b1f00538 100644 --- a/code/modules/antagonists/bloodsuckers/structures/bloodsucker_life.dm +++ b/code/modules/antagonists/bloodsuckers/structures/bloodsucker_life.dm @@ -215,8 +215,7 @@ if(current_eyes) current_eyes.flash_protect = max(initial(current_eyes.flash_protect) - 1, - 1) current_eyes.sight_flags = SEE_MOBS - current_eyes.see_in_dark = 8 - current_eyes.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + current_eyes.setOrganDamage(0) //making sure if(my_clan?.get_clan() == CLAN_LASOMBRA && ishuman(bloodsuckeruser)) var/mob/living/carbon/human/bloodsucker = bloodsuckeruser @@ -286,7 +285,7 @@ owner.current.adjust_jitter(3 SECONDS) // BLOOD_VOLUME_SURVIVE: [122] - Blur Vision if(bloodsucker_blood_volume < BLOOD_VOLUME_SURVIVE(owner.current)) - owner.current.blur_eyes((8 - 8 * (bloodsucker_blood_volume / BLOOD_VOLUME_BAD(owner.current)))* 2 SECONDS) + owner.current.adjust_eye_blur((8 - 8 * (bloodsucker_blood_volume / BLOOD_VOLUME_BAD(owner.current)))* 2 SECONDS) // The more blood, the better the Regeneration, get too low blood, and you enter Frenzy. if(bloodsucker_blood_volume < (FRENZY_THRESHOLD_ENTER + humanity_lost * 10) && !frenzied) diff --git a/code/modules/antagonists/changeling/powers/headcrab.dm b/code/modules/antagonists/changeling/powers/headcrab.dm index 920cad4f34d7..45765c9006be 100644 --- a/code/modules/antagonists/changeling/powers/headcrab.dm +++ b/code/modules/antagonists/changeling/powers/headcrab.dm @@ -31,7 +31,7 @@ if(eyes) to_chat(H, span_userdanger("You are blinded by a shower of blood!")) H.Stun(20) - H.blur_eyes(20) + H.adjust_eye_blur(20) eyes.applyOrganDamage(5) H.adjust_confusion(3 SECONDS) for(var/mob/living/silicon/S in range(2,user)) diff --git a/code/modules/antagonists/changeling/powers/tiny_prick.dm b/code/modules/antagonists/changeling/powers/tiny_prick.dm index 5631ac7de2f6..0fc3a1fa17c4 100644 --- a/code/modules/antagonists/changeling/powers/tiny_prick.dm +++ b/code/modules/antagonists/changeling/powers/tiny_prick.dm @@ -235,7 +235,7 @@ to_chat(target, span_danger("Your eyes burn horrifically!")) target.become_nearsighted(EYE_DAMAGE) target.blind_eyes(20) - target.blur_eyes(40) + target.adjust_eye_blur(40) return TRUE /datum/action/changeling/sting/LSD diff --git a/code/modules/antagonists/clockcult/clock_effects/general_markers.dm b/code/modules/antagonists/clockcult/clock_effects/general_markers.dm index eda88f27e706..b37f58ddd271 100644 --- a/code/modules/antagonists/clockcult/clock_effects/general_markers.dm +++ b/code/modules/antagonists/clockcult/clock_effects/general_markers.dm @@ -4,7 +4,7 @@ desc = "Some big guy. For you." clockwork_desc = "One of Ratvar's generals." alpha = 200 - layer = MASSIVE_OBJ_LAYER + plane = MASSIVE_OBJ_PLANE /obj/effect/clockwork/general_marker/Initialize(mapload) . = ..() diff --git a/code/modules/antagonists/clockcult/clock_items/wraith_spectacles.dm b/code/modules/antagonists/clockcult/clock_items/wraith_spectacles.dm index bac0e1fac941..b2fc01309dae 100644 --- a/code/modules/antagonists/clockcult/clock_items/wraith_spectacles.dm +++ b/code/modules/antagonists/clockcult/clock_items/wraith_spectacles.dm @@ -53,20 +53,16 @@ to_chat(victim, span_userdanger("Your eyes explode with horrific pain!")) victim.emote("scream") eyes.applyOrganDamage(eyes.maxHealth) - victim.adjust_blurriness(30) + victim.adjust_eye_blur(30) victim.adjust_blindness(30) return TRUE /obj/item/clothing/glasses/wraith_spectacles/proc/set_vision_vars(update_vision) - lighting_alpha = null tint = 0 vision_flags = NONE - darkness_view = 2 if(!up) if(is_servant_of_ratvar(loc)) - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE vision_flags = SEE_MOBS | SEE_TURFS | SEE_OBJS - darkness_view = 3 else tint = 3 if(update_vision && iscarbon(loc)) @@ -161,7 +157,7 @@ eyes.applyOrganDamage(0.5) eye_damage_done += 0.5 if(eye_damage_done >= 20) - H.adjust_blurriness(2) + H.adjust_eye_blur(2) if(eye_damage_done >= nearsight_breakpoint) if(!HAS_TRAIT(H, TRAIT_NEARSIGHT)) to_chat(H, span_nzcrentr("Your vision doubles, then trembles. Darkness begins to close in. You can't keep this up!")) diff --git a/code/modules/antagonists/clockcult/clock_mobs/_eminence.dm b/code/modules/antagonists/clockcult/clock_mobs/_eminence.dm index efcbd6f882ec..8ed7296d9442 100644 --- a/code/modules/antagonists/clockcult/clock_mobs/_eminence.dm +++ b/code/modules/antagonists/clockcult/clock_mobs/_eminence.dm @@ -11,11 +11,13 @@ icon_state = "eminence" mouse_opacity = MOUSE_OPACITY_OPAQUE move_on_shuttle = TRUE - see_in_dark = 8 invisibility = INVISIBILITY_OBSERVER layer = FLY_LAYER faction = list("ratvar") - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + // VERY red, to fit the eyes + lighting_cutoff_red = 22 + lighting_cutoff_green = 5 + lighting_cutoff_blue = 5 var/turf/last_failed_turf var/static/superheated_walls = 0 var/lastWarning = 0 diff --git a/code/modules/antagonists/clockcult/clock_structure.dm b/code/modules/antagonists/clockcult/clock_structure.dm index 768d550e5378..b4d71902b183 100644 --- a/code/modules/antagonists/clockcult/clock_structure.dm +++ b/code/modules/antagonists/clockcult/clock_structure.dm @@ -130,7 +130,7 @@ /obj/structure/destructible/clockwork/massive name = "massive construct" desc = "A very large construction." - layer = MASSIVE_OBJ_LAYER + plane = MASSIVE_OBJ_PLANE density = FALSE resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF | FREEZE_PROOF diff --git a/code/modules/antagonists/cult/cult_structures.dm b/code/modules/antagonists/cult/cult_structures.dm index 7727d52ce3ff..716568c4736c 100644 --- a/code/modules/antagonists/cult/cult_structures.dm +++ b/code/modules/antagonists/cult/cult_structures.dm @@ -282,7 +282,7 @@ max_integrity = 200 break_sound = 'sound/effects/meteorimpact.ogg' break_message = span_warning("The pillar crumbles!") - layer = MASSIVE_OBJ_LAYER + plane = MASSIVE_OBJ_PLANE var/alt = 0 is_endgame = TRUE @@ -342,7 +342,7 @@ max_integrity = 600 break_sound = 'sound/effects/glassbr2.ogg' break_message = span_warning("The bloodstone resonates violently before crumbling to the floor!") - layer = MASSIVE_OBJ_LAYER + plane = MASSIVE_OBJ_PLANE light_color = "#FF0000" var/current_fullness = 0 var/anchor = FALSE //are we the bloodstone used to summon Nar'sie? used in the final part of the summoning diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm index c6d9cee7a905..2178b39cbb87 100644 --- a/code/modules/antagonists/cult/runes.dm +++ b/code/modules/antagonists/cult/runes.dm @@ -525,6 +525,12 @@ structure_check() searches for nearby cultist structures required for the invoca /obj/effect/rune/narsie/conceal() //can't hide this, and you wouldn't want to return +GLOBAL_VAR_INIT(narsie_effect_last_modified, 0) +GLOBAL_VAR_INIT(narsie_summon_count, 0) +/proc/set_narsie_count(new_count) + GLOB.narsie_summon_count = new_count + SEND_GLOBAL_SIGNAL(COMSIG_NARSIE_SUMMON_UPDATE, GLOB.narsie_summon_count) + /obj/effect/rune/narsie/attack_hand(mob/living/user) if(user.mind?.has_antag_datum(/datum/antagonist/cult/master)) req_cultists -= 4 //leader counts as 5 cultists if they are the invoker diff --git a/code/modules/antagonists/demon/general_powers.dm b/code/modules/antagonists/demon/general_powers.dm index 41834bb33b6a..050712982846 100644 --- a/code/modules/antagonists/demon/general_powers.dm +++ b/code/modules/antagonists/demon/general_powers.dm @@ -45,8 +45,9 @@ melee_damage_lower = 20 melee_damage_upper = 20 wound_bonus = -15 - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + lighting_cutoff_red = 22 + lighting_cutoff_green = 5 + lighting_cutoff_blue = 5 loot = (/obj/effect/decal/cleanable/blood) del_on_death = TRUE @@ -108,7 +109,7 @@ ..() return TRUE playsound(caster, 'sound/magic/demon_attack1.ogg', 75, TRUE) - victim.blur_eyes(15) //huge array of relatively minor effects. + victim.adjust_eye_blur(15) //huge array of relatively minor effects. victim.adjust_jitter(5 SECONDS) victim.set_confusion_if_lower(5 SECONDS) victim.adjust_disgust(40) diff --git a/code/modules/antagonists/devil/imp/imp.dm b/code/modules/antagonists/devil/imp/imp.dm index 7c76013f3401..b4474c51e689 100644 --- a/code/modules/antagonists/devil/imp/imp.dm +++ b/code/modules/antagonists/devil/imp/imp.dm @@ -31,8 +31,10 @@ obj_damage = 40 melee_damage_lower = 10 melee_damage_upper = 15 - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + // You KNOW we're doing a lightly purple red + lighting_cutoff_red = 30 + lighting_cutoff_green = 10 + lighting_cutoff_blue = 20 var/boost = 0 bloodcrawl = BLOODCRAWL_EAT var/list/consumed_mobs = list() diff --git a/code/modules/antagonists/disease/disease_mob.dm b/code/modules/antagonists/disease/disease_mob.dm index a9c6d361e279..9ebd36567bdc 100644 --- a/code/modules/antagonists/disease/disease_mob.dm +++ b/code/modules/antagonists/disease/disease_mob.dm @@ -13,14 +13,17 @@ the new instance inside the host to be updated to the template's stats. icon_state = "marker" mouse_opacity = MOUSE_OPACITY_ICON move_on_shuttle = FALSE - see_in_dark = 8 see_invisible = SEE_INVISIBLE_LIVING invisibility = INVISIBILITY_OBSERVER layer = BELOW_MOB_LAYER - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE sight = SEE_SELF|SEE_THRU initial_language_holder = /datum/language_holder/universal + // Pale green, bright enough to have good vision + lighting_cutoff_red = 5 + lighting_cutoff_green = 35 + lighting_cutoff_blue = 20 + var/freemove = TRUE var/freemove_end = 0 var/const/freemove_time = 1200 diff --git a/code/modules/antagonists/eldritch_cult/eldritch_items.dm b/code/modules/antagonists/eldritch_cult/eldritch_items.dm index 693016e600dc..32d16a4ff7ee 100644 --- a/code/modules/antagonists/eldritch_cult/eldritch_items.dm +++ b/code/modules/antagonists/eldritch_cult/eldritch_items.dm @@ -303,7 +303,8 @@ desc = "Allows the user to swap between three hud types, science, medical, and diagnostic" icon_state = "godeye" item_state = "godeye" - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + // Blue, light blue + color_cutoffs = list(15, 30, 40) hud_type = DATA_HUD_SECURITY_BASIC /obj/item/clothing/glasses/hud/toggle/eldritch_eye/equipped(mob/living/user, slot) diff --git a/code/modules/antagonists/eldritch_cult/knowledge/mind_lore.dm b/code/modules/antagonists/eldritch_cult/knowledge/mind_lore.dm index a39b68b38f7c..195140c21169 100644 --- a/code/modules/antagonists/eldritch_cult/knowledge/mind_lore.dm +++ b/code/modules/antagonists/eldritch_cult/knowledge/mind_lore.dm @@ -31,7 +31,7 @@ if(!ishuman(target)) return COMPONENT_BLOCK_HAND_USE var/mob/living/carbon/human/human_target = target - human_target.blur_eyes(1 SECONDS) + human_target.adjust_eye_blur(1 SECONDS) human_target.Knockdown(2 SECONDS) /datum/eldritch_knowledge/spell/eldritchbolt @@ -76,7 +76,7 @@ if(!ishuman(target)) return COMPONENT_BLOCK_HAND_USE var/mob/living/carbon/human/human_target = target - human_target.blur_eyes(2 SECONDS) + human_target.adjust_eye_blur(2 SECONDS) human_target.blind_eyes(1 SECONDS) /datum/eldritch_knowledge/spell/assault diff --git a/code/modules/antagonists/horror/horror.dm b/code/modules/antagonists/horror/horror.dm index 03ca12a00b9c..c476f05a1f97 100644 --- a/code/modules/antagonists/horror/horror.dm +++ b/code/modules/antagonists/horror/horror.dm @@ -9,8 +9,9 @@ maxHealth = 50 melee_damage_lower = 10 melee_damage_upper = 10 - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + lighting_cutoff_red = 22 + lighting_cutoff_green = 5 + lighting_cutoff_blue = 5 stop_automated_movement = TRUE attacktext = "bites" speak_emote = list("gurgles") diff --git a/code/modules/antagonists/morph/morph.dm b/code/modules/antagonists/morph/morph.dm index 4513e199850e..25f2cf9fff4e 100644 --- a/code/modules/antagonists/morph/morph.dm +++ b/code/modules/antagonists/morph/morph.dm @@ -24,8 +24,10 @@ obj_damage = 50 melee_damage_lower = 20 melee_damage_upper = 20 - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + // Oh you KNOW it's gonna be real green + lighting_cutoff_red = 10 + lighting_cutoff_green = 35 + lighting_cutoff_blue = 15 vision_range = 1 // Only attack when target is close wander = FALSE attack_vis_effect = ATTACK_EFFECT_BITE //nom nom nom diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm index b0f30b70856b..fdd5d2b8690e 100644 --- a/code/modules/antagonists/revenant/revenant.dm +++ b/code/modules/antagonists/revenant/revenant.dm @@ -3,7 +3,6 @@ //Don't hear deadchat and are NOT normal ghosts //Admin-spawn or random event -#define INVISIBILITY_REVENANT 50 #define REVENANT_NAME_FILE "revenant_names.json" /mob/living/simple_animal/revenant @@ -21,14 +20,16 @@ invisibility = INVISIBILITY_REVENANT health = INFINITY //Revenants don't use health, they use essence instead maxHealth = INFINITY - layer = GHOST_LAYER + plane = GHOST_PLANE healable = FALSE spacewalk = TRUE sight = SEE_MOBS | SEE_OBJS | SEE_TURFS | SEE_SELF throwforce = 0 - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + // Going for faint purple spoopy ghost + lighting_cutoff_red = 20 + lighting_cutoff_green = 15 + lighting_cutoff_blue = 35 response_help = "passes through" response_disarm = "swings through" response_harm = "punches through" @@ -72,10 +73,6 @@ flags_1 |= RAD_NO_CONTAMINATE_1 ADD_TRAIT(src, TRAIT_SIXTHSENSE, INNATE_TRAIT) - // Starting spells - var/datum/action/cooldown/spell/night_vision/revenant/vision = new(src) - vision.Grant(src) - var/datum/action/cooldown/spell/list_target/telepathy/revenant/telepathy = new(src) telepathy.Grant(src) diff --git a/code/modules/antagonists/revenant/revenant_abilities.dm b/code/modules/antagonists/revenant/revenant_abilities.dm index 1253d94a738a..dae162f7c257 100644 --- a/code/modules/antagonists/revenant/revenant_abilities.dm +++ b/code/modules/antagonists/revenant/revenant_abilities.dm @@ -96,16 +96,7 @@ to_chat(src, span_revenwarning("You are not close enough to siphon [target ? "[target]'s":"[target.p_their()]"] soul. The link has been broken.")) draining = FALSE essence_drained = 0 - -//Toggle night vision: lets the revenant toggle its night vision -/datum/action/cooldown/spell/night_vision/revenant - name = "Toggle Darkvision" - panel = "Revenant Abilities" - background_icon_state = "bg_revenant" - button_icon = 'icons/mob/actions/actions_revenant.dmi' - button_icon_state = "r_nightvision" - toggle_span = "revennotice" - + //Transmit: the revemant's only direct way to communicate. Sends a single message silently to a single mob /datum/action/cooldown/spell/list_target/telepathy/revenant name = "Revenant Transmit" diff --git a/code/modules/antagonists/slaughter/slaughter.dm b/code/modules/antagonists/slaughter/slaughter.dm index 5b186f6696c5..adaa1d088467 100644 --- a/code/modules/antagonists/slaughter/slaughter.dm +++ b/code/modules/antagonists/slaughter/slaughter.dm @@ -33,8 +33,9 @@ obj_damage = 50 melee_damage_lower = 30 melee_damage_upper = 30 - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + lighting_cutoff_red = 22 + lighting_cutoff_green = 5 + lighting_cutoff_blue = 5 bloodcrawl = BLOODCRAWL_EAT var/playstyle_string = "You are a slaughter demon, a terrible creature from another realm. You have a single desire: To kill. \ You may use the \"Blood Crawl\" ability near blood pools to travel through them, appearing and disappearing from the station at will. \ diff --git a/code/modules/antagonists/traitor/syndicate_contract.dm b/code/modules/antagonists/traitor/syndicate_contract.dm index 52fed5895983..2025c419f1ad 100644 --- a/code/modules/antagonists/traitor/syndicate_contract.dm +++ b/code/modules/antagonists/traitor/syndicate_contract.dm @@ -163,7 +163,7 @@ M.flash_act() M.adjust_confusion(10 SECONDS) - M.blur_eyes(0.5 SECONDS) + M.adjust_eye_blur(0.5 SECONDS) to_chat(M, span_warning("You feel strange...")) sleep(6 SECONDS) to_chat(M, span_warning("That pod did something to you...")) @@ -172,7 +172,7 @@ to_chat(M, span_warning("Your head pounds... It feels like it's going to burst out your skull!")) M.flash_act() M.adjust_confusion(20 SECONDS) - M.blur_eyes(3) + M.adjust_eye_blur(3) sleep(3 SECONDS) to_chat(M, span_warning("Your head pounds...")) sleep(10 SECONDS) @@ -181,7 +181,7 @@ to_chat(M, "A million voices echo in your head... \"Your mind held many valuable secrets - \ we thank you for providing them. Your value is expended, and you will be ransomed back to your station. We always get paid, \ so it's only a matter of time before we ship you back...\"") - M.blur_eyes(1 SECONDS) + M.adjust_eye_blur(1 SECONDS) M.adjust_dizzy(1.5 SECONDS) M.adjust_confusion(20 SECONDS) @@ -220,7 +220,7 @@ M.forceMove(return_pod) M.flash_act() - M.blur_eyes(30) + M.adjust_eye_blur(30) M.adjust_dizzy(35 SECONDS) M.adjust_confusion(20 SECONDS) diff --git a/code/modules/atmospherics/gasmixtures/gas_mixture.dm b/code/modules/atmospherics/gasmixtures/gas_mixture.dm index e3d56b5e471c..af260e5397f7 100644 --- a/code/modules/atmospherics/gasmixtures/gas_mixture.dm +++ b/code/modules/atmospherics/gasmixtures/gas_mixture.dm @@ -299,6 +299,7 @@ get_true_breath_pressure(pp) --> gas_pp = pp/breath_pp*total_moles() //math is under the assumption temperatures are equal var/self_moles = get_moles(gas_id) var/other_moles = other.get_moles(gas_id) + if(abs(self_moles / return_volume() - other_moles / other.return_volume()) > min_p_delta / (R_IDEAL_GAS_EQUATION * return_temperature())) . = TRUE var/total_moles = self_moles + other_moles diff --git a/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm b/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm index a309095d2716..e2669a8e7f65 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm @@ -123,7 +123,7 @@ else if(!last_pressure_delta) set_light(1) - SSvis_overlays.add_vis_overlay(src, icon, "circ-off", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir) + SSvis_overlays.add_vis_overlay(src, icon, "circ-off", ABOVE_LIGHTING_PLANE, dir) return else if(last_pressure_delta > ONE_ATMOSPHERE) //fast @@ -131,15 +131,15 @@ set_light(3,2,"#4F82FF") else set_light(3,2,"#FF3232") - SSvis_overlays.add_vis_overlay(src, icon, "circ-ex[mode?"cold":"hot"]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir) - SSvis_overlays.add_vis_overlay(src, icon, "circ-run", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir) + SSvis_overlays.add_vis_overlay(src, icon, "circ-ex[mode?"cold":"hot"]", ABOVE_LIGHTING_PLANE, dir) + SSvis_overlays.add_vis_overlay(src, icon, "circ-run", ABOVE_LIGHTING_PLANE, dir) else //slow if(mode) set_light(2,1,"#4F82FF") else set_light(2,1,"#FF3232") - SSvis_overlays.add_vis_overlay(src, icon, "circ-[mode?"cold":"hot"]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir) - SSvis_overlays.add_vis_overlay(src, icon, "circ-slow", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir) + SSvis_overlays.add_vis_overlay(src, icon, "circ-[mode?"cold":"hot"]", ABOVE_LIGHTING_PLANE, dir) + SSvis_overlays.add_vis_overlay(src, icon, "circ-slow", ABOVE_LIGHTING_PLANE, dir) /obj/machinery/atmospherics/components/binary/circulator/wrench_act(mob/living/user, obj/item/I) if(user.a_intent == INTENT_HARM) diff --git a/code/modules/atmospherics/machinery/other/meter.dm b/code/modules/atmospherics/machinery/other/meter.dm index 3e511d438366..1be56c11d6ce 100644 --- a/code/modules/atmospherics/machinery/other/meter.dm +++ b/code/modules/atmospherics/machinery/other/meter.dm @@ -3,7 +3,7 @@ desc = "It measures something." icon = 'icons/obj/atmospherics/pipes/meter.dmi' icon_state = "meterX" - layer = GAS_METER_LAYER + layer = HIGH_PIPE_LAYER power_channel = AREA_USAGE_ENVIRON use_power = IDLE_POWER_USE idle_power_usage = 2 diff --git a/code/modules/awaymissions/super_secret_room.dm b/code/modules/awaymissions/super_secret_room.dm index f6e18b6ec5b5..7c8af5490bb9 100644 --- a/code/modules/awaymissions/super_secret_room.dm +++ b/code/modules/awaymissions/super_secret_room.dm @@ -4,7 +4,8 @@ verb_say = "intones" icon = 'icons/obj/structures.dmi' icon_state = "speaking_tile" - layer = 5 + layer = FLY_LAYER + plane = ABOVE_GAME_PLANE resistance_flags = INDESTRUCTIBLE var/speaking = FALSE var/times_spoken_to = 0 diff --git a/code/modules/buildmode/buttons.dm b/code/modules/buildmode/buttons.dm index 8468962b9c38..217a583cf147 100644 --- a/code/modules/buildmode/buttons.dm +++ b/code/modules/buildmode/buttons.dm @@ -2,7 +2,8 @@ icon = 'icons/misc/buildmode.dmi' var/datum/buildmode/bd // If we don't do this, we get occluded by item action buttons - layer = ABOVE_HUD_LAYER + plane = ABOVE_HUD_PLANE + /atom/movable/screen/buildmode/New(bld) bd = bld diff --git a/code/modules/cargo/bounties/special.dm b/code/modules/cargo/bounties/special.dm index 0072cb11f629..fce400e49f20 100644 --- a/code/modules/cargo/bounties/special.dm +++ b/code/modules/cargo/bounties/special.dm @@ -3,7 +3,7 @@ description = "Nanotrasen is interested in studying Xenomorph biology. Ship a set of organs to be thoroughly compensated." reward = 25000 required_count = 3 - wanted_types = list(/obj/item/organ/brain/alien, /obj/item/organ/alien, /obj/item/organ/body_egg/alien_embryo, /obj/item/organ/liver/alien, /obj/item/organ/tongue/alien, /obj/item/organ/eyes/night_vision/alien) + wanted_types = list(/obj/item/organ/brain/alien, /obj/item/organ/alien, /obj/item/organ/body_egg/alien_embryo, /obj/item/organ/liver/alien, /obj/item/organ/tongue/alien, /obj/item/organ/eyes/alien) /datum/bounty/item/syndicate_documents name = "Syndicate Documents" diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index 9f876156b699..a3d7db2478b3 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -173,3 +173,32 @@ /// Whether or not this client has the combo HUD enabled var/combo_hud_enabled = FALSE + + var/list/parallax_layers + var/list/parallax_layers_cached + ///Tracks say() usage for ic/dchat while slowmode is enabled + COOLDOWN_DECLARE(say_slowmode) + + ///this is the last recorded client eye by SSparallax/fire() + var/atom/movable/movingmob + var/turf/previous_turf + ///world.time of when we can state animate()ing parallax again + var/dont_animate_parallax + /// Direction our current area wants to move parallax + var/parallax_movedir = 0 + /// How many parallax layers to show our client + var/parallax_layers_max = 4 + /// Timer for the area directional animation + var/parallax_animate_timer + /// Do we want to do parallax animations at all? + /// Exists to prevent laptop fires + var/do_parallax_animations = TRUE + var/parallax_throttle = 0 //ds between updates + var/last_parallax_shift //world.time of last update + /** + * Assoc list with all the active maps - when a screen obj is added to + * a map, it's put in here as well. + * + * Format: list( = list(/atom/movable/screen)) + */ + var/list/screen_maps = list() diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 31adecb27a05..9786155eb104 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -482,52 +482,15 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( ////////////// /client/Del() - //if(credits) - //QDEL_LIST(credits) - log_access("Logout: [key_name(src)]") - if(holder) - adminGreet(1) - holder.owner = null - GLOB.permissions.admins -= src - if (!GLOB.permissions.admins.len && SSticker.IsRoundInProgress()) //Only report this stuff if we are currently playing. - var/cheesy_message = pick( - "I have no admins online!",\ - "I'm all alone :(",\ - "I'm feeling lonely :(",\ - "I'm so lonely :(",\ - "Why does nobody love me? :(",\ - "I want a man :(",\ - "Where has everyone gone?",\ - "I need a hug :(",\ - "Someone come hold me :(",\ - "I need someone on me :(",\ - "What happened? Where has everyone gone?",\ - "Forever alone :("\ - ) - - send2irc("Server", "[cheesy_message] (No admins online)") - qdel(holder) - if(ckey in GLOB.permissions.deadmins) - qdel(GLOB.permissions.deadmins[ckey]) - - GLOB.ahelp_tickets.ClientLogout(src) - GLOB.directory -= ckey - GLOB.clients -= src - - SSambience.remove_ambience_client(src) - - var/datum/connection_log/CL = GLOB.connection_logs[ckey] - if(CL) - CL.logout(mob) - - QDEL_LIST_ASSOC_VAL(char_render_holders) - if(movingmob != null) - movingmob.client_mobs_in_contents -= mob - UNSETEMPTY(movingmob.client_mobs_in_contents) - seen_messages = null - Master.UpdateTickRate() - world.sync_logout_with_db(connection_number) // yogs - logout logging - + if(!gc_destroyed) + gc_destroyed = world.time + if (!QDELING(src)) + stack_trace("Client does not purport to be QDELING, this is going to cause bugs in other places!") + + // Yes this is the same as what's found in qdel(). Yes it does need to be here + // Get off my back + SEND_SIGNAL(src, COMSIG_PARENT_QDELETING, TRUE) + Destroy() //Clean up signals and timers. return ..() /client/proc/set_client_age_from_db(connectiontopic) @@ -1116,3 +1079,12 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( if(holder) holder.particool = new /datum/particle_editor(in_atom) holder.particool.ui_interact(mob) +/// Clears the client's screen, aside from ones that opt out +/client/proc/clear_screen() + for (var/object in screen) + if (istype(object, /atom/movable/screen)) + var/atom/movable/screen/screen_object = object + if (!screen_object.clear_with_screen) + continue + + screen -= object diff --git a/code/modules/client/preferences/ambient_occlusion.dm b/code/modules/client/preferences/ambient_occlusion.dm index a81efca00bd8..ebadc64451ce 100644 --- a/code/modules/client/preferences/ambient_occlusion.dm +++ b/code/modules/client/preferences/ambient_occlusion.dm @@ -5,8 +5,7 @@ savefile_identifier = PREFERENCE_PLAYER /datum/preference/toggle/ambient_occlusion/apply_to_client(client/client, value) - var/atom/movable/screen/plane_master/game_world/plane_master = locate() in client?.screen - if (!plane_master) - return + /// Backdrop for the game world plane. + for(var/atom/movable/screen/plane_master/plane_master as anything in client.mob?.hud_used?.get_true_plane_masters(GAME_PLANE)) + plane_master.show_to(client.mob) - plane_master.backdrop(client.mob) diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 2a5300161ea3..3914e3abc87e 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -12,7 +12,7 @@ var/visor_flags_inv = 0 //same as visor_flags, but for flags_inv var/visor_flags_cover = 0 //same as above, but for flags_cover //what to toggle when toggled with weldingvisortoggle() - var/visor_vars_to_toggle = VISOR_FLASHPROTECT | VISOR_TINT | VISOR_VISIONFLAGS | VISOR_DARKNESSVIEW | VISOR_INVISVIEW + var/visor_vars_to_toggle = VISOR_FLASHPROTECT | VISOR_TINT | VISOR_VISIONFLAGS | VISOR_INVISVIEW lefthand_file = 'icons/mob/inhands/clothing_lefthand.dmi' righthand_file = 'icons/mob/inhands/clothing_righthand.dmi' var/alt_desc = null diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm index 7c433361c60d..cc1236235cfd 100644 --- a/code/modules/clothing/glasses/_glasses.dm +++ b/code/modules/clothing/glasses/_glasses.dm @@ -10,14 +10,24 @@ resistance_flags = NONE materials = list(/datum/material/glass = 250) var/vision_flags = 0 - var/darkness_view = 2//Base human is 2 var/invis_view = SEE_INVISIBLE_LIVING //admin only for now var/invis_override = 0 //Override to allow glasses to set higher than normal see_invis - var/lighting_alpha var/list/icon/current = list() //the current hud icons var/vision_correction = 0 //does wearing these glasses correct some of our vision defects? var/glass_colour_type //colors your vision when worn + /// A percentage of how much rgb to "max" on the lighting plane + /// This lets us brighten darkness without washing out bright color + var/lighting_cutoff = null + /// Similar to lighting_cutoff, except it has individual r g and b components in the same 0-100 scale + var/list/color_cutoffs = null +// Potentially replace glass_color_type with a setup that colors lighting by dropping segments of different componets +// Like the current idea, but applied without the mass cutoff (maybe? somehow?) +// That or just a light color to the lighting plane, that'd work too +// Enough to make it visible but not so much that it's a pain + +// That, or just make stuff that uses lighting_cutoff have colored offsets and all, like you were planning + /obj/item/clothing/glasses/suicide_act(mob/living/carbon/user) user.visible_message(span_suicide("[user] is stabbing \the [src] into [user.p_their()] eyes! It looks like [user.p_theyre()] trying to commit suicide!")) return BRUTELOSS @@ -31,8 +41,6 @@ ..() if(visor_vars_to_toggle & VISOR_VISIONFLAGS) vision_flags ^= initial(vision_flags) - if(visor_vars_to_toggle & VISOR_DARKNESSVIEW) - darkness_view ^= initial(darkness_view) if(visor_vars_to_toggle & VISOR_INVISVIEW) invis_view ^= initial(invis_view) @@ -51,7 +59,7 @@ to_chat(H, span_danger("[src] overloads and blinds you!")) H.flash_act(visual = 1) H.blind_eyes(3) - H.blur_eyes(5) + H.adjust_eye_blur(5) eyes.applyOrganDamage(5) /obj/item/clothing/glasses/meson @@ -59,9 +67,9 @@ desc = "Used by engineering and mining staff to see basic structural and terrain layouts through walls, regardless of lighting conditions." icon_state = "meson" item_state = "meson" - darkness_view = 2 vision_flags = SEE_TURFS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + // Mesons get to be lightly green + color_cutoffs = list(5, 15, 5) glass_colour_type = /datum/client_colour/glass_colour/lightgreen clothing_traits = list(TRAIT_MESONS) @@ -74,9 +82,10 @@ desc = "An optical meson scanner fitted with an amplified visible light spectrum overlay, providing greater visual clarity in darkness." icon_state = "nvgmeson" item_state = "nvgmeson" - darkness_view = 8 flash_protect = -1 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + // Night vision mesons get the same but more intense + color_cutoffs = list(10, 30, 10) + lighting_cutoff = LIGHTING_CUTOFF_HIGH glass_colour_type = /datum/client_colour/glass_colour/green /obj/item/clothing/glasses/meson/gar @@ -137,18 +146,19 @@ desc = "A pair of snazzy goggles used to protect against chemical spills that happen in complete darkness. Fitted with an analyzer for scanning items and reagents." icon_state = "sciencehudnight" item_state = "sciencehudnight" - darkness_view = 8 flash_protect = -1 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + // Real vivid purple + color_cutoffs = list(50, 10, 30) + lighting_cutoff = LIGHTING_CUTOFF_HIGH /obj/item/clothing/glasses/night name = "night vision goggles" desc = "You can totally see in the dark now!" icon_state = "night" item_state = "glasses" - darkness_view = 8 flash_protect = -1 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + // Dark green + color_cutoffs = list(10, 30, 10) glass_colour_type = /datum/client_colour/glass_colour/green /obj/item/clothing/glasses/science/suicide_act(mob/living/carbon/user) @@ -164,7 +174,6 @@ /obj/item/clothing/glasses/eyepatch/bigboss name = "faded eyepatch" desc = "Offers night vision and protection from flashes. Another mission, right boss?" - darkness_view = 8 flash_protect = 1 /obj/item/clothing/glasses/monocle @@ -187,7 +196,6 @@ desc = "Used by miners to detect ores deep within the rock." icon_state = "material" item_state = "glasses" - darkness_view = 0 /obj/item/clothing/glasses/material/mining/gar name = "gar material scanner" @@ -234,7 +242,6 @@ desc = "Strangely ancient technology used to help provide rudimentary eye cover. Enhanced shielding blocks flashes." icon_state = "sun" item_state = "sunglasses" - darkness_view = 1 flash_protect = 1 tint = 1 glass_colour_type = /datum/client_colour/glass_colour/gray @@ -317,7 +324,6 @@ item_state = "blindfold" flash_protect = 2 tint = 3 - darkness_view = 1 dog_fashion = /datum/dog_fashion/head /obj/item/clothing/glasses/blindfold/equipped(mob/living/carbon/human/user, slot) @@ -371,7 +377,8 @@ icon_state = "thermal" item_state = "glasses" vision_flags = SEE_MOBS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + // Going for an orange color here + color_cutoffs = list(25, 8, 5) flash_protect = -1 glass_colour_type = /datum/client_colour/glass_colour/red @@ -462,8 +469,8 @@ icon_state = "godeye" item_state = "godeye" vision_flags = SEE_TURFS - darkness_view = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + // Blue, light blue + color_cutoffs = list(15, 30, 40) resistance_flags = LAVA_PROOF | FIRE_PROOF clothing_flags = SCAN_REAGENTS var/datum/action/cooldown/expose/expose_ability diff --git a/code/modules/clothing/glasses/engine_goggles.dm b/code/modules/clothing/glasses/engine_goggles.dm index 23ac10427b4b..de0761e3cfea 100644 --- a/code/modules/clothing/glasses/engine_goggles.dm +++ b/code/modules/clothing/glasses/engine_goggles.dm @@ -19,8 +19,8 @@ actions_types = list(/datum/action/item_action/toggle_mode) vision_flags = NONE - darkness_view = 2 invis_view = SEE_INVISIBLE_LIVING + color_cutoffs = null var/list/modes = list(MODE_NONE = MODE_MESON, MODE_MESON = MODE_TRAY, MODE_TRAY = MODE_RAD, MODE_RAD = MODE_NONE) var/mode = MODE_NONE @@ -42,14 +42,12 @@ switch(mode) if(MODE_MESON) vision_flags = SEE_TURFS - darkness_view = 1 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + color_cutoffs = list(15, 12, 0) if(MODE_TRAY) //undoes the last mode, meson vision_flags = NONE - darkness_view = 2 - lighting_alpha = null - + color_cutoffs = list() + if(ishuman(user)) var/mob/living/carbon/human/H = user if(H.glasses == src) @@ -99,7 +97,6 @@ var/mutable_appearance/MA = new() MA.maptext = span_maptext("[strength]k") MA.color = "#04e604" - MA.layer = RAD_TEXT_LAYER MA.plane = GAME_PLANE pic.appearance = MA flick_overlay(pic, list(user.client), 10) diff --git a/code/modules/clothing/glasses/hud.dm b/code/modules/clothing/glasses/hud.dm index 33592b773bf9..eefa6d6b7381 100644 --- a/code/modules/clothing/glasses/hud.dm +++ b/code/modules/clothing/glasses/hud.dm @@ -43,9 +43,10 @@ desc = "An advanced medical heads-up display that allows doctors to find patients in complete darkness." icon_state = "healthhudnight" item_state = "glasses" - darkness_view = 8 flash_protect = -1 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + // Blue green, dark + color_cutoffs = list(10, 10, 30) + lighting_cutoff = LIGHTING_CUTOFF_HIGH glass_colour_type = /datum/client_colour/glass_colour/green /obj/item/clothing/glasses/hud/health/meson @@ -54,7 +55,8 @@ icon_state = "mesonhealth" item_state = "mesonhealth" vision_flags = SEE_TURFS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + // Mesons get to be lightly green + color_cutoffs = list(5, 5, 15) clothing_traits = list(TRAIT_MESONS) glass_colour_type = /datum/client_colour/glass_colour/lightblue @@ -62,7 +64,6 @@ name = "medical HUDSunglasses" desc = "Sunglasses with a medical HUD." icon_state = "sunhudmed" - darkness_view = 1 flash_protect = 1 tint = 1 glass_colour_type = /datum/client_colour/glass_colour/blue @@ -84,9 +85,10 @@ desc = "A robotics diagnostic HUD fitted with a light amplifier." icon_state = "diagnostichudnight" item_state = "glasses" - darkness_view = 8 flash_protect = -1 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + // Pale yellow + color_cutoffs = list(30, 20, 10) + lighting_cutoff = LIGHTING_CUTOFF_HIGH glass_colour_type = /datum/client_colour/glass_colour/green /obj/item/clothing/glasses/hud/diagnostic/sunglasses @@ -143,7 +145,6 @@ name = "security HUDSunglasses" desc = "Sunglasses with a security HUD." icon_state = "sunhudsec" - darkness_view = 1 flash_protect = 1 tint = 1 glass_colour_type = /datum/client_colour/glass_colour/darkred @@ -157,9 +158,8 @@ name = "night vision security HUD" desc = "An advanced heads-up display which provides ID data and vision in complete darkness." icon_state = "securityhudnight" - darkness_view = 8 flash_protect = -1 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + color_cutoffs = list(35, 10, 10) glass_colour_type = /datum/client_colour/glass_colour/green /obj/item/clothing/glasses/hud/security/sunglasses/gars @@ -228,7 +228,7 @@ icon_state = "thermal" hud_type = DATA_HUD_SECURITY_ADVANCED vision_flags = SEE_MOBS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + color_cutoffs = list(25, 8, 5) glass_colour_type = /datum/client_colour/glass_colour/red /obj/item/clothing/glasses/hud/toggle/thermal/attack_self(mob/user) @@ -236,13 +236,17 @@ switch (hud_type) if (DATA_HUD_MEDICAL_ADVANCED) icon_state = "meson" + color_cutoffs = list(5, 15, 5) change_glass_color(user, /datum/client_colour/glass_colour/green) if (DATA_HUD_SECURITY_ADVANCED) icon_state = "thermal" + color_cutoffs = list(25, 8, 5) change_glass_color(user, /datum/client_colour/glass_colour/red) else icon_state = "purple" + color_cutoffs = list(15, 0, 25) change_glass_color(user, /datum/client_colour/glass_colour/purple) + user.update_sight() user.update_inv_glasses() /obj/item/clothing/glasses/hud/toggle/thermal/emp_act(severity) diff --git a/code/modules/events/flutes.dm b/code/modules/events/flutes.dm index 10734d1859b7..ac328f006291 100644 --- a/code/modules/events/flutes.dm +++ b/code/modules/events/flutes.dm @@ -57,23 +57,23 @@ /datum/round_event/flutes/proc/flute_vis_flicker(mob/living/carbon/M) to_chat(M, span_warning("Your vision flickers.")) - M.blur_eyes(15) + M.adjust_eye_blur(15) /datum/round_event/flutes/proc/flute_headache(mob/living/carbon/M) to_chat(M, span_warning("You get an intense headache!")) - M.blur_eyes(15) + M.adjust_eye_blur(15) M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 15, 20, 30) M.adjustStaminaLoss(15) /datum/round_event/flutes/proc/flute_tremble(mob/living/carbon/M) to_chat(M, span_warning("Something trembles along the edge of your vision, your eyes water, with the familiar beat of blood racing through your head.")) - M.blur_eyes(30) + M.adjust_eye_blur(30) M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 30, 35) M.adjustStaminaLoss(30) /datum/round_event/flutes/proc/flute_chanting(mob/living/carbon/M) to_chat(M, "You hear faint chanting.. You feel a heavy weight upon your shoulders, as something shifts it's gaze towards you..") - M.blur_eyes(30) + M.adjust_eye_blur(30) M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 50, 60) M.adjustStaminaLoss(50) ADD_TRAIT(M, TRAIT_UNSTABLE, M) @@ -82,7 +82,7 @@ /datum/round_event/flutes/proc/flute_starlight(mob/living/carbon/M) to_chat(M, span_warning("As the nearest stars light your skin and your station, you can make out the faint whispers being spoken in turn with the monotone flutes playing beyond you. You feel so tired, as the struts of metal piping, the walls, the floor twist in unnatural ways, as the lights dim.")) sleep(30 SECONDS) - M.blur_eyes(40) + M.adjust_eye_blur(40) M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 65, 70, 80) ADD_TRAIT(M, TRAIT_UNSTABLE, M) //sanity = 50 @@ -108,7 +108,7 @@ to_chat(M, span_suicide("Your flesh undulates, and boils off your bones. You were blind, yet now you've seen a glimpse behind the cosmic curtain.")) //sanity = 25 REMOVE_TRAIT(M, TRAIT_UNSTABLE, M) - M.blur_eyes(5) + M.adjust_eye_blur(5) M.adjustStaminaLoss(90) M.adjustBruteLoss(60, 70, 75, 80, 85) M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 65, 70, 75, 80, 90) @@ -120,7 +120,7 @@ ADD_TRAIT(M, TRAIT_UNSTABLE, M) //sanity = 1 M.SetSleeping(30) - M.blur_eyes(40) + M.adjust_eye_blur(40) M.adjustStaminaLoss(99) to_chat(M, span_narsie("Y'HAH HT'HU THRZHZU. UA'KLL GHRT AWN ZUU!")) M.adjustBruteLoss(60, 70, 75, 80, 85) diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 8ede48819396..3984c220b126 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -1023,7 +1023,6 @@ GLOBAL_LIST_INIT(hallucination_list, list( slots_free += ui_storage2 if(slots_free.len) target.halitem.screen_loc = pick(slots_free) - target.halitem.layer = ABOVE_HUD_LAYER target.halitem.plane = ABOVE_HUD_PLANE switch(rand(1,6)) if(1) //revolver diff --git a/code/modules/food_and_drinks/food/snacks_pie.dm b/code/modules/food_and_drinks/food/snacks_pie.dm index b0b63954b023..31298dfc77ce 100644 --- a/code/modules/food_and_drinks/food/snacks_pie.dm +++ b/code/modules/food_and_drinks/food/snacks_pie.dm @@ -49,7 +49,7 @@ creamoverlay.icon_state = "creampie_human" if(stunning) H.Paralyze(20) //splat! - H.adjust_blurriness(1) + H.adjust_eye_blur(1) H.visible_message(span_warning("[H] is creamed by [src]!"), span_userdanger("You've been creamed by [src]!")) playsound(H, "desceration", 50, TRUE) if(!H.creamed) // one layer at a time @@ -301,4 +301,4 @@ bonus_reagents = list(/datum/reagent/consumable/nutriment = 1, /datum/reagent/consumable/nutriment/vitamin = 4) list_reagents = list(/datum/reagent/consumable/nutriment = 4, /datum/reagent/consumable/nutriment/vitamin = 4) tastes = list("pie" = 1, "dark chocolate" = 3) - foodtype = GRAIN | SUGAR | CHOCOLATE \ No newline at end of file + foodtype = GRAIN | SUGAR | CHOCOLATE diff --git a/code/modules/food_and_drinks/plate.dm b/code/modules/food_and_drinks/plate.dm index 693d0727ba54..54fb5f412922 100644 --- a/code/modules/food_and_drinks/plate.dm +++ b/code/modules/food_and_drinks/plate.dm @@ -64,7 +64,7 @@ /obj/item/plate/proc/AddToPlate(obj/item/item_to_plate) vis_contents += item_to_plate item_to_plate.vis_flags |= VIS_INHERIT_PLANE - item_to_plate.layer = ABOVE_HUD_LAYER + item_to_plate.plane = ABOVE_HUD_PLANE RegisterSignal(item_to_plate, COMSIG_MOVABLE_MOVED, PROC_REF(ItemMoved)) RegisterSignal(item_to_plate, COMSIG_PARENT_QDELETING, PROC_REF(ItemMoved)) @@ -188,4 +188,4 @@ . = ..() AddComponent(/datum/component/caltrop, force) -#undef PLATE_SHARD_PIECES \ No newline at end of file +#undef PLATE_SHARD_PIECES diff --git a/code/modules/hydroponics/grown/nettle.dm b/code/modules/hydroponics/grown/nettle.dm index f94de039f47a..f53b16f47ab1 100644 --- a/code/modules/hydroponics/grown/nettle.dm +++ b/code/modules/hydroponics/grown/nettle.dm @@ -114,4 +114,4 @@ to_chat(M, span_danger("You are blinded by the powerful acid of [src]!")) log_combat(user, M, "attacked", src) - M.adjust_blurriness(force/7) + M.adjust_eye_blur(force/7) diff --git a/code/modules/lighting/lighting_object.dm b/code/modules/lighting/lighting_object.dm index 49b3ec20e454..4a26ae86279e 100644 --- a/code/modules/lighting/lighting_object.dm +++ b/code/modules/lighting/lighting_object.dm @@ -15,7 +15,7 @@ return . = ..() - current_underlay = mutable_appearance(LIGHTING_ICON, "atransparent", source.z, LIGHTING_PLANE, 255, RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM) + current_underlay = mutable_appearance(LIGHTING_ICON, "transparent", source.z, source, LIGHTING_PLANE, 255, RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM) affected_turf = source if (affected_turf.lighting_object) @@ -88,12 +88,12 @@ if((rr & gr & br & ar) && (rg + gg + bg + ag + rb + gb + bb + ab == 8)) //anything that passes the first case is very likely to pass the second, and addition is a little faster in this case affected_turf.underlays -= current_underlay - current_underlay.icon_state = "atransparent" + current_underlay.icon_state = "lighting_transparent" current_underlay.color = null affected_turf.underlays += current_underlay else if(!set_luminosity) affected_turf.underlays -= current_underlay - current_underlay.icon_state = "adark" + current_underlay.icon_state = "lighting_dark" current_underlay.color = null affected_turf.underlays += current_underlay else diff --git a/code/modules/lighting/static_lighting_area.dm b/code/modules/lighting/static_lighting_area.dm new file mode 100644 index 000000000000..e05868d0b829 --- /dev/null +++ b/code/modules/lighting/static_lighting_area.dm @@ -0,0 +1,59 @@ +/// List of plane offset + 1 -> object to display to use +/// Fills with offsets as they are generated +/// Holds a list of objects that represent starlight. The idea is to render_source them +/// So modifying starlight requires touching only one place (NOTE: this doesn't work for the area overlays) +/// In order to modify them you need to use set_starlight. Areas don't work with render sources it looks like +GLOBAL_LIST_INIT_TYPED(starlight_objects, /obj, list(starlight_object(0))) +/obj/starlight_appearance + icon = 'icons/effects/alphacolors.dmi' + icon_state = "white" + layer = LIGHTING_PRIMARY_LAYER + blend_mode = BLEND_ADD + screen_loc = "1,1" + +/proc/starlight_object(offset) + var/obj/starlight_appearance/glow = new() + SET_PLANE_W_SCALAR(glow, LIGHTING_PLANE, offset) + glow.layer = LIGHTING_PRIMARY_LAYER + glow.blend_mode = BLEND_ADD + glow.color = GLOB.starlight_color + glow.render_target = SPACE_OVERLAY_RENDER_TARGET(offset) + return glow + +/// List of plane offset + 1 -> mutable appearance to use +/// Fills with offsets as they are generated +/// They mirror their appearance from the starlight objects, which lets us save +/// time updating them +GLOBAL_LIST_INIT_TYPED(starlight_overlays, /obj, list(starlight_overlay(0))) + +/proc/starlight_overlay(offset) + var/mutable_appearance/glow = new /mutable_appearance() + SET_PLANE_W_SCALAR(glow, LIGHTING_PLANE, offset) + glow.layer = LIGHTING_PRIMARY_LAYER + glow.blend_mode = BLEND_ADD + glow.render_source = SPACE_OVERLAY_RENDER_TARGET(offset) + return glow + +/area + ///Whether this area allows static lighting and thus loads the lighting objects + var/static_lighting = TRUE + +//Non static lighting areas. +//Any lighting area that wont support static lights. +//These areas will NOT have corners generated. + +///regenerates lighting objects for turfs in this area, primary use is VV changes +/area/proc/create_area_lighting_objects() + for(var/turf/T in src) + if(T.space_lit) + continue + T.lighting_build_overlay() + CHECK_TICK + +///Removes lighting objects from turfs in this area if we have them, primary use is VV changes +/area/proc/remove_area_lighting_objects() + for(var/turf/T in src) + if(T.space_lit) + continue + T.lighting_clear_overlay() + CHECK_TICK diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm index 0858fb4ea0dd..1d065cb064eb 100644 --- a/code/modules/mapping/mapping_helpers.dm +++ b/code/modules/mapping/mapping_helpers.dm @@ -10,7 +10,7 @@ var/list/baseturf_to_replace var/baseturf - layer = POINT_LAYER + plane = POINT_PLANE /obj/effect/baseturf_helper/Initialize(mapload) . = ..() diff --git a/code/modules/mapping/ruins.dm b/code/modules/mapping/ruins.dm index e6d67c1f05df..2b8755b513a5 100644 --- a/code/modules/mapping/ruins.dm +++ b/code/modules/mapping/ruins.dm @@ -122,7 +122,7 @@ for(var/v in current_pick.always_spawn_with) if(current_pick.always_spawn_with[v] == PLACE_BELOW) var/turf/T = locate(1,1,target_z) - if(!SSmapping.get_turf_below(T)) + if(!GET_TURF_BELOW(T)) if(forced_z) continue outer else @@ -182,7 +182,7 @@ if(PLACE_DEFAULT) forced_ruins[linked] = -1 if(PLACE_BELOW) - forced_ruins[linked] = SSmapping.get_turf_below(placed_turf) + forced_ruins[linked] = GET_TURF_BELOW(placed_turf) forced_z = 0 //Update the availible list diff --git a/code/modules/mapping/space_management/multiz_helpers.dm b/code/modules/mapping/space_management/multiz_helpers.dm index ca7cd8d52042..34f2774b5aa8 100644 --- a/code/modules/mapping/space_management/multiz_helpers.dm +++ b/code/modules/mapping/space_management/multiz_helpers.dm @@ -1,10 +1,11 @@ /proc/get_step_multiz(ref, dir) + var/turf/us = get_turf(ref) if(dir & UP) dir &= ~UP - return get_step(SSmapping.get_turf_above(get_turf(ref)), dir) + return get_step(GET_TURF_ABOVE(us), dir) if(dir & DOWN) dir &= ~DOWN - return get_step(SSmapping.get_turf_below(get_turf(ref)), dir) + return get_step(GET_TURF_BELOW(us), dir) return get_step(ref, dir) /proc/get_dir_multiz(turf/us, turf/them) @@ -15,23 +16,34 @@ if(us.z == them.z) return get_dir(us, them) else - var/turf/T = us.above() + var/turf/T = GET_TURF_ABOVE(us) var/dir = NONE if(T && (T.z == them.z)) dir = UP else - T = us.below() + T = GET_TURF_BELOW(us) if(T && (T.z == them.z)) dir = DOWN else return get_dir(us, them) return (dir | get_dir(us, them)) -/turf/proc/above() - return get_step_multiz(src, UP) +/proc/get_lowest_turf(atom/ref) + var/turf/us = get_turf(ref) + var/turf/next = GET_TURF_BELOW(us) + while(next) + us = next + next = GET_TURF_BELOW(us) + return us -/turf/proc/below() - return get_step_multiz(src, DOWN) +// I wish this was lisp +/proc/get_highest_turf(atom/ref) + var/turf/us = get_turf(ref) + var/turf/next = GET_TURF_ABOVE(us) + while(next) + us = next + next = GET_TURF_ABOVE(us) + return us /proc/dir_inverse_multiz(dir) var/holder = dir & (UP|DOWN) @@ -45,3 +57,9 @@ holder = UP dir |= holder return dir + +/turf/proc/above() + return get_step_multiz(src, UP) + +/turf/proc/below() + return get_step_multiz(src, DOWN) diff --git a/code/modules/mapping/space_management/space_reservation.dm b/code/modules/mapping/space_management/space_reservation.dm index 12137ebf4ca5..cfe9b5043b89 100644 --- a/code/modules/mapping/space_management/space_reservation.dm +++ b/code/modules/mapping/space_management/space_reservation.dm @@ -13,6 +13,13 @@ var/turf_type = /turf/open/space ///Distance away from the cordon where we can put a "sort-cordon" and run some extra code (see make_repel). 0 makes nothing happen var/pre_cordon_distance = 0 + /// The z stack size of the reservation. Note that reservations are ALWAYS reserved from the bottom up + var/z_size = 0 + /// List of the bottom left turfs. Indexed by what their z index for this reservation is + var/list/bottom_left_turfs = list() + + /// List of the top right turfs. Indexed by what their z index for this reservation is + var/list/top_right_turfs = list() /datum/turf_reservation/transit turf_type = /turf/open/space/transit diff --git a/code/modules/mapping/space_management/traits.dm b/code/modules/mapping/space_management/traits.dm index 9ba8d96d5e1c..7545b2508a95 100644 --- a/code/modules/mapping/space_management/traits.dm +++ b/code/modules/mapping/space_management/traits.dm @@ -49,24 +49,66 @@ . += S.z_value break -// Attempt to get the turf below the provided one according to Z traits -/datum/controller/subsystem/mapping/proc/get_turf_below(turf/T) - if (!T) - return - var/offset = level_trait(T.z, ZTRAIT_DOWN) - if (!offset) - return - return locate(T.x, T.y, T.z + offset) - -// Attempt to get the turf above the provided one according to Z traits -/datum/controller/subsystem/mapping/proc/get_turf_above(turf/T) - if (!T) - return - var/offset = level_trait(T.z, ZTRAIT_UP) - if (!offset) - return - return locate(T.x, T.y, T.z + offset) +/// Calculates the effective bounds information for the given turf. Returns a list of the information, or null if not applicable. +/datum/turf_reservation/proc/calculate_turf_bounds_information(turf/target) + for(var/z_idx in 1 to z_size) + var/turf/bottom_left = bottom_left_turfs[z_idx] + var/turf/top_right = top_right_turfs[z_idx] + var/bl_x = bottom_left.x + var/bl_y = bottom_left.y + var/tr_x = top_right.x + var/tr_y = top_right.y + if(target.x < bl_x) + continue + + if(target.y < bl_y) + continue + + if(target.x > tr_x) + continue + + if(target.y > tr_y) + continue + + var/list/return_information = list() + return_information["z_idx"] = z_idx + return_information["offset_x"] = target.x - bl_x + return_information["offset_y"] = target.y - bl_y + return return_information + return null + +/// Gets the turf below the given target. Returns null if there is no turf below the target +/datum/turf_reservation/proc/get_turf_below(turf/target) + var/list/bounds_info = calculate_turf_bounds_information(target) + if(isnull(bounds_info)) + return null + + var/z_idx = bounds_info["z_idx"] + // check what z level, if its the max, then there is no turf below + if(z_idx == z_size) + return null + + var/offset_x = bounds_info["offset_x"] + var/offset_y = bounds_info["offset_y"] + var/turf/bottom_left = bottom_left_turfs[z_idx + 1] + return locate(bottom_left.x + offset_x, bottom_left.y + offset_y, bottom_left.z) + +/// Gets the turf above the given target. Returns null if there is no turf above the target +/datum/turf_reservation/proc/get_turf_above(turf/target) + var/list/bounds_info = calculate_turf_bounds_information(target) + if(isnull(bounds_info)) + return null + + var/z_idx = bounds_info["z_idx"] + // check what z level, if its the min, then there is no turf above + if(z_idx == 1) + return null + + var/offset_x = bounds_info["offset_x"] + var/offset_y = bounds_info["offset_y"] + var/turf/bottom_left = bottom_left_turfs[z_idx - 1] + return locate(bottom_left.x + offset_x, bottom_left.y + offset_y, bottom_left.z) // Prefer not to use this one too often /datum/controller/subsystem/mapping/proc/get_station_center() var/station_z = levels_by_trait(ZTRAIT_STATION)[1] diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index 91289727d244..5bad51bfcc99 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -299,7 +299,7 @@ GLOBAL_LIST_EMPTY(aide_list) light_flags = LIGHT_ATTACHED layer = ABOVE_ALL_MOB_LAYER var/sight_flags = SEE_MOBS - var/lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + var/list/color_cutoffs = list(10, 25, 25) /obj/effect/wisp/orbit(atom/thing, radius, clockwise, rotation_speed, rotation_segments, pre_rotation, lockinorbit) . = ..() @@ -317,8 +317,8 @@ GLOBAL_LIST_EMPTY(aide_list) /obj/effect/wisp/proc/update_user_sight(mob/user) user.sight |= sight_flags - if(!isnull(lighting_alpha)) - user.lighting_alpha = min(user.lighting_alpha, lighting_alpha) + if(!isnull(color_cutoffs)) + user.lighting_color_cutoffs = blend_cutoff_colors(user.lighting_color_cutoffs, color_cutoffs) //Red/Blue Cubes /obj/item/warp_cube diff --git a/code/modules/mining/lavaland/ruins/gym.dm b/code/modules/mining/lavaland/ruins/gym.dm index 54d4a61cabf5..0ecbf2db6693 100644 --- a/code/modules/mining/lavaland/ruins/gym.dm +++ b/code/modules/mining/lavaland/ruins/gym.dm @@ -24,6 +24,7 @@ desc = "Just looking at this thing makes you feel tired." density = TRUE anchored = TRUE + blocks_emissive = EMISSIVE_BLOCK_UNIQUE var/icon_state_inuse /obj/structure/weightmachine/proc/AnimateMachine(mob/living/user) diff --git a/code/modules/mining/minebot.dm b/code/modules/mining/minebot.dm index 5ccc373a53fb..c1ea6bfafaf4 100644 --- a/code/modules/mining/minebot.dm +++ b/code/modules/mining/minebot.dm @@ -205,12 +205,15 @@ var/mob/living/simple_animal/hostile/mining_drone/user = owner if(user.sight & SEE_TURFS) user.sight &= ~SEE_TURFS - user.lighting_alpha = initial(user.lighting_alpha) + user.lighting_cutoff_red += 5 + user.lighting_cutoff_green += 15 + user.lighting_cutoff_blue += 5 else user.sight |= SEE_TURFS - user.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - - user.sync_lighting_plane_alpha() + user.lighting_cutoff_red -= 5 + user.lighting_cutoff_green -= 15 + user.lighting_cutoff_blue -= 5 + user.sync_lighting_plane_cutoff() to_chat(user, span_notice("You toggle your meson vision [(user.sight & SEE_TURFS) ? "on" : "off"].")) diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm index 646b2cf26cec..f1e05ac9a6e7 100644 --- a/code/modules/mining/ores_coins.dm +++ b/code/modules/mining/ores_coins.dm @@ -154,7 +154,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ if(C.is_eyes_covered()) C.visible_message(span_danger("[C]'s eye protection blocks the sand!"), span_warning("Your eye protection blocks the sand!")) return - C.adjust_blurriness(6) + C.adjust_eye_blur(6) C.adjustStaminaLoss(15)//the pain from your eyes burning does stamina damage C.adjust_confusion(5 SECONDS) to_chat(C, span_userdanger("\The [src] gets into your eyes! The pain, it burns!")) @@ -603,7 +603,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ flick("coin_[cmineral]_flip", src) icon_state = "coin_[cmineral]_[coinflip]" if(flash) - SSvis_overlays.add_vis_overlay(src, icon, "flash", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, unique = TRUE) + SSvis_overlays.add_vis_overlay(src, icon, "flash", ABOVE_LIGHTING_PLANE, unique = TRUE) playsound(loc, 'sound/items/coinflip.ogg', 50, TRUE) var/oldloc = loc sleep(1.5 SECONDS) diff --git a/code/modules/mob/camera/camera.dm b/code/modules/mob/camera/camera.dm index fe06cbf0f341..dbf07aed090f 100644 --- a/code/modules/mob/camera/camera.dm +++ b/code/modules/mob/camera/camera.dm @@ -7,11 +7,14 @@ move_resist = INFINITY status_flags = GODMODE // You can't damage it. mouse_opacity = MOUSE_OPACITY_TRANSPARENT - see_in_dark = 7 invisibility = INVISIBILITY_ABSTRACT // No one can see us sight = SEE_SELF move_on_shuttle = FALSE +/mob/camera/update_sight() + lighting_color_cutoffs = list(lighting_cutoff_red, lighting_cutoff_green, lighting_cutoff_blue) + return ..() + /mob/camera/experience_pressure_difference() return diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 6f2f8e73ed36..2a9e8ea0c618 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -8,12 +8,11 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER) desc = "It's a g-g-g-g-ghooooost!" //jinkies! icon = 'icons/mob/mob.dmi' icon_state = "ghost" - layer = GHOST_LAYER + layer = GHOST_PLANE stat = DEAD density = FALSE see_invisible = SEE_INVISIBLE_OBSERVER - see_in_dark = 100 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + lighting_cutoff = LIGHTING_CUTOFF_MEDIUM invisibility = INVISIBILITY_OBSERVER hud_type = /datum/hud/ghost movement_type = GROUND | FLYING @@ -597,15 +596,15 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp /mob/dead/observer/verb/toggle_darkness() set name = "Toggle Darkness" set category = "Ghost" - switch(lighting_alpha) - if (LIGHTING_PLANE_ALPHA_VISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - if (LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - if (LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE + switch(lighting_cutoff) + if (LIGHTING_CUTOFF_VISIBLE) + lighting_cutoff = LIGHTING_CUTOFF_MEDIUM + if (LIGHTING_CUTOFF_MEDIUM) + lighting_cutoff = LIGHTING_CUTOFF_HIGH + if (LIGHTING_CUTOFF_HIGH) + lighting_cutoff = LIGHTING_CUTOFF_FULLBRIGHT else - lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE + lighting_cutoff = LIGHTING_CUTOFF_VISIBLE update_sight() diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index b30a1abc79f2..e3fcfb9bb200 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -189,7 +189,6 @@ dropItemToGround(get_item_for_held_index(hand_index), force = TRUE) I.forceMove(src) held_items[hand_index] = I - I.layer = ABOVE_HUD_LAYER I.plane = ABOVE_HUD_PLANE I.equipped(src, ITEM_SLOT_HANDS, no_sound) if(I.pulledby) diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index ad04d2d20b2b..704c92a8eaf5 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -68,7 +68,7 @@ if(BLOOD_BAD) adjustOxyLoss(round((BLOOD_VOLUME_NORMAL(src) - blood_volume) * 0.02, 1)) if(prob(5)) - blur_eyes(6) + adjust_eye_blur(6) to_chat(src, span_warning("You feel very [word].")) if(BLOOD_SURVIVE) adjustOxyLoss(5) diff --git a/code/modules/mob/living/brain/status_procs.dm b/code/modules/mob/living/brain/status_procs.dm index 1c5aeb1f47c0..e69de29bb2d1 100644 --- a/code/modules/mob/living/brain/status_procs.dm +++ b/code/modules/mob/living/brain/status_procs.dm @@ -1,25 +0,0 @@ -//Here are the procs used to modify status effects of a mob. -//The effects include: stun, knockdown, unconscious, sleeping, resting, jitteriness, dizziness -// eye damage, eye_blind, eye_blurry, druggy, TRAIT_BLIND trait, and TRAIT_NEARSIGHT trait. - -/////////////////////////////////// EYE_BLIND //////////////////////////////////// - -/mob/living/brain/blind_eyes() // no eyes to damage or heal - return - -/mob/living/brain/adjust_blindness() - return - -/mob/living/brain/set_blindness() - return - -/////////////////////////////////// EYE_BLURRY //////////////////////////////////// - -/mob/living/brain/blur_eyes() - return - -/mob/living/brain/adjust_blurriness() - return - -/mob/living/brain/set_blurriness() - return \ No newline at end of file diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm index 995728c0f2ad..5250deb93416 100644 --- a/code/modules/mob/living/carbon/alien/alien.dm +++ b/code/modules/mob/living/carbon/alien/alien.dm @@ -6,11 +6,11 @@ faction = list(ROLE_ALIEN) ventcrawler = VENTCRAWLER_ALWAYS sight = SEE_MOBS - see_in_dark = 4 verb_say = "hisses" initial_language_holder = /datum/language_holder/alien bubble_icon = "alien" type_of_meat = /obj/item/reagent_containers/food/snacks/meat/slab/xeno + blocks_emissive = EMISSIVE_BLOCK_UNIQUE var/obj/item/card/id/wear_id = null // Fix for station bounced radios -- Skie var/has_fine_manipulation = 0 @@ -40,7 +40,7 @@ internal_organs += new /obj/item/organ/brain/alien internal_organs += new /obj/item/organ/alien/hivenode internal_organs += new /obj/item/organ/tongue/alien - internal_organs += new /obj/item/organ/eyes/night_vision/alien + internal_organs += new /obj/item/organ/eyes/alien internal_organs += new /obj/item/organ/liver/alien internal_organs += new /obj/item/organ/ears ..() diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index eb97186f2f4f..03536d25cff7 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -558,81 +558,77 @@ return if(stat == DEAD) if(SSmapping.level_trait(z, ZTRAIT_NOXRAY)) - sight = null + set_sight(null) else if(is_secret_level(z)) - sight = initial(sight) + set_sight(initial(sight)) else - sight = (SEE_TURFS|SEE_MOBS|SEE_OBJS) - see_in_dark = 8 - see_invisible = SEE_INVISIBLE_OBSERVER + set_sight(SEE_TURFS|SEE_MOBS|SEE_OBJS) + set_invis_see(SEE_INVISIBLE_OBSERVER) return + var/new_sight = initial(sight) + lighting_cutoff = initial(lighting_cutoff) + lighting_color_cutoffs = list(lighting_cutoff_red, lighting_cutoff_green, lighting_cutoff_blue) sight = initial(sight) see_infrared = initial(see_infrared) - lighting_alpha = initial(lighting_alpha) var/obj/item/organ/eyes/E = getorganslot(ORGAN_SLOT_EYES) if(!E) update_tint() else see_invisible = E.see_invisible - see_in_dark = E.see_in_dark sight |= E.sight_flags - if(!isnull(E.lighting_alpha)) - lighting_alpha = E.lighting_alpha if(istype(E, /obj/item/organ/eyes/ethereal) && client) //special view range ethereal eyes client.view_size.resetToDefault(getScreenSize(client.prefs.read_preference(/datum/preference/toggle/widescreen))) client.view_size.addTo("2x2") - for(var/image/I in infra_images) - if(client) - client.images.Remove(I) - infra_images = list() - remove_client_colour(/datum/client_colour/monochrome_infra) + var/obj/item/organ/eyes/eyes = getorganslot(ORGAN_SLOT_EYES) + if(eyes) + set_invis_see(eyes.see_invisible) + new_sight |= eyes.sight_flags + if(!isnull(eyes.lighting_cutoff)) + lighting_cutoff = eyes.lighting_cutoff + if(!isnull(eyes.color_cutoffs)) + lighting_color_cutoffs = blend_cutoff_colors(lighting_color_cutoffs, eyes.color_cutoffs) - if(client.eye != src) + + if(client.eye && client.eye != src) var/atom/A = client.eye if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates. return if(glasses) - var/obj/item/clothing/glasses/G = glasses - sight |= G.vision_flags - see_in_dark = max(G.darkness_view, see_in_dark) - if(G.invis_override) - see_invisible = G.invis_override + new_sight |= glasses.vision_flags + if(glasses.invis_override) + set_invis_see(glasses.invis_override) else - see_invisible = min(G.invis_view, see_invisible) - if(!isnull(G.lighting_alpha)) - lighting_alpha = min(lighting_alpha, G.lighting_alpha) - - if(HAS_TRAIT(src, TRAIT_INFRARED_VISION)) - add_client_colour(/datum/client_colour/monochrome_infra) - var/image/A = null - see_infrared = TRUE - lighting_alpha = min(lighting_alpha, LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE) - - if(client) - for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) - A = image('icons/mob/simple_human.dmi', H, "fullwhite") - A.name = "white haze" - A.override = 1 - infra_images |= A - client.images |= A + set_invis_see(min(glasses.invis_view, see_invisible)) + if(!isnull(glasses.lighting_cutoff)) + lighting_cutoff = max(lighting_cutoff, glasses.lighting_cutoff) + if(!isnull(glasses.color_cutoffs)) + lighting_color_cutoffs = blend_cutoff_colors(lighting_color_cutoffs, glasses.color_cutoffs) + + + if(HAS_TRAIT(src, TRAIT_TRUE_NIGHT_VISION)) + lighting_cutoff = max(lighting_cutoff, LIGHTING_CUTOFF_HIGH) + + if(HAS_TRAIT(src, TRAIT_MESON_VISION)) + new_sight |= SEE_TURFS + lighting_cutoff = max(lighting_cutoff, LIGHTING_CUTOFF_MEDIUM) if(HAS_TRAIT(src, TRAIT_THERMAL_VISION)) - sight |= (SEE_MOBS) - lighting_alpha = min(lighting_alpha, LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE) + new_sight |= SEE_MOBS + lighting_cutoff = max(lighting_cutoff, LIGHTING_CUTOFF_MEDIUM) if(HAS_TRAIT(src, TRAIT_XRAY_VISION)) - sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS) - see_in_dark = max(see_in_dark, 8) + new_sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS if(see_override) - see_invisible = see_override + set_invis_see(see_override) if(SSmapping.level_trait(z, ZTRAIT_NOXRAY)) - sight = null + new_sight = NONE + set_sight(new_sight) return ..() /mob/living/carbon/update_stamina_hud(shown_stamina_amount) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 25436281deae..8ed364c9cd6d 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -550,7 +550,7 @@ if(eyes.damage > 10) blind_eyes(damage) - blur_eyes(damage * rand(3, 6)) + adjust_eye_blur(damage * rand(3, 6)) if(eyes.damage > 20) if(prob(eyes.damage - 20)) diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm index 3e46bd2d5f20..39c2d4f18ec9 100644 --- a/code/modules/mob/living/carbon/carbon_defines.dm +++ b/code/modules/mob/living/carbon/carbon_defines.dm @@ -4,6 +4,7 @@ possible_a_intents = list(INTENT_HELP, INTENT_HARM) hud_possible = list(HEALTH_HUD,STATUS_HUD,ANTAG_HUD,GLAND_HUD,NANITE_HUD,DIAG_NANITE_FULL_HUD) has_limbs = 1 + blocks_emissive = EMISSIVE_BLOCK_NONE held_items = list(null, null) /// List of /obj/item/organ in the mob. /// They don't go in the contents for some reason I don't want to know. diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index 2f5282263552..517e9b1fef8b 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -132,7 +132,7 @@ if(G.vision_correction) clear_fullscreen("nearsighted") clear_fullscreen("eye_damage") - if(G.vision_flags || G.darkness_view || G.invis_override || G.invis_view || !isnull(G.lighting_alpha)) + if(G.vision_flags || G.invis_override || G.invis_view || !isnull(G.lighting_cutoff)) update_sight() update_inv_glasses() if(ITEM_SLOT_GLOVES) @@ -221,7 +221,7 @@ if(G.vision_correction) if(HAS_TRAIT(src, TRAIT_NEARSIGHT)) overlay_fullscreen("nearsighted", /atom/movable/screen/fullscreen/impaired, 1) - if(G.vision_flags || G.darkness_view || G.invis_override || G.invis_view || !isnull(G.lighting_alpha)) + if(G.vision_flags || G.invis_override || G.invis_view || !isnull(G.lighting_cutoff)) update_sight() if(!QDELETED(src)) update_inv_glasses() diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 4fac185604c2..b7789f20d03c 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -98,7 +98,7 @@ else adjust_blindness(-1) if(eye_blurry) //blurry eyes heal slowly - adjust_blurriness(-1) + adjust_eye_blur(-1) if (getOrganLoss(ORGAN_SLOT_BRAIN) >= 60) SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "brain_damage", /datum/mood_event/brain_damage) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 1694da2f6b43..78390d8c75ab 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1861,7 +1861,7 @@ GLOBAL_LIST_EMPTY(features_by_species) H.visible_message(span_danger("[H] has been knocked senseless!"), \ span_userdanger("[H] has been knocked senseless!")) H.set_confusion_if_lower(20 SECONDS) - H.adjust_blurriness(10) + H.adjust_eye_blur(10) if(prob(10)) H.gain_trauma(/datum/brain_trauma/mild/concussion) else diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm index c4b8bd0be8d4..4aca5549e3d8 100644 --- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm @@ -13,7 +13,7 @@ inherent_traits = list(TRAIT_RADIMMUNE,TRAIT_VIRUSIMMUNE,TRAIT_NOBREATH,TRAIT_GENELESS,TRAIT_NOHUNGER) changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_PRIDE | MIRROR_MAGIC - mutanteyes = /obj/item/organ/eyes/night_vision + mutanteyes = /obj/item/organ/eyes/shadow /datum/species/shadow/spec_life(mob/living/carbon/human/H) @@ -89,7 +89,7 @@ no_equip = list(ITEM_SLOT_MASK, ITEM_SLOT_OCLOTHING, ITEM_SLOT_GLOVES, ITEM_SLOT_FEET, ITEM_SLOT_ICLOTHING, ITEM_SLOT_SUITSTORE) species_traits = list(NOBLOOD,NO_UNDERWEAR,NO_DNA_COPY,NOTRANSSTING,NOEYESPRITES,NOFLASH) inherent_traits = list(TRAIT_RESISTCOLD,TRAIT_NOBREATH,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_NOGUNS,TRAIT_RADIMMUNE,TRAIT_VIRUSIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER,TRAIT_NOHUNGER) - mutanteyes = /obj/item/organ/eyes/night_vision/nightmare + mutanteyes = /obj/item/organ/eyes/shadow mutant_organs = list(/obj/item/organ/heart/nightmare) mutantbrain = /obj/item/organ/brain/nightmare diff --git a/code/modules/mob/living/carbon/human/species_types/zombies.dm b/code/modules/mob/living/carbon/human/species_types/zombies.dm index 41ed79f0ff96..4f3a9832fafb 100644 --- a/code/modules/mob/living/carbon/human/species_types/zombies.dm +++ b/code/modules/mob/living/carbon/human/species_types/zombies.dm @@ -51,7 +51,7 @@ mutanthands = /obj/item/zombie_hand armor = 20 // 120 damage to KO a zombie, which kills it speedmod = 1.6 - mutanteyes = /obj/item/organ/eyes/night_vision/zombie + mutanteyes = /obj/item/organ/eyes/zombie var/heal_rate = 1 var/regen_cooldown = 0 changesource_flags = MIRROR_BADMIN | WABBAJACK | ERT_SPAWN diff --git a/code/modules/mob/living/carbon/inventory.dm b/code/modules/mob/living/carbon/inventory.dm index f56a8d5d5274..21becca2178d 100644 --- a/code/modules/mob/living/carbon/inventory.dm +++ b/code/modules/mob/living/carbon/inventory.dm @@ -70,7 +70,6 @@ if(observe.client) observe.client.screen -= I I.forceMove(src) - I.layer = ABOVE_HUD_LAYER I.plane = ABOVE_HUD_PLANE I.appearance_flags |= NO_CLIENT_COLOR var/not_handled = FALSE diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm index 42afcb604854..7b8cba63935e 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/damage_procs.dm @@ -99,7 +99,7 @@ if(!HAS_TRAIT(src, TRAIT_RADIMMUNE)&& !(status_flags & GODMODE)) radiation += max(effect * hit_percent, 0) if(EFFECT_EYE_BLUR) - blur_eyes(effect * hit_percent) + adjust_eye_blur(effect * hit_percent) if(EFFECT_PARALYZE) Paralyze(effect * hit_percent) if(EFFECT_IMMOBILIZE) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 8baca92c259c..7a28d1d9a11d 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -121,9 +121,7 @@ eye_blind = max(eye_blind-1,1) if(eye_blurry) //blurry eyes heal slowly eye_blurry = max(eye_blurry-1, 0) - if(client) - update_eye_blur() - + /mob/living/proc/update_damage_hud() return diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 138e4f0635d7..82e163ada415 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -609,7 +609,7 @@ set_nutrition(NUTRITION_LEVEL_FED + 50) bodytemperature = BODYTEMP_NORMAL set_blindness(0) - set_blurriness(0) + set_eye_blur(0) cure_nearsighted() cure_blind() @@ -1545,13 +1545,13 @@ GLOBAL_LIST_EMPTY(fire_appearances) if(E) E.setOrganDamage(var_value) if("eye_blurry") - set_blurriness(var_value) + set_eye_blur(var_value) if("maxHealth") updatehealth() if("resize") update_transform() - if("lighting_alpha") - sync_lighting_plane_alpha() + if("lighting_cutoff") + sync_lighting_plane_cutoff() /mob/living/vv_get_header() . = ..() @@ -1682,3 +1682,15 @@ GLOBAL_LIST_EMPTY(fire_appearances) // if(!resting) // get_up() set_resting(FALSE) + +/mob/living/proc/move_to_error_room() + var/obj/effect/landmark/error/error_landmark = locate(/obj/effect/landmark/error) in GLOB.landmarks_list + if(error_landmark) + forceMove(error_landmark.loc) + else + forceMove(locate(4,4,1)) //Even if the landmark is missing, this should put them in the error room. + //If you're here from seeing this error, I'm sorry. I'm so very sorry. The error landmark should be a sacred object that nobody has any business messing with, and someone did! + //Consider seeing a therapist. + var/ERROR_ERROR_LANDMARK_ERROR = "ERROR-ERROR: ERROR landmark missing!" + log_mapping(ERROR_ERROR_LANDMARK_ERROR) + CRASH(ERROR_ERROR_LANDMARK_ERROR) diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 4288d6291183..f76206f6c8fd 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -1,7 +1,6 @@ /mob/living see_invisible = SEE_INVISIBLE_LIVING sight = 0 - see_in_dark = 2 hud_possible = list(HEALTH_HUD,STATUS_HUD,ANTAG_HUD,NANITE_HUD,DIAG_NANITE_FULL_HUD) pressure_resistance = 10 infra_luminosity = 10 @@ -149,3 +148,5 @@ var/num_hands = 2 ///How many usable hands does this mob currently have. Should only be changed through set_usable_hands() var/usable_hands = 2 + /// What our current gravity state is. Used to avoid duplicate animates and such + var/gravity_state = null diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index e2c4194c4c2f..a731d2728090 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -23,7 +23,6 @@ status_flags = CANSTUN|CANPUSH a_intent = INTENT_HARM //so we always get pushed instead of trying to swap sight = SEE_TURFS | SEE_MOBS | SEE_OBJS - see_in_dark = 8 hud_type = /datum/hud/ai med_hud = DATA_HUD_MEDICAL_BASIC sec_hud = DATA_HUD_SECURITY_BASIC diff --git a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm index efd4184eee1f..5c23d7f61142 100644 --- a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm +++ b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm @@ -2,7 +2,7 @@ // // The datum containing all the chunks. -#define CHUNK_SIZE 16 // Only chunk sizes that are to the power of 2. E.g: 2, 4, 8, 16, etc.. +// #define CHUNK_SIZE 16 // Only chunk sizes that are to the power of 2. E.g: 2, 4, 8, 16, etc.. GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new) diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index 7d9c128e622e..0c0cf4554fdf 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -111,17 +111,15 @@ /mob/living/silicon/ai/update_sight() see_invisible = initial(see_invisible) - see_in_dark = initial(see_in_dark) sight = initial(sight) if(aiRestorePowerRoutine && !available_ai_cores()) sight = sight&~SEE_TURFS sight = sight&~SEE_MOBS sight = sight&~SEE_OBJS - see_in_dark = 0 if(see_override) see_invisible = see_override - sync_lighting_plane_alpha() + sync_lighting_plane_cutoff() /mob/living/silicon/ai/proc/start_RestorePowerRoutine() diff --git a/code/modules/mob/living/silicon/robot/inventory.dm b/code/modules/mob/living/silicon/robot/inventory.dm index 90f8eab243b4..2751c0d0dd26 100644 --- a/code/modules/mob/living/silicon/robot/inventory.dm +++ b/code/modules/mob/living/silicon/robot/inventory.dm @@ -81,7 +81,6 @@ held_items[module_num] = item_module item_module.equipped(src, ITEM_SLOT_HANDS) item_module.mouse_opacity = initial(item_module.mouse_opacity) - item_module.layer = ABOVE_HUD_LAYER item_module.plane = ABOVE_HUD_PLANE item_module.cyborg_equip(src) item_module.forceMove(src) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index ea28cd315e22..5245db86dbca 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -9,8 +9,8 @@ designation = "Default" ///used for displaying the prefix & getting the current module of cyborg has_limbs = 1 hud_type = /datum/hud/robot - blocks_emissive = EMISSIVE_BLOCK_GENERIC - light_system = MOVABLE_LIGHT + blocks_emissive = EMISSIVE_BLOCK_UNIQUE + light_system = MOVABLE_LIGHT_DIRECTIONAL light_on = FALSE var/custom_name = "" @@ -127,7 +127,6 @@ robot_modules_background = new() robot_modules_background.icon_state = "block" - robot_modules_background.layer = HUD_LAYER //Objects that appear on screen are on layer ABOVE_HUD_LAYER, UI should be just below it. robot_modules_background.plane = HUD_PLANE ident = rand(1, 999) @@ -1065,14 +1064,13 @@ sight = initial(sight) else sight = (SEE_TURFS|SEE_MOBS|SEE_OBJS) - see_in_dark = 8 see_invisible = SEE_INVISIBLE_OBSERVER return see_invisible = initial(see_invisible) - see_in_dark = initial(see_in_dark) sight = initial(sight) - lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE + lighting_cutoff = LIGHTING_CUTOFF_VISIBLE + lighting_color_cutoffs = list(lighting_cutoff_red, lighting_cutoff_green, lighting_cutoff_blue) if(client.eye != src) var/atom/A = client.eye @@ -1081,28 +1079,24 @@ if(sight_mode & BORGMESON) sight |= SEE_TURFS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - see_in_dark = 2 + lighting_color_cutoffs = blend_cutoff_colors(lighting_color_cutoffs, list(5, 15, 5)) - if(sight_mode & BORGMESON_NIGHTVISION) + if(sight_mode & BORGMESON) sight |= SEE_TURFS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - see_in_dark = 8 + lighting_color_cutoffs = blend_cutoff_colors(lighting_color_cutoffs, list(5, 30, 5)) if(sight_mode & BORGMATERIAL) sight |= SEE_OBJS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - see_in_dark = 2 + lighting_color_cutoffs = blend_cutoff_colors(lighting_color_cutoffs, list(20, 25, 40)) if(sight_mode & BORGXRAY) sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS) see_invisible = SEE_INVISIBLE_LIVING - see_in_dark = 8 if(sight_mode & BORGTHERM) sight |= SEE_MOBS see_invisible = min(see_invisible, SEE_INVISIBLE_LIVING) - see_in_dark = 8 + lighting_color_cutoffs = blend_cutoff_colors(lighting_color_cutoffs, list(25, 8, 5)) if(see_override) see_invisible = see_override @@ -1110,7 +1104,7 @@ if(SSmapping.level_trait(z, ZTRAIT_NOXRAY)) sight = null - sync_lighting_plane_alpha() + sync_lighting_plane_cutoff() /mob/living/silicon/robot/update_stat() if(status_flags & GODMODE) diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 7e2cb6838954..63f34ab86dbb 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -6,7 +6,6 @@ verb_exclaim = "declares" verb_yell = "alarms" initial_language_holder = /datum/language_holder/synthetic - see_in_dark = 8 infra_luminosity = 0 bubble_icon = "machine" weather_immunities = list("ash") diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm index 2332142dc0e4..461f7e347f6c 100644 --- a/code/modules/mob/living/simple_animal/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs.dm @@ -16,8 +16,10 @@ stop_automated_movement = 1 status_flags = CANPUSH attack_sound = 'sound/weapons/punch1.ogg' - see_in_dark = 7 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + // Vivid red, cause cult theme + lighting_cutoff_red = 30 + lighting_cutoff_green = 5 + lighting_cutoff_blue = 20 damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) minbodytemp = 0 diff --git a/code/modules/mob/living/simple_animal/eldritch_demons.dm b/code/modules/mob/living/simple_animal/eldritch_demons.dm index 1bd51dd4308b..3d1f7afefdf0 100644 --- a/code/modules/mob/living/simple_animal/eldritch_demons.dm +++ b/code/modules/mob/living/simple_animal/eldritch_demons.dm @@ -15,8 +15,10 @@ stop_automated_movement = 1 AIStatus = AI_OFF attack_sound = 'sound/weapons/punch1.ogg' - see_in_dark = 7 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + // Sort of greenish brown, to match the vibeTM + lighting_cutoff_red = 20 + lighting_cutoff_green = 25 + lighting_cutoff_blue = 5 damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) minbodytemp = 0 diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index 925bbe3462b9..f69b10974dd4 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -13,8 +13,6 @@ emote_see = list("shakes its head.", "shivers.") speak_chance = 1 turns_per_move = 5 - see_in_dark = 6 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE ventcrawler = VENTCRAWLER_ALWAYS pass_flags = PASSTABLE | PASSCOMPUTER mob_biotypes = MOB_ORGANIC|MOB_BEAST diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm index 0a967ca90dbe..5e6046c6d1b8 100644 --- a/code/modules/mob/living/simple_animal/friendly/dog.dm +++ b/code/modules/mob/living/simple_animal/friendly/dog.dm @@ -15,8 +15,10 @@ emote_hear = list("barks!", "woofs!", "yaps.","pants.") emote_see = list("shakes its head.", "chases its tail.","shivers.") faction = list("neutral") - see_in_dark = 5 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + // VERY red, to fit the eyes + lighting_cutoff_red = 22 + lighting_cutoff_green = 5 + lighting_cutoff_blue = 5 speak_chance = 1 turns_per_move = 10 gold_core_spawnable = FRIENDLY_SPAWN diff --git a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm index 3f28397fa82e..507a1b29f4bf 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm @@ -48,8 +48,10 @@ faction = list("neutral","silicon","turret") dextrous = TRUE dextrous_hud_type = /datum/hud/dextrous/drone - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - see_in_dark = 7 + // Going for a sort of pale green here + lighting_cutoff_red = 30 + lighting_cutoff_green = 35 + lighting_cutoff_blue = 25 can_be_held = TRUE held_items = list(null, null) ignores_capitalism = TRUE // Yogs -- Lets drones buy a damned smoke for christ's sake diff --git a/code/modules/mob/living/simple_animal/friendly/drone/inventory.dm b/code/modules/mob/living/simple_animal/friendly/drone/inventory.dm index 1124de738d39..56652f0ea487 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/inventory.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/inventory.dm @@ -66,7 +66,6 @@ I.screen_loc = null // will get moved if inventory is visible I.forceMove(src) - I.layer = ABOVE_HUD_LAYER I.plane = ABOVE_HUD_PLANE switch(slot) diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm index ff77a98efdf5..b091a7a0d843 100644 --- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm +++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm @@ -11,7 +11,6 @@ emote_see = list("shakes its head.", "stamps a foot.", "glares around.") speak_chance = 1 turns_per_move = 5 - see_in_dark = 6 butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 4, /obj/item/clothing/head/yogs/goatpelt = 1) response_help = "pets" response_disarm = "gently pushes aside" @@ -127,7 +126,6 @@ emote_see = list("shakes its head.") speak_chance = 1 turns_per_move = 5 - see_in_dark = 6 butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 6) response_help = "pets" response_disarm = "gently pushes aside" @@ -349,7 +347,6 @@ emote_see = list("nibbles at the ground.") speak_chance = 1 turns_per_move = 5 - see_in_dark = 6 butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 4) response_help = "pets" response_disarm = "gently pushes aside" diff --git a/code/modules/mob/living/simple_animal/friendly/fox.dm b/code/modules/mob/living/simple_animal/friendly/fox.dm index efb43b056b3d..91b763b6e3a9 100644 --- a/code/modules/mob/living/simple_animal/friendly/fox.dm +++ b/code/modules/mob/living/simple_animal/friendly/fox.dm @@ -12,7 +12,6 @@ emote_see = list("shakes its head.", "shivers.") speak_chance = 1 turns_per_move = 5 - see_in_dark = 6 butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 1) //3 -> 1, foxes aren't Big response_help = "pets" response_disarm = "gently pushes aside" diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm index 0db366f360e3..453cb5c9fd0f 100644 --- a/code/modules/mob/living/simple_animal/friendly/mouse.dm +++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm @@ -29,7 +29,6 @@ GLOBAL_VAR_INIT(mouse_killed, 0) emote_see = list("runs in a circle.", "shakes.") speak_chance = 1 turns_per_move = 5 - see_in_dark = 8 maxHealth = 5 health = 5 butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab/mouse = 1) @@ -41,7 +40,6 @@ GLOBAL_VAR_INIT(mouse_killed, 0) pass_flags = PASSTABLE | PASSGRILLE | PASSMOB mob_size = MOB_SIZE_TINY mob_biotypes = MOB_ORGANIC|MOB_BEAST - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE can_be_held = TRUE //mouse gaming worn_slot_flags = ITEM_SLOT_HEAD var/body_color //brown, gray and white, leave blank for random diff --git a/code/modules/mob/living/simple_animal/friendly/penguin.dm b/code/modules/mob/living/simple_animal/friendly/penguin.dm index d4042069e51d..e23795675f64 100644 --- a/code/modules/mob/living/simple_animal/friendly/penguin.dm +++ b/code/modules/mob/living/simple_animal/friendly/penguin.dm @@ -10,7 +10,6 @@ emote_see = list("shakes its beak.", "flaps it's wings.","preens itself.") faction = list("penguin") minbodytemp = 0 - see_in_dark = 5 speak_chance = 1 turns_per_move = 10 icon = 'icons/mob/penguins.dmi' diff --git a/code/modules/mob/living/simple_animal/guardian/types/ranged.dm b/code/modules/mob/living/simple_animal/guardian/types/ranged.dm index 9ff6b8827553..dac773498b73 100644 --- a/code/modules/mob/living/simple_animal/guardian/types/ranged.dm +++ b/code/modules/mob/living/simple_animal/guardian/types/ranged.dm @@ -63,23 +63,33 @@ if(namedatum) P.color = namedatum.color -/mob/living/simple_animal/hostile/guardian/ranged/ToggleLight() +/mob/living/simple_animal/hostile/guardian/ranged/toggle_light() var/msg - switch(lighting_alpha) - if (LIGHTING_PLANE_ALPHA_VISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + switch(lighting_cutoff) + if (LIGHTING_CUTOFF_VISIBLE) + lighting_cutoff_red = 10 + lighting_cutoff_green = 10 + lighting_cutoff_blue = 15 msg = "You activate your night vision." - if (LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + if (LIGHTING_CUTOFF_MEDIUM) + lighting_cutoff_red = 25 + lighting_cutoff_green = 25 + lighting_cutoff_blue = 35 msg = "You increase your night vision." - if (LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE + if (LIGHTING_CUTOFF_HIGH) + lighting_cutoff_red = 35 + lighting_cutoff_green = 35 + lighting_cutoff_blue = 50 msg = "You maximize your night vision." else lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE + lighting_cutoff_red = 0 + lighting_cutoff_green = 0 + lighting_cutoff_blue = 0 msg = "You deactivate your night vision." + sync_lighting_plane_cutoff() + to_chat(src, span_notice(msg)) - to_chat(src, span_notice("[msg]")) /mob/living/simple_animal/hostile/guardian/ranged/verb/Snare() diff --git a/code/modules/mob/living/simple_animal/hostile/alien.dm b/code/modules/mob/living/simple_animal/hostile/alien.dm index ead1ba836cb9..5561f20b757d 100644 --- a/code/modules/mob/living/simple_animal/hostile/alien.dm +++ b/code/modules/mob/living/simple_animal/hostile/alien.dm @@ -31,8 +31,10 @@ faction = list(ROLE_ALIEN) status_flags = CANPUSH minbodytemp = 0 - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + // Going for a dark purple here + lighting_cutoff_red = 30 + lighting_cutoff_green = 15 + lighting_cutoff_blue = 50 unique_name = 1 gold_core_spawnable = NO_SPAWN deathsound = 'sound/voice/hiss6.ogg' diff --git a/code/modules/mob/living/simple_animal/hostile/bear.dm b/code/modules/mob/living/simple_animal/hostile/bear.dm index b8af2e8da74f..a5588ada87b6 100644 --- a/code/modules/mob/living/simple_animal/hostile/bear.dm +++ b/code/modules/mob/living/simple_animal/hostile/bear.dm @@ -14,7 +14,6 @@ speak_chance = 1 taunt_chance = 25 turns_per_move = 5 - see_in_dark = 6 butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab/bear = 5, /obj/item/clothing/head/bearpelt = 1) response_help = "pets" response_disarm = "gently pushes aside" diff --git a/code/modules/mob/living/simple_animal/hostile/eyeballs.dm b/code/modules/mob/living/simple_animal/hostile/eyeballs.dm index f14228d72160..f08e6b472af7 100644 --- a/code/modules/mob/living/simple_animal/hostile/eyeballs.dm +++ b/code/modules/mob/living/simple_animal/hostile/eyeballs.dm @@ -26,3 +26,7 @@ faction = list("spooky") del_on_death = 1 random_color = FALSE + // Redish ethereal glow. These lads live on the cult ship + lighting_cutoff_red = 40 + lighting_cutoff_green = 20 + lighting_cutoff_blue = 30 diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm index bfc88357e0b8..820234f07f18 100644 --- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm @@ -46,8 +46,7 @@ attack_sound = 'sound/weapons/bite.ogg' unique_name = 1 gold_core_spawnable = HOSTILE_SPAWN - see_in_dark = 4 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + footstep_type = FOOTSTEP_MOB_CLAW var/busy = SPIDER_IDLE var/playable_spider = FALSE diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/_jungle_mobs.dm b/code/modules/mob/living/simple_animal/hostile/jungle/_jungle_mobs.dm index 959bef634b01..af1afb4bac93 100644 --- a/code/modules/mob/living/simple_animal/hostile/jungle/_jungle_mobs.dm +++ b/code/modules/mob/living/simple_animal/hostile/jungle/_jungle_mobs.dm @@ -12,6 +12,8 @@ response_harm = "strikes" status_flags = NONE a_intent = INTENT_HARM - see_in_dark = 4 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + // Let's do a blue, since they'll be on green turfs if this shit is ever finished + lighting_cutoff_red = 5 + lighting_cutoff_green = 20 + lighting_cutoff_blue = 25 mob_size = MOB_SIZE_LARGE diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm b/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm index 7607ed386e14..09cf499c80b0 100644 --- a/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm +++ b/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm @@ -57,7 +57,7 @@ name = "beam of solar energy" icon_state = "solar_beam" icon = 'icons/effects/beam.dmi' - layer = LIGHTING_LAYER + plane = LIGHTING_PLANE duration = 5 randomdir = FALSE diff --git a/code/modules/mob/living/simple_animal/hostile/killertomato.dm b/code/modules/mob/living/simple_animal/hostile/killertomato.dm index 17658cc7c1b4..2a26a7060f8e 100644 --- a/code/modules/mob/living/simple_animal/hostile/killertomato.dm +++ b/code/modules/mob/living/simple_animal/hostile/killertomato.dm @@ -9,7 +9,6 @@ turns_per_move = 5 maxHealth = 30 health = 30 - see_in_dark = 3 butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab/killertomato = 2) response_help = "prods" response_disarm = "pushes aside" diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm index 711b67818090..35c5c617a2b6 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm @@ -52,6 +52,10 @@ Difficulty: Medium attack_action_types = list(/datum/action/innate/megafauna_attack/create_skull, /datum/action/innate/megafauna_attack/charge_target) small_sprite_type = /datum/action/small_sprite/megafauna/legion + // Purple, but bright cause we're gonna need to spot mobs on lavaland + lighting_cutoff_red = 35 + lighting_cutoff_green = 20 + lighting_cutoff_blue = 45 /datum/action/innate/megafauna_attack/create_skull name = "Create Legion Skull" diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm index 5ee206d44c72..611a4a7d5b8a 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm @@ -279,9 +279,10 @@ weather_immunities = list(WEATHER_LAVA, WEATHER_ASH) obj_damage = 30 environment_smash = ENVIRONMENT_SMASH_STRUCTURES - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - + // Purple, but bright cause we're gonna need to spot mobs on lavaland + lighting_cutoff_red = 35 + lighting_cutoff_green = 20 + lighting_cutoff_blue = 45 /mob/living/simple_animal/hostile/big_legion/Initialize(mapload) .=..() diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/mining_mobs.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/mining_mobs.dm index ff2ab1ed7ed9..4d78888ddba4 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/mining_mobs.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/mining_mobs.dm @@ -16,8 +16,10 @@ var/crusher_loot var/throw_message = "bounces off of" var/fromtendril = FALSE - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + // Pale purple, should be red enough to see stuff on lavaland + lighting_cutoff_red = 25 + lighting_cutoff_green = 15 + lighting_cutoff_blue = 35 mob_size = MOB_SIZE_LARGE var/icon_aggro = null var/crusher_drop_mod = 25 diff --git a/code/modules/mob/living/simple_animal/hostile/rat.dm b/code/modules/mob/living/simple_animal/hostile/rat.dm index a99c17ffc4eb..518e8210049b 100644 --- a/code/modules/mob/living/simple_animal/hostile/rat.dm +++ b/code/modules/mob/living/simple_animal/hostile/rat.dm @@ -13,8 +13,6 @@ obj_damage = 5 speak_chance = 1 turns_per_move = 5 - see_in_dark = 6 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE maxHealth = 15 health = 15 butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab/mouse = 1) diff --git a/code/modules/mob/living/simple_animal/hostile/regalrat.dm b/code/modules/mob/living/simple_animal/hostile/regalrat.dm index 8aee2da1ca0e..b2cfd9d32b77 100644 --- a/code/modules/mob/living/simple_animal/hostile/regalrat.dm +++ b/code/modules/mob/living/simple_animal/hostile/regalrat.dm @@ -9,7 +9,6 @@ turns_per_move = 5 maxHealth = 70 health = 70 - see_in_dark = 15 obj_damage = 10 butcher_results = list(/obj/item/clothing/head/crown = 1,) response_help = "glares at" @@ -24,7 +23,11 @@ ventcrawler = VENTCRAWLER_ALWAYS unique_name = TRUE faction = list("rat") - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + // Slightly brown red, for the eyes + // Might be a bit too dim + lighting_cutoff_red = 22 + lighting_cutoff_green = 8 + lighting_cutoff_blue = 5 var/datum/action/cooldown/riot var/datum/action/cooldown/domain var/opening_airlock = FALSE diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/bat.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/bat.dm index 25567bc1d6aa..cf4fad70e66d 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/bat.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/bat.dm @@ -14,8 +14,6 @@ maxHealth = 15 health = 15 spacewalk = TRUE - see_in_dark = 10 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE harm_intent_damage = 6 melee_damage_lower = 6 melee_damage_upper = 5 diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/trianglespider.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/trianglespider.dm index 2986111e54b5..f041887315e4 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/trianglespider.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/trianglespider.dm @@ -26,5 +26,3 @@ attack_sound = 'sound/weapons/bite.ogg' unique_name = 1 gold_core_spawnable = HOSTILE_SPAWN - see_in_dark = 4 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE diff --git a/code/modules/mob/living/simple_animal/hostile/skeleton.dm b/code/modules/mob/living/simple_animal/hostile/skeleton.dm index ce1eb05e880d..5f342e06eccd 100644 --- a/code/modules/mob/living/simple_animal/hostile/skeleton.dm +++ b/code/modules/mob/living/simple_animal/hostile/skeleton.dm @@ -29,8 +29,10 @@ stat_attack = UNCONSCIOUS gold_core_spawnable = HOSTILE_SPAWN faction = list("skeleton") - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + // Going for a sort of pale bluegreen here, shooting for boneish + lighting_cutoff_red = 15 + lighting_cutoff_green = 25 + lighting_cutoff_blue = 35 deathmessage = "collapses into a pile of bones!" del_on_death = 1 loot = list(/obj/effect/decal/remains/human) diff --git a/code/modules/mob/living/simple_animal/hostile/smspider.dm b/code/modules/mob/living/simple_animal/hostile/smspider.dm index 52350aa77c05..ebe30b9a04a2 100644 --- a/code/modules/mob/living/simple_animal/hostile/smspider.dm +++ b/code/modules/mob/living/simple_animal/hostile/smspider.dm @@ -21,8 +21,10 @@ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_plas" = 0, "max_plas" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) robust_searching = 1 faction = list("hostile") - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + // Gold, supermatter tinted + lighting_cutoff_red = 30 + lighting_cutoff_green = 30 + lighting_cutoff_blue = 10 ventcrawler = VENTCRAWLER_ALWAYS deathmessage = "falls to the ground, its shard dulling to a miserable grey!" var/overcharged = FALSE // if true, spider will not die if it dusts a limb @@ -55,4 +57,4 @@ icon_living = "smspideroc" maxHealth = 25 health = 25 - overcharged = TRUE \ No newline at end of file + overcharged = TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/space_dragon.dm b/code/modules/mob/living/simple_animal/hostile/space_dragon.dm index 6ea19253f06b..3bab5bd24821 100644 --- a/code/modules/mob/living/simple_animal/hostile/space_dragon.dm +++ b/code/modules/mob/living/simple_animal/hostile/space_dragon.dm @@ -38,8 +38,6 @@ icon_dead = "spacedragon_dead" health_doll_icon = "spacedragon" obj_damage = 50 - see_in_dark = 7 //yogs - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE //yogs environment_smash = ENVIRONMENT_SMASH_NONE flags_1 = HEAR_1 | PREVENT_CONTENTS_EXPLOSION_1 mob_size = MOB_SIZE_LARGE diff --git a/code/modules/mob/living/simple_animal/hostile/statue.dm b/code/modules/mob/living/simple_animal/hostile/statue.dm index 55c8f67fa8ba..ee396f27c7fa 100644 --- a/code/modules/mob/living/simple_animal/hostile/statue.dm +++ b/code/modules/mob/living/simple_animal/hostile/statue.dm @@ -36,8 +36,10 @@ animate_movement = NO_STEPS // Do not animate movement, you jump around as you're a scary statue. hud_possible = list(ANTAG_HUD) - see_in_dark = 13 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + // Dim purple, I want it to be possible to miss people in the dark but not easy to + lighting_cutoff_red = 15 + lighting_cutoff_green = 10 + lighting_cutoff_blue = 25 vision_range = 12 aggro_vision_range = 12 @@ -64,8 +66,6 @@ flicker.Grant(src) var/datum/action/cooldown/spell/aoe/blindness/blind = new(src) blind.Grant(src) - var/datum/action/cooldown/spell/night_vision/night_vision = new(src) - night_vision.Grant(src) // Set creator if(creator) diff --git a/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm b/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm index 4ed3901312fe..ae31adfe8284 100644 --- a/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm +++ b/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm @@ -94,7 +94,10 @@ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) unsuitable_atmos_damage = 0 sight = SEE_SELF|SEE_MOBS|SEE_OBJS|SEE_TURFS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + // Real green, cause of course + lighting_cutoff_red = 10 + lighting_cutoff_green = 35 + lighting_cutoff_blue = 20 faction = list("hostile","vines","plants") initial_language_holder = /datum/language_holder/venus del_on_death = TRUE diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 2a9110f00bfb..81002d25154b 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -480,12 +480,10 @@ sight = initial(sight) else sight = (SEE_TURFS|SEE_MOBS|SEE_OBJS) - see_in_dark = 8 see_invisible = SEE_INVISIBLE_OBSERVER return see_invisible = initial(see_invisible) - see_in_dark = initial(see_in_dark) sight = initial(sight) if(SSmapping.level_trait(z, ZTRAIT_NOXRAY)) sight = null @@ -493,7 +491,7 @@ var/atom/A = client.eye if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates. return - sync_lighting_plane_alpha() + sync_lighting_plane_cutoff() /mob/living/simple_animal/get_idcard(hand_first) return access_card diff --git a/code/modules/mob/living/simple_animal/status_procs.dm b/code/modules/mob/living/simple_animal/status_procs.dm deleted file mode 100644 index 62a262f9e605..000000000000 --- a/code/modules/mob/living/simple_animal/status_procs.dm +++ /dev/null @@ -1,25 +0,0 @@ - -/mob/living/simple_animal/blind_eyes() - return - -/mob/living/simple_animal/adjust_blindness() - return - -/mob/living/simple_animal/set_blindness() - return - - - -/mob/living/simple_animal/blur_eyes() - return - -/mob/living/simple_animal/adjust_blurriness() - return - -/mob/living/simple_animal/set_blurriness() - return - - - -/mob/living/simple_animal/become_blind() - return diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 399aa3e7786d..4e007f5699d4 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -259,19 +259,25 @@ //This entire if/else chain could be in two lines but isn't for readibilties sake. var/msg = message + var/msg_type = MSG_VISUAL + if(M.see_invisible < invisibility)//if src is invisible to M msg = blind_message + msg_type = MSG_AUDIBLE else if(T != loc && T != src) //if src is inside something and not a turf. + if(M != loc) // Only give the blind message to hearers that aren't the location + msg = blind_message + msg_type = MSG_AUDIBLE + else if(!HAS_TRAIT(M, TRAIT_HEAR_THROUGH_DARKNESS) && M.lighting_cutoff < LIGHTING_CUTOFF_HIGH && T.is_softly_lit() && !in_range(T,M)) //if it is too dark, unless we're right next to them. msg = blind_message - else if(M.lighting_alpha > LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE && T.is_softly_lit() && !in_range(T,M)) //if it is too dark. - msg = blind_message + msg_type = MSG_AUDIBLE if(!msg) continue if(visible_message_flags & EMOTE_MESSAGE && runechat_prefs_check(M, visible_message_flags) && !is_blind(M)) M.create_chat_message(src, raw_message = raw_msg, runechat_flags = visible_message_flags) - M.show_message(msg, MSG_VISUAL, blind_message, MSG_AUDIBLE) + M.show_message(msg, msg_type, blind_message, MSG_AUDIBLE) @@ -1240,15 +1246,15 @@ ///Update the lighting plane and sight of this mob (sends COMSIG_MOB_UPDATE_SIGHT) /mob/proc/update_sight() SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) - sync_lighting_plane_alpha() + sync_lighting_plane_cutoff() ///Set the lighting plane hud alpha to the mobs lighting_alpha var -/mob/proc/sync_lighting_plane_alpha() - if(hud_used) - var/atom/movable/screen/plane_master/lighting/L = hud_used.plane_masters["[LIGHTING_PLANE]"] - if (L) - L.alpha = lighting_alpha - +/mob/proc/sync_lighting_plane_cutoff() + if(!hud_used) + return + for(var/atom/movable/screen/plane_master/rendering_plate/lighting/light as anything in hud_used.get_true_plane_masters(RENDER_PLANE_LIGHTING)) + light.set_light_cutoff(lighting_cutoff, lighting_color_cutoffs) + ///Update the mouse pointer of the attached client in this mob /mob/proc/update_mouse_pointer() if (!client) @@ -1266,6 +1272,10 @@ if(client.mouse_override_icon) client.mouse_pointer_icon = client.mouse_override_icon +/mob/proc/has_nightvision() + // Somewhat conservative, basically is your lighting plane bright enough that you the user can see stuff + var/light_offset = (lighting_color_cutoffs[1] + lighting_color_cutoffs[2] + lighting_color_cutoffs[3]) / 3 + lighting_cutoff + return light_offset >= LIGHTING_NIGHTVISION_THRESHOLD ///This mob is abile to read books /mob/proc/is_literate() diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index d1a482ed007f..7307065a2d22 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -17,8 +17,23 @@ mouse_drag_pointer = MOUSE_ACTIVE_POINTER throwforce = 10 blocks_emissive = EMISSIVE_BLOCK_GENERIC - - var/lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE + // we never want to hide a turf because it's not lit + // We can rely on the lighting plane to handle that for us + see_in_dark = 1e6 + + /// Percentage of how much rgb to max the lighting plane at + /// This lets us brighten it without washing out color + /// Scale from 0-100, reset off update_sight() + var/lighting_cutoff = LIGHTING_CUTOFF_VISIBLE + // Individual color max for red, we can use this to color darkness without tinting the light + var/lighting_cutoff_red = 0 + // Individual color max for green, we can use this to color darkness without tinting the light + var/lighting_cutoff_green = 0 + // Individual color max for blue, we can use this to color darkness without tinting the light + var/lighting_cutoff_blue = 0 + /// A list of red, green and blue cutoffs + /// This is what actually gets applied to the mob, it's modified by things like glasses + var/list/lighting_color_cutoffs = null var/datum/mind/mind var/static/next_mob_id = 0 /// The current client inhabiting this mob. Managed by login/logout @@ -237,3 +252,4 @@ var/datum/focus var/fake_client = FALSE // Currently only used for examines + diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index d42751d8181b..e9b5fe3be285 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -612,3 +612,8 @@ if(chosen_bodypart.status < BODYPART_ROBOTIC) amount += chosen_bodypart.burn_dam return amount + +/mob/proc/default_lighting_cutoff() + if(client?.combo_hud_enabled && client?.prefs?.toggles & COMBOHUD_LIGHTING) + return LIGHTING_CUTOFF_FULLBRIGHT + return initial(lighting_cutoff) diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index e62c604fd91f..5ebf5ca364d6 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -528,7 +528,7 @@ return ..() ///Move a mob between z levels, if it's valid to move z's on this turf -/mob/proc/zMove(dir, feedback = FALSE) +/mob/proc/zMoveMob(dir, feedback = FALSE) if(dir != UP && dir != DOWN) return FALSE var/turf/target = get_step_multiz(src, dir) diff --git a/code/modules/mob/status_procs.dm b/code/modules/mob/status_procs.dm index 345156098958..d74abe19e4ca 100644 --- a/code/modules/mob/status_procs.dm +++ b/code/modules/mob/status_procs.dm @@ -71,35 +71,6 @@ clear_alert("blind") clear_fullscreen("blind") -/** - * Make the mobs vision blurry - */ -/mob/proc/blur_eyes(amount) - if(amount>0) - eye_blurry = max(amount, eye_blurry) - update_eye_blur() - -/** - * Adjust the current blurriness of the mobs vision by amount - */ -/mob/proc/adjust_blurriness(amount) - eye_blurry = max(eye_blurry+amount, 0) - update_eye_blur() - -///Set the mobs blurriness of vision to an amount -/mob/proc/set_blurriness(amount) - eye_blurry = max(amount, 0) - update_eye_blur() - -///Apply the blurry overlays to a mobs clients screen -/mob/proc/update_eye_blur() - if(!client) - return - var/atom/movable/screen/plane_master/floor/OT = locate(/atom/movable/screen/plane_master/floor) in client.screen - var/atom/movable/screen/plane_master/game_world/GW = locate(/atom/movable/screen/plane_master/game_world) in client.screen - GW.backdrop(src) - OT.backdrop(src) - ///Adjust the disgust level of a mob /mob/proc/adjust_disgust(amount) return @@ -122,3 +93,31 @@ // 10C has 3 range (2 tiles) // 0C has 0 range (0 tiles) infra_luminosity = round(max((bodytemperature - T0C)/3, 0)) + +/// Sight here is the mob.sight var, which tells byond what to actually show to our client +/// See [code\__DEFINES\sight.dm] for more details +/mob/proc/set_sight(new_value) + SHOULD_CALL_PARENT(TRUE) + if(sight == new_value) + return + var/old_sight = sight + sight = new_value + + SEND_SIGNAL(src, COMSIG_MOB_SIGHT_CHANGE, new_value, old_sight) + +/mob/proc/add_sight(new_value) + set_sight(sight | new_value) + +/mob/proc/clear_sight(new_value) + set_sight(sight & ~new_value) + +/// see invisibility is the mob's capability to see things that ought to be hidden from it +/// Can think of it as a primitive version of changing the alpha of planes +/// We mostly use it to hide ghosts, no real reason why +/mob/proc/set_invis_see(new_sight) + SHOULD_CALL_PARENT(TRUE) + if(new_sight == see_invisible) + return + var/old_invis = see_invisible + see_invisible = new_sight + SEND_SIGNAL(src, COMSIG_MOB_SEE_INVIS_CHANGE, see_invisible, old_invis) diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index 18a00b5e236f..e2cd3fd4170a 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -268,35 +268,23 @@ . += get_modular_computer_parts_examine(user) -/obj/item/modular_computer/update_icon(updates=ALL) +/obj/item/modular_computer/update_icon_state() + if(!icon_state_powered || !icon_state_unpowered) //no valid icon, don't update. + return ..() + icon_state = enabled ? icon_state_powered : icon_state_unpowered + return ..() + +/obj/item/modular_computer/update_overlays() . = ..() - if(!physical) + var/init_icon = initial(icon) + if(!init_icon) return - SSvis_overlays.remove_vis_overlay(physical, physical.managed_vis_overlays) - var/program_overlay = "" - var/is_broken = obj_integrity <= integrity_failure - if(overlay_skin) - program_overlay = "[overlay_skin]-" - if(!enabled) - if(use_power() && !isnull(icon_state_screensaver)) - program_overlay += icon_state_screensaver - else - icon_state = icon_state_unpowered - else - icon_state = icon_state_powered - if(is_broken) - program_overlay += "bsod" - else - if(active_program) - program_overlay += active_program.program_icon_state ? "[active_program.program_icon_state]" : "[icon_state_menu]" - else - program_overlay += icon_state_menu - - SSvis_overlays.add_vis_overlay(physical, physical.icon, program_overlay, physical.layer, physical.plane, physical.dir) - SSvis_overlays.add_vis_overlay(physical, physical.icon, program_overlay, physical.layer, EMISSIVE_PLANE, physical.dir) - if(is_broken) - SSvis_overlays.add_vis_overlay(physical, physical.icon, "broken", physical.layer, physical.plane, physical.dir) + if(enabled) + . += active_program ? mutable_appearance(init_icon, active_program.program_icon_state) : mutable_appearance(init_icon, icon_state_menu) + if(obj_integrity <= integrity_failure) + . += mutable_appearance(init_icon, "bsod") + . += mutable_appearance(init_icon, "broken") /obj/item/modular_computer/equipped() . = ..() @@ -345,7 +333,7 @@ else to_chat(user, span_notice("You press the power button and start up \the [src].")) enabled = TRUE - update_appearance(UPDATE_ICON) + update_appearance() play_computer_sound(startup_sound, get_clamped_volume(), FALSE) ui_interact(user) else // Unpowered @@ -487,7 +475,7 @@ var/mob/user = usr if(user && istype(user)) ui_interact(user) // Re-open the UI on this computer. It should show the main screen now. - update_appearance(UPDATE_ICON) + update_appearance() // Returns 0 for No Signal, 1 for Low Signal and 2 for Good Signal. 3 is for wired connection (always-on) /obj/item/modular_computer/proc/get_ntnet_status(specific_action = 0) @@ -503,7 +491,7 @@ var/obj/item/computer_hardware/network_card/network_card = all_components[MC_NET] return SSnetworks.station_network.add_log(text, network_card) -/obj/item/modular_computer/proc/shutdown_computer(loud = 1) +/obj/item/modular_computer/proc/shutdown_computer(loud = TRUE) kill_program(forced = TRUE) for(var/datum/computer_file/program/P in idle_threads) P.kill_program(forced = TRUE) @@ -511,8 +499,8 @@ if(loud) physical.visible_message(span_notice("\The [src] shuts down.")) enabled = FALSE - update_appearance(UPDATE_ICON) play_computer_sound(shutdown_sound, get_clamped_volume(), FALSE) + update_appearance() /** * Toggles the computer's flashlight, if it has one. @@ -524,7 +512,7 @@ if(!has_light) return FALSE set_light_on(!light_on) - update_appearance(UPDATE_ICON) + update_appearance() return TRUE /** diff --git a/code/modules/modular_computers/file_system/programs/security/secureye.dm b/code/modules/modular_computers/file_system/programs/security/secureye.dm index 82b6f9ac1c29..4969b10be6f4 100644 --- a/code/modules/modular_computers/file_system/programs/security/secureye.dm +++ b/code/modules/modular_computers/file_system/programs/security/secureye.dm @@ -1,3 +1,4 @@ + #define DEFAULT_MAP_SIZE 15 /datum/computer_file/program/secureye @@ -185,6 +186,7 @@ camlist["[cam.c_tag]"] = cam return camlist + ////////////////// //Mining Cameras// ////////////////// @@ -215,3 +217,5 @@ program_icon = "dungeon" network = list("labor") + +#undef DEFAULT_MAP_SIZE diff --git a/code/modules/modular_computers/hardware/card_slot.dm b/code/modules/modular_computers/hardware/card_slot.dm index f0d10f749525..807c43d78b64 100644 --- a/code/modules/modular_computers/hardware/card_slot.dm +++ b/code/modules/modular_computers/hardware/card_slot.dm @@ -77,7 +77,7 @@ if(ishuman(user)) var/mob/living/carbon/human/H = user H.sec_hud_set_ID() - + holder.update_appearance() return TRUE @@ -93,6 +93,7 @@ to_chat(user, "You remove the card from \the [src].") playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) + holder.update_appearance() return TRUE /obj/item/computer_hardware/card_slot/attackby(obj/item/I, mob/living/user) diff --git a/code/modules/paperwork/paperplane.dm b/code/modules/paperwork/paperplane.dm index 99adb60cdc7d..0a7af4a43f1d 100644 --- a/code/modules/paperwork/paperplane.dm +++ b/code/modules/paperwork/paperplane.dm @@ -46,7 +46,7 @@ var/obj/item/organ/eyes/eyes = user.getorganslot(ORGAN_SLOT_EYES) user.Stun(200) user.visible_message(span_suicide("[user] jams [src] in [user.p_their()] nose. It looks like [user.p_theyre()] trying to commit suicide!")) - user.adjust_blurriness(6) + user.adjust_eye_blur(6) if(eyes) eyes.applyOrganDamage(rand(6,8)) sleep(1 SECONDS) @@ -114,7 +114,7 @@ if(H.is_eyes_covered()) return visible_message(span_danger("\The [src] hits [H] in the eye!")) - H.adjust_blurriness(6) + H.adjust_eye_blur(6) eyes.applyOrganDamage(rand(6,8)) H.Paralyze(40) H.emote("scream") diff --git a/code/modules/point/point.dm b/code/modules/point/point.dm index bda5e2a63407..d28d1811d82f 100644 --- a/code/modules/point/point.dm +++ b/code/modules/point/point.dm @@ -68,7 +68,7 @@ name = "pointer" icon = 'icons/mob/screen_gen.dmi' icon_state = "arrow" - layer = POINT_LAYER + plane = POINT_PLANE duration = POINT_TIME /obj/effect/temp_visual/point/Initialize(mapload, set_invis = 0) diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 215bfb7fab95..b7555ca63918 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -69,7 +69,7 @@ dir_amount = 4\ ) - var/lon_range = 1.5 + var/light_on_range = 1.5 var/area/area var/areastring = null var/obj/item/stock_parts/cell/cell @@ -230,7 +230,7 @@ /obj/machinery/power/apc/handle_atom_del(atom/A) if(A == cell) cell = null - update_appearance(UPDATE_ICON) + update_appearance() updateUsrDialog() /obj/machinery/power/apc/proc/make_terminal() @@ -265,7 +265,7 @@ make_terminal() addtimer(CALLBACK(src, PROC_REF(update)), 5) - update_appearance(UPDATE_ICON) + update_appearance() /obj/machinery/power/apc/examine(mob/user) . = ..() @@ -323,8 +323,11 @@ cell = best_cell W.play_rped_sound() -/obj/machinery/power/apc/update_icon(updates) - updates = check_updates() +/obj/machinery/power/apc/update_appearance(updates = check_updates()) + icon_update_needed = FALSE + if(!updates) + return + . = ..() // And now, separately for cleanness, the lighting changing if(update_state & UPSTATE_ALLGOOD) @@ -335,15 +338,13 @@ light_color = LIGHT_COLOR_BLUE if(APC_FULLY_CHARGED) light_color = LIGHT_COLOR_GREEN - set_light(lon_range) + set_light(light_on_range) else if(update_state & UPSTATE_BLUESCREEN) light_color = LIGHT_COLOR_BLUE - set_light(lon_range) + set_light(light_on_range) else set_light(0) - icon_update_needed = FALSE - // update the APC icon to show the three base states // also add overlays for indicator lights /obj/machinery/power/apc/update_icon_state() @@ -374,21 +375,20 @@ /obj/machinery/power/apc/update_overlays() . = ..() if(!(update_state & UPSTATE_ALLGOOD)) - SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays) + return - SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays) if(!(stat & (BROKEN|MAINT)) && update_state & UPSTATE_ALLGOOD) - SSvis_overlays.add_vis_overlay(src, icon, "apcox-[locked]", layer, plane, dir) - SSvis_overlays.add_vis_overlay(src, icon, "apcox-[locked]", layer, EMISSIVE_PLANE, dir) - SSvis_overlays.add_vis_overlay(src, icon, "apco3-[charging]", layer, plane, dir) - SSvis_overlays.add_vis_overlay(src, icon, "apco3-[charging]", layer, EMISSIVE_PLANE, dir) + . += mutable_appearance(icon, "apcox-[locked]") + . += emissive_appearance(icon, "apcox-[locked]", src) + . += mutable_appearance(icon, "apco3-[charging]") + . += emissive_appearance(icon, "apco3-[charging]", src) if(operating) - SSvis_overlays.add_vis_overlay(src, icon, "apco0-[equipment]", layer, plane, dir) - SSvis_overlays.add_vis_overlay(src, icon, "apco0-[equipment]", layer, EMISSIVE_PLANE, dir) - SSvis_overlays.add_vis_overlay(src, icon, "apco1-[lighting]", layer, plane, dir) - SSvis_overlays.add_vis_overlay(src, icon, "apco1-[lighting]", layer, EMISSIVE_PLANE, dir) - SSvis_overlays.add_vis_overlay(src, icon, "apco2-[environ]", layer, plane, dir) - SSvis_overlays.add_vis_overlay(src, icon, "apco2-[environ]", layer, EMISSIVE_PLANE, dir) + . += mutable_appearance(icon, "apco0-[equipment]") + . += emissive_appearance(icon, "apco0-[equipment]", src) + . += mutable_appearance(icon, "apco1-[lighting]") + . += emissive_appearance(icon, "apco1-[lighting]", src) + . += mutable_appearance(icon, "apco2-[environ]") + . += emissive_appearance(icon, "apco2-[environ]", src) /obj/machinery/power/apc/proc/check_updates() var/last_update_state = update_state @@ -515,7 +515,7 @@ else if (opened!=APC_COVER_REMOVED) opened = APC_COVER_CLOSED coverlocked = TRUE //closing cover relocks it - update_appearance(UPDATE_ICON) + update_appearance() return else if (!(stat & BROKEN)) if(coverlocked && !(stat & MAINT)) // locked... @@ -526,7 +526,7 @@ return else opened = APC_COVER_OPENED - update_appearance(UPDATE_ICON) + update_appearance() return else W.play_tool_sound(src) @@ -535,7 +535,7 @@ W.play_tool_sound(src) to_chat(user, span_notice("You pry the broken cover off of [src].")) opened = APC_COVER_REMOVED - update_appearance(UPDATE_ICON) + update_appearance() return /obj/machinery/power/apc/screwdriver_act(mob/living/user, obj/item/W) @@ -547,10 +547,10 @@ user.visible_message("[user] removes \the [cell] from [src]!",span_notice("You remove \the [cell].")) var/turf/T = get_turf(user) cell.forceMove(T) - cell.update_appearance(UPDATE_ICON) + cell.update_appearance() cell = null charging = APC_NOT_CHARGING - update_appearance(UPDATE_ICON) + update_appearance() return else switch (has_electronics) @@ -567,14 +567,14 @@ else to_chat(user, span_warning("There is nothing to secure!")) return - update_appearance(UPDATE_ICON) + update_appearance() else if(obj_flags & EMAGGED) to_chat(user, span_warning("The interface is broken!")) return else panel_open = !panel_open to_chat(user, span_notice("The wires have been [panel_open ? "exposed" : "unexposed"].")) - update_appearance(UPDATE_ICON) + update_appearance() /obj/machinery/power/apc/wirecutter_act(mob/living/user, obj/item/W) if (terminal && opened) @@ -623,7 +623,7 @@ "[user.name] has inserted the power cell to [src.name]!",\ span_notice("You insert the power cell.")) chargecount = 0 - update_appearance(UPDATE_ICON) + update_appearance() else if (W.GetID()) togglelock(user) else if (istype(W, /obj/item/stack/cable_coil) && opened) @@ -701,7 +701,7 @@ chargecount = 0 user.visible_message(span_notice("[user] fabricates a weak power cell and places it into [src]."), \ span_warning("Your [P.name] whirrs with strain as you create a weak power cell and place it into [src]!")) - update_appearance(UPDATE_ICON) + update_appearance() else to_chat(user, span_warning("[src] has both electronics and a cell.")) return @@ -716,7 +716,7 @@ to_chat(user, span_notice("You replace missing APC's cover.")) qdel(W) opened = APC_COVER_OPENED - update_appearance(UPDATE_ICON) + update_appearance() return if (has_electronics) to_chat(user, span_warning("You cannot repair this APC until you remove the electronics still inside!")) @@ -730,7 +730,7 @@ obj_integrity = max_integrity if (opened==APC_COVER_REMOVED) opened = APC_COVER_OPENED - update_appearance(UPDATE_ICON) + update_appearance() else if(istype(W, /obj/item/clockwork/integration_cog) && is_servant_of_ratvar(user)) if(integration_cog) to_chat(user, span_warning("This APC already has a cog.")) @@ -739,7 +739,7 @@ user.visible_message(span_warning("[user] slices [src]'s cover lock, and it swings wide open!"), \ span_alloy("You slice [src]'s cover lock apart with [W], and the cover swings open.")) opened = APC_COVER_OPENED - update_appearance(UPDATE_ICON) + update_appearance() else user.visible_message(span_warning("[user] presses [W] into [src]!"), \ span_alloy("You hold [W] in place within [src], and it slowly begins to warm up...")) @@ -756,7 +756,7 @@ playsound(src, 'sound/machines/clockcult/steam_whoosh.ogg', 50, FALSE) opened = APC_COVER_CLOSED locked = FALSE - update_appearance(UPDATE_ICON) + update_appearance() return else if(istype(W, /obj/item/apc_powercord)) return //because we put our fancy code in the right places, and this is all in the powercord's afterattack() @@ -812,7 +812,7 @@ chargecount = 0 user.visible_message(span_notice("[user] fabricates a weak power cell and places it into [src]."), \ span_warning("Your [the_rcd.name] whirrs with strain as you create a weak power cell and place it into [src]!")) - update_appearance(UPDATE_ICON) + update_appearance() return TRUE else to_chat(user, span_warning("[src] has both electronics and a cell.")) @@ -832,7 +832,7 @@ if((allowed(usr) && !wires.is_cut(WIRE_IDSCAN) && !malfhack) || integration_cog) locked = !locked to_chat(user, span_notice("You [ locked ? "lock" : "unlock"] the APC interface.")) - update_appearance(UPDATE_ICON) + update_appearance() updateUsrDialog() else to_chat(user, span_warning("Access denied.")) @@ -843,10 +843,10 @@ return last_light_switch = world.time area.lightswitch = !area.lightswitch - area.update_appearance(UPDATE_ICON) + area.update_appearance() for(var/obj/machinery/light_switch/L in area) - L.update_appearance(UPDATE_ICON) + L.update_appearance() area.power_change() @@ -876,7 +876,7 @@ opened = APC_COVER_REMOVED coverlocked = FALSE visible_message(span_warning("The APC cover is knocked down!")) - update_appearance(UPDATE_ICON) + update_appearance() /obj/machinery/power/apc/emag_act(mob/user, obj/item/card/emag/emag_card) if((obj_flags & EMAGGED) || malfhack) @@ -895,7 +895,7 @@ obj_flags |= EMAGGED locked = FALSE to_chat(user, span_notice("You emag the APC interface.")) - update_appearance(UPDATE_ICON) + update_appearance() return TRUE // attack with hand - remove cell (if cover open) or interact with the APC @@ -912,10 +912,10 @@ if(cell) user.visible_message("[user] removes \the [cell] from [src]!",span_notice("You remove \the [cell].")) user.put_in_hands(cell) - cell.update_appearance(UPDATE_ICON) + cell.update_appearance() src.cell = null charging = APC_NOT_CHARGING - src.update_appearance(UPDATE_ICON) + src.update_appearance() return if((stat & MAINT) && !opened) //no board; no interface return @@ -1064,7 +1064,7 @@ to_chat(usr, "The APC does not respond to the command.") else locked = !locked - update_appearance(UPDATE_ICON) + update_appearance() . = TRUE if("cover") coverlocked = !coverlocked @@ -1076,20 +1076,20 @@ chargemode = !chargemode if(!chargemode) charging = APC_NOT_CHARGING - update_appearance(UPDATE_ICON) + update_appearance() . = TRUE if("channel") if(params["eqp"]) equipment = setsubsystem(text2num(params["eqp"])) - update_appearance(UPDATE_ICON) + update_appearance() update() else if(params["lgt"]) lighting = setsubsystem(text2num(params["lgt"])) - update_appearance(UPDATE_ICON) + update_appearance() update() else if(params["env"]) environ = setsubsystem(text2num(params["env"])) - update_appearance(UPDATE_ICON) + update_appearance() update() . = TRUE if("overload") @@ -1107,7 +1107,7 @@ malfvacate() if("reboot") failure_timer = 0 - update_appearance(UPDATE_ICON) + update_appearance() update() if("emergency_lighting") emergency_lights = !emergency_lights @@ -1132,7 +1132,7 @@ add_hiddenprint(user) log_combat(user, src, "turned [operating ? "on" : "off"]") update() - update_appearance(UPDATE_ICON) + update_appearance() /obj/machinery/power/apc/proc/malfhack(mob/living/silicon/ai/malf) if(!istype(malf)) @@ -1273,7 +1273,7 @@ /obj/machinery/power/apc/process() if(icon_update_needed) - update_appearance(UPDATE_ICON) + update_appearance() if(stat & (BROKEN|MAINT)) return if(!area.requires_power) @@ -1448,7 +1448,7 @@ if(APC_RESET_EMP) equipment = 3 environ = 3 - update_appearance(UPDATE_ICON) + update_appearance() update() // damage and destruction acts @@ -1464,7 +1464,7 @@ lighting = 0 equipment = 0 environ = 0 - update_appearance(UPDATE_ICON) + update_appearance() update() addtimer(CALLBACK(src, PROC_REF(reset), APC_RESET_EMP), (6 * severity) SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE) diff --git a/code/modules/power/generator.dm b/code/modules/power/generator.dm index 43f7e4347f63..96010ab33117 100644 --- a/code/modules/power/generator.dm +++ b/code/modules/power/generator.dm @@ -58,7 +58,7 @@ var/L = min(round(lastgenlev/100000), 11) if(L != 0) - SSvis_overlays.add_vis_overlay(src, icon, "teg-op[L]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir) + SSvis_overlays.add_vis_overlay(src, icon, "teg-op[L]", ABOVE_LIGHTING_PLANE, dir) #define GENRATE 800 // generator output coefficient from Q diff --git a/code/modules/power/reactor/reactor.dm b/code/modules/power/reactor/reactor.dm index e0a8597112e2..25ae2655b59c 100644 --- a/code/modules/power/reactor/reactor.dm +++ b/code/modules/power/reactor/reactor.dm @@ -593,10 +593,10 @@ T.assume_air(coolant_input) T.assume_air(moderator_input) T.assume_air(coolant_output) - var/turf/lower_turf = SSmapping.get_turf_below(T) + var/turf/lower_turf = GET_TURF_BELOW(T) if(lower_turf) // reactor fuel will melt down into the lower levels on multi-z maps like icemeta new /obj/structure/reactor_corium(lower_turf) - var/turf/lowest_turf = SSmapping.get_turf_below(lower_turf) + var/turf/lowest_turf = GET_TURF_BELOW(lower_turf) if(lowest_turf) // WE NEED TO GO DEEPER new /obj/structure/reactor_corium(lower_turf) explosion(get_turf(src), 0, 5, 10, 20, TRUE, TRUE) diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index 3d45e0554f19..89cebd706384 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -4,7 +4,7 @@ #define SINGULARITY_INTEREST_NONSPACE 2 /atom/movable/gravity_lens - plane = SINGULARITY_EFFECT_PLANE + plane = GRAVITY_PULSE_PLANE //plane = GHOST_LAYER appearance_flags = PIXEL_SCALE | RESET_TRANSFORM icon = 'icons/effects/512x512.dmi' @@ -19,7 +19,7 @@ anchored = TRUE density = TRUE move_resist = INFINITY - layer = MASSIVE_OBJ_LAYER + plane = MASSIVE_OBJ_PLANE light_range = 6 appearance_flags = LONG_GLIDE var/current_size = 1 diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index 49c3ec5f7170..82412f16069c 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -214,6 +214,7 @@ righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' w_class = WEIGHT_CLASS_BULKY // Pretty big! anchored = FALSE + blocks_emissive = EMISSIVE_BLOCK_UNIQUE var/tracker = 0 var/glass_type = null var/multiplier = 1 diff --git a/code/modules/power/tracker.dm b/code/modules/power/tracker.dm index ec2bc2e82197..5aaa474a3c5f 100644 --- a/code/modules/power/tracker.dm +++ b/code/modules/power/tracker.dm @@ -10,6 +10,7 @@ icon_state = "tracker" density = TRUE use_power = NO_POWER_USE + blocks_emissive = EMISSIVE_BLOCK_UNIQUE max_integrity = 250 integrity_failure = 0.2 diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index c7d5c005fb67..8a9df0de9e67 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -13,6 +13,7 @@ mouse_opacity = MOUSE_OPACITY_TRANSPARENT movement_type = FLYING wound_bonus = CANT_WOUND // can't wound by default + blocks_emissive = EMISSIVE_BLOCK_GENERIC var/hitsound = 'sound/weapons/pierce.ogg' var/hitsound_wall = "" diff --git a/code/modules/projectiles/projectile/special/hallucination.dm b/code/modules/projectiles/projectile/special/hallucination.dm index e6a31bc9e621..b2de093296c7 100644 --- a/code/modules/projectiles/projectile/special/hallucination.dm +++ b/code/modules/projectiles/projectile/special/hallucination.dm @@ -152,7 +152,7 @@ /obj/projectile/hallucination/laser/hal_apply_effect() hal_target.adjustStaminaLoss(20) - hal_target.blur_eyes(2) + hal_target.adjust_eye_blur(2) /obj/projectile/hallucination/taser name = "electrode" diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm index f31b14ed7da7..5300a9a98767 100644 --- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm @@ -2383,7 +2383,7 @@ All effects don't start immediately, but rather get worse over time; the rate is /datum/reagent/consumable/ethanol/inocybeshine/on_mob_life(mob/living/carbon/M) if(prob(10)) M.adjustStaminaLoss(10,0) - M.blur_eyes(3) + M.adjust_eye_blur(3) M.adjust_disgust(1) . = TRUE return ..() diff --git a/code/modules/reagents/chemistry/reagents/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drink_reagents.dm index 2f8cd7d85297..3669ab2062c8 100644 --- a/code/modules/reagents/chemistry/reagents/drink_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drink_reagents.dm @@ -59,7 +59,7 @@ glass_desc = "It's just like a carrot but without crunching." /datum/reagent/consumable/carrotjuice/on_mob_life(mob/living/carbon/M) - M.adjust_blurriness(-1) + M.adjust_eye_blur(-1) M.adjust_blindness(-1) switch(current_cycle) if(1 to 20) @@ -1116,4 +1116,4 @@ taste_description = "citrus soda with cucumber" glass_icon_state = "cucumber_lemonade" glass_name = "cucumber lemonade" - glass_desc = "Lemonade, with added cucumber." \ No newline at end of file + glass_desc = "Lemonade, with added cucumber." diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm index 7ee1568d0ce5..927de10fd7cc 100644 --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -317,7 +317,7 @@ else if ( mouth_covered ) // Reduced effects if partially protected if(prob(50)) victim.emote("scream") - victim.blur_eyes(14) + victim.adjust_eye_blur(14) victim.blind_eyes(10) victim.set_confusion_if_lower(10 SECONDS) victim.damageoverlaytemp = 75 @@ -327,7 +327,7 @@ else if ( eyes_covered ) // Eye cover is better than mouth cover if(prob(20)) victim.emote("cough") - victim.blur_eyes(4) + victim.adjust_eye_blur(4) victim.set_confusion_if_lower(5 SECONDS) victim.damageoverlaytemp = 50 M.adjustStaminaLoss(3) @@ -335,7 +335,7 @@ else // Oh dear :D if(prob(60)) victim.emote("scream") - victim.blur_eyes(14) + victim.adjust_eye_blur(14) victim.blind_eyes(10) victim.set_confusion_if_lower(12 SECONDS) victim.damageoverlaytemp = 100 @@ -637,13 +637,13 @@ if(!M.eye_blurry) to_chat(M, "Tears well up in your eyes!") M.blind_eyes(2) - M.blur_eyes(5) + M.adjust_eye_blur(5) return ..() /datum/reagent/consumable/tearjuice/on_mob_life(mob/living/carbon/M) ..() if(M.eye_blurry) //Don't worsen vision if it was otherwise fine - M.blur_eyes(4) + M.adjust_eye_blur(4) if(prob(10)) to_chat(M, "Your eyes sting!") M.blind_eyes(2) @@ -679,7 +679,7 @@ M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 2*REM, 150) M.adjustToxLoss(3*REM,0) M.adjustStaminaLoss(10*REM,0) - M.blur_eyes(5) + M.adjust_eye_blur(5) . = TRUE ..() @@ -942,3 +942,7 @@ nutriment_factor = 15 * REAGENTS_METABOLISM color = "#D9A066" // rgb: 217, 160, 102 taste_description = "peanuts" + +/// Gets just how much nutrition this reagent is worth for the passed mob +/datum/reagent/consumable/proc/get_nutriment_factor(mob/living/carbon/eater) + return nutriment_factor * REAGENTS_METABOLISM * 2 diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 24d73f8a1047..fa18ac543c5b 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -46,7 +46,7 @@ M.adjustToxLoss(-5, 0, TRUE) M.remove_status_effect(/datum/status_effect/hallucination) REMOVE_TRAITS_NOT_IN(M, list(SPECIES_TRAIT, ROUNDSTART_TRAIT, ORGAN_TRAIT)) - M.set_blurriness(0) + M.set_eye_blur(0) M.set_blindness(0) M.SetKnockdown(0, FALSE) M.SetStun(0, FALSE) @@ -821,15 +821,15 @@ to_chat(M, span_warning("Your vision slowly returns...")) M.cure_blind(EYE_DAMAGE) M.cure_nearsighted(EYE_DAMAGE) - M.blur_eyes(35) + M.adjust_eye_blur(35) else if(HAS_TRAIT_FROM(M, TRAIT_NEARSIGHT, EYE_DAMAGE)) to_chat(M, span_warning("The blackness in your peripheral vision fades.")) M.cure_nearsighted(EYE_DAMAGE) - M.blur_eyes(10) + M.adjust_eye_blur(10) else if(M.eye_blind || M.eye_blurry) M.set_blindness(0) - M.set_blurriness(0) + M.set_eye_blur(0) ..() /datum/reagent/medicine/atropine diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index b4d7d89552f9..d883e49a5b9d 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -299,7 +299,7 @@ /datum/reagent/toxin/spore/on_mob_life(mob/living/carbon/C) C.damageoverlaytemp = 60 C.update_damage_hud() - C.blur_eyes(3) + C.adjust_eye_blur(3) return ..() /datum/reagent/toxin/spore_burning @@ -471,7 +471,7 @@ switch(pick(1, 2, 3, 4)) if(1) to_chat(M, span_danger("You can barely see!")) - M.blur_eyes(3) + M.adjust_eye_blur(3) if(2) M.emote("cough") if(3) diff --git a/code/modules/research/xenobiology/crossbreeding/_structures.dm b/code/modules/research/xenobiology/crossbreeding/_structures.dm index 54c6f83ede08..5cd551f7d3d4 100644 --- a/code/modules/research/xenobiology/crossbreeding/_structures.dm +++ b/code/modules/research/xenobiology/crossbreeding/_structures.dm @@ -422,7 +422,7 @@ GLOBAL_LIST_EMPTY(bluespace_slime_crystals) blood_amt -= 50 to_chat(user, span_notice("You touch the crystal, and see blood transforming into an organ!")) playsound(src, 'sound/magic/demon_consume.ogg', 50, 1) - var/type = pick(/obj/item/reagent_containers/food/snacks/meat/slab,/obj/item/organ/heart,/obj/item/organ/heart/freedom,/obj/item/organ/lungs,/obj/item/organ/lungs/plasmaman,/obj/item/organ/lungs/ethereal,/obj/item/organ/lungs/slime,/obj/item/organ/liver,/obj/item/organ/liver/plasmaman,/obj/item/organ/liver/alien,/obj/item/organ/eyes,/obj/item/organ/eyes/night_vision/alien,/obj/item/organ/eyes/night_vision,/obj/item/organ/eyes/night_vision/mushroom,/obj/item/organ/tongue,/obj/item/organ/stomach,/obj/item/organ/stomach/plasmaman,/obj/item/organ/stomach/cell/ethereal,/obj/item/organ/ears,/obj/item/organ/ears/cat,/obj/item/organ/ears/penguin) + var/type = pick(/obj/item/reagent_containers/food/snacks/meat/slab,/obj/item/organ/heart,/obj/item/organ/heart/freedom,/obj/item/organ/lungs,/obj/item/organ/lungs/plasmaman,/obj/item/organ/lungs/ethereal,/obj/item/organ/lungs/slime,/obj/item/organ/liver,/obj/item/organ/liver/plasmaman,/obj/item/organ/liver/alien,/obj/item/organ/eyes,/obj/item/organ/eyes/alien,/obj/item/organ/eyes/night_vision,/obj/item/organ/eyes/night_vision/mushroom,/obj/item/organ/tongue,/obj/item/organ/stomach,/obj/item/organ/stomach/plasmaman,/obj/item/organ/stomach/cell/ethereal,/obj/item/organ/ears,/obj/item/organ/ears/cat,/obj/item/organ/ears/penguin) new type(get_turf(src)) /obj/structure/slime_crystal/red/attacked_by(obj/item/I, mob/living/user) diff --git a/code/modules/shuttle/navigation_computer.dm b/code/modules/shuttle/navigation_computer.dm index c38dcbb9c178..ab2f851020b4 100644 --- a/code/modules/shuttle/navigation_computer.dm +++ b/code/modules/shuttle/navigation_computer.dm @@ -286,8 +286,9 @@ /mob/camera/aiEye/remote/shuttle_docker/update_remote_sight(mob/living/user) user.sight = BLIND|SEE_TURFS - user.lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE - user.sync_lighting_plane_alpha() + // Pale blue, should look nice I think + user.lighting_color_cutoffs = list(30, 40, 50) + user.sync_lighting_plane_cutoff() return TRUE /datum/action/innate/shuttledocker_rotate diff --git a/code/modules/shuttle/shuttle_creation/shuttle_creator_eye.dm b/code/modules/shuttle/shuttle_creation/shuttle_creator_eye.dm index f8c60e4eb9de..fe5faeee75f4 100644 --- a/code/modules/shuttle/shuttle_creation/shuttle_creator_eye.dm +++ b/code/modules/shuttle/shuttle_creation/shuttle_creator_eye.dm @@ -15,8 +15,9 @@ /mob/camera/aiEye/remote/shuttle_creation/update_remote_sight(mob/living/user) user.sight = BLIND|SEE_TURFS - user.lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE - user.sync_lighting_plane_alpha() + // Pale blue, should look nice I think + user.lighting_color_cutoffs = list(30, 40, 50) + user.sync_lighting_plane_cutoff() return TRUE /mob/camera/aiEye/remote/shuttle_creation/relaymove(mob/user, direct) @@ -50,6 +51,6 @@ if(eye_user?.client) eye_user.client.images -= user_image var/image/I = image(icon, loc, icon_state, FLY_LAYER, dir) - I.plane = MASSIVE_OBJ_LAYER + I.plane = MASSIVE_OBJ_PLANE user_image = I eye_user.client.images += user_image diff --git a/code/modules/spells/spell_types/hivemind.dm b/code/modules/spells/spell_types/hivemind.dm index f1fd17b6b7d5..f8222acbeeb2 100644 --- a/code/modules/spells/spell_types/hivemind.dm +++ b/code/modules/spells/spell_types/hivemind.dm @@ -212,7 +212,7 @@ if(!hive.is_carbon_member(target)) power *= 0.5 target.blind_eyes(4*power) - target.blur_eyes(30*power) + target.adjust_eye_blur(30*power) target.minimumDeafTicks(15*power) //equivalent to 30s deafness max target.adjust_jitter(power SECONDS) target.silent += 10*power diff --git a/code/modules/spells/spell_types/jaunt/wirecrawl.dm b/code/modules/spells/spell_types/jaunt/wirecrawl.dm index a1829b04dbf6..84b4a367be0a 100644 --- a/code/modules/spells/spell_types/jaunt/wirecrawl.dm +++ b/code/modules/spells/spell_types/jaunt/wirecrawl.dm @@ -108,7 +108,6 @@ wire.visible_message(span_warning("[jaunter] zips into [wire]!")) jaunter.extinguish_mob() - jaunter.sight |= (SEE_TURFS|BLIND) jaunter.add_wirevision(wire) holder.travelled = wire.powernet do_sparks(10, FALSE, jaunter) @@ -135,7 +134,6 @@ if(!exit_jaunt(jaunter, get_turf(wire))) return FALSE - jaunter.sight &= ~(SEE_TURFS|BLIND) jaunter.remove_wirevision() do_sparks(10, FALSE, jaunter) @@ -214,6 +212,8 @@ if(!totalMembers.len) return + + sight |= (SEE_TURFS|BLIND) if(client) for(var/object in totalMembers)//cables and power machinery are not the same unfortunately @@ -239,6 +239,7 @@ for(var/image/current_image in wires_shown) client.images -= current_image wires_shown.len = 0 + sight &= ~(SEE_TURFS|BLIND) /obj/item/wirecrawl diff --git a/code/modules/spells/spell_types/pointed/blind.dm b/code/modules/spells/spell_types/pointed/blind.dm index f4c9b9c0958a..ba3bfbd87ba7 100644 --- a/code/modules/spells/spell_types/pointed/blind.dm +++ b/code/modules/spells/spell_types/pointed/blind.dm @@ -39,5 +39,5 @@ to_chat(cast_on, span_warning("Your eyes cry out in pain!")) cast_on.adjust_blindness(eye_blind_duration) - cast_on.blur_eyes(eye_blur_duration) + cast_on.adjust_eye_blur(eye_blur_duration) return TRUE diff --git a/code/modules/spells/spell_types/self/night_vision.dm b/code/modules/spells/spell_types/self/night_vision.dm deleted file mode 100644 index c3b3fc9977fa..000000000000 --- a/code/modules/spells/spell_types/self/night_vision.dm +++ /dev/null @@ -1,39 +0,0 @@ -//Toggle Night Vision -/datum/action/cooldown/spell/night_vision - name = "Toggle Nightvision" - desc = "Toggle your nightvision mode." - - cooldown_time = 1 SECONDS - spell_requirements = NONE - - /// The span the "toggle" message uses when sent to the user - var/toggle_span = "notice" - -/datum/action/cooldown/spell/night_vision/New(Target) - . = ..() - name = "[name] \[ON\]" - -/datum/action/cooldown/spell/night_vision/is_valid_target(atom/cast_on) - return isliving(cast_on) - -/datum/action/cooldown/spell/night_vision/cast(mob/living/cast_on) - . = ..() - to_chat(cast_on, "You toggle your night vision.") - - var/next_mode_text = "" - switch(cast_on.lighting_alpha) - if (LIGHTING_PLANE_ALPHA_VISIBLE) - cast_on.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - next_mode_text = "More" - if (LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE) - cast_on.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - next_mode_text = "Full" - if (LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE) - cast_on.lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE - next_mode_text = "OFF" - else - cast_on.lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE - next_mode_text = "ON" - - cast_on.update_sight() - name = "[initial(name)] \[[next_mode_text]\]" \ No newline at end of file diff --git a/code/modules/surgery/eye_surgery.dm b/code/modules/surgery/eye_surgery.dm index 9cfb874c4b65..94d02d6901fd 100644 --- a/code/modules/surgery/eye_surgery.dm +++ b/code/modules/surgery/eye_surgery.dm @@ -49,7 +49,7 @@ target.cure_blind(list(EYE_DAMAGE)) target.set_blindness(0) target.cure_nearsighted(list(EYE_DAMAGE)) - target.blur_eyes(35) //this will fix itself slowly. + target.adjust_eye_blur(35) //this will fix itself slowly. E.setOrganDamage(0) return TRUE diff --git a/code/modules/surgery/organs/augments_chest.dm b/code/modules/surgery/organs/augments_chest.dm index 0ab2df136add..c58ac8b27f2f 100644 --- a/code/modules/surgery/organs/augments_chest.dm +++ b/code/modules/surgery/organs/augments_chest.dm @@ -337,7 +337,7 @@ owner.set_drugginess(4 * severity) owner.adjust_hallucinations((50 * severity) SECONDS) - owner.blur_eyes(2 * severity) + owner.adjust_eye_blur(2 * severity) owner.adjust_dizzy(severity SECONDS) time_on += severity owner.adjustFireLoss(severity) diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm index e8212d573f2b..1a98633c3a53 100644 --- a/code/modules/surgery/organs/eyes.dm +++ b/code/modules/surgery/organs/eyes.dm @@ -20,14 +20,16 @@ low_threshold_cleared = span_info("Your vision is cleared of any ailment.") var/sight_flags = 0 - var/see_in_dark = 2 var/tint = 0 var/eye_color = "" //set to a hex code to override a mob's eye color var/eye_icon_state = "eyes" var/old_eye_color = "fff" var/flash_protect = 0 var/see_invisible = SEE_INVISIBLE_LIVING - var/lighting_alpha + /// How much darkness to cut out of your view (basically, night vision) + var/lighting_cutoff = null + /// List of color cutoffs from eyes, or null if not applicable + var/list/color_cutoffs = null var/no_glasses var/damaged = TRUE //damaged indicates that our eyes are undergoing some level of negative effect,starts as true so it removes the impaired vision overlay if it is replacing damaged eyes @@ -40,8 +42,6 @@ HMN.eye_color = eye_color else eye_color = HMN.eye_color - if(HAS_TRAIT(HMN, TRAIT_NIGHT_VISION) && !lighting_alpha) - lighting_alpha = LIGHTING_PLANE_ALPHA_NV_TRAIT M.update_tint() owner.update_sight() if(M.has_dna() && ishuman(M)) @@ -55,7 +55,7 @@ HMN.update_body() M.cure_blind(list(EYE_DAMAGE)) // can't be blind from eye damage if there's no eye to be damaged, still blind from not having eyes though M.cure_nearsighted(list(EYE_DAMAGE)) // likewise for nearsightedness - M.set_blurriness(0) // no eyes to blur + M.set_eye_blur(0) // no eyes to blur M.update_tint() M.update_sight() @@ -117,48 +117,83 @@ #undef OFFSET_X #undef OFFSET_Y +#define NIGHTVISION_LIGHT_OFF 0 +#define NIGHTVISION_LIGHT_LOW 1 +#define NIGHTVISION_LIGHT_MID 2 +#define NIGHTVISION_LIGHT_HIG 3 + /obj/item/organ/eyes/night_vision name = "shadow eyes" desc = "A spooky set of eyes that can see in the dark." - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + actions_types = list(/datum/action/item_action/organ_action/use) - var/night_vision = TRUE + // These lists are used as the color cutoff for the eye + // They need to be filled out for subtypes + var/list/low_light_cutoff + var/list/medium_light_cutoff + var/list/high_light_cutoff + var/light_level = NIGHTVISION_LIGHT_OFF + +/obj/item/organ/eyes/night_vision/Initialize(mapload) + . = ..() + + if(length(low_light_cutoff) != 3 || length(medium_light_cutoff) != 3 || length(high_light_cutoff) != 3) + stack_trace("[type] did not have fully filled out color cutoff lists") + if(low_light_cutoff) + color_cutoffs = low_light_cutoff.Copy() + light_level = NIGHTVISION_LIGHT_LOW /obj/item/organ/eyes/night_vision/ui_action_click() sight_flags = initial(sight_flags) - switch(lighting_alpha) - if (LIGHTING_PLANE_ALPHA_VISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - if (LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - if (LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE + switch(light_level) + if (NIGHTVISION_LIGHT_OFF) + color_cutoffs = low_light_cutoff.Copy() + light_level = NIGHTVISION_LIGHT_LOW + if (NIGHTVISION_LIGHT_LOW) + color_cutoffs = medium_light_cutoff.Copy() + light_level = NIGHTVISION_LIGHT_MID + if (NIGHTVISION_LIGHT_MID) + color_cutoffs = high_light_cutoff.Copy() + light_level = NIGHTVISION_LIGHT_HIG else - lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE - sight_flags &= ~SEE_BLACKNESS + color_cutoffs = null + light_level = NIGHTVISION_LIGHT_OFF owner.update_sight() -/obj/item/organ/eyes/night_vision/alien +#undef NIGHTVISION_LIGHT_OFF +#undef NIGHTVISION_LIGHT_LOW +#undef NIGHTVISION_LIGHT_MID +#undef NIGHTVISION_LIGHT_HIG + +/obj/item/organ/eyes/night_vision/mushroom + name = "fung-eye" + desc = "While on the outside they look inert and dead, the eyes of mushroom people are actually very advanced." + low_light_cutoff = list(0, 15, 20) + medium_light_cutoff = list(0, 20, 35) + high_light_cutoff = list(0, 40, 50) + +//innate nightvision eyes +/obj/item/organ/eyes/alien name = "alien eyes" desc = "It turned out they had them after all!" sight_flags = SEE_MOBS + color_cutoffs = list(25, 5, 42) + lighting_cutoff = LIGHTING_CUTOFF_HIGH -/obj/item/organ/eyes/night_vision/zombie +/obj/item/organ/eyes/zombie name = "undead eyes" desc = "Somewhat counterintuitively, these half-rotten eyes actually have superior vision to those of a living human." + color_cutoffs = list(25, 35, 5) + lighting_cutoff = LIGHTING_CUTOFF_HIGH -/obj/item/organ/eyes/night_vision/nightmare +/obj/item/organ/eyes/shadow name = "burning red eyes" desc = "Even without their shadowy owner, looking at these eyes gives you a sense of dread." icon_state = "burning_eyes" - -/obj/item/organ/eyes/night_vision/mushroom - name = "fung-eye" - desc = "While on the outside they look inert and dead, the eyes of mushroom people are actually very advanced." + color_cutoffs = list(20, 10, 40) + lighting_cutoff = LIGHTING_CUTOFF_HIGH ///Robotic - /obj/item/organ/eyes/robotic name = "robotic eyes" icon_state = "cybernetic_eyeballs" @@ -171,19 +206,21 @@ . = ..() if(!owner || . & EMP_PROTECT_SELF) return + var/obj/item/organ/eyes/eyes = owner.getorganslot(ORGAN_SLOT_EYES) to_chat(owner, span_danger("your eyes overload and blind you!")) owner.flash_act(override_blindness_check = 1) - owner.blind_eyes(severity / 2) - owner.blur_eyes(8) - applyOrganDamage(2 * severity) + owner.blind_eyes(5) + owner.adjust_eye_blur(8) + eyes.applyOrganDamage(20 / severity) /obj/item/organ/eyes/robotic/xray name = "\improper meson eyes" desc = "These cybernetic eyes will give you meson-vision. Looks like it could withstand seeing a supermatter crystal!." eye_color = "00FF00" - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + // We're gonna downshift green and blue a bit so darkness looks yellow + color_cutoffs = list(25, 8, 5) sight_flags = SEE_TURFS - see_in_dark = 4 + /obj/item/organ/eyes/robotic/xray/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = FALSE, initialising) . = ..() @@ -198,16 +235,15 @@ desc = "These cybernetic eyes will give you true X-ray vision. Blinking is futile." eye_color = "000" sight_flags = SEE_MOBS | SEE_OBJS | SEE_TURFS - see_in_dark = 8 /obj/item/organ/eyes/robotic/thermals name = "thermal eyes" desc = "These cybernetic eye implants will give you thermal vision. Vertical slit pupil included." eye_color = "FC0" sight_flags = SEE_MOBS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + // We're gonna downshift green and blue a bit so darkness looks yellow + color_cutoffs = list(25, 8, 5) flash_protect = -1 - see_in_dark = 8 /obj/item/organ/eyes/robotic/flashlight name = "flashlight eyes" @@ -454,8 +490,18 @@ /obj/item/organ/eyes/polysmorph name = "polysmorph eyes" desc = "Eyes from a polysmorph, capable of retaining slightly more vision in low light environments" - lighting_alpha = LIGHTING_PLANE_ALPHA_NV_TRAIT - see_in_dark = 5 + color_cutoffs = list(25, 5, 42) + +/obj/item/organ/eyes/zombie + name = "undead eyes" + desc = "Somewhat counterintuitively, these half-rotten eyes actually have superior vision to those of a living human." + color_cutoffs = list(25, 35, 5) + +/obj/item/organ/eyes/alien + name = "alien eyes" + desc = "It turned out they had them after all!" + sight_flags = SEE_MOBS + color_cutoffs = list(25, 5, 42) /obj/item/organ/eyes/ethereal name = "fractal eyes" diff --git a/code/modules/surgery/organs/stomach.dm b/code/modules/surgery/organs/stomach.dm index 52c76be97411..8a298b911f9e 100644 --- a/code/modules/surgery/organs/stomach.dm +++ b/code/modules/surgery/organs/stomach.dm @@ -63,7 +63,7 @@ H.adjust_dizzy(5 SECONDS) if(H.disgust >= DISGUST_LEVEL_DISGUSTED) if(prob(25)) - H.blur_eyes(3) //We need to add more shit down here + H.adjust_eye_blur(3) //We need to add more shit down here H.adjust_disgust(-0.5 * disgust_metabolism) switch(H.disgust) diff --git a/code/modules/swarmers/swarmer_objs.dm b/code/modules/swarmers/swarmer_objs.dm index 69cda8ec6278..c5801983b834 100644 --- a/code/modules/swarmers/swarmer_objs.dm +++ b/code/modules/swarmers/swarmer_objs.dm @@ -46,7 +46,7 @@ icon_state = "swarmer_console" armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 100, BOMB = 50, BIO = 100, RAD = 100, FIRE = 100, ACID = 100) max_integrity = 400 - layer = MASSIVE_OBJ_LAYER + plane = MASSIVE_OBJ_PLANE light_color = LIGHT_COLOR_CYAN light_range = 10 anchored = TRUE diff --git a/code/modules/unit_tests/focus_only_tests.dm b/code/modules/unit_tests/focus_only_tests.dm index 426a236ab4bd..ba6d827b7f0a 100644 --- a/code/modules/unit_tests/focus_only_tests.dm +++ b/code/modules/unit_tests/focus_only_tests.dm @@ -6,6 +6,12 @@ /// and you will only test the check for invalid overlays in appearance building. /datum/unit_test/focus_only +/// Checks that every created emissive has a valid icon_state +/datum/unit_test/focus_only/invalid_emissives + +/// Checks that every overlay passed into build_appearance_list exists in the icon +/datum/unit_test/focus_only/invalid_overlays + /// Checks that every created emissive has a valid icon_state /datum/unit_test/focus_only/multiple_space_initialization diff --git a/code/modules/vehicles/_vehicle.dm b/code/modules/vehicles/_vehicle.dm index 0fe04abfd05a..836fcd5035c2 100644 --- a/code/modules/vehicles/_vehicle.dm +++ b/code/modules/vehicles/_vehicle.dm @@ -7,6 +7,7 @@ armor = list(MELEE = 30, BULLET = 30, LASER = 30, ENERGY = 0, BOMB = 30, BIO = 0, RAD = 0, FIRE = 60, ACID = 60) density = TRUE anchored = FALSE + blocks_emissive = EMISSIVE_BLOCK_GENERIC var/list/mob/occupants //mob = bitflags of their control level. var/max_occupants = 1 var/max_drivers = 1 diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm index 87f510ce3c10..96927a06ac2e 100644 --- a/code/modules/vending/_vending.dm +++ b/code/modules/vending/_vending.dm @@ -92,6 +92,8 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C var/forcecrit = 0 var/num_shards = 7 var/list/pinned_mobs = list() + light_power = 0.7 + light_range = MINIMUM_USEFUL_LIGHT_RANGE /** @@ -185,6 +187,9 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C /// how many items have been inserted in a vendor var/loaded_items = 0 + ///Name of lighting mask for the vending machine + var/light_mask + /// used for narcing on underages var/obj/item/radio/alertradio @@ -266,16 +271,28 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C else ..() -/obj/machinery/vending/update_icon_state() +/obj/machinery/vending/update_appearance(updates=ALL) . = ..() + if(stat & BROKEN) + set_light(0) + return + set_light(powered() ? MINIMUM_USEFUL_LIGHT_RANGE : 0) + +/obj/machinery/vending/update_icon_state() if(stat & BROKEN) icon_state = "[initial(icon_state)]-broken" + return ..() + icon_state = "[initial(icon_state)][powered() ? null : "-off"]" + return ..() + +/obj/machinery/vending/update_overlays() + . = ..() + if(!light_mask) return - if(powered()) - icon_state = initial(icon_state) - else - icon_state = "[initial(icon_state)]-off" + //SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays) + if(!(stat & BROKEN) && powered()) + . += emissive_appearance(icon, light_mask, src) /obj/machinery/vending/obj_break(damage_flag) diff --git a/code/modules/vending/assist.dm b/code/modules/vending/assist.dm index 3a5c4c0bb1c4..a710699ae542 100644 --- a/code/modules/vending/assist.dm +++ b/code/modules/vending/assist.dm @@ -13,8 +13,11 @@ product_ads = "Only the finest!;Have some tools.;The most robust equipment.;The finest gear in space!" default_price = 10 extra_price = 50 + light_mask = "generic-light-mask" + light_color = LIGHT_COLOR_BLUE payment_department = NO_FREEBIES /obj/item/vending_refill/assist machine_name = "Vendomat" icon_state = "refill_engi" + diff --git a/code/modules/vending/autodrobe.dm b/code/modules/vending/autodrobe.dm index 2cd504b0cbc7..d61713292d39 100644 --- a/code/modules/vending/autodrobe.dm +++ b/code/modules/vending/autodrobe.dm @@ -232,6 +232,8 @@ /obj/item/clothing/mask/yogs/richard = 1) //yogs end refill_canister = /obj/item/vending_refill/autodrobe default_price = 50 + light_mask="theater-light-mask" + light_color = LIGHT_COLOR_BLUE extra_price = 75 payment_department = ACCOUNT_SRV diff --git a/code/modules/vending/boozeomat.dm b/code/modules/vending/boozeomat.dm index 06b9519e5fc7..e9eb31b62131 100644 --- a/code/modules/vending/boozeomat.dm +++ b/code/modules/vending/boozeomat.dm @@ -48,6 +48,8 @@ req_access = list(ACCESS_BAR) refill_canister = /obj/item/vending_refill/boozeomat default_price = 20 + light_mask = "boozeomat-light-mask" + light_color = LIGHT_COLOR_BLUE extra_price = 50 payment_department = ACCOUNT_SRV diff --git a/code/modules/vending/cartridge.dm b/code/modules/vending/cartridge.dm index dd08f9011069..2d719dda31c2 100644 --- a/code/modules/vending/cartridge.dm +++ b/code/modules/vending/cartridge.dm @@ -10,6 +10,8 @@ default_price = 50 extra_price = 100 payment_department = ACCOUNT_SRV + light_mask="cart-light-mask" + light_color = LIGHT_COLOR_BLUE /obj/item/vending_refill/cart machine_name = "PTech" diff --git a/code/modules/vending/cigarette.dm b/code/modules/vending/cigarette.dm index e86c6985e87d..31c7053a6d79 100644 --- a/code/modules/vending/cigarette.dm +++ b/code/modules/vending/cigarette.dm @@ -22,6 +22,8 @@ refill_canister = /obj/item/vending_refill/cigarette default_price = 10 extra_price = 50 + light_mask = "cigs-light-mask" + light_color = LIGHT_COLOR_BLUE payment_department = ACCOUNT_SRV /obj/machinery/vending/cigarette/syndicate diff --git a/code/modules/vending/clothesmate.dm b/code/modules/vending/clothesmate.dm index dc53821bdcba..e3046000e24a 100644 --- a/code/modules/vending/clothesmate.dm +++ b/code/modules/vending/clothesmate.dm @@ -216,6 +216,8 @@ refill_canister = /obj/item/vending_refill/clothing default_price = 50 extra_price = 75 + light_mask = "wardrobe-light-mask" + light_color = LIGHT_COLOR_BLUE payment_department = NO_FREEBIES /obj/machinery/vending/clothing/canLoadItem(obj/item/I,mob/user) diff --git a/code/modules/vending/coffee.dm b/code/modules/vending/coffee.dm index 007135a5ef80..8afc43e7423c 100644 --- a/code/modules/vending/coffee.dm +++ b/code/modules/vending/coffee.dm @@ -11,6 +11,8 @@ refill_canister = /obj/item/vending_refill/coffee default_price = 10 extra_price = 25 + light_mask = "coffee-light-mask" + light_color = LIGHT_COLOR_BLUE payment_department = ACCOUNT_SRV /obj/item/vending_refill/coffee machine_name = "Solar's Best Hot Drinks" diff --git a/code/modules/vending/cola.dm b/code/modules/vending/cola.dm index ba76ed700420..ae616c386d13 100644 --- a/code/modules/vending/cola.dm +++ b/code/modules/vending/cola.dm @@ -46,36 +46,50 @@ /obj/machinery/vending/cola/blue icon_state = "Cola_Machine" + light_mask = "cola-light-mask" + light_color = LIGHT_COLOR_BLUE /obj/machinery/vending/cola/black icon_state = "cola_black" + light_mask = "cola-light-mask" + light_color = LIGHT_COLOR_BLUE /obj/machinery/vending/cola/red icon_state = "red_cola" name = "\improper Space Cola Vendor" desc = "It vends cola, in space." product_slogans = "Cola in space!" + light_mask = "red_cola-light-mask" + light_color = LIGHT_COLOR_BLUE /obj/machinery/vending/cola/space_up icon_state = "space_up" name = "\improper Space-up! Vendor" desc = "Indulge in an explosion of flavor." product_slogans = "Space-up! Like a hull breach in your mouth." + light_mask = "space_up-light-mask" + light_color = LIGHT_COLOR_BLUE /obj/machinery/vending/cola/starkist icon_state = "starkist" name = "\improper Star-kist Vendor" desc = "The taste of a star in liquid form." product_slogans = "Drink the stars! Star-kist!" + light_mask = "starkist-light-mask" + light_color = LIGHT_COLOR_BLUE /obj/machinery/vending/cola/sodie icon_state = "soda" + light_mask = "soda-light-mask" + light_color = LIGHT_COLOR_BLUE /obj/machinery/vending/cola/pwr_game icon_state = "pwr_game" name = "\improper Pwr Game Vendor" desc = "You want it, we got it. Brought to you in partnership with Vlad's Salads." product_slogans = "The POWER that gamers crave! PWR GAME!" + light_mask = "pwr_game-light-mask" + light_color = LIGHT_COLOR_BLUE /obj/machinery/vending/cola/shamblers name = "\improper Shambler's Vendor" @@ -91,6 +105,8 @@ /obj/item/reagent_containers/food/drinks/soda_cans/shamblers = 10) product_slogans = "~Shake me up some of that Shambler's Juice!~" product_ads = "Refreshing!;Jyrbv dv lg jfdv fw kyrk Jyrdscvi'j Alztv!;Over 1 trillion souls drank!;Thirsty? Nyp efk uizeb kyv uribevjj?;Kyv Jyrdscvi uizebj kyv ezxyk!;Drink up!;Krjkp." + light_mask = "shamblers-light-mask" + light_color = LIGHT_COLOR_BLUE /obj/machinery/vending/cola/shamblers/prison products = list(/obj/item/reagent_containers/food/drinks/soda_cans/shamblers = 80) diff --git a/code/modules/vending/dinnerware.dm b/code/modules/vending/dinnerware.dm index ee2ac944ee1d..b073ae8925cd 100644 --- a/code/modules/vending/dinnerware.dm +++ b/code/modules/vending/dinnerware.dm @@ -28,6 +28,8 @@ refill_canister = /obj/item/vending_refill/dinnerware default_price = 5 extra_price = 50 + light_mask = "dinnerware-light-mask" + light_color = LIGHT_COLOR_BLUE payment_department = ACCOUNT_SRV /obj/item/vending_refill/dinnerware diff --git a/code/modules/vending/engineering.dm b/code/modules/vending/engineering.dm index 0dbc4254676d..eb5ed6cefec4 100644 --- a/code/modules/vending/engineering.dm +++ b/code/modules/vending/engineering.dm @@ -30,8 +30,10 @@ refill_canister = /obj/item/vending_refill/engineering default_price = 50 extra_price = 60 + light_mask = "engi-light-mask" + light_color = LIGHT_COLOR_BLUE payment_department = ACCOUNT_ENG /obj/item/vending_refill/engineering machine_name = "Robco Tool Maker" - icon_state = "refill_engi" \ No newline at end of file + icon_state = "refill_engi" diff --git a/code/modules/vending/engivend.dm b/code/modules/vending/engivend.dm index b04c44f6e54d..6369d8a82dec 100644 --- a/code/modules/vending/engivend.dm +++ b/code/modules/vending/engivend.dm @@ -27,6 +27,8 @@ refill_canister = /obj/item/vending_refill/engivend default_price = 20 extra_price = 50 + light_mask = "engivend-light-mask" + light_color = LIGHT_COLOR_BLUE payment_department = ACCOUNT_ENG /obj/item/vending_refill/engivend diff --git a/code/modules/vending/games.dm b/code/modules/vending/games.dm index 2ea25268a052..9b66dad95693 100644 --- a/code/modules/vending/games.dm +++ b/code/modules/vending/games.dm @@ -24,6 +24,8 @@ refill_canister = /obj/item/vending_refill/games default_price = 10 extra_price = 25 + light_mask = "games-light-mask" + light_color = LIGHT_COLOR_BLUE payment_department = ACCOUNT_SRV /obj/item/vending_refill/games diff --git a/code/modules/vending/liberation.dm b/code/modules/vending/liberation.dm index d500661b5821..2c18d040d754 100644 --- a/code/modules/vending/liberation.dm +++ b/code/modules/vending/liberation.dm @@ -30,4 +30,6 @@ resistance_flags = FIRE_PROOF default_price = 50 extra_price = 100 - payment_department = ACCOUNT_SEC \ No newline at end of file + light_mask = "liberation-light-mask" + light_color = LIGHT_COLOR_BLUE + payment_department = ACCOUNT_SEC diff --git a/code/modules/vending/liberation_toy.dm b/code/modules/vending/liberation_toy.dm index 141df9a93cae..d91daf5af144 100644 --- a/code/modules/vending/liberation_toy.dm +++ b/code/modules/vending/liberation_toy.dm @@ -29,4 +29,6 @@ refill_canister = /obj/item/vending_refill/donksoft default_price = 25 extra_price = 50 + light_mask = "donksoft-light-mask" + light_color = LIGHT_COLOR_BLUE payment_department = ACCOUNT_SRV diff --git a/code/modules/vending/magivend.dm b/code/modules/vending/magivend.dm index 016b06d9bae9..f10b9852c70a 100644 --- a/code/modules/vending/magivend.dm +++ b/code/modules/vending/magivend.dm @@ -18,4 +18,6 @@ resistance_flags = FIRE_PROOF default_price = 25 extra_price = 50 + light_color = LIGHT_COLOR_BLUE + light_mask = "magivend-light-mask" payment_department = ACCOUNT_SRV diff --git a/code/modules/vending/medical.dm b/code/modules/vending/medical.dm index d646b9e628ca..83b2b14491b0 100644 --- a/code/modules/vending/medical.dm +++ b/code/modules/vending/medical.dm @@ -43,6 +43,8 @@ refill_canister = /obj/item/vending_refill/medical default_price = 25 extra_price = 100 + light_color = LIGHT_COLOR_BLUE + light_mask = "med-light-mask" payment_department = ACCOUNT_MED /obj/item/vending_refill/medical diff --git a/code/modules/vending/medical_wall.dm b/code/modules/vending/medical_wall.dm index ffcc66dd08c6..67a6d3a7d334 100644 --- a/code/modules/vending/medical_wall.dm +++ b/code/modules/vending/medical_wall.dm @@ -18,6 +18,8 @@ refill_canister = /obj/item/vending_refill/wallmed default_price = 25 extra_price = 100 + light_mask = "wallmed-light-mask" + light_color = LIGHT_COLOR_BLUE payment_department = ACCOUNT_MED /obj/item/vending_refill/wallmed diff --git a/code/modules/vending/megaseed.dm b/code/modules/vending/megaseed.dm index b26cc8daebc0..3d55ddbebd24 100644 --- a/code/modules/vending/megaseed.dm +++ b/code/modules/vending/megaseed.dm @@ -63,6 +63,8 @@ refill_canister = /obj/item/vending_refill/hydroseeds default_price = 10 extra_price = 50 + light_mask = "seeds-light-mask" + light_color = LIGHT_COLOR_BLUE payment_department = ACCOUNT_SRV /obj/item/vending_refill/hydroseeds diff --git a/code/modules/vending/modularpc.dm b/code/modules/vending/modularpc.dm index b44b4ce5a651..1b3425963500 100644 --- a/code/modules/vending/modularpc.dm +++ b/code/modules/vending/modularpc.dm @@ -26,6 +26,8 @@ refill_canister = /obj/item/vending_refill/modularpc default_price = 30 extra_price = 250 + light_color = LIGHT_COLOR_BLUE + light_mask = "modular-light-mask" payment_department = ACCOUNT_SCI /obj/item/vending_refill/modularpc diff --git a/code/modules/vending/nutrimax.dm b/code/modules/vending/nutrimax.dm index d3741f7f8529..66d4d34f83a8 100644 --- a/code/modules/vending/nutrimax.dm +++ b/code/modules/vending/nutrimax.dm @@ -19,8 +19,10 @@ refill_canister = /obj/item/vending_refill/hydronutrients default_price = 10 extra_price = 50 + light_mask = "nutri-light-mask" + light_color = LIGHT_COLOR_BLUE payment_department = ACCOUNT_SRV /obj/item/vending_refill/hydronutrients machine_name = "NutriMax" - icon_state = "refill_plant" \ No newline at end of file + icon_state = "refill_plant" diff --git a/code/modules/vending/robotics.dm b/code/modules/vending/robotics.dm index e49d45141ef1..864811785a3e 100644 --- a/code/modules/vending/robotics.dm +++ b/code/modules/vending/robotics.dm @@ -17,6 +17,8 @@ default_price = 50 extra_price = 75 payment_department = ACCOUNT_SCI + light_color = LIGHT_COLOR_BLUE + light_mask = "robotics-light-mask" /obj/item/vending_refill/robotics machine_name = "Robotech Deluxe" diff --git a/code/modules/vending/security.dm b/code/modules/vending/security.dm index ed5bf1b84d89..f28f4c87ad0f 100644 --- a/code/modules/vending/security.dm +++ b/code/modules/vending/security.dm @@ -27,6 +27,8 @@ refill_canister = /obj/item/vending_refill/security default_price = 100 extra_price = 150 + light_mask = "sec-light-mask" + light_color = LIGHT_COLOR_BLUE payment_department = ACCOUNT_SEC /obj/machinery/vending/security/pre_throw(obj/item/I) diff --git a/code/modules/vending/snack.dm b/code/modules/vending/snack.dm index 33ad829b4c9a..e40f8963c560 100644 --- a/code/modules/vending/snack.dm +++ b/code/modules/vending/snack.dm @@ -23,6 +23,8 @@ default_price = 20 extra_price = 30 payment_department = ACCOUNT_SRV + light_color = LIGHT_COLOR_BLUE + light_mask = "snack-light-mask" input_display_header = "Chef's Food Selection" /obj/item/vending_refill/snack diff --git a/code/modules/vending/sovietsoda.dm b/code/modules/vending/sovietsoda.dm index 01832df2855a..599ed04a9bbc 100644 --- a/code/modules/vending/sovietsoda.dm +++ b/code/modules/vending/sovietsoda.dm @@ -9,8 +9,10 @@ refill_canister = /obj/item/vending_refill/sovietsoda default_price = 1 extra_price = 1 + light_mask = "soviet-light-mask" + light_color = LIGHT_COLOR_BLUE payment_department = NO_FREEBIES /obj/item/vending_refill/sovietsoda machine_name = "BODA" - icon_state = "refill_cola" \ No newline at end of file + icon_state = "refill_cola" diff --git a/code/modules/vending/sustenance.dm b/code/modules/vending/sustenance.dm index 266a58e10ab0..4004bf67a870 100644 --- a/code/modules/vending/sustenance.dm +++ b/code/modules/vending/sustenance.dm @@ -14,6 +14,8 @@ refill_canister = /obj/item/vending_refill/sustenance default_price = 0 extra_price = 0 + light_color = LIGHT_COLOR_BLUE + light_mask = "snack-light-mask" payment_department = NO_FREEBIES /obj/item/vending_refill/sustenance diff --git a/code/modules/vending/toys.dm b/code/modules/vending/toys.dm index 2cf117a730d2..ec7d79b145f0 100644 --- a/code/modules/vending/toys.dm +++ b/code/modules/vending/toys.dm @@ -36,6 +36,8 @@ refill_canister = /obj/item/vending_refill/donksoft default_price = 25 extra_price = 50 + light_mask = "donksoft-light-mask" + light_color = LIGHT_COLOR_BLUE payment_department = ACCOUNT_SRV /obj/item/vending_refill/donksoft diff --git a/code/modules/vending/wardrobes.dm b/code/modules/vending/wardrobes.dm index 97e8716b0b14..baa0e924006c 100644 --- a/code/modules/vending/wardrobes.dm +++ b/code/modules/vending/wardrobes.dm @@ -6,6 +6,9 @@ extra_price = 75 payment_department = NO_FREEBIES input_display_header = "Returned Clothing" + light_mask = "wardrobe-light-mask" + light_color = LIGHT_COLOR_BLUE + /obj/machinery/vending/wardrobe/canLoadItem(obj/item/I,mob/user) if(I.type in products) diff --git a/code/modules/vending/youtool.dm b/code/modules/vending/youtool.dm index 4d90076586e9..159984db340e 100644 --- a/code/modules/vending/youtool.dm +++ b/code/modules/vending/youtool.dm @@ -24,4 +24,6 @@ resistance_flags = FIRE_PROOF default_price = 20 extra_price = 80 + light_color = LIGHT_COLOR_BLUE + light_mask = "tool-light-mask" payment_department = ACCOUNT_ENG diff --git a/code/modules/visual/render_step.dm b/code/modules/visual/render_step.dm new file mode 100644 index 000000000000..2dae53b44ba0 --- /dev/null +++ b/code/modules/visual/render_step.dm @@ -0,0 +1,92 @@ +/** + * Internal atom that uses render relays to apply "appearance things" to a render source + * Branch, subtypes have behavior +*/ +/atom/movable/render_step + name = "render step" + plane = DEFAULT_PLANE + layer = FLOAT_LAYER + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + //Why? + //render_targets copy the transform of the target as well, but vis_contents also applies the transform + //we'll display using that, so we gotta reset + appearance_flags = KEEP_APART|KEEP_TOGETHER|RESET_TRANSFORM + +/atom/movable/render_step/Initialize(mapload, atom/source) + . = ..() + verbs.Cut() //Cargo cultttttt + + if(!source) + return + + render_source = source.render_target + SET_PLANE_EXPLICIT(src, initial(plane), source) + RegisterSignal(source, COMSIG_PARENT_QDELETING, PROC_REF(on_source_deleting)) + +/atom/movable/render_step/ex_act(severity) + return FALSE + +/atom/movable/render_step/singularity_act() + return + +/atom/movable/render_step/singularity_pull() + return + +/atom/movable/render_step/blob_act() + return + +//Prevents people from moving these after creation, because they shouldn't be. +/atom/movable/render_step/forceMove(atom/destination, no_tp=FALSE, harderforce = FALSE) + if(harderforce) + return ..() + +/atom/movable/render_step/proc/on_source_deleting(atom/source) + SIGNAL_HANDLER + + if(!QDELING(src)) + qdel(src) + +/** + * Render step that modfies an atom's color + * Useful for creating coherent emissive blockers out of things like glass floors by lowering alpha statically using matrixes + * Other stuff too I'm sure + */ +/atom/movable/render_step/color + name = "color step" + //RESET_COLOR is obvious I hope + appearance_flags = KEEP_APART|KEEP_TOGETHER|RESET_COLOR|RESET_TRANSFORM + +/atom/movable/render_step/color/Initialize(mapload, atom/source, color) + . = ..() + src.color = color + +/** + * Render step that makes the passed in render source block emissives + * + * Copies an appearance vis render_target and render_source on to the emissive blocking plane. + * This means that the atom in question will block any emissive sprites. + * This should only be used internally. If you are directly creating more of these, you're + * almost guaranteed to be doing something wrong. + */ +/atom/movable/render_step/emissive_blocker + name = "emissive blocker" + plane = EMISSIVE_PLANE + appearance_flags = EMISSIVE_APPEARANCE_FLAGS|RESET_TRANSFORM + +/atom/movable/render_step/emissive_blocker/Initialize(mapload, atom/source) + . = ..() + src.color = GLOB.em_block_color + +/** + * Render step that makes the passed in render source GLOW + * + * Copies an appearance vis render_target and render_source on to the emissive plane + */ +/atom/movable/render_step/emissive + name = "emissive" + plane = EMISSIVE_PLANE + appearance_flags = EMISSIVE_APPEARANCE_FLAGS|RESET_TRANSFORM + +/atom/movable/render_step/emissive/Initialize(mapload, source) + . = ..() + src.color = GLOB.emissive_color diff --git a/html/changelog.html b/html/changelog.html index 2e8eae5ec37d..89303a037945 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -1814,6 +1814,769 @@

iloveloopers updated:

  • New Acid Spit mutation obtainable from Polysmorphs
  • New Acid Spit sound effect
  • + +

    07 October 2023

    +

    Moltijoe updated:

    +
      +
    • Some detective gear is no longer invisible
    • +
    + +

    06 October 2023

    +

    Moltijoe updated:

    +
      +
    • Ethereals can now pick whatever colour they want
    • +
    • Explosive fist now creates turf fires instead of atmos hotspots
    • +
    • Fixes fringe cases where certain ultraviolence or worldbreaker abilities could be used when they shouldn't be
    • +
    +

    cowbot92 updated:

    +
      +
    • Added new heretic ritual
    • +
    • Removed heretic curses
    • +
    + +

    05 October 2023

    +

    SapphicOverload updated:

    +
      +
    • fixed lizard snouts again
    • +
    + +

    04 October 2023

    +

    @SapphicOverload, @redmoogle, @maxion12345, @Kmc2000 updated:

    +
      +
    • added the Advanced Gas-Cooled Nuclear Reactor
    • +
    • slightly improved radiation subsystem performance
    • +
    +

    Moltijoe updated:

    +
      +
    • Preterni can't eat ores with a covered mouth
    • +
    • People can carry chairs and wheelchairs on their back
    • +
    + +

    03 October 2023

    +

    cark updated:

    +
      +
    • adds another light and a light switch to dets office on box
    • +
    +

    Moltijoe updated:

    +
      +
    • Fixes a bug with bloodsuckers only healing robotic limbs on non robotic species
    • +
    +

    SomeguyManperson updated:

    +
      +
    • cryo is no longer slow
    • +
    +

    ToasterBiome updated:

    +
      +
    • various box fixes to bring it up to standard
    • +
    +

    cowbot92 updated:

    +
      +
    • mask of madness does not make you mad forever now
    • +
    • mask of madness now lets you use internals w/ it
    • +
    + +

    02 October 2023

    +

    cowbot92 updated:

    +
      +
    • adjusts all heretic spells to let mute people use them
    • +
    + +

    01 October 2023

    +

    @neeshacark updated:

    +
      +
    • buffed detective's armor to be the values it rightfully should be
    • +
    +

    Kell-E updated:

    +
      +
    • It's no longer possible to remove embedded objects from yourself while unconcious or paralyzed.
    • +
    • Adds a new negative trait: Drunkard
    • +
    +

    Moltijoe updated:

    +
      +
    • Adds a 2 second delay to leaving ventcrawling
    • +
    • No longer need to click adjacent tiles to attack using worldbreaker's pummel
    • +
    • Can no longer use worldbreaker abilities on items in inventory
    • +
    • regular fire extinguishers now make the person being sprayed more wet
    • +
    • Cautery tools have 3 force and do burn damage
    • +
    • Advanced Catuery tools in drill mode are sharp and have 4 force
    • +
    • Preternis water damage is stronger but starts out weaker
    • +
    • Preternis water damage now properly makes them spark
    • +
    • Preternis burn mod increased to 1.2
    • +
    • Preterni are worse at punching
    • +
    • Preterni heat up and cool down slightly faster
    • +
    • Powerhungry species no longer look fat when fully charged
    • +
    • Phytosians no longer passively regen robotic limbs
    • +
    • Worldbreaker is even hungrier
    • +
    • Worldbreaker gives a bottomless "stomach"
    • +
    +

    cowbot92 updated:

    +
      +
    • Added new heretic transmutation
    • +
    • tweaked heretic UI a tiny bit to have more clarity
    • +
    • tweaked void armor effect + armor values
    • +
    • adjusts void ascension
    • +
    • added new sprites for mask of madness transmutation
    • +
    +

    warface1234455 updated:

    +
      +
    • Add an option to toggle between using departmental budget or cargo budget when requesting a package
    • +
    • Using department budget to order now actually show the departmental name to avoid confusion
    • +
    • Fixes issue when departmental budget amount not appearing on the app
    • +
    • Fixes NT IRN app not printing requisition paper
    • +
    • Fixes modular computer's NT IRN app showing buyer as unknown because it doesnt recognize the pda
    • +
    • Refactored code a bit
    • +
    • private crate can now be sent back to CC if unlocked
    • +
    • change stabilized plasma decal sprite to something that doesnt block you from clicking the tile
    • +
    + +

    29 September 2023

    +

    warface1234455(code and seed sprite), baiomu (3d lime sprite) updated:

    +
      +
    • new plant 3d lime
    • +
    • lime can now be mutated to 3d lime
    • +
    • new 3d lime seed
    • +
    +

    Aquizit updated:

    +
      +
    • Added Janitor Closet back to destination tagger menu
    • +
    • Changed a couple of the group names to be more accurate
    • +
    +

    Kell-E updated:

    +
      +
    • Hallucinations from RDS will now last significantly longer.
    • +
    +

    LoliconSlayer updated:

    +
      +
    • Pet collars are for actual pets
    • +
    +

    Moltijoe updated:

    +
      +
    • Added 7 new round start lawsets
    • +
    • Modified some existing lawsets to reference things other than specifically humans
    • +
    +

    Mqiib updated:

    +
      +
    • Brig physician labcoats should be not invisible now
    • +
    +

    SapphicOverload updated:

    +
      +
    • fixed firefighting foam creating stabilized plasma whether or not it actually scrubbed any plasma from the air
    • +
    +

    SomeguyManperson updated:

    +
      +
    • apostate blade attack cooldown increased by 40%, putting its damage in line with other nullrods
    • +
    +

    cowbot92 updated:

    +
      +
    • Adjusts void path spells
    • +
    • Adjusts ominous armor values
    • +
    • Adjusts amount of targets you can choose
    • +
    +

    warface1234455 updated:

    +
      +
    • You can now see the amount flesh damage and healing in a burn wound using health scanner
    • +
    • health scanner now accurately indicates amount of infection in burn wound as well as the percentage of sanitzation/infestation
    • +
    • Fix unnable to give rnd console access to ID
    • +
    • Changed the "R&D Access" desc on id mod to "Toxins Access" desc so it wont confuse people
    • +
    + +

    28 September 2023

    +

    cark updated:

    +
      +
    • v1 can no longer be bought if there are under 25 players in the round
    • +
    +

    xPokee updated:

    +
      +
    • Adds a frog holoform for pAIs.
    • +
    +

    ToasterBiome updated:

    +
      +
    • you can no longer use hilbert's hotel while incapacitated
    • +
    +

    cowbot92 updated:

    +
      +
    • Adds new heretic path - Void
    • +
    • Adds new sounds to go with heretic abilities
    • +
    • Adds new sprites for Void Blade and abilities
    • +
    + +

    27 September 2023

    +

    @Moltijoe @Scrambledeggs00 updated:

    +
      +
    • Adds new nullrod, aspergillum and aspersorium
    • +
    • Sprites for aspergillum and aspersorium
    • +
    +

    @Mqiib @Ebin-Halcyon updated:

    +
      +
    • TONS of updated sprites for lizards, jumpsuits, and some external wear
    • +
    • Some new digitigrade versions of biosuits/armors
    • +
    • (Temporary) removed light tiger stripe tails
    • +
    • Removes soul
    • +
    • I can't believe it's finally done
    • +
    +

    ChesterTheCheesy updated:

    +
      +
    • cannot activate luminescent slime core effects while incapacitated
    • +
    +

    cowbot92 updated:

    +
      +
    • Adjusts Heretic Book Desc
    • +
    • Allows IPC & Pretenis (and robotic limbs) to heal on rust tiles
    • +
    • Allows heretics to gain points w/o needing book in bag
    • +
    • non-heretics can no longer wear heretic stuff
    • +
    +

    ynot01 updated:

    +
      +
    • Toolboxes now universally do 3 more damage
    • +
    • Welding tool flames now do 3 less damage
    • +
    + +

    26 September 2023

    +

    cark updated:

    +
      +
    • fixes missing wall in asteroid medbay
    • +
    +

    Moltijoe updated:

    +
      +
    • fixes a "bug" with borg hypospray recharge time being 10x what it was supposed to be
    • +
    • Cogging an APC removes access requirement to locking
    • +
    • Random chance for APCs to be unlocked round start
    • +
    • Battle Royale 😴 💤
    • +
    +

    Therandomhoboo updated:

    +
      +
    • Fixed the typo for when you make syndicate screwdriver (flavour text)
    • +
    • Horror's got that very needed eye surgery to give them vision in the dark
    • +
    +

    Yarinoi updated:

    +
      +
    • CANNOT STOP HULK!!!!
    • +
    • HULK TWEAKED TO SERVE PURPOSE OF TRAITOR ITEM BETTER!!!
    • +
    +

    cowbot92 updated:

    +
      +
    • Added new UI for heretics
    • +
    • Removed old heretic UI
    • +
    • Adjusted how heretic codex works slightly
    • +
    +

    warface1234455 updated:

    +
      +
    • bluespace harvester can now output ores based on weight for 30K points, you will get 70 ores total, with the quantity of each ore type determined by its weight.
    • +
    • adjust debug material box diamond counts to 50 like others
    • +
    • bluespace harvester no longer output things on top of the machine
    • +
    • removed some unnecessary metal/glass sheets in cultural tab
    • +
    • Give cyborg tablet access to the cyborg monitor program.
    • +
    • Fix the issue of the log file not being downloaded in the cyborg monitor program.
    • +
    + +

    25 September 2023

    +

    ToasterBiome, Someguymanperson updated:

    +
      +
    • Research circuit boards in R&D can be "hacked" with a screwdriver to unlock access.
    • +
    +

    cark updated:

    +
      +
    • new ghost role: icemoon hermit
    • +
    +

    JohnFulpWillard updated:

    +
      +
    • Super Chameleon (Changeling chameleon) can no longer be downloaded from a DNA console.
    • +
    • Tanks with devices attached to it now properly remove their appearance when the devices are removed.
    • +
    +

    Kell-E updated:

    +
      +
    • It's no longer possible to draw infinite sanguirite from an epipen.
    • +
    +

    MajManatee updated:

    +
      +
    • staff no longer are deafened by prayer
    • +
    +

    Moltijoe updated:

    +
      +
    • Fixes a "bug" with holoparasite body dusting (!)
    • +
    +

    SapphicOverload updated:

    +
      +
    • fixed undocumented change that nerfed lizards, they no longer have slower tool speed
    • +
    +

    SomeguyManperson updated:

    +
      +
    • stuff nobody will read has been written
    • +
    +

    wonderinghost updated:

    +
      +
    • adds admin fax sound
    • +
    • fixes centcom dissapearing from fax list if switching off
    • +
    + +

    24 September 2023

    +

    cark updated:

    +
      +
    • new icemoon ghostrole - Icemoon Walkers
    • +
    • icemoon seed vault is now added
    • +
    • adds a new shuttle - the cozy shuttle
    • +
    +

    Moltijoe updated:

    +
      +
    • Holoparasites no longer dust liches upon death
    • +
    • Holoparasites lose 4 random stats upon revival by a lich
    • +
    +

    Therandomhoboo updated:

    +
      +
    • Added nutriments to Polypore, Porcini, inocybe & ember so grind results will work
    • +
    • Ash flora, mushroom shavings, stem, cap & leaves now give mushroom powder when you grind them
    • +
    • Hallucinogen Mushroom & Hippie's delight no longer perma dizzy you
    • +
    • Hippie's delight numbers for how long their effects last (dizzy, druggy, jitter etc) have been sorted out (Thanks to @Moltijoe)
    • +
    + +

    23 September 2023

    +

    Kell-E updated:

    +
      +
    • Added a chemical heater board to secure storage
    • +
    • The flesh heretic "Lover's Exsanguination" research no longer breaks their mansus grasp.
    • +
    • Paramedics, Geneticists and Network Admins no longer ignore the existance of jumpskirts.
    • +
    +

    Moltijoe updated:

    +
      +
    • Tweaks accessibility flags for mouth and eyes
    • +
    +

    SapphicOverload updated:

    +
      +
    • the turbine now generates power from a difference in pressure instead of just moving the gas anyway and making power for free (respect conservation of energy!!!)
    • +
    • turbine computer tgui shows the turbine's internal pressure
    • +
    • fixed the turbine making free energy from nowhere at low RPM
    • +
    • fixed the turbine forcing air into the outlet turf when it's blocked
    • +
    • fixed inlet and outlet turfs not updating air properly
    • +
    +

    SomeguyManperson updated:

    +
      +
    • blurred vision from alcohol shouldn't last 2 trillion years
    • +
    • Redspace dust now only teleports if injected in a 5 unit dose
    • +
    +

    Therandomhoboo updated:

    +
      +
    • Removed the potential to spawn the clown effect for the anomalous crystal after you beat the colossus megafauna
    • +
    +

    Yarinoi updated:

    +
      +
    • HULK IS NOW BUYABLE BY HUMAN TRAITOR FOR 15 TC!!!
    • +
    • MINOR CHANGES TO HULK!!!
    • +
    • Hulk isn't buyable anymore until the allow_transfer bug gets fixed, sorry.
    • +
    +

    cowbot92 updated:

    +
      +
    • Adjusts Famished Roar and All Knowing Mansus Grasp
    • +
    +

    iloveloopers updated:

    +
      +
    • Clockwork cult teleport sound is quieter
    • +
    + +

    22 September 2023

    +

    @Scrambledeggs00 @Moltijoe updated:

    +
      +
    • Syndicate medipens have a unique inhand sprite
    • +
    • Gives some medipens new sprites
    • +
    • Medipen item sprites + syndie medipen inhand
    • +
    +

    cark updated:

    +
      +
    • fixes interrogation chairs on box and makes the window bigger
    • +
    • lavaland hermit has proper walls
    • +
    • icemeta station river has been replaced with a chasm which drops into the mining river
    • +
    +

    Altoids updated:

    +
      +
    • Fixed IPCs with no uniform having IDs that cannot be stolen or removed via the inventory menu
    • +
    • In the inventory menu, people who do not need their uniform to hold their belt/ID/etc. now display their inventory slightly differently, hopefully enough to remind you to strip IPCs completely when you need to.
    • +
    +

    Moltijoe updated:

    +
      +
    • Lets nukies and clownops buy species restricted items if species swapped by an admin
    • +
    • Medhud examine of implants now splits them into individual lines
    • +
    • Preterni can recharge using other electricity using species
    • +
    • All megafauna leave items when dusting a miner
    • +
    • gibbing always leaves the victim's items
    • +
    • Android species is no longer invisible
    • +
    • Pacifism touch
    • +
    +

    Therandomhoboo updated:

    +
      +
    • Increased Req to unlock Warden job role from 5hrs to 10hrs
    • +
    +

    ToasterBiome updated:

    +
      +
    • Pride week is now Pride month
    • +
    +

    Yarinoi updated:

    +
      +
    • Dials back Atheist's Fedora users red pill prescription, allowing them to throw their fedora without it embedding into people.
    • +
    +

    cowbot92 updated:

    +
      +
    • Adjusts some heretic spells for the Mind Path
    • +
    +

    solwashere updated:

    +
      +
    • shock touch now logs attacks
    • +
    + +

    20 September 2023

    +

    Altoids updated:

    +
      +
    • Fixed being able to use Last Stand as Changeling to get a null loc. No rubies for you!
    • +
    +

    Aquizit updated:

    +
      +
    • Out with boxy trim, in with sleek trim.
    • +
    +

    Moltijoe updated:

    +
      +
    • Battleroyale 🤡
    • +
    +

    SapphicOverload updated:

    +
      +
    • digitigrade robot legs have less awful code now
    • +
    +

    adamsong updated:

    +
      +
    • fixed convert to traitor button not properly grabbing objectives
    • +
    +

    cowbot92 updated:

    +
      +
    • Adjusted spell Amygdala Assault to be point click target vs straight forward fire & Mental Obfuscation nerf
    • +
    • fixed a few typos in mind epilogue as well as fixed ascension title
    • +
    +

    warface1234455 updated:

    +
      +
    • Fix unnable to put screwdriver into deconstructive analyzer or autolathe, you will now have to use disarm intent to open/close panel and help intent to put the screw in.
    • +
    + +

    19 September 2023

    +

    Moltijoe updated:

    +
      +
    • Preterni only get 1/2 the heat from charging
    • +
    +

    SapphicOverload updated:

    +
      +
    • fixed IPCs taking 3x more damage in crit than intended
    • +
    +

    cowbot92 updated:

    +
      +
    • Adds a new heretic path to the game! Adjusts ascension on other paths as well to look cooler.
    • +
    • Adds a new heretic blade to go along with the new path.
    • +
    + +

    18 September 2023

    +

    cark updated:

    +
      +
    • miners will sometimes shout a secret phrase when mining
    • +
    +

    Moltijoe updated:

    +
      +
    • Powerhungry species hunger icons now use the correct icons
    • +
    +

    wonderinghost updated:

    +
      +
    • adds pencil holders to station maps
    • +
    + +

    17 September 2023

    +

    Marmio64 updated:

    +
      +
    • Sinful demon form now has 200 health rather than 160, and heals 10 hp per hit rather than 8 as a result.
    • +
    +

    Moltijoe updated:

    +
      +
    • Adds some mutations into the battle royale loot drop pool
    • +
    • Use ctrl or alt click to go down or up a ladder
    • +
    • Preterni heat up as they recharge
    • +
    +

    SapphicOverload updated:

    +
      +
    • explosions cause turf fires instead of hotspots
    • +
    + +

    16 September 2023

    +

    Moltijoe updated:

    +
      +
    • preternis can now not starve again
    • +
    • Halloween starts 4 days earlier
    • +
    + +

    15 September 2023

    +

    Moltijoe updated:

    +
      +
    • Preterni consume more power and take slightly longer to fully charge
    • +
    + +

    14 September 2023

    +

    Moltijoe updated:

    +
      +
    • URINAL CAKES HAVE MORE AMMONIA AND CHLORINE
    • +
    +

    tattax updated:

    +
      +
    • fixed some bugs
    • +
    +

    warface1234455 updated:

    +
      +
    • Wooden barrel build cost lowered from 30-10 woods
    • +
    • Wooden barrel now properly drops materials upon deconstruction
    • +
    • Wooden barrel wont be damaged by containers anymore
    • +
    + +

    13 September 2023

    +

    cark updated:

    +
      +
    • fixes mapping errors at ai sat on box
    • +
    • fixes gravity in pubby space ruin
    • +
    +

    Moltijoe updated:

    +
      +
    • Autotomy no longer violently dismembers the user
    • +
    • Autotomy genetic instability reduced to 10 from 30
    • +
    • more battle royale tweaks
    • +
    • tweaked some battle royale loot drop weight
    • +
    • loot drop crates delete themselves after a delay
    • +
    • Medical doctor and paramedic spawn with medical belts
    • +
    +

    Runian updated:

    +
      +
    • The Cyborg Remote Monitoring program now displays the area name and coordinates of any listed cyborgs.
    • +
    • Having buckled a mob once as a cyborg no longer prevents you from getting petted ever again.
    • +
    +

    SapphicOverload updated:

    +
      +
    • icemoon surface is less bright
    • +
    • fixed icemoon movement speed in some areas
    • +
    +

    SomeguyManperson updated:

    +
      +
    • bitflag stuff, no player-facing changes
    • +
    • holodeck animals no longer provide a mood buff
    • +
    +

    ToasterBiome updated:

    +
      +
    • you won't clutch your chest while having a heart attack if you have no arms.
    • +
    • some webhook actions won't wait a tick to finish anymore
    • +
    • increases alien tool's bluespace tap cost from 60,000 to 250,000
    • +
    +

    azzzertyy updated:

    +
      +
    • pride jackets are not contraband
    • +
    +

    solwashere updated:

    +
      +
    • Deletes the lemoline recipe
    • +
    +

    warface1234455 updated:

    +
      +
    • Geranium seed can now be mutated into Bee balm
    • +
    • Bee balm can no longer be mutated into Geranium
    • +
    + +

    12 September 2023

    +

    Ehj236 updated:

    +
      +
    • You can no longer move up and down Z levels with a mech (to glitch through the floor)
    • +
    +

    Moltijoe updated:

    +
      +
    • holy light properly applies the heal boost buff
    • +
    +

    Runian updated:

    +
      +
    • Janiborgs start with a "floor autocleaner" item that mimics their automatic cleaning, except it is toggleable and requires it to be equipped to work.
    • +
    • Janiborgs no longer automatically clean things when moving things.
    • +
    +

    azzzertyy updated:

    +
      +
    • you can no longer duplicate resurrection serum
    • +
    + +

    11 September 2023

    +

    tattax updated:

    +
      +
    • readds pacify (spell) to the game
    • +
    + +

    09 September 2023

    +

    MidoriWroth, Ghommie, ktlwjec updated:

    +
      +
    • Peanuts. Seeds start in the botany vendor. They can be grinded to make peanut butter.
    • +
    • Breadsticks. Raw breadsticks crafted with 1 dough slice, 3u salt, 2 butter slices. Baked in the oven.
    • +
    • Granola bar. Crafted with 1 oat stalk, 1 peanut, 1 chocolate, 1 raisins, 2u sugar.
    • +
    • Onigiri. Crafted with 1 seaweed sheet and 1 boiled rice. Can be customised with other ingredients.
    • +
    • Peanut butter cookie. Crafted with 5u peanut butter, 1 pastry base.
    • +
    • Brownie batter. Raw brownie batter crafted with 5u flour, 5u sugar, 2 eggs, 5u coco powder, 4 butter slices. Baked in the oven and then sliced into 4 pieces.
    • +
    • Peanut butter and banana sandwich. Crafted with 2 bread slices, 5u peanut butter, 1 banana.
    • +
    • Peanut butter and jelly sandwich. Crafted with 2 bread slices, 5u peanut butter, 5u cherry jelly.
    • +
    • Nigiri sushi. Crafted with 1 seaweed sheet, 1 boiled rice, 1 carp meat, 2u soy sauce.
    • +
    +

    cark updated:

    +
      +
    • the beach biodome no longer spawns on lavaland
    • +
    +

    MajManatee updated:

    +
      +
    • Added 3 new ghost types
    • +
    +

    Moltijoe updated:

    +
      +
    • Cultist mirror shield no longer spawns infinite illusions if blocking a shotgun
    • +
    • Cultist mirror shield can only spawn 4 illusions before breaking instead of 5
    • +
    • Cultist mirror shield illusions have 1 health and 0 damage
    • +
    • Blood cult spear is huge instead of normal size
    • +
    • Northern Coat is no longer ckey locked
    • +
    • Holy Light water no longer applies a heal boost
    • +
    • Holy Light bible now applies a 30 second duration heal boost
    • +
    • Holy Light bible now heals for 10 instead of 40 with a 10 second cooldown instead of 12 and has no favor cost
    • +
    • Holy Light heal boost now increases by 100% (200% in chapel) instead of 20% (40% in chapel)
    • +
    • Holy Light medbot applies the heal boost status effect instead of injecting holy water
    • +
    • Borg reagent dispensers regen in seconds rather than deciseconds
    • +
    • Borg reagent dispensers regen less affected by lag
    • +
    • Spawns 3 clockcult + 1 for every 8 players over 30 instead of 4 + 1 for every 10
    • +
    • CPR only requires lungs, not needing to breathe
    • +
    • His Grace can be bought by assistants
    • +
    • Drag and drop iv drip filling
    • +
    • Cultist shield robe has 1 shield charge, but regens after 45 seconds of not getting hit
    • +
    +

    Runian updated:

    +
      +
    • Syndicate Surgery Duffel Bag's description in the Uplink now accurately tells you that the surgery tools have double toolspeed (0.5) and includes a surgical mat.
    • +
    • Failed cyborg shoves no longer push them back two tiles.
    • +
    • Hulk punches on cyborgs now properly pushes them back two tiles.
    • +
    • PKA upgrades properly eject when crowbarred.
    • +
    • A new upgrade for engineering and mining cyborgs that replaces their normal meson vision with night vision meson vision.
    • +
    • Cyborg meson/material dark vision is now consistent with the human version of 2 tiles.
    • +
    • Cyborg meson/material lighting alpha is now consistent with the human version.
    • +
    +

    SomeguyManperson updated:

    +
      +
    • Syndicate icemoon base has been downgraded to keep the syndicate on the base
    • +
    +

    azzzertyy updated:

    +
      +
    • Chaplain no longer throws people into snow on Icemeta
    • +
    • No more superspeed on icemeta
    • +
    +

    solwashere updated:

    +
      +
    • Adds "testing" to the GitHub PR template.
    • +
    +

    warface1234455 updated:

    +
      +
    • Bluespace harvester can now output alien tool for 60000 points
    • +
    + +

    08 September 2023

    +

    MajManatee updated:

    +
      +
    • generator fuel can be dragged into the generator now.
    • +
    +

    Moltijoe updated:

    +
      +
    • Preterni are slightly more likely to catch viruses than before
    • +
    • Viruses in preterni progress faster
    • +
    • Preternis water damage does ~50% more stamina damage
    • +
    +

    adamsong updated:

    +
      +
    • fixed photos being bugged as hell
    • +
    + +

    07 September 2023

    +

    cark updated:

    +
      +
    • removes the two hospital ruins on lavaland
    • +
    +

    Runian updated:

    +
      +
    • Clown Cyborg's pie cannon now properly recharges every 4 seconds (2 ticks) instead of every 2 seconds (1 tick).
    • +
    • All self-recharging pneumatic cannon now recharge based on delta_time instead of ticks. This means that you won't lose out when lag happens.
    • +
    +

    SapphicOverload updated:

    +
      +
    • hecata clan necromancy reverts you back to your original species after it ends instead of staying a zombie
    • +
    +

    adamsong updated:

    +
      +
    • bluespace harvester now has a 10 minute cooldown after spawning a portal when not emagged
    • +
    + +

    06 September 2023

    +

    Runian updated:

    +
      +
    • You must now be adjacent to unlock or lock a robot's cover instead of infinite range.
    • +
    + +

    05 September 2023

    +

    Addust updated:

    +
      +
    • syndicate icemoon is slightly less shit. _**I CANNOT FIX THE RANDOM FAUNA SPAWNS. THAT'S THE ICEMOON DEV'S PROBLEM.**_
    • +
    • i fucked up
    • +
    +

    Runian updated:

    +
      +
    • Pulsing Mech Fabricator's WIRE_ZAP no longer breaks your limbs unless you are adjacent to it as intended.
    • +
    +

    SomeguyManperson updated:

    +
      +
    • holy light now gains more favor when gaining favor
    • +
    +

    wonderinghost updated:

    +
      +
    • Added pencil holder to bureaucracy crate and game.
    • +
    • adds pencil holder files
    • +
    + +

    04 September 2023

    +

    cark updated:

    +
      +
    • donors can take pride hoodies as a donor item now
    • +
    +

    ktlwjec updated:

    +
      +
    • Roboticist and geneticist get rnd console access on skeleton crew.
    • +
    • Engineers and shaft miners can use the rnd console on skeleton crew.
    • +
    +

    Addust updated:

    +
      +
    • The Syndicate has stopped throwing their supposedly important listening stations into gibtonite-filled shithole rocks, and has also supplied a few extra supplies.
    • +
    • i forgor an air alarm in syndie lavaland
    • +
    +

    Marmio64 updated:

    +
      +
    • Hecata Bloodsuckers need to revive 4-5 people rather than 7-8
    • +
    +

    Moltijoe updated:

    +
      +
    • vials reskin using the proper sprite again
    • +
    +

    Runian updated:

    +
      +
    • Certain words in the German accent such as "research" and "scientist" are now properly accented.
    • +
    +

    SapphicOverload updated:

    +
      +
    • fixed bloodsucker vassalization not removing mindshield implants
    • +
    +

    SomeguyManperson updated:

    +
      +
    • coders are no longer required to explain why "the game functions correctly" is good for the game
    • +
    +

    adamsong updated:

    +
      +
    • fixed AI shell lawsync not working
    • +
    • fixed photos forgetting about people who are cremated
    • +
    • fixed photos not recognizing targets who were cloned
    • +
    • fixed photos thinking dead people are alive after copying
    • +
    +

    warface1234455 updated:

    +
      +
    • Replaces old pda with modern pda in pda box
    • +
    GoonStation 13 Development Team diff --git a/icons/effects/alphacolors.dmi b/icons/effects/alphacolors.dmi index c3dbcaee39be..f3241ba010d9 100644 Binary files a/icons/effects/alphacolors.dmi and b/icons/effects/alphacolors.dmi differ diff --git a/icons/effects/lighting_object.dmi b/icons/effects/lighting_object.dmi index 077043809d64..49d16c58acf8 100644 Binary files a/icons/effects/lighting_object.dmi and b/icons/effects/lighting_object.dmi differ diff --git a/icons/obj/barsigns.dmi b/icons/obj/barsigns.dmi index 46b9cfb8a746..584395f14644 100644 Binary files a/icons/obj/barsigns.dmi and b/icons/obj/barsigns.dmi differ diff --git a/icons/obj/monitors.dmi b/icons/obj/monitors.dmi index 06e2f8782ab0..860a11a75ed5 100644 Binary files a/icons/obj/monitors.dmi and b/icons/obj/monitors.dmi differ diff --git a/icons/obj/power.dmi b/icons/obj/power.dmi index 36a6ee743e76..9074aa86d43b 100644 Binary files a/icons/obj/power.dmi and b/icons/obj/power.dmi differ diff --git a/icons/obj/vending.dmi b/icons/obj/vending.dmi index e915bb259c74..f9cd68d8ddae 100644 Binary files a/icons/obj/vending.dmi and b/icons/obj/vending.dmi differ diff --git a/icons/obj/wallmounts.dmi b/icons/obj/wallmounts.dmi index fcbc0830b76b..2e392b221d95 100644 Binary files a/icons/obj/wallmounts.dmi and b/icons/obj/wallmounts.dmi differ diff --git a/tgui/packages/tgui/interfaces/CameraConsole.js b/tgui/packages/tgui/interfaces/CameraConsole.js deleted file mode 100644 index 8ece49eff205..000000000000 --- a/tgui/packages/tgui/interfaces/CameraConsole.js +++ /dev/null @@ -1,143 +0,0 @@ -import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; -import { classes } from 'common/react'; -import { createSearch } from 'common/string'; -import { useBackend, useLocalState } from '../backend'; -import { Button, ByondUi, Flex, Input, Section } from '../components'; -import { Window } from '../layouts'; - -/** - * Returns previous and next camera names relative to the currently - * active camera. - */ -export const prevNextCamera = (cameras, activeCamera) => { - if (!activeCamera) { - return []; - } - const index = cameras.findIndex(camera => ( - camera.name === activeCamera.name - )); - return [ - cameras[index - 1]?.name, - cameras[index + 1]?.name, - ]; -}; - -/** - * Camera selector. - * - * Filters cameras, applies search terms and sorts the alphabetically. - */ -export const selectCameras = (cameras, searchText = '') => { - const testSearch = createSearch(searchText, camera => camera.name); - return flow([ - // Null camera filter - filter(camera => camera?.name), - // Optional search term - searchText && filter(testSearch), - // Slightly expensive, but way better than sorting in BYOND - sortBy(camera => camera.name), - ])(cameras); -}; - -export const CameraConsole = (props, context) => { - const { act, data } = useBackend(context); - const { mapRef, activeCamera } = data; - const cameras = selectCameras(data.cameras); - const [ - prevCameraName, - nextCameraName, - ] = prevNextCamera(cameras, activeCamera); - return ( - -
    - - - -
    -
    -
    - Camera: - {activeCamera - && activeCamera.name - || '—'} -
    -
    -
    - -
    -
    - ); -}; - -export const CameraConsoleContent = (props, context) => { - const { act, data } = useBackend(context); - const [ - searchText, - setSearchText, - ] = useLocalState(context, 'searchText', ''); - const { activeCamera } = data; - const cameras = selectCameras(data.cameras, searchText); - return ( - - - setSearchText(value)} /> - - -
    - {cameras.map(camera => ( - // We're not using the component here because performance - // would be absolutely abysmal (50+ ms for each re-render). -
    [act('switch_camera', { - name: camera.name, - }), document.getElementsByClassName('CameraConsole__left')[0].focus()]}> - {camera.name} -
    - ))} -
    -
    -
    - ); -}; diff --git a/tgui/packages/tgui/interfaces/CameraConsole.tsx b/tgui/packages/tgui/interfaces/CameraConsole.tsx new file mode 100644 index 000000000000..7b898011f7f3 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CameraConsole.tsx @@ -0,0 +1,214 @@ +import { filter, sortBy } from 'common/collections'; +import { flow } from 'common/fp'; +import { BooleanLike, classes } from 'common/react'; +import { createSearch } from 'common/string'; +import { useBackend, useLocalState } from '../backend'; +import { Button, ByondUi, Input, NoticeBox, Section, Stack } from '../components'; +import { Window } from '../layouts'; + +type Data = { + activeCamera: Camera & { status: BooleanLike }; + cameras: Camera[]; + can_spy: BooleanLike; + mapRef: string; + network: string[]; +}; + +type Camera = { + name: string; + ref: string; +}; + +/** + * Returns previous and next camera names relative to the currently + * active camera. + */ +const prevNextCamera = ( + cameras: Camera[], + activeCamera: Camera & { status: BooleanLike } +) => { + if (!activeCamera || cameras.length < 2) { + return []; + } + + const index = cameras.findIndex((camera) => camera.ref === activeCamera.ref); + + switch (index) { + case -1: // Current camera is not in the list + return [cameras[cameras.length - 1].ref, cameras[0].ref]; + + case 0: // First camera + if (cameras.length === 2) return [cameras[1].ref, cameras[1].ref]; // Only two + + return [cameras[cameras.length - 1].ref, cameras[index + 1].ref]; + + case cameras.length - 1: // Last camera + if (cameras.length === 2) return [cameras[0].ref, cameras[0].ref]; + + return [cameras[index - 1].ref, cameras[0].ref]; + + default: + // Middle camera + return [cameras[index - 1].ref, cameras[index + 1].ref]; + } +}; + +/** + * Camera selector. + * + * Filters cameras, applies search terms and sorts the alphabetically. + */ +const selectCameras = (cameras: Camera[], searchText = ''): Camera[] => { + const testSearch = createSearch(searchText, (camera: Camera) => camera.name); + + return flow([ + filter((camera: Camera) => !!camera.name), + // Optional search term + searchText && filter(testSearch), + // Slightly expensive, but way better than sorting in BYOND + sortBy((camera: Camera) => camera), + ])(cameras); +}; + +export const CameraConsole = (props) => { + return ( + + + + + + ); +}; + +export const CameraContent = (props) => { + return ( + + + + + + + + + ); +}; + +const CameraSelector = (props, context) => { + const { act, data } = useBackend(context); + const [searchText, setSearchText] = useLocalState(context, 'searchText', ''); + const { activeCamera } = data; + const cameras = selectCameras(data.cameras, searchText); + + return ( + + + setSearchText(value)} + /> + + +
    + {cameras.map((camera) => ( + // We're not using the component here because performance + // would be absolutely abysmal (50+ ms for each re-render). +
    + act('switch_camera', { + camera: camera.ref, + }) + }> + {camera.name} +
    + ))} +
    +
    +
    + ); +}; + +const CameraControls = (props, context) => { + const { act, data } = useBackend(context); + const { activeCamera, can_spy, mapRef } = data; + const [searchText] = useLocalState(context, 'searchText', ''); + + const cameras = selectCameras(data.cameras, searchText); + + const [prevCamera, nextCamera] = prevNextCamera(cameras, activeCamera); + + return ( +
    + + + + + {activeCamera?.status ? ( + {activeCamera.name} + ) : ( + No input signal + )} + + + + {!!can_spy && ( +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/NtosSecurEye.js b/tgui/packages/tgui/interfaces/NtosSecurEye.js deleted file mode 100644 index 8d4bc6274528..000000000000 --- a/tgui/packages/tgui/interfaces/NtosSecurEye.js +++ /dev/null @@ -1,60 +0,0 @@ -import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; -import { classes } from 'common/react'; -import { createSearch } from 'common/string'; -import { Fragment } from 'inferno'; -import { useBackend, useLocalState } from '../backend'; -import { Button, ByondUi, Input, Section } from '../components'; -import { NtosWindow } from '../layouts'; -import { prevNextCamera, selectCameras, CameraConsoleContent } from './CameraConsole'; -import { logger } from "../logging"; - -export const NtosSecurEye = (props, context) => { - const { act, data, config } = useBackend(context); - const { PC_device_theme, mapRef, activeCamera } = data; - const cameras = selectCameras(data.cameras); - const [ - prevCameraName, - nextCameraName, - ] = prevNextCamera(cameras, activeCamera); - return ( - - -
    - -
    -
    -
    - Camera: - {activeCamera - && activeCamera.name - || '—'} -
    -
    -
    - -
    -
    -
    - ); -}; diff --git a/tgui/packages/tgui/interfaces/NtosSecureEye.tsx b/tgui/packages/tgui/interfaces/NtosSecureEye.tsx new file mode 100644 index 000000000000..c445b6f160f5 --- /dev/null +++ b/tgui/packages/tgui/interfaces/NtosSecureEye.tsx @@ -0,0 +1,12 @@ +import { NtosWindow } from '../layouts'; +import { CameraContent } from './CameraConsole'; + +export const NtosSecurEye = (props) => { + return ( + + + + + + ); +}; diff --git a/tgui/packages/tgui/styles/interfaces/CameraConsole.scss b/tgui/packages/tgui/styles/interfaces/CameraConsole.scss deleted file mode 100644 index 7e8f99fdcceb..000000000000 --- a/tgui/packages/tgui/styles/interfaces/CameraConsole.scss +++ /dev/null @@ -1,53 +0,0 @@ -@use '../base.scss'; - -$background-color: rgba(0, 0, 0, 0.33) !default; - -.CameraConsole__left { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: base.em(220px); -} - -.CameraConsole__right { - position: absolute; - top: 0; - bottom: 0; - left: base.em(220px); - right: 0; - background-color: $background-color; -} - -.CameraConsole__toolbar { - position: absolute; - top: 0; - left: 0; - right: 0; - height: 2em; - line-height: 2em; - margin: 0.25em 1em 0; -} - -.CameraConsole__toolbarRight { - position: absolute; - top: 0; - right: 0; - height: 2em; - line-height: 2em; - margin: 0.33em 0.5em 0; -} - -.CameraConsole__map { - position: absolute; - top: base.em(26px); - bottom: 0; - left: 0; - right: 0; - margin: 0.5em; - text-align: center; - - .NoticeBox { - margin-top: calc(50% - 2em); - } -} diff --git a/tgui/packages/tgui/styles/main.scss b/tgui/packages/tgui/styles/main.scss index d60242ae8627..9a7a42fcf5aa 100644 --- a/tgui/packages/tgui/styles/main.scss +++ b/tgui/packages/tgui/styles/main.scss @@ -48,7 +48,6 @@ // Interfaces @include meta.load-css('./interfaces/AlertModal.scss'); -@include meta.load-css('./interfaces/CameraConsole.scss'); @include meta.load-css('./interfaces/InspectorBooth.scss'); @include meta.load-css('./interfaces/ListInput.scss'); @include meta.load-css('./interfaces/HellishRunes.scss'); diff --git a/yogstation.dme b/yogstation.dme index 710605c1580a..33f0d86abd40 100644 --- a/yogstation.dme +++ b/yogstation.dme @@ -41,6 +41,7 @@ #include "code\__DEFINES\bindings.dm" #include "code\__DEFINES\bloodsuckers.dm" #include "code\__DEFINES\callbacks.dm" +#include "code\__DEFINES\cameranets.dm" #include "code\__DEFINES\cargo.dm" #include "code\__DEFINES\chat.dm" #include "code\__DEFINES\cinematics.dm" @@ -75,12 +76,15 @@ #include "code\__DEFINES\inventory.dm" #include "code\__DEFINES\is_helpers.dm" #include "code\__DEFINES\jobs.dm" +#include "code\__DEFINES\lag_switch.dm" #include "code\__DEFINES\language.dm" #include "code\__DEFINES\layers.dm" #include "code\__DEFINES\lighting.dm" #include "code\__DEFINES\logging.dm" #include "code\__DEFINES\machines.dm" #include "code\__DEFINES\magic.dm" +#include "code\__DEFINES\map_switch.dm" +#include "code\__DEFINES\mapping.dm" #include "code\__DEFINES\maps.dm" #include "code\__DEFINES\materials.dm" #include "code\__DEFINES\maths.dm" @@ -93,6 +97,7 @@ #include "code\__DEFINES\move_force.dm" #include "code\__DEFINES\movement.dm" #include "code\__DEFINES\movespeed_modification.dm" +#include "code\__DEFINES\multiz.dm" #include "code\__DEFINES\nanites.dm" #include "code\__DEFINES\networks.dm" #include "code\__DEFINES\obj_flags.dm" @@ -120,6 +125,7 @@ #include "code\__DEFINES\shuttles.dm" #include "code\__DEFINES\sight.dm" #include "code\__DEFINES\sound.dm" +#include "code\__DEFINES\space.dm" #include "code\__DEFINES\spaceman_dmm.dm" #include "code\__DEFINES\span.dm" #include "code\__DEFINES\speech_channels.dm" @@ -145,15 +151,18 @@ #include "code\__DEFINES\wounds.dm" #include "code\__DEFINES\dcs\flags.dm" #include "code\__DEFINES\dcs\helpers.dm" +#include "code\__DEFINES\dcs\signals\mapping.dm" #include "code\__DEFINES\dcs\signals\signal_species.dm" #include "code\__DEFINES\dcs\signals\signals_action.dm" #include "code\__DEFINES\dcs\signals\signals_area.dm" +#include "code\__DEFINES\dcs\signals\signals_client.dm" #include "code\__DEFINES\dcs\signals\signals_datum.dm" #include "code\__DEFINES\dcs\signals\signals_food.dm" #include "code\__DEFINES\dcs\signals\signals_gib.dm" #include "code\__DEFINES\dcs\signals\signals_global.dm" #include "code\__DEFINES\dcs\signals\signals_global_object.dm" #include "code\__DEFINES\dcs\signals\signals_heretic.dm" +#include "code\__DEFINES\dcs\signals\signals_huds.dm" #include "code\__DEFINES\dcs\signals\signals_janitor.dm" #include "code\__DEFINES\dcs\signals\signals_mood.dm" #include "code\__DEFINES\dcs\signals\signals_moveloop.dm" @@ -208,10 +217,12 @@ #include "code\__HELPERS\_extools_api.dm" #include "code\__HELPERS\_lists.dm" #include "code\__HELPERS\_logging.dm" +#include "code\__HELPERS\_planes.dm" #include "code\__HELPERS\_string_lists.dm" #include "code\__HELPERS\animations.dm" #include "code\__HELPERS\areas.dm" #include "code\__HELPERS\AStar.dm" +#include "code\__HELPERS\cameras.dm" #include "code\__HELPERS\cmp.dm" #include "code\__HELPERS\colors.dm" #include "code\__HELPERS\config.dm" @@ -227,6 +238,7 @@ #include "code\__HELPERS\icon_smoothing.dm" #include "code\__HELPERS\icons.dm" #include "code\__HELPERS\level_traits.dm" +#include "code\__HELPERS\lighting.dm" #include "code\__HELPERS\maths.dm" #include "code\__HELPERS\matrices.dm" #include "code\__HELPERS\mobs.dm" @@ -244,6 +256,7 @@ #include "code\__HELPERS\screen_objs.dm" #include "code\__HELPERS\shell.dm" #include "code\__HELPERS\stat_tracking.dm" +#include "code\__HELPERS\string_lists.dm" #include "code\__HELPERS\text.dm" #include "code\__HELPERS\time.dm" #include "code\__HELPERS\traits.dm" @@ -312,14 +325,20 @@ #include "code\_onclick\hud\lavaland_elite.dm" #include "code\_onclick\hud\living.dm" #include "code\_onclick\hud\map_popups.dm" +#include "code\_onclick\hud\map_view.dm" #include "code\_onclick\hud\monkey.dm" #include "code\_onclick\hud\movable_screen_objects.dm" #include "code\_onclick\hud\pai.dm" #include "code\_onclick\hud\parallax.dm" #include "code\_onclick\hud\picture_in_picture.dm" #include "code\_onclick\hud\plane_master.dm" +#include "code\_onclick\hud\plane_master_controller.dm" +#include "code\_onclick\hud\plane_master_group.dm" +#include "code\_onclick\hud\plane_master_subtypes.dm" #include "code\_onclick\hud\radial.dm" #include "code\_onclick\hud\radial_persistent.dm" +#include "code\_onclick\hud\random_layer.dm" +#include "code\_onclick\hud\render_plate.dm" #include "code\_onclick\hud\revenanthud.dm" #include "code\_onclick\hud\robot.dm" #include "code\_onclick\hud\screen_objects.dm" @@ -329,6 +348,7 @@ #include "code\controllers\controller.dm" #include "code\controllers\failsafe.dm" #include "code\controllers\globals.dm" +#include "code\controllers\lag_switch.dm" #include "code\controllers\master.dm" #include "code\controllers\subsystem.dm" #include "code\controllers\configuration\config_entry.dm" @@ -485,6 +505,7 @@ #include "code\datums\actions\items\stealth_box.dm" #include "code\datums\actions\items\toggles.dm" #include "code\datums\actions\items\vortex_recall.dm" +#include "code\datums\actions\mobs\adjust_vision.dm" #include "code\datums\actions\mobs\language_menu.dm" #include "code\datums\actions\mobs\ninja.dm" #include "code\datums\actions\mobs\small_sprite.dm" @@ -516,6 +537,7 @@ #include "code\datums\components\butchering.dm" #include "code\datums\components\caltrop.dm" #include "code\datums\components\chasm.dm" +#include "code\datums\components\connect_mob_behalf.dm" #include "code\datums\components\construction.dm" #include "code\datums\components\curse_of_hunger.dm" #include "code\datums\components\decal.dm" @@ -530,6 +552,7 @@ #include "code\datums\components\gunpoint.dm" #include "code\datums\components\heal_react.dm" #include "code\datums\components\heirloom.dm" +#include "code\datums\components\hide_highest_offset.dm" #include "code\datums\components\hot_ice.dm" #include "code\datums\components\ice_walk.dm" #include "code\datums\components\igniter.dm" @@ -577,6 +600,7 @@ #include "code\datums\components\twohanded.dm" #include "code\datums\components\uplink.dm" #include "code\datums\components\waddling.dm" +#include "code\datums\components\wall_mounted.dm" #include "code\datums\components\wearertargeting.dm" #include "code\datums\components\wet_floor.dm" #include "code\datums\components\crafting\antag.dm" @@ -680,6 +704,7 @@ #include "code\datums\elements\regenerator.dm" #include "code\datums\elements\rust.dm" #include "code\datums\elements\squish.dm" +#include "code\datums\elements\turf_transparency.dm" #include "code\datums\elements\update_icon_blocker.dm" #include "code\datums\elements\decals\_decals.dm" #include "code\datums\elements\decals\blood.dm" @@ -784,6 +809,7 @@ #include "code\datums\status_effects\debuffs\jitteriness.dm" #include "code\datums\status_effects\debuffs\knuckleroot.dm" #include "code\datums\status_effects\debuffs\red_eye.dm" +#include "code\datums\status_effects\debuffs\screen_blur.dm" #include "code\datums\status_effects\debuffs\silenced.dm" #include "code\datums\status_effects\debuffs\speech_debuffs.dm" #include "code\datums\traits\_quirk.dm" @@ -825,6 +851,7 @@ #include "code\datums\wounds\slash.dm" #include "code\datums\wounds\scars\_scars.dm" #include "code\game\alternate_appearance.dm" +#include "code\game\atom_invisibility.dm" #include "code\game\atoms.dm" #include "code\game\atoms_movable.dm" #include "code\game\communications.dm" @@ -900,6 +927,7 @@ #include "code\game\machinery\aug_manipulator.dm" #include "code\game\machinery\autolathe.dm" #include "code\game\machinery\bank_machine.dm" +#include "code\game\machinery\barsigns.dm" #include "code\game\machinery\Beacon.dm" #include "code\game\machinery\bounty_board.dm" #include "code\game\machinery\buttons.dm" @@ -1402,7 +1430,6 @@ #include "code\game\objects\items\two_handed\spears.dm" #include "code\game\objects\structures\aliens.dm" #include "code\game\objects\structures\artstuff.dm" -#include "code\game\objects\structures\barsigns.dm" #include "code\game\objects\structures\bedsheet_bin.dm" #include "code\game\objects\structures\catwalk.dm" #include "code\game\objects\structures\crateshelf.dm" @@ -1519,6 +1546,7 @@ #include "code\game\turfs\closed.dm" #include "code\game\turfs\open.dm" #include "code\game\turfs\turf.dm" +#include "code\game\turfs\baseturfs\baseturfs.dm" #include "code\game\turfs\openspace\openspace.dm" #include "code\game\turfs\simulated\chasm.dm" #include "code\game\turfs\simulated\dirtystation.dm" @@ -2611,6 +2639,7 @@ #include "code\modules\lighting\lighting_setup.dm" #include "code\modules\lighting\lighting_source.dm" #include "code\modules\lighting\lighting_turf.dm" +#include "code\modules\lighting\static_lighting_area.dm" #include "code\modules\mapping\map_template.dm" #include "code\modules\mapping\mapping_helpers.dm" #include "code\modules\mapping\minimap.dm" @@ -2896,7 +2925,6 @@ #include "code\modules\mob\living\simple_animal\parrot.dm" #include "code\modules\mob\living\simple_animal\shade.dm" #include "code\modules\mob\living\simple_animal\simple_animal.dm" -#include "code\modules\mob\living\simple_animal\status_procs.dm" #include "code\modules\mob\living\simple_animal\bot\atmosbot.dm" #include "code\modules\mob\living\simple_animal\bot\bot.dm" #include "code\modules\mob\living\simple_animal\bot\cleanbot.dm" @@ -3624,7 +3652,6 @@ #include "code\modules\spells\spell_types\self\lichdom.dm" #include "code\modules\spells\spell_types\self\mime_vow.dm" #include "code\modules\spells\spell_types\self\mutate.dm" -#include "code\modules\spells\spell_types\self\night_vision.dm" #include "code\modules\spells\spell_types\self\personality_commune.dm" #include "code\modules\spells\spell_types\self\rod_form.dm" #include "code\modules\spells\spell_types\self\smoke.dm" @@ -3810,6 +3837,7 @@ #include "code\modules\vending\toys.dm" #include "code\modules\vending\wardrobes.dm" #include "code\modules\vending\youtool.dm" +#include "code\modules\visual\render_step.dm" #include "code\modules\VR\vr_human.dm" #include "code\modules\VR\vr_sleeper.dm" #include "code\modules\zombie\items.dm" diff --git a/yogstation/code/datums/martial/explosive_fist.dm b/yogstation/code/datums/martial/explosive_fist.dm index bd2d3b791f14..2d400f84bbfd 100644 --- a/yogstation/code/datums/martial/explosive_fist.dm +++ b/yogstation/code/datums/martial/explosive_fist.dm @@ -366,7 +366,7 @@ var/armor_block = D.run_armor_check(hed, BOMB) D.apply_damage(A.get_punchdamagehigh() + 3, BURN, BODY_ZONE_HEAD, armor_block) //10 burn (vs bomb armor) D.emote("scream") - D.blur_eyes(4) + D.adjust_eye_blur(4) A.apply_damage(10, BURN, BODY_ZONE_CHEST, 0) //Take some unblockable damage since you're using your inner flame or something diff --git a/yogstation/code/datums/status_effects/neutral.dm b/yogstation/code/datums/status_effects/neutral.dm index 258cf221dc3c..809cb5ab86c2 100644 --- a/yogstation/code/datums/status_effects/neutral.dm +++ b/yogstation/code/datums/status_effects/neutral.dm @@ -46,7 +46,6 @@ var/icon/temp = icon(sniffee.icon, sniffee.icon_state) var/image/scent_glow = image(temp, layer = ABOVE_MOB_LAYER, loc = sniffee) scent_glow.copy_overlays(sniffee) - scent_glow.layer = HUD_LAYER scent_glow.plane = HUD_PLANE scent_glow.appearance_flags = NO_CLIENT_COLOR scent_glow.color = scent_color @@ -90,7 +89,6 @@ if(!red_thirst) red_thirst = owner.overlay_fullscreen("thirsting", /atom/movable/screen/fullscreen/brute, 4) red_thirst.alpha = 0 - red_thirst.layer = HUD_LAYER red_thirst.plane = HUD_PLANE animate(red_thirst, alpha = 255, time = 1 SECONDS, easing = EASE_IN) //fade IN to_chat(owner, span_userdanger("As the scent of your prey overwhelms your sense of smell, the thrill of the hunt empowers you!")) diff --git a/yogstation/code/game/gamemodes/shadowling/shadowling.dm b/yogstation/code/game/gamemodes/shadowling/shadowling.dm index 44b224ba7e78..8c49e5e19ca0 100644 --- a/yogstation/code/game/gamemodes/shadowling/shadowling.dm +++ b/yogstation/code/game/gamemodes/shadowling/shadowling.dm @@ -146,7 +146,7 @@ Made by Xhuis inherent_traits = list(TRAIT_NOGUNS, TRAIT_RESISTCOLD, TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE, TRAIT_NOBREATH, TRAIT_RADIMMUNE, TRAIT_VIRUSIMMUNE, TRAIT_PIERCEIMMUNE) no_equip = list(ITEM_SLOT_MASK, ITEM_SLOT_EYES, ITEM_SLOT_GLOVES, ITEM_SLOT_FEET, ITEM_SLOT_ICLOTHING, ITEM_SLOT_SUITSTORE) nojumpsuit = TRUE - mutanteyes = /obj/item/organ/eyes/night_vision/alien/sling + mutanteyes = /obj/item/organ/eyes/alien/sling burnmod = 1.5 //1.5x burn damage, 2x is excessive heatmod = 1.5 var/mutable_appearance/eyes_overlay diff --git a/yogstation/code/game/gamemodes/vampire/vampire_bat.dm b/yogstation/code/game/gamemodes/vampire/vampire_bat.dm index 75fd9ccf8341..80abc4fb8fad 100644 --- a/yogstation/code/game/gamemodes/vampire/vampire_bat.dm +++ b/yogstation/code/game/gamemodes/vampire/vampire_bat.dm @@ -13,8 +13,6 @@ maxHealth = 20 health = 20 speed = 0 - see_in_dark = 10 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE harm_intent_damage = 7 melee_damage_lower = 5 melee_damage_upper = 7 diff --git a/yogstation/code/game/turfs/change_turf.dm b/yogstation/code/game/turfs/change_turf.dm index 45ea3eca74db..3e0fdf6d164e 100644 --- a/yogstation/code/game/turfs/change_turf.dm +++ b/yogstation/code/game/turfs/change_turf.dm @@ -5,4 +5,14 @@ if(islist(baseturfs)) return ChangeTurf(baseturfs[1],baseturfs[1],flags) else - return ChangeTurf(baseturfs, baseturfs, flags) // The bottom baseturf will never go away \ No newline at end of file + return ChangeTurf(baseturfs, baseturfs, flags) // The bottom baseturf will never go away + + +/// Attempts to replace a tile with lattice. Amount is the amount of tiles to scrape away. +/turf/proc/attempt_lattice_replacement(amount = 2) + if(lattice_underneath) + var/turf/new_turf = ScrapeAway(amount, flags = CHANGETURF_INHERIT_AIR) + if(!istype(new_turf, /turf/open/floor)) + new /obj/structure/lattice(src) + else + ScrapeAway(amount, flags = CHANGETURF_INHERIT_AIR) diff --git a/yogstation/code/modules/antagonists/shadowling/shadowling_abilities.dm b/yogstation/code/modules/antagonists/shadowling/shadowling_abilities.dm index 4990a34d07d3..d52ee24b9c75 100644 --- a/yogstation/code/modules/antagonists/shadowling/shadowling_abilities.dm +++ b/yogstation/code/modules/antagonists/shadowling/shadowling_abilities.dm @@ -993,30 +993,30 @@ spell_requirements = NONE -/datum/action/cooldown/spell/thrall_night_vision/cast(mob/living/carbon/human/user) - . = ..() - if(!.) - return FALSE - if(!is_shadow_or_thrall(user)) - return - var/obj/item/organ/eyes/eyes = user.getorganslot(ORGAN_SLOT_EYES) - if(!eyes) - return - eyes.sight_flags = initial(eyes.sight_flags) - switch(eyes.lighting_alpha) - if (LIGHTING_PLANE_ALPHA_VISIBLE) - eyes.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - eyes.see_in_dark = 8 - if (LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE) - eyes.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - if (LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE) - eyes.lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE - else - eyes.lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE - eyes.see_in_dark = 2 //default - user.update_sight() - - return TRUE +// /datum/action/cooldown/spell/thrall_night_vision/cast(mob/living/carbon/human/user) +// . = ..() +// if(!.) +// return FALSE +// if(!is_shadow_or_thrall(user)) +// return +// var/obj/item/organ/eyes/eyes = user.getorganslot(ORGAN_SLOT_EYES) +// if(!eyes) +// return +// eyes.sight_flags = initial(eyes.sight_flags) +// switch(eyes.lighting_alpha) +// if (LIGHTING_PLANE_ALPHA_VISIBLE) +// eyes.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE +// eyes.see_in_dark = 8 +// if (LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE) +// eyes.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE +// if (LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE) +// eyes.lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE +// else +// eyes.lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE +// eyes.see_in_dark = 2 //default +// user.update_sight() + +// return TRUE /datum/action/cooldown/spell/lesser_shadowling_hivemind //Lets a thrall talk with their allies name = "Lesser Commune" diff --git a/yogstation/code/modules/antagonists/slaughter/slaughter.dm b/yogstation/code/modules/antagonists/slaughter/slaughter.dm index ed62fb6cd5ed..75e90ee01660 100644 --- a/yogstation/code/modules/antagonists/slaughter/slaughter.dm +++ b/yogstation/code/modules/antagonists/slaughter/slaughter.dm @@ -32,8 +32,6 @@ wound_bonus = -10 bare_wound_bonus = 0 sharpness = SHARP_EDGED - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE var/playstyle_string = "You are a slaughter demon, a terrible creature from another realm. You have a single desire: To kill. \ Alt-click blood pools to travel through them, appearing and disappearing from the station at will. \ diff --git a/yogstation/code/modules/clothing/glasses/_glasses.dm b/yogstation/code/modules/clothing/glasses/_glasses.dm index d79a523f5a98..9070e85df29d 100644 --- a/yogstation/code/modules/clothing/glasses/_glasses.dm +++ b/yogstation/code/modules/clothing/glasses/_glasses.dm @@ -3,7 +3,6 @@ desc = "Made in china" icon_state = "sun" item_state = "sunglasses" - darkness_view = 1 flash_protect = 0 tint = 1 glass_colour_type = /datum/client_colour/glass_colour/gray @@ -25,4 +24,4 @@ name = "eye patch" desc = "Arrrggg now you too can be a real pirate matey." icon_state = "eyepatch" - item_state = "eyepatch" \ No newline at end of file + item_state = "eyepatch" diff --git a/yogstation/code/modules/guardian/abilities/special/pocket.dm b/yogstation/code/modules/guardian/abilities/special/pocket.dm index 438e87521f7d..e98154ac2949 100644 --- a/yogstation/code/modules/guardian/abilities/special/pocket.dm +++ b/yogstation/code/modules/guardian/abilities/special/pocket.dm @@ -288,7 +288,7 @@ GLOBAL_LIST_EMPTY(pocket_mirrors) G.status_flags &= ~GODMODE /obj/effect/manifestation - layer = ABOVE_LIGHTING_LAYER + plane = ABOVE_LIGHTING_PLANE appearance_flags = KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE alpha = 0 mouse_opacity = FALSE diff --git a/yogstation/code/modules/guardian/guardian.dm b/yogstation/code/modules/guardian/guardian.dm index d2d02ad0f4e6..1848f7508bd1 100644 --- a/yogstation/code/modules/guardian/guardian.dm +++ b/yogstation/code/modules/guardian/guardian.dm @@ -50,7 +50,6 @@ GLOBAL_LIST_INIT(guardian_projectile_damage, list( light_range = 3 light_on = FALSE hud_type = /datum/hud/guardian - see_in_dark = 8 var/list/barrier_images = list() var/custom_name = FALSE var/atk_cooldown = 10 @@ -113,17 +112,17 @@ GLOBAL_LIST_INIT(guardian_projectile_damage, list( if (sx - range - 1 < 1 || sx + range + 1 > world.maxx || sy - range - 1 < 1 || sy + range + 1 > world.maxy) return for (var/turf/T in getline(locate(sx - range, sy + range + 1, sz), locate(sx + range, sy + range + 1, sz))) - barrier_images += image('yogstation/icons/effects/effects.dmi', T, "barrier", ABOVE_LIGHTING_LAYER, SOUTH) + barrier_images += image('yogstation/icons/effects/effects.dmi', T, "barrier", ABOVE_LIGHTING_PLANE, SOUTH) for (var/turf/T in getline(locate(sx - range, sy - range - 1, sz), locate(sx + range, sy - range - 1, sz))) - barrier_images += image('yogstation/icons/effects/effects.dmi', T, "barrier", ABOVE_LIGHTING_LAYER, NORTH) + barrier_images += image('yogstation/icons/effects/effects.dmi', T, "barrier", ABOVE_LIGHTING_PLANE, NORTH) for (var/turf/T in getline(locate(sx - range - 1, sy - range, sz), locate(sx - range - 1, sy + range, sz))) - barrier_images += image('yogstation/icons/effects/effects.dmi', T, "barrier", ABOVE_LIGHTING_LAYER, EAST) + barrier_images += image('yogstation/icons/effects/effects.dmi', T, "barrier", ABOVE_LIGHTING_PLANE, EAST) for (var/turf/T in getline(locate(sx + range + 1, sy - range, sz), locate(sx + range + 1, sy + range, sz))) - barrier_images += image('yogstation/icons/effects/effects.dmi', T, "barrier", ABOVE_LIGHTING_LAYER, WEST) - barrier_images += image('yogstation/icons/effects/effects.dmi', locate(sx - range - 1 , sy + range + 1, sz), "barrier", ABOVE_LIGHTING_LAYER, SOUTHEAST) - barrier_images += image('yogstation/icons/effects/effects.dmi', locate(sx + range + 1, sy + range + 1, sz), "barrier", ABOVE_LIGHTING_LAYER, SOUTHWEST) - barrier_images += image('yogstation/icons/effects/effects.dmi', locate(sx + range + 1, sy - range - 1, sz), "barrier", ABOVE_LIGHTING_LAYER, NORTHWEST) - barrier_images += image('yogstation/icons/effects/effects.dmi', locate(sx - range - 1, sy - range - 1, sz), "barrier", ABOVE_LIGHTING_LAYER, NORTHEAST) + barrier_images += image('yogstation/icons/effects/effects.dmi', T, "barrier", ABOVE_LIGHTING_PLANE, WEST) + barrier_images += image('yogstation/icons/effects/effects.dmi', locate(sx - range - 1 , sy + range + 1, sz), "barrier", ABOVE_LIGHTING_PLANE, SOUTHEAST) + barrier_images += image('yogstation/icons/effects/effects.dmi', locate(sx + range + 1, sy + range + 1, sz), "barrier", ABOVE_LIGHTING_PLANE, SOUTHWEST) + barrier_images += image('yogstation/icons/effects/effects.dmi', locate(sx + range + 1, sy - range - 1, sz), "barrier", ABOVE_LIGHTING_PLANE, NORTHWEST) + barrier_images += image('yogstation/icons/effects/effects.dmi', locate(sx - range - 1, sy - range - 1, sz), "barrier", ABOVE_LIGHTING_PLANE, NORTHEAST) for (var/image/I in barrier_images) I.layer = ABOVE_LIGHTING_PLANE I.plane = FLOOR_PLANE diff --git a/yogstation/code/modules/jungleland/jungle_datums.dm b/yogstation/code/modules/jungleland/jungle_datums.dm index 477ea44f8a09..3145ab49c532 100644 --- a/yogstation/code/modules/jungleland/jungle_datums.dm +++ b/yogstation/code/modules/jungleland/jungle_datums.dm @@ -11,7 +11,7 @@ screen_loc = "CENTER-9,CENTER-7" appearance_flags = TILE_BOUND layer = ABOVE_OPEN_TURF_LAYER - plane = BLACKNESS_PLANE + plane = DEFAULT_PLANE alpha = 0 //we animate it ourselves //wall trip @@ -21,7 +21,7 @@ screen_loc = "CENTER-9,CENTER-7" appearance_flags = TILE_BOUND layer = BELOW_MOB_LAYER - plane = BLACKNESS_PLANE + plane = DEFAULT_PLANE alpha = 0 //we animate it ourselves // reagents @@ -111,7 +111,7 @@ if(!L.hud_used || !L.client) return - var/atom/movable/screen/plane_master/game_world/game_plane = L.hud_used.plane_masters["[GAME_PLANE]"] + var/atom/movable/screen/plane_master/game_plane = L.hud_used.plane_masters["[GAME_PLANE]"] var/atom/movable/screen/plane_master/floor/floor_plane = L.hud_used.plane_masters["[FLOOR_PLANE]"] cached_screen = L.overlay_fullscreen("polycybin_trip",/atom/movable/screen/fullscreen/trip) @@ -134,7 +134,7 @@ L.clear_fullscreen("polycybin_gtrip") -/datum/reagent/jungle/polybycin/proc/update_filters(mob/living/L) +/datum/reagent/jungle/polybycin/proc/update_jungle_filters(mob/living/L) if(!L.client) return @@ -243,7 +243,7 @@ /datum/reagent/toxin/meduracha/on_mob_life(mob/living/carbon/M) M.damageoverlaytemp = 60 M.update_damage_hud() - M.blur_eyes(3) + M.adjust_eye_blur(3) return ..() /datum/reagent/quinine diff --git a/yogstation/code/modules/jungleland/jungle_structures.dm b/yogstation/code/modules/jungleland/jungle_structures.dm index 366bf7d4d5c7..095b5cad2c1f 100644 --- a/yogstation/code/modules/jungleland/jungle_structures.dm +++ b/yogstation/code/modules/jungleland/jungle_structures.dm @@ -152,7 +152,7 @@ L.set_blindness(20) SEND_SIGNAL(L,COMSIG_JUNGLELAND_TAR_CURSE_PROC) else - L.set_blurriness(20) + L.set_eye_blur(20) return ..() /obj/effect/timed_attack/tar_priest/tendril replace_icon_state = "tar_shade_tendril" diff --git a/yogstation/code/modules/mining/lavaland/bloodbook.dm b/yogstation/code/modules/mining/lavaland/bloodbook.dm index 747c487ddc94..d1a7b56caf97 100644 --- a/yogstation/code/modules/mining/lavaland/bloodbook.dm +++ b/yogstation/code/modules/mining/lavaland/bloodbook.dm @@ -756,7 +756,7 @@ desc = "A blood-red prison." density = FALSE anchored = TRUE - layer = MASSIVE_OBJ_LAYER + plane= MASSIVE_OBJ_PLANE icon = 'icons/obj/projectiles.dmi' icon_state = "leaper" max_integrity = 500 @@ -782,14 +782,14 @@ /obj/structure/prop anchored = TRUE density = FALSE - layer = MASSIVE_OBJ_LAYER + plane = MASSIVE_OBJ_PLANE max_integrity = 500 /obj/structure/prop/killrock name = "ruby spire" desc = "An unrelenting claw." anchored = TRUE - layer = MASSIVE_OBJ_LAYER + plane = MASSIVE_OBJ_PLANE icon = 'icons/obj/cult_64x64.dmi' icon_state = "bloodstone-enter1" var/stage = 1 @@ -870,7 +870,7 @@ /obj/structure/slash icon = 'icons/effects/effects.dmi' icon_state = "slash" - layer = POINT_LAYER + plane = POINT_PLANE color = "#ffa600" /obj/structure/slash/Initialize(mapload) diff --git a/yogstation/code/modules/mob/living/carbon/human/species_types/darkspawn.dm b/yogstation/code/modules/mob/living/carbon/human/species_types/darkspawn.dm index 6d9808c035f0..0087efc26d69 100644 --- a/yogstation/code/modules/mob/living/carbon/human/species_types/darkspawn.dm +++ b/yogstation/code/modules/mob/living/carbon/human/species_types/darkspawn.dm @@ -13,7 +13,7 @@ no_equip = list(ITEM_SLOT_MASK, ITEM_SLOT_OCLOTHING, ITEM_SLOT_GLOVES, ITEM_SLOT_FEET, ITEM_SLOT_ICLOTHING, ITEM_SLOT_SUITSTORE, ITEM_SLOT_HEAD) species_traits = list(NOBLOOD,NO_UNDERWEAR,NO_DNA_COPY,NOTRANSSTING,NOEYESPRITES,NOFLASH) inherent_traits = list(TRAIT_NOGUNS, TRAIT_RESISTCOLD, TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE, TRAIT_NOBREATH, TRAIT_RADIMMUNE, TRAIT_VIRUSIMMUNE, TRAIT_PIERCEIMMUNE, TRAIT_NODISMEMBER, TRAIT_NOHUNGER) - mutanteyes = /obj/item/organ/eyes/night_vision/alien + mutanteyes = /obj/item/organ/eyes/alien var/list/upgrades = list() COOLDOWN_DECLARE(reflect_cd_1) COOLDOWN_DECLARE(reflect_cd_2) diff --git a/yogstation/code/modules/mob/living/carbon/human/species_types/preternis/organs.dm b/yogstation/code/modules/mob/living/carbon/human/species_types/preternis/organs.dm index 86a321e42c10..590e174bc205 100644 --- a/yogstation/code/modules/mob/living/carbon/human/species_types/preternis/organs.dm +++ b/yogstation/code/modules/mob/living/carbon/human/species_types/preternis/organs.dm @@ -1,8 +1,6 @@ /obj/item/organ/eyes/robotic/preternis name = "preternis eyes" - desc = "An experimental upgraded version of eyes that can see in the dark. They are designed to fit preternis" - see_in_dark = PRETERNIS_NV_ON - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + desc = "A form archaic robotic eyes that can see in the dark. " //preternis eyes need to be powered by a preternis to function, in a non preternis they slowly power down to blindness status = ORGAN_ROBOTIC organ_flags = ORGAN_SYNTHETIC @@ -14,25 +12,26 @@ now_fixed = span_info("Lines of text scroll in your vision as your eyes begin rebooting.") high_threshold_cleared = span_info("Your Preternis eyes have recharged enough to re-enable most functionality.") low_threshold_cleared = span_info("Your Preternis eyes have almost fully recharged.") - var/powered = TRUE actions_types = list(/datum/action/item_action/organ_action/use) - var/night_vision = TRUE + var/powered = TRUE + var/night_vision = FALSE + // This list is used as the color cutoff for the night vision + var/list/colour_cutoff_list = list(12, 0, 50) + // This variable is the actual night vision strength + var/light_cutoff = LIGHTING_CUTOFF_HIGH /obj/item/organ/eyes/robotic/preternis/ui_action_click() if(damage > low_threshold) //no nightvision if your eyes are hurt return sight_flags = initial(sight_flags) - switch(lighting_alpha) - if (LIGHTING_PLANE_ALPHA_VISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - if (LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - if (LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE - else - lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE - sight_flags &= ~SEE_BLACKNESS + night_vision = !night_vision + if (night_vision) + color_cutoffs = colour_cutoff_list.Copy() + lighting_cutoff = light_cutoff + else + color_cutoffs = null + lighting_cutoff = null owner.update_sight() /obj/item/organ/eyes/robotic/preternis/on_life() @@ -52,20 +51,6 @@ //to simulate running out of power, they take damage owner.adjustOrganLoss(ORGAN_SLOT_EYES,0.5) - if(damage < low_threshold) - if(see_in_dark == PRETERNIS_NV_OFF) - see_in_dark = PRETERNIS_NV_ON - owner.update_sight() - else - //if your eyes start getting hurt no more nightvision - if(see_in_dark == PRETERNIS_NV_ON) - see_in_dark = PRETERNIS_NV_OFF - owner.update_sight() - if(lighting_alpha < LIGHTING_PLANE_ALPHA_VISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE - sight_flags &= ~SEE_BLACKNESS - owner.update_sight() - /obj/item/organ/eyes/robotic/preternis/examine(mob/user) . = ..() if(status == ORGAN_ROBOTIC && (organ_flags & ORGAN_FAILING)) @@ -77,6 +62,10 @@ else if(damage > high_threshold) . += span_warning("[src] seem to flicker on and off. They must be pretty low on charge without being in a Preternis") + +/////////////////////////////////////////////////////////// +//----------------------Preternis Lungs------------------// +/////////////////////////////////////////////////////////// /obj/item/organ/lungs/preternis name = "preternis lungs" desc = "A specialized set of lungs. Due to the cybernetic nature of these lungs, they are far less resistant to cold but are more heat resistant and more efficent at filtering oxygen." @@ -106,7 +95,10 @@ heat_level_2_damage = 7 heat_level_3_threshold = 35000 //are you on the fucking surface of the sun or something? heat_level_3_damage = 25 //you should already be dead - + +/////////////////////////////////////////////////////////// +//---------------------Preternis Stomach-----------------// +/////////////////////////////////////////////////////////// /obj/item/organ/stomach/cell/preternis name = "preternis cell-stomach" desc = "Calling it a stomach is perhaps a bit generous. It's better at grinding rocks than dissolving food. Also works as a power cell." diff --git a/yogstation/code/modules/mob/living/carbon/human/species_types/szlachta.dm b/yogstation/code/modules/mob/living/carbon/human/species_types/szlachta.dm index dd1a717f7b14..be23ef61881f 100644 --- a/yogstation/code/modules/mob/living/carbon/human/species_types/szlachta.dm +++ b/yogstation/code/modules/mob/living/carbon/human/species_types/szlachta.dm @@ -17,7 +17,7 @@ inherent_traits = list(TRAIT_NOGUNS, TRAIT_RESISTCOLD, TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE, TRAIT_NOBREATH, TRAIT_RADIMMUNE, TRAIT_VIRUSIMMUNE, TRAIT_PIERCEIMMUNE, TRAIT_NODISMEMBER, TRAIT_MONKEYLIKE, TRAIT_NOCRITDAMAGE, TRAIT_GENELESS, TRAIT_NOSOFTCRIT, TRAIT_NOHARDCRIT, TRAIT_HARDLY_WOUNDED, TRAIT_HUSK) - mutanteyes = /obj/item/organ/eyes/night_vision/alien + mutanteyes = /obj/item/organ/eyes/alien /datum/species/szlachta/on_species_gain(mob/living/carbon/C, datum/species/old_species) . = ..() diff --git a/yogstation/code/modules/mob/living/living.dm b/yogstation/code/modules/mob/living/living.dm index 772af13c60b8..17fd8746224d 100644 --- a/yogstation/code/modules/mob/living/living.dm +++ b/yogstation/code/modules/mob/living/living.dm @@ -17,5 +17,6 @@ /mob/living/update_sight() . = ..() for(var/mob/living/simple_animal/hostile/guardian/holopara as anything in hasparasites()) - holopara.lighting_alpha = lighting_alpha + holopara.lighting_cutoff = lighting_cutoff holopara.update_sight() + update_wire_vision() diff --git a/yogstation/code/modules/mob/living/simple_animal/hostile/floor_cluwne.dm b/yogstation/code/modules/mob/living/simple_animal/hostile/floor_cluwne.dm index ea83caace5f1..84648cf40bd2 100644 --- a/yogstation/code/modules/mob/living/simple_animal/hostile/floor_cluwne.dm +++ b/yogstation/code/modules/mob/living/simple_animal/hostile/floor_cluwne.dm @@ -242,7 +242,7 @@ GLOBAL_VAR_INIT(floor_cluwnes, 0) switch(stage) if(STAGE_HAUNT) if(prob(5)) - H.blur_eyes(1) + H.adjust_eye_blur(1) if(prob(5)) H.playsound_local(src,'yogstation/sound/voice/cluwnelaugh2_reversed.ogg', 1) @@ -421,7 +421,7 @@ GLOBAL_VAR_INIT(floor_cluwnes, 0) H.invisibility = initial(H.invisibility) H.density = initial(H.density) H.anchored = initial(H.anchored) - H.blur_eyes(10) + H.adjust_eye_blur(10) animate(H.client,color = old_color, time = 2 SECONDS) eating = FALSE diff --git a/yogstation/code/modules/spacepods/spacepod.dm b/yogstation/code/modules/spacepods/spacepod.dm index 9fad31f3cc32..7d184a10ceee 100644 --- a/yogstation/code/modules/spacepods/spacepod.dm +++ b/yogstation/code/modules/spacepods/spacepod.dm @@ -14,7 +14,7 @@ GLOBAL_LIST_INIT(spacepods_list, list()) density = 1 opacity = FALSE dir = NORTH // always points north because why not - layer = SPACEPOD_LAYER + layer = VEHICLE_LAYER bound_width = 64 bound_height = 64 animate_movement = NO_STEPS // we do our own gliding here diff --git a/yogstation/code/modules/surgery/organs/shadowling_organs.dm b/yogstation/code/modules/surgery/organs/shadowling_organs.dm index 309ac0e96d12..1182b11f6ed6 100644 --- a/yogstation/code/modules/surgery/organs/shadowling_organs.dm +++ b/yogstation/code/modules/surgery/organs/shadowling_organs.dm @@ -1,5 +1,5 @@ //Snowflake spot for putting sling organ related stuff -/obj/item/organ/eyes/night_vision/alien/sling +/obj/item/organ/eyes/alien/sling name = "shadowling eyes" desc = "The eyes of a spooky shadowling!" @@ -62,7 +62,7 @@ playsound(S, 'sound/effects/bang.ogg', 50, 1) return FALSE var/obj/item/organ/eyes/eyes = M.getorganslot(ORGAN_SLOT_EYES) - eyes.lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE + eyes.lighting_cutoff = LIGHTING_CUTOFF_VISIBLE eyes.sight_flags = initial(eyes.sight_flags) M.update_sight() M.remove_thrall()