Refactors transit tubes (#22452)

* Rework transit tube pods to not use timers

* please work

* yes

* henri review

* Fix mapping mishaps

* fix icon mishaps

* re-adds missing intercomm
This commit is contained in:
Luc
2023-10-17 19:42:42 +01:00
committed by GitHub
parent 23e83945de
commit 744b6e3a61
11 changed files with 563 additions and 576 deletions
@@ -8,8 +8,10 @@
// one.
/obj/structure/transit_tube/station
name = "station tube station"
desc = "The lynchpin of the transit system."
icon = 'icons/obj/pipes/transit_tube_station.dmi'
icon_state = "closed"
icon_state = "closed_station0"
base_icon_state = "station0"
exit_delay = 1
enter_delay = 2
var/pod_moving = FALSE
@@ -18,6 +20,8 @@
var/hatch_state = TRANSIT_TUBE_CLOSED
var/list/disallowed_mobs = list(/mob/living/silicon/ai)
/// Direction by which you can board the tube
var/boarding_dir
/obj/structure/transit_tube/station/Initialize(mapload)
. = ..()
@@ -27,24 +31,32 @@
STOP_PROCESSING(SSobj, src)
return ..()
// Stations which will send the tube in the opposite direction after their stop.
/obj/structure/transit_tube/station/reverse
reverse_launch = TRUE
/obj/structure/transit_tube/station/init_tube_dirs()
// Tube station directions are simply 90 to either side of
// the exit.
switch(dir)
if(NORTH)
tube_dirs = list(EAST, WEST)
if(SOUTH)
tube_dirs = list(EAST, WEST)
if(EAST)
tube_dirs = list(NORTH, SOUTH)
if(WEST)
tube_dirs = list(NORTH, SOUTH)
boarding_dir = reverse_direction(dir)
/obj/structure/transit_tube/station/should_stop_pod(pod, from_dir)
return TRUE
/obj/structure/transit_tube/station/Bumped(mob/living/L)
if(!pod_moving && hatch_state == TRANSIT_TUBE_OPEN && isliving(L) && !is_type_in_list(L, disallowed_mobs))
var/failed = FALSE
if(!pod_moving && L.dir == boarding_dir && hatch_state == TRANSIT_TUBE_OPEN && isliving(L) && !is_type_in_list(L, disallowed_mobs))
for(var/obj/structure/transit_tube_pod/pod in loc)
if(pod.contents.len)
failed = TRUE
else if(!pod.moving && (pod.dir in directions()))
if(length(pod.contents))
to_chat(L, "<span class='warning'>The pod is already occupied.</span>")
return
if(!pod.moving && ((pod.dir in directions()) || (reverse_launch && (turn(pod.dir, 180) in directions()))))
pod.move_into(L)
return
if(failed)
to_chat(L, "<span class='warning'>The pod is already occupied.</span>")
@@ -88,31 +100,36 @@
/obj/structure/transit_tube/station/proc/open_hatch()
if(hatch_state == TRANSIT_TUBE_CLOSED)
icon_state = "opening"
icon_state = "opening_[base_icon_state]"
hatch_state = TRANSIT_TUBE_OPENING
addtimer(CALLBACK(src, PROC_REF(open_hatch_callback)), OPEN_DURATION)
/obj/structure/transit_tube/station/proc/open_hatch_callback()
if(hatch_state == TRANSIT_TUBE_OPENING)
icon_state = "open"
hatch_state = TRANSIT_TUBE_OPEN
addtimer(CALLBACK(src, PROC_REF(finish_animation)), OPEN_DURATION)
/obj/structure/transit_tube/station/proc/close_hatch()
if(hatch_state == TRANSIT_TUBE_OPEN)
icon_state = "closing"
icon_state = "closing_[base_icon_state]"
hatch_state = TRANSIT_TUBE_CLOSING
addtimer(CALLBACK(src, PROC_REF(close_hatch_calllback)), CLOSE_DURATION)
addtimer(CALLBACK(src, PROC_REF(finish_animation)), CLOSE_DURATION)
/obj/structure/transit_tube/station/proc/close_hatch_calllback()
if(hatch_state == TRANSIT_TUBE_CLOSING)
icon_state = "closed"
hatch_state = TRANSIT_TUBE_CLOSED
/obj/structure/transit_tube/station/proc/finish_animation()
switch(hatch_state)
if(TRANSIT_TUBE_OPENING)
icon_state = "open_[base_icon_state]"
hatch_state = TRANSIT_TUBE_OPEN
for(var/obj/structure/transit_tube_pod/pod in loc)
for(var/thing in pod)
if(ismob(thing))
var/mob/mob_content = thing
if(mob_content.client && mob_content.stat < UNCONSCIOUS)
continue // Let the mobs with clients decide what they want to do themselves.
var/atom/movable/movable_content = thing
movable_content.forceMove(loc) //Everything else is moved out of.
if(TRANSIT_TUBE_CLOSING)
icon_state = "closed_[base_icon_state]"
hatch_state = TRANSIT_TUBE_CLOSED
/obj/structure/transit_tube/station/proc/launch_pod()
for(var/obj/structure/transit_tube_pod/pod in loc)
if(!pod.moving && (pod.dir in directions()))
if(!pod.moving && ((pod.dir in directions()) || (reverse_launch && (turn(pod.dir, 180) in directions()))))
addtimer(CALLBACK(src, PROC_REF(launch_pod_callback), pod), 5)
return
@@ -131,7 +148,7 @@
if(!nexttube)
pod.dir = turn(pod.dir, 180)
if(hatch_state == TRANSIT_TUBE_CLOSED && pod)
if(hatch_state == TRANSIT_TUBE_CLOSED && pod && pod.loc == loc)
pod.follow_tube()
pod_moving = FALSE
@@ -142,6 +159,7 @@
/obj/structure/transit_tube/station/pod_stopped(obj/structure/transit_tube_pod/pod, from_dir)
pod_moving = TRUE
pod.stop_following()
addtimer(CALLBACK(src, PROC_REF(pod_stopped_callback), pod), 5)
/obj/structure/transit_tube/station/proc/pod_stopped_callback(obj/structure/transit_tube_pod/pod)
@@ -152,10 +170,103 @@
pod_moving = FALSE
pod.mix_air()
// Tube station directions are simply 90 to either side of
// the exit.
/obj/structure/transit_tube/station/init_dirs()
tube_dirs = list(turn(dir, 90), turn(dir, -90))
/obj/structure/transit_tube/station/flipped
icon_state = "closed_station1"
base_icon_state = "station1"
/obj/structure/transit_tube/station/flipped/init_tube_dirs()
..()
boarding_dir = dir
// Stations which will send the tube in the opposite direction after their stop.
/obj/structure/transit_tube/station/reverse
icon_state = "closed_terminus0"
base_icon_state = "terminus0"
reverse_launch = TRUE
/obj/structure/transit_tube/station/reverse/init_tube_dirs()
tube_dirs = list(turn(dir, -90))
boarding_dir = reverse_direction(dir)
/obj/structure/transit_tube/station/reverse/flipped
icon_state = "closed_terminus1"
base_icon_state = "terminus1"
/obj/structure/transit_tube/station/reverse/flipped/init_tube_dirs()
..()
boarding_dir = dir
//special dispenser station, it creates a pod for you to enter when you bump into it.
/obj/structure/transit_tube/station/dispenser
name = "station tube pod dispenser"
icon_state = "open_dispenser0"
desc = "The lynchpin of a GOOD transit system."
enter_delay = 1
base_icon_state = "dispenser0"
hatch_state = TRANSIT_TUBE_OPEN
/obj/structure/transit_tube/station/dispenser/examine(mob/user)
. = ..()
. += "<span class='notice'>This station will create a pod for you to ride, no need to wait for one.</span>"
. += "<span class='notice'>Any pods arriving at this station will be reclaimed.</span>"
/obj/structure/transit_tube/station/dispenser/close_hatch()
. = ..()
return
/obj/structure/transit_tube/station/dispenser/launch_pod()
for(var/obj/structure/transit_tube_pod/pod in loc)
if(!pod.moving)
pod_moving = TRUE
pod.follow_tube()
pod_moving = FALSE
return TRUE
return FALSE
/obj/structure/transit_tube/station/dispenser/Bumped(atom/movable/AM)
if(!(istype(AM) && AM.dir == boarding_dir) || AM.anchored)
return
var/obj/structure/transit_tube_pod/dispensed/pod = new(loc)
AM.visible_message("<span class='notice'>[pod] forms around [AM].</span>", "<span class='notice'>[pod] materializes around you.</span>")
playsound(src, 'sound/weapons/emitter2.ogg', 50, TRUE)
pod.dir = turn(dir, -90)
pod.move_into(AM)
launch_pod()
/obj/structure/transit_tube/station/dispenser/pod_stopped(obj/structure/transit_tube_pod/pod)
playsound(src, 'sound/machines/ding.ogg', 50, TRUE)
qdel(pod)
/obj/structure/transit_tube/station/dispenser/flipped
icon_state = "open_dispenser1"
base_icon_state = "dispenser1"
/obj/structure/transit_tube/station/dispenser/flipped/init_tube_dirs()
..()
boarding_dir = dir
/obj/structure/transit_tube/station/dispenser/reverse
reverse_launch = TRUE
icon_state = "open_terminusdispenser0"
base_icon_state = "terminusdispenser0"
/obj/structure/transit_tube/station/dispenser/reverse/init_tube_dirs()
tube_dirs = list(turn(dir, 90))
boarding_dir = reverse_direction(dir)
/obj/structure/transit_tube/station/dispenser/reverse/flipped
icon_state = "open_terminusdispenser1"
base_icon_state = "terminusdispenser1"
/obj/structure/transit_tube/station/dispenser/reverse/flipped/init_tube_dirs()
..()
boarding_dir = dir
#undef CLOSE_DURATION
#undef OPEN_DURATION
@@ -4,8 +4,10 @@
// Mappers: you can use "Generate Instances from Icon-states"
// to get the different pieces.
/obj/structure/transit_tube
name = "transit tube"
desc = "A pneumatic tube that brings you from here to there."
icon = 'icons/obj/pipes/transit_tube.dmi'
icon_state = "E-W"
icon_state = "straight"
density = TRUE
layer = 3.1
anchored = TRUE
@@ -13,29 +15,39 @@
var/exit_delay = 1
var/enter_delay = 0
// alldirs in global.dm is the same list of directions, but since
// the specific order matters to get a usable icon_state, it is
// copied here so that, in the unlikely case that alldirs is changed,
// this continues to work.
var/global/list/tube_dir_list = list(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST)
/obj/structure/transit_tube/Initialize(mapload, new_direction)
. = ..()
if(new_direction)
setDir(new_direction)
// set up our appearance after the initialize in case someone's setting our direction afterwards
// (especially for things like admin spawning)
addtimer(CALLBACK(src, PROC_REF(setup_appearance)), 1)
/obj/structure/transit_tube/proc/setup_appearance()
init_tube_dirs()
update_appearance()
/obj/structure/transit_tube/Destroy()
for(var/obj/structure/transit_tube_pod/P in loc)
P.empty_pod()
return ..()
/obj/structure/transit_tube/CanPass(atom/movable/mover, turf/target)
if(istype(mover) && mover.checkpass(PASSGLASS))
return 1
return TRUE
return !density
// When destroyed by explosions, properly handle contents.
/obj/structure/transit_tube/ex_act(severity)
switch(severity)
if(1.0)
if(EXPLODE_DEVASTATE)
for(var/atom/movable/AM in contents)
AM.loc = loc
AM.ex_act(severity++)
qdel(src)
return
if(2.0)
if(EXPLODE_HEAVY)
if(prob(50))
for(var/atom/movable/AM in contents)
AM.loc = loc
@@ -43,20 +55,12 @@
qdel(src)
return
if(3.0)
if(EXPLODE_LIGHT)
return
/obj/structure/transit_tube/New(loc)
..(loc)
if(tube_dirs == null)
init_dirs()
// Called to check if a pod should stop upon entering this tube.
/obj/structure/transit_tube/proc/should_stop_pod(pod, from_dir)
return 0
return FALSE
// Called when a pod stops in this tube section.
/obj/structure/transit_tube/proc/pod_stopped(pod, from_dir)
@@ -76,18 +80,18 @@
for(var/direction in directions())
if(direction == from_dir)
return 1
return TRUE
return 0
return FALSE
/obj/structure/transit_tube/proc/has_exit(in_dir)
for(var/direction in directions())
if(direction == in_dir)
return 1
return TRUE
return 0
return FALSE
@@ -122,191 +126,157 @@
/obj/structure/transit_tube/proc/enter_delay(pod, to_dir)
return enter_delay
/obj/structure/transit_tube/proc/init_tube_dirs()
switch(dir)
if(NORTH, SOUTH)
tube_dirs = list(NORTH, SOUTH)
if(EAST, WEST)
tube_dirs = list(EAST, WEST)
// Parse the icon_state into a list of directions.
// This means that mappers can use Dream Maker's built in
// "Generate Instances from Icon-states" option to get all
// variations. Additionally, as a separate proc, sub-types
// can handle it more intelligently.
/obj/structure/transit_tube/proc/init_dirs()
if(icon_state == "auto")
// Additional delay, for map loading.
spawn(1)
init_dirs_automatic()
/obj/structure/transit_tube/update_overlays()
. = ..()
for(var/direction in directions())
if(!IS_DIR_DIAGONAL(direction))
. += create_tube_overlay(direction)
continue
if(!(direction & NORTH))
continue
. += create_tube_overlay(direction ^ (NORTH|SOUTH), NORTH)
if(direction & EAST)
. += create_tube_overlay(direction ^ (EAST|WEST), EAST)
else
. += create_tube_overlay(direction ^ (EAST|WEST), WEST)
/obj/structure/transit_tube/proc/create_tube_overlay(direction, shift_dir)
// We use image() because a mutable appearance will have its dir mirror the parent which sort of fucks up what we're doing here
var/image/tube_overlay = image(icon, dir = direction)
if(shift_dir)
tube_overlay.icon_state = "decorative_diag"
switch(shift_dir)
if(NORTH)
tube_overlay.pixel_y = 32
if(SOUTH)
tube_overlay.pixel_y = -32
if(EAST)
tube_overlay.pixel_x = 32
if(WEST)
tube_overlay.pixel_x = -32
else
tube_dirs = parse_dirs(icon_state)
tube_overlay.icon_state = "decorative"
if(copytext(icon_state, 1, 3) == "D-" || findtextEx(icon_state, "Pass"))
density = FALSE
tube_overlay.overlays += emissive_blocker(icon, tube_overlay.icon_state, src)
return tube_overlay
//Some of these are mostly for mapping use
/obj/structure/transit_tube/horizontal
dir = WEST
/obj/structure/transit_tube/diagonal
icon_state = "diagonal"
/obj/structure/transit_tube/diagonal/init_tube_dirs()
switch(dir)
if(NORTH)
tube_dirs = list(NORTHEAST, SOUTHWEST)
if(SOUTH)
tube_dirs = list(NORTHEAST, SOUTHWEST)
if(EAST)
tube_dirs = list(NORTHWEST, SOUTHEAST)
if(WEST)
tube_dirs = list(NORTHWEST, SOUTHEAST)
//mostly for mapping use
/obj/structure/transit_tube/diagonal/topleft
dir = WEST
/obj/structure/transit_tube/diagonal/crossing
density = FALSE
icon_state = "diagonal_crossing"
//mostly for mapping use
/obj/structure/transit_tube/diagonal/crossing/topleft
dir = WEST
// Initialize dirs by searching for tubes that do/might connect
// on nearby turfs. Create corner pieces if nessecary.
// Pick two directions, preferring tubes that already connect
// to loc, or other auto tubes if there aren't enough connections.
/obj/structure/transit_tube/proc/init_dirs_automatic()
var/list/connected = list()
var/list/connected_auto = list()
/obj/structure/transit_tube/curved
icon_state = "curved0"
for(var/direction in tube_dir_list)
var/location = get_step(loc, direction)
for(var/obj/structure/transit_tube/tube in location)
if(tube.directions() == null && tube.icon_state == "auto")
connected_auto += direction
break
/obj/structure/transit_tube/curved/init_tube_dirs()
switch(dir)
if(NORTH)
tube_dirs = list(NORTH, SOUTHWEST)
if(SOUTH)
tube_dirs = list(SOUTH, NORTHEAST)
if(EAST)
tube_dirs = list(EAST, NORTHWEST)
if(WEST)
tube_dirs = list(SOUTHEAST, WEST)
else if(turn(direction, 180) in tube.directions())
connected += direction
break
/obj/structure/transit_tube/curved/flipped
icon_state = "curved1"
connected += connected_auto
tube_dirs = select_automatic_dirs(connected)
if(length(tube_dirs) == 2 && tube_dir_list.Find(tube_dirs[1]) > tube_dir_list.Find(tube_dirs[2]))
tube_dirs.Swap(1, 2)
generate_automatic_corners(tube_dirs)
select_automatic_icon_state(tube_dirs)
/obj/structure/transit_tube/curved/flipped/init_tube_dirs()
switch(dir)
if(NORTH)
tube_dirs = list(NORTH, SOUTHEAST)
if(SOUTH)
tube_dirs = list(SOUTH, NORTHWEST)
if(EAST)
tube_dirs = list(EAST, SOUTHWEST)
if(WEST)
tube_dirs = list(NORTHEAST, WEST)
/obj/structure/transit_tube/junction
icon_state = "junction0"
// Given a list of directions, look a pair that forms a 180 or
// 135 degree angle, and return a list containing the pair.
// If none exist, return list(connected[1], turn(connected[1], 180)
/obj/structure/transit_tube/proc/select_automatic_dirs(connected)
if(length(connected) < 1)
return list()
/obj/structure/transit_tube/junction/init_tube_dirs()
switch(dir)
if(NORTH)
tube_dirs = list(NORTH, SOUTHEAST, SOUTHWEST)//ending with the prefered direction
if(SOUTH)
tube_dirs = list(SOUTH, NORTHWEST, NORTHEAST)
if(EAST)
tube_dirs = list(EAST, SOUTHWEST, NORTHWEST)
if(WEST)
tube_dirs = list(WEST, NORTHEAST, SOUTHEAST)
for(var/i = 1, i <= length(connected), i++)
for(var/j = i + 1, j <= length(connected), j++)
var/d1 = connected[i]
var/d2 = connected[j]
/obj/structure/transit_tube/junction/flipped
icon_state = "junction1"
if(d1 == turn(d2, 135) || d1 == turn(d2, 180) || d1 == turn(d2, 225))
return list(d1, d2)
return list(connected[1], turn(connected[1], 180))
/obj/structure/transit_tube/junction/flipped/init_tube_dirs()
switch(dir)
if(NORTH)
tube_dirs = list(NORTH, SOUTHWEST, SOUTHEAST)//ending with the prefered direction
if(SOUTH)
tube_dirs = list(SOUTH, NORTHEAST, NORTHWEST)
if(EAST)
tube_dirs = list(EAST, NORTHWEST, SOUTHWEST)
if(WEST)
tube_dirs = list(WEST, SOUTHEAST, NORTHEAST)
/obj/structure/transit_tube/crossing
icon_state = "crossing"
density = FALSE
/obj/structure/transit_tube/proc/select_automatic_icon_state(directions)
if(length(directions) == 2)
icon_state = "[dir2text_short(directions[1])]-[dir2text_short(directions[2])]"
//mostly for mapping use
/obj/structure/transit_tube/crossing/horizontal
dir = WEST
// cosmetic "cap" for tubes. Note that tubes can't enter this.
/obj/structure/transit_tube/cap
icon_state = "cap"
/obj/structure/transit_tube/cap/init_tube_dirs()
tube_dirs = list(turn(dir, 180)) // back the way we came
// Look for diagonal directions, generate the decorative corners in each.
/obj/structure/transit_tube/proc/generate_automatic_corners(directions)
for(var/direction in directions)
if(direction == 5 || direction == 6 || direction == 9 || direction == 10)
if(direction & NORTH)
create_automatic_decorative_corner(get_step(loc, NORTH), direction ^ 3)
else
create_automatic_decorative_corner(get_step(loc, SOUTH), direction ^ 3)
if(direction & EAST)
create_automatic_decorative_corner(get_step(loc, EAST), direction ^ 12)
else
create_automatic_decorative_corner(get_step(loc, WEST), direction ^ 12)
// Generate a corner, if one doesn't exist for the direction on the turf.
/obj/structure/transit_tube/proc/create_automatic_decorative_corner(location, direction)
var/state = "D-[dir2text_short(direction)]"
for(var/obj/structure/transit_tube/tube in location)
if(tube.icon_state == state)
return
var/obj/structure/transit_tube/tube = new(location)
tube.icon_state = state
tube.init_dirs()
// Uses a list() to cache return values. Since they should
// never be edited directly, all tubes with a certain
// icon_state can just reference the same list. In theory,
// reduces memory usage, and improves CPU cache usage.
// In reality, I don't know if that is quite how BYOND works,
// but it is probably safer to assume the existence of, and
// rely on, a sufficiently smart compiler/optimizer.
/obj/structure/transit_tube/proc/parse_dirs(text)
var/global/list/direction_table = list()
if(text in direction_table)
return direction_table[text]
var/list/split_text = splittext(text, "-")
// If the first token is D, the icon_state represents
// a purely decorative tube, and doesn't actually
// connect to anything.
if(split_text[1] == "D")
direction_table[text] = list()
return null
var/list/directions = list()
for(var/text_part in split_text)
var/direction = text2dir_extended(text_part)
if(direction > 0)
directions += direction
direction_table[text] = directions
return directions
// A copy of text2dir, extended to accept one and two letter
// directions, and to clearly return 0 otherwise.
/obj/structure/transit_tube/proc/text2dir_extended(direction)
switch(uppertext(direction))
if("NORTH", "N")
return 1
if("SOUTH", "S")
return 2
if("EAST", "E")
return 4
if("WEST", "W")
return 8
if("NORTHEAST", "NE")
return 5
if("NORTHWEST", "NW")
return 9
if("SOUTHEAST", "SE")
return 6
if("SOUTHWEST", "SW")
return 10
// A copy of dir2text, which returns the short one or two letter
// directions used in tube icon states.
/obj/structure/transit_tube/proc/dir2text_short(direction)
switch(direction)
if(1)
return "N"
if(2)
return "S"
if(4)
return "E"
if(8)
return "W"
if(5)
return "NE"
if(6)
return "SE"
if(9)
return "NW"
if(10)
return "SW"
/obj/structure/transit_tube/cap/has_entrance(from_dir)
return FALSE
/obj/structure/transit_tube/cap/create_tube_overlay()
// cap sprites already have overlays
return
@@ -1,3 +1,6 @@
#define MOVE_ANIMATION_STAGE_ONE 1
#define MOVE_ANIMATION_STAGE_TWO 2
/obj/structure/transit_tube_pod
icon = 'icons/obj/pipes/transit_tube_pod.dmi'
icon_state = "pod"
@@ -6,9 +9,25 @@
density = TRUE
var/moving = FALSE
var/datum/gas_mixture/air_contents = new()
/// The tube we're currently traveling through
var/obj/structure/transit_tube/current_tube = null
/// The direction of our next transit from pipe to pipe. Stored between process calls here.
var/next_dir
/// The location of our next target tube. Stored same as above
var/next_loc
/// How long to wait when entering a new tube
var/enter_delay = 0
/// How long to wait when exiting a new tube
var/exit_delay
/// The next time we'll be moving
COOLDOWN_DECLARE(move_cooldown)
/// The move animation mode for the next processing tick
var/current_move_anim_mode = MOVE_ANIMATION_STAGE_ONE
/// Icon state in use while we're occupied
var/occupied_icon_state = "pod_occupied"
/obj/structure/transit_tube_pod/New(loc)
..(loc)
/obj/structure/transit_tube_pod/Initialize(mapload)
. = ..()
air_contents.oxygen = MOLES_O2STANDARD * 2
air_contents.nitrogen = MOLES_N2STANDARD
@@ -18,9 +37,16 @@
spawn(5)
follow_tube()
/obj/structure/transit_tube_pod/update_icon_state()
. = ..()
icon_state = length(contents) ? occupied_icon_state : initial(icon_state)
/obj/structure/transit_tube_pod/proc/stop_following()
STOP_PROCESSING(SStransit_tube, src)
/obj/structure/transit_tube_pod/Destroy()
for(var/atom/movable/AM in contents)
AM.forceMove(get_turf(src))
empty_pod()
stop_following()
return ..()
/obj/structure/transit_tube_pod/Process_Spacemove()
@@ -28,66 +54,111 @@
return TRUE
else return ..()
/obj/structure/transit_tube_pod/proc/empty_pod(atom/location)
if(!location)
location = get_turf(src)
for(var/atom/movable/M in contents)
M.forceMove(location)
update_appearance()
/obj/structure/transit_tube_pod/crowbar_act(mob/living/user, obj/item/I)
. = ..()
if(moving)
return
if(length(contents))
I.play_tool_sound(src)
user.visible_message("<span class='notice'>[user] pries [src] open.</span>")
empty_pod()
/obj/structure/transit_tube_pod/process()
..()
if(!COOLDOWN_FINISHED(src, move_cooldown))
return
var/move_result = move_animation(current_move_anim_mode)
if(isnull(move_result))
if(isnull(current_tube) || (!(dir in current_tube.directions()) && !(reverse_direction(dir) in current_tube.directions())))
outside_tube()
return PROCESS_KILL
current_move_anim_mode = move_result
/obj/structure/transit_tube_pod/proc/outside_tube()
var/list/savedcontents = contents.Copy()
var/saveddir = dir
var/turf/destination = get_edge_target_turf(src, saveddir)
visible_message("<span class='warning'>[src] ejects its insides out!</span>")
for(var/i in savedcontents)
var/atom/movable/AM = i
AM.throw_at(destination, rand(1, 3), 5)
/obj/structure/transit_tube_pod/proc/move_animation(stage = MOVE_ANIMATION_STAGE_ONE)
if(stage == MOVE_ANIMATION_STAGE_ONE)
next_dir = current_tube.get_exit(dir)
if(!next_dir)
moving = FALSE
density = TRUE
return
exit_delay = current_tube.exit_delay
next_loc = get_step(src, next_dir)
current_tube = null
for(var/obj/structure/transit_tube/tube in next_loc)
if(tube.has_entrance(next_dir))
current_tube = tube
break
if(isnull(current_tube))
setDir(next_dir)
Move(get_step(loc, dir), dir) // Allow collisions when leaving the tubes.
moving = FALSE
density = TRUE
return
enter_delay = current_tube.enter_delay(src, next_dir)
if(enter_delay > 0)
COOLDOWN_START(src, move_cooldown, enter_delay)
return MOVE_ANIMATION_STAGE_TWO
stage = MOVE_ANIMATION_STAGE_TWO
if(stage == MOVE_ANIMATION_STAGE_TWO)
setDir(next_dir)
forceMove(next_loc) // When moving from one tube to another, skip collision and such.
density = current_tube.density
if(current_tube?.should_stop_pod(src, next_dir))
current_tube.pod_stopped(src, dir)
else
COOLDOWN_START(src, move_cooldown, exit_delay)
return MOVE_ANIMATION_STAGE_ONE
density = TRUE
moving = FALSE
return MOVE_ANIMATION_STAGE_ONE
/obj/structure/transit_tube_pod/proc/follow_tube(reverse_launch)
if(moving)
return
moving = TRUE
spawn()
var/obj/structure/transit_tube/current_tube = null
var/next_dir
var/next_loc
var/last_delay = 0
var/exit_delay
if(reverse_launch)
dir = turn(dir, 180) // Back it up
for(var/obj/structure/transit_tube/tube in loc)
if(tube.has_exit(dir))
current_tube = tube
break
while(current_tube)
next_dir = current_tube.get_exit(dir)
if(!next_dir)
break
exit_delay = current_tube.exit_delay(src, dir)
last_delay += exit_delay
sleep(exit_delay)
next_loc = get_step(loc, next_dir)
current_tube = null
for(var/obj/structure/transit_tube/tube in next_loc)
if(tube.has_entrance(next_dir))
current_tube = tube
break
if(current_tube == null)
setDir(next_dir)
Move(get_step(loc, dir), dir) // Allow collisions when leaving the tubes.
break
last_delay = current_tube.enter_delay(src, next_dir)
sleep(last_delay)
setDir(next_dir)
forceMove(next_loc) // When moving from one tube to another, skip collision and such.
density = current_tube.density
if(current_tube && current_tube.should_stop_pod(src, next_dir))
current_tube.pod_stopped(src, dir)
break
density = TRUE
for(var/obj/structure/transit_tube/tube in loc)
if(tube.has_exit(dir))
current_tube = tube
break
if(!isnull(current_tube))
START_PROCESSING(SStransit_tube, src)
// move_animation(MOVE_ANIMATION_STAGE_ONE)
else
moving = FALSE
// Should I return a copy here? If the caller edits or qdel()s the returned
// datum, there might be problems if I don't...
/obj/structure/transit_tube_pod/return_air()
@@ -174,8 +245,8 @@
return
/obj/structure/transit_tube_pod/proc/move_into(atom/movable/A)
icon_state = "pod_occupied"
A.forceMove(src)
update_appearance()
/obj/structure/transit_tube_pod/proc/eject_mindless(direction)
for(var/atom/movable/A in contents)
@@ -188,9 +259,24 @@
/obj/structure/transit_tube_pod/proc/eject(atom/movable/A, direction)
icon_state = "pod"
A.forceMove(loc)
update_appearance()
A.Move(get_step(loc, direction), direction)
if(ismob(A))
var/mob/M = A
M.reset_perspective(null)
/obj/structure/transit_tube_pod/dispensed
name = "temporary transit tube pod"
desc = "Gets you from here to there, and no further."
icon_state = "temppod"
occupied_icon_state = "temppod_occupied"
/obj/structure/transit_tube_pod/dispensed/outside_tube()
if(!QDELETED(src))
qdel(src)
#undef MOVE_ANIMATION_STAGE_TWO
#undef MOVE_ANIMATION_STAGE_ONE