Files
Matt Atlas 94d92803b4 Replaces our lighting system with CM's. (#21465)
Depends on #21458.

Ports https://github.com/cmss13-devs/cmss13/pull/4229, with the original
authors as:

- https://github.com/tgstation/TerraGov-Marine-Corps/pull/1964 for the
lighting controller (A-lexa)
- https://github.com/tgstation/TerraGov-Marine-Corps/pull/4747 and
https://github.com/tgstation/TerraGov-Marine-Corps/pull/7263 for the
lighting (TiviPlus)
- https://github.com/tgstation/tgstation/pull/54520 for the dir lighting
component
- https://github.com/tgstation/tgstation/pull/75018 for the out of
bounds fix in lighting
- https://github.com/tgstation/TerraGov-Marine-Corps/pull/6678 for the
emissives (TiviPlus)

The main driving reason behind this is that current lighting consumes
way too much processing power, especially for things like odysseys/away
sites where a billion light sources are processing/moving at once and
the game slows down to a crawl. Hopefully this improves the situation by
a good margin, but we will need some testmerging to confirm that.
<img width="1349" height="1349" alt="image"
src="https://github.com/user-attachments/assets/1059ba2b-c0c5-495a-9c76-2d75d0c42bf2"
/>
<img width="1349" height="1349" alt="image"
src="https://github.com/user-attachments/assets/9704b0f6-4cf6-4dfd-a6cb-5702ad07d677"
/>


- [x] Resolve todos
- [x] Look into open space fuckery (border objects)

---------

Co-authored-by: Matt Atlas <liermattia@gmail.com>
Co-authored-by: JohnWildkins <john.wildkins@gmail.com>
2025-11-04 21:27:42 +00:00

362 lines
13 KiB
Plaintext

/// Used for placing '/obj/effect/elevator' to the northern corners of the shaft.
#define SHAFT_WIDTH 6
/// Used for locating center of the shaft.
#define SHAFT_CENTER_OFFSET 3
#define ELEVATOR_DEPARTING_X 224
#define ELEVATOR_DEPARTING_Y -96
/datum/shuttle/autodock/ferry/supply
category = /datum/shuttle/autodock/ferry/supply
/// The location to hide at while pretending to be in-transit.
var/away_location = 1
var/late_chance = 80
var/max_late_time = 300
/// A list for step_triggers, used for toggling fall functionality. Making the elevator shaft tiles safe to stand or not depening on where the elevator is.
var/list/step_trigger_group = list()
/// Elevator effects for each northern corners of the shaft. This way the entire elevator won't disappear if there's an opaque obstacle in the sight line of original turf.
/// Later on hardcoded to have their icons positioned on top of each other.
/// These contain elevator shaft sprite. '/obj/effect/elevator/animation_overlay/elevator_animation' is used as a masked layer onto this.
var/obj/effect/elevator/NW
var/obj/effect/elevator/NE
/// This effect will contain an image, copy of the turfs at CC supply shuttle zone. Added onto the elevator shaft effect as an off-centered overlay.
var/obj/effect/elevator/animation_overlay/elevator_animation
/// Hatch layers. Works similar to 'elevator_animation'.
var/obj/effect/elevator/animation_overlay/hatch/left/hatch_left
var/obj/effect/elevator/animation_overlay/hatch/right/hatch_right
/// Locations caches for Horizon elevator bay.
var/target_dest_x
var/target_dest_y
var/target_dest_z
/// Area cache of Horizon elevator bay.
var/area/horizon_elevator_area
/// Underlays of the elevator saved before transit.
var/list/saved_underlays
/datum/shuttle/autodock/ferry/supply/New(var/_name, var/obj/effect/shuttle_landmark/start_waypoint)
..(_name, start_waypoint)
SScargo.shuttle = src
addtimer(CALLBACK(src, PROC_REF(prepare_elevator)), 1 MINUTE) // pseudo initialize. We give mapload some time before initializing rest of the properties
/datum/shuttle/autodock/ferry/supply/proc/prepare_elevator()
var/obj/dest_helper = SSshuttle.cargo_dest_helper
if(!dest_helper)
crash_with("No cargo destination helper found for the elevator!")
target_dest_x = dest_helper.x
target_dest_y = dest_helper.y
target_dest_z = dest_helper.z
horizon_elevator_area = get_area(dest_helper)
var/center_dest_x = target_dest_x + SHAFT_CENTER_OFFSET
var/group_number
for(var/obj/effect/step_trigger/cargo_elevator/ST in horizon_elevator_area) // sorts step triggers by their distance to the shaft center
group_number = "distance_[abs(ST.x - center_dest_x)]"
LAZYADDASSOCLIST(step_trigger_group, group_number, ST)
elevator_animation = new /obj/effect/elevator/animation_overlay()
elevator_animation.pixel_x = ELEVATOR_DEPARTING_X // 7 tiles
elevator_animation.pixel_y = ELEVATOR_DEPARTING_Y // 3 tiles
hatch_left = new /obj/effect/elevator/animation_overlay/hatch/left()
hatch_right = new /obj/effect/elevator/animation_overlay/hatch/right()
// North-West corner
NW = new /obj/effect/elevator(get_turf(src))
NW.pixel_y = -192 // 6 tiles
NW.vis_contents += elevator_animation
NW.vis_contents += hatch_left
NW.vis_contents += hatch_right
// North-East corner
NE = new /obj/effect/elevator(get_turf(src))
NE.pixel_x = -192
NE.pixel_y = -192
NE.vis_contents += elevator_animation
NE.vis_contents += hatch_left
NE.vis_contents += hatch_right
// We position the shaft beforehand
NW.forceMove(locate(target_dest_x, target_dest_y, target_dest_z))
NE.forceMove(locate(target_dest_x + SHAFT_WIDTH, target_dest_y, target_dest_z))
/datum/shuttle/autodock/ferry/supply/short_jump(var/area/destination)
if(moving_status != SHUTTLE_IDLE)
return
if(isnull(location))
return
//it would be cool to play a sound here
moving_status = SHUTTLE_WARMUP
spawn(warmup_time*10)
if (moving_status == SHUTTLE_IDLE)
return //someone cancelled the launch
if (at_station() && forbidden_atoms_check())
//cancel the launch because of forbidden atoms. announce over supply channel?
moving_status = SHUTTLE_IDLE
return
if (!at_station()) //at centcom
if(!SScargo.buy()) //Check if the shuttle can be sent
moving_status = SHUTTLE_IDLE //Dont move the shuttle
return
flip_rotating_alarms() // elevator is coming up, prepare the lights
playsound(locate(target_dest_x + SHAFT_CENTER_OFFSET, target_dest_y - SHAFT_CENTER_OFFSET, target_dest_z), 'sound/machines/warning-buzzer-2.ogg', 60, FALSE)
//We pretend it's a long_jump by making the shuttle stay at centcom for the "in-transit" period.
var/obj/effect/shuttle_landmark/away_waypoint = get_location_waypoint(away_location)
moving_status = SHUTTLE_INTRANSIT
//If we are at the away_landmark then we are just pretending to move, otherwise actually do the move
if (next_location == away_waypoint)
attempt_move(away_waypoint)
play_elevator_animation(TRUE) // returning to CC, play the animation after elevator physically leaves
//wait ETA here.
arrive_time = world.time + SScargo.movetime
while (world.time <= arrive_time)
sleep(5)
if (next_location != away_waypoint)
//late
if (prob(late_chance))
sleep(rand(0,max_late_time))
play_elevator_animation() // coming from CC, play the animation before elevator physically arrives, delay the process
attempt_move(destination)
moving_status = SHUTTLE_IDLE
if (!at_station()) //at centcom
SScargo.sell()
// returns 1 if the supply shuttle should be prevented from moving because it contains forbidden atoms
/datum/shuttle/autodock/ferry/supply/proc/forbidden_atoms_check()
if (!at_station())
return 0 //if badmins want to send mobs or a nuke on the supply shuttle from centcom we don't care
for(var/area/A in shuttle_area)
if(SScargo.forbidden_atoms_check(A))
return 1
/datum/shuttle/autodock/ferry/supply/proc/at_station()
return (!location)
//returns 1 if the shuttle is idle and we can still mess with the cargo shopping list
/datum/shuttle/autodock/ferry/supply/proc/idle()
return (moving_status == SHUTTLE_IDLE)
//returns the ETA in minutes
/datum/shuttle/autodock/ferry/supply/proc/eta_minutes()
var/ticksleft = arrive_time - world.time
return round(ticksleft/600,1)
/**************************
Elevator Animations
***************************/
/datum/shuttle/autodock/ferry/supply/proc/play_elevator_animation(returning_to_CC = FALSE)
// Positioning the shafts
NW.forceMove(locate(target_dest_x, target_dest_y, target_dest_z))
NE.forceMove(locate(target_dest_x + SHAFT_WIDTH, target_dest_y, target_dest_z))
for(var/area/A in shuttle_area) // we have to do this here or we turn the elevator into a white cube
for(var/obj/machinery/light/L in A) // we turn off lights to avoid having to fight the lighting plane
L.stat |= POWEROFF
L.update()
sleep(2 SECONDS)
elevator_animation.icon_state = "elevator_test"
for(var/area/A in shuttle_area) // an image copy of the elevator platform is prepared here
for(var/turf/T in A)
LAZYADDASSOC(saved_underlays, T, T.underlays)
T.underlays.Cut()
elevator_animation.vis_contents += T
for(var/atom/movable/AM in T.contents)
AM.blocks_emissive = FALSE
AM.update_emissive_blocker()
if(!returning_to_CC)
handle_arrival_sequence() // coming to Horizon
else
handle_departure_sequence() // leaving the Horizon
for(var/turf/T in elevator_animation.vis_contents)
T.underlays = LAZYACCESS(saved_underlays, T)
LAZYREMOVE(saved_underlays, T)
for(var/atom/movable/AM in T.contents)
AM.blocks_emissive = initial(AM.blocks_emissive)
AM.update_emissive_blocker()
AM.update_light()
for(var/obj/machinery/light/L in T.contents)
L.stat &= ~POWEROFF
L.update()
elevator_animation.icon_state = null
elevator_animation.vis_contents.Cut() // animation is done, we can get rid of the elevator platform image
/datum/shuttle/autodock/ferry/supply/proc/handle_arrival_sequence()
var/obj/effect/step_trigger/cargo_elevator/trigger
playsound(locate(target_dest_x + SHAFT_CENTER_OFFSET, target_dest_y - SHAFT_CENTER_OFFSET, target_dest_z), 'sound/machines/industrial_lift_raising.ogg', 100, FALSE)
INVOKE_ASYNC(src, PROC_REF(handle_step_triggers))
animate(hatch_left, pixel_x = -112, time = 5 SECONDS)
animate(hatch_right, pixel_x = 112, time = 5 SECONDS)
sleep(4 SECONDS)
animate(elevator_animation, pixel_x = 0, pixel_y = 0, time = 4 SECONDS)
sleep(4.2 SECONDS)
for(var/group_key in step_trigger_group)
for(trigger in step_trigger_group[group_key])
trigger.safe_to_walk = TRUE
// We move the shafts away to avoid layering issues with turfs
NW.moveToNullspace()
NE.moveToNullspace()
addtimer(CALLBACK(src, PROC_REF(flip_rotating_alarms)), 4 SECONDS)
addtimer(CALLBACK(src, PROC_REF(toggle_railings)), 2 SECONDS)
/datum/shuttle/autodock/ferry/supply/proc/handle_departure_sequence()
flip_rotating_alarms()
toggle_railings()
sleep(2 SECONDS)
// checking for standers is redundant here, since the elevator won't leave if a forbidden type is standing
// we also want to avoid sending people to CC level in a case where we don't immediately retrieve them back after doing so
playsound(locate(target_dest_x + SHAFT_CENTER_OFFSET, target_dest_y - SHAFT_CENTER_OFFSET, target_dest_z), 'sound/machines/industrial_lift_lowering.ogg', 100, FALSE)
animate(elevator_animation, pixel_x = ELEVATOR_DEPARTING_X, pixel_y = ELEVATOR_DEPARTING_Y, time = 4 SECONDS)
sleep(4.2 SECONDS)
animate(hatch_left, pixel_x = 0, time = 5 SECONDS)
animate(hatch_right, pixel_x = 0, time = 5 SECONDS)
addtimer(CALLBACK(src, PROC_REF(flip_rotating_alarms)), 9 SECOND)
/// This attempts to sync up with hatch animation, by activating groups of step triggers in delayed order.
/datum/shuttle/autodock/ferry/supply/proc/handle_step_triggers()
var/obj/effect/step_trigger/cargo_elevator/trigger
var/group_key
for(var/i in 0 to 3)
group_key = "distance_[i]"
for(trigger in step_trigger_group[group_key]) // checking for the things standing on top of the hatch, then we send them to falling
for(var/atom/movable/AM in get_turf(trigger))
if(istype(AM, /obj/effect)) // avoid picking up step_triggers
continue
trigger.handle_falling(AM)
trigger.safe_to_walk = FALSE
sleep(1 SECOND)
/datum/shuttle/autodock/ferry/supply/proc/flip_rotating_alarms()
for(var/obj/machinery/rotating_alarm/RA in horizon_elevator_area)
RA.toggle_state()
/datum/shuttle/autodock/ferry/supply/proc/toggle_railings()
for(var/obj/structure/railing/retractable/R in horizon_elevator_area)
R.toggle_state()
/obj/effect/landmark/destination_helper/cargo_elevator
name = "cargo elevator destination helper"
/obj/effect/landmark/destination_helper/cargo_elevator/Initialize(mapload)
..()
return INITIALIZE_HINT_LATELOAD
/obj/effect/landmark/destination_helper/cargo_elevator/LateInitialize()
. = ..()
if(SSshuttle.cargo_dest_helper)
log_and_message_admins("Multiple cargo destination helpers detected; overriding.")
SSshuttle.cargo_dest_helper = src
/obj/effect/landmark/destination_helper/cargo_elevator/Destroy()
if(SSshuttle.cargo_dest_helper == src)
SSshuttle.cargo_dest_helper = null
return ..()
/obj/effect/step_trigger/cargo_elevator
name = "cargo elevator shaft"
icon = 'icons/effects/map_effects.dmi'
simulated = FALSE // this prevents the effects getting transported along with the elevator
/// List of turfs within the 'area/supply/dock'. Shared between all instances.
var/static/list/possible_turfs_to_land_on = list()
/// Boolean, determines whether the movables should fall or not.
var/safe_to_walk = TRUE
/obj/effect/step_trigger/cargo_elevator/Initialize()
. = ..()
if(!possible_turfs_to_land_on.len)
possible_turfs_to_land_on = get_area_turfs(/area/supply/dock)
/obj/effect/step_trigger/cargo_elevator/Destroy()
LAZYNULL(possible_turfs_to_land_on)
return ..()
/obj/effect/step_trigger/cargo_elevator/Trigger(atom/movable/AM)
if(safe_to_walk)
return
INVOKE_ASYNC(src, PROC_REF(handle_falling), AM)
/obj/effect/step_trigger/cargo_elevator/proc/handle_falling(atom/movable/AM)
if(!isliving(AM) && !isobj(AM)) // only mobs and objects
return
if(isliving(AM))
var/mob/living/L = AM
if(L.CanAvoidGravity())
return
L.apply_effect(2, WEAKEN)
INVOKE_ASYNC(src, PROC_REF(animate_falling), AM) // this needs to run asynchronously, otherwise will cause issues
/obj/effect/step_trigger/cargo_elevator/proc/animate_falling(atom/movable/AM)
sleep(0.1 SECOND) // allowing some time for mobs to get weaken effect applied
var/oldalpha = AM.alpha
var/oldcolor = AM.color
var/old_pixel_y = AM.pixel_y
animate(AM, transform = matrix() - matrix(), alpha = 0, color = rgb(0, 0, 0), time = 1 SECOND)
for(var/i in 1 to 5)
//Make sure the item is still there after our sleep
if(!AM || QDELETED(AM))
return
AM.pixel_y--
sleep(0.2 SECONDS)
//Make sure the item is still there after our sleep
if(!AM || QDELETED(AM))
return
AM.visible_message(SPAN_WARNING("[AM] falls into [src]!"), SPAN_DANGER("You stumble and stare into \the [src] as you fall. It will be a long ride..."))
AM.forceMove(pick(possible_turfs_to_land_on))
AM.alpha = oldalpha
AM.color = oldcolor
AM.transform = matrix()
AM.pixel_y = old_pixel_y
if(isliving(AM))
var/mob/living/L = AM
L.adjustBruteLoss(rand(30,50))
L.dir = pick(GLOB.cardinals)
#undef SHAFT_WIDTH
#undef SHAFT_CENTER_OFFSET
#undef ELEVATOR_DEPARTING_X
#undef ELEVATOR_DEPARTING_Y