Files
Casper3667 0ae5bc0574 Fixed the elevator time display and increased the elevator timer (#22510)
fixes https://github.com/Aurorastation/Aurora.3/issues/22409

The reason for the increased elevator timer was due to how rarely it
came up that the supplier time would be longer than the base 30 second
timer, so instead the two timers were combined to trial how a longer
timer would feel.
2026-05-28 10:34:33 +00:00

374 lines
14 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 = launch_estimate()
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 seconds
/datum/shuttle/autodock/ferry/supply/proc/eta_seconds()
var/ticksleft = arrive_time - world.time
if(ticksleft > 0)
return "[round(ticksleft/10,1)] seconds"
else
return "soon."
/// Gets the cargo moving time
/datum/shuttle/autodock/ferry/supply/proc/launch_estimate(var/in_seconds = FALSE)
var/time = world.time + SScargo.movetime
if(in_seconds)
time = time - world.time
return round(time/10,1)
else
return time
/**************************
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/structure/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/structure/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/structure/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