mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2026-01-08 08:23:49 +00:00
New turf proc: assume_gas(). Maps to air.adjust_gas_temp(). Lots of optimizations to processing, fire, lighting, HasEntered() and more. Zones now process all fire data and existance in one go, fire objects only handle spreading. Most code has been ported straight so some of it mightn't be ideally structured for the new gas_mixtures. Signed-off-by: Mloc-Argent <colmohici@gmail.com>
640 lines
16 KiB
Plaintext
640 lines
16 KiB
Plaintext
|
|
// Basic transit tubes. Straight pieces, curved sections,
|
|
// and basic splits/joins (no routing logic).
|
|
// Mappers: you can use "Generate Instances from Icon-states"
|
|
// to get the different pieces.
|
|
/obj/structure/transit_tube
|
|
icon = 'icons/obj/pipes/transit_tube.dmi'
|
|
icon_state = "E-W"
|
|
density = 1
|
|
layer = 3.1
|
|
anchored = 1.0
|
|
var/list/tube_dirs = null
|
|
var/exit_delay = 2
|
|
var/enter_delay = 1
|
|
|
|
// 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)
|
|
|
|
|
|
// A place where tube pods stop, and people can get in or out.
|
|
// Mappers: use "Generate Instances from Directions" for this
|
|
// one.
|
|
/obj/structure/transit_tube/station
|
|
icon = 'icons/obj/pipes/transit_tube_station.dmi'
|
|
icon_state = "closed"
|
|
exit_delay = 2
|
|
enter_delay = 3
|
|
var/pod_moving = 0
|
|
var/automatic_launch_time = 100
|
|
|
|
var/const/OPEN_DURATION = 6
|
|
var/const/CLOSE_DURATION = 6
|
|
|
|
|
|
|
|
/obj/structure/transit_tube_pod
|
|
icon = 'icons/obj/pipes/transit_tube_pod.dmi'
|
|
icon_state = "pod"
|
|
animate_movement = FORWARD_STEPS
|
|
anchored = 1.0
|
|
density = 1
|
|
var/moving = 0
|
|
var/datum/gas_mixture/air_contents = new()
|
|
|
|
|
|
|
|
/obj/structure/transit_tube_pod/Del()
|
|
for(var/atom/movable/AM in contents)
|
|
AM.loc = loc
|
|
|
|
..()
|
|
|
|
|
|
|
|
// When destroyed by explosions, properly handle contents.
|
|
obj/structure/ex_act(severity)
|
|
switch(severity)
|
|
if(1.0)
|
|
for(var/atom/movable/AM in contents)
|
|
AM.loc = loc
|
|
AM.ex_act(severity++)
|
|
|
|
del(src)
|
|
return
|
|
if(2.0)
|
|
if(prob(50))
|
|
for(var/atom/movable/AM in contents)
|
|
AM.loc = loc
|
|
AM.ex_act(severity++)
|
|
|
|
del(src)
|
|
return
|
|
if(3.0)
|
|
return
|
|
|
|
|
|
|
|
/obj/structure/transit_tube_pod/New(loc)
|
|
..(loc)
|
|
|
|
air_contents.adjust_multi("oxygen", MOLES_O2STANDARD * 2, "nitrogen", MOLES_N2STANDARD)
|
|
air_contents.temperature = T20C
|
|
|
|
// Give auto tubes time to align before trying to start moving
|
|
spawn(5)
|
|
follow_tube()
|
|
|
|
|
|
|
|
/obj/structure/transit_tube/New(loc)
|
|
..(loc)
|
|
|
|
if(tube_dirs == null)
|
|
init_dirs()
|
|
|
|
|
|
|
|
/obj/structure/transit_tube/Bumped(mob/AM as mob|obj)
|
|
var/obj/structure/transit_tube/T = locate() in AM.loc
|
|
if(T)
|
|
AM << "<span class='warning'>The tube's support pylons block your way.</span>"
|
|
return ..()
|
|
else
|
|
AM.loc = src.loc
|
|
AM << "<span class='info'>You slip under the tube.</span>"
|
|
|
|
|
|
/obj/structure/transit_tube/station/New(loc)
|
|
..(loc)
|
|
|
|
|
|
|
|
/obj/structure/transit_tube/station/Bumped(mob/AM as mob|obj)
|
|
if(!pod_moving && icon_state == "open" && istype(AM, /mob))
|
|
for(var/obj/structure/transit_tube_pod/pod in loc)
|
|
if(pod.contents.len)
|
|
AM << "<span class=The pod is already occupied.</span>"
|
|
return
|
|
else if(!pod.moving && pod.dir in directions())
|
|
AM.loc = pod
|
|
return
|
|
|
|
|
|
/obj/structure/transit_tube/station/attack_hand(mob/user as mob)
|
|
if(!pod_moving)
|
|
for(var/obj/structure/transit_tube_pod/pod in loc)
|
|
if(!pod.moving && pod.dir in directions())
|
|
if(icon_state == "closed")
|
|
open_animation()
|
|
|
|
else if(icon_state == "open")
|
|
close_animation()
|
|
|
|
|
|
|
|
/obj/structure/transit_tube/station/proc/open_animation()
|
|
if(icon_state == "closed")
|
|
icon_state = "opening"
|
|
spawn(OPEN_DURATION)
|
|
if(icon_state == "opening")
|
|
icon_state = "open"
|
|
|
|
|
|
|
|
/obj/structure/transit_tube/station/proc/close_animation()
|
|
if(icon_state == "open")
|
|
icon_state = "closing"
|
|
spawn(CLOSE_DURATION)
|
|
if(icon_state == "closing")
|
|
icon_state = "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())
|
|
spawn(5)
|
|
pod_moving = 1
|
|
close_animation()
|
|
sleep(CLOSE_DURATION + 2)
|
|
|
|
//reverse directions for automated cycling
|
|
var/turf/next_loc = get_step(loc, pod.dir)
|
|
var/obj/structure/transit_tube/nexttube
|
|
for(var/obj/structure/transit_tube/tube in next_loc)
|
|
if(tube.has_entrance(pod.dir))
|
|
nexttube = tube
|
|
break
|
|
if(!nexttube)
|
|
pod.dir = turn(pod.dir, 180)
|
|
|
|
if(icon_state == "closed" && pod)
|
|
pod.follow_tube()
|
|
|
|
pod_moving = 0
|
|
|
|
return
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
/obj/structure/transit_tube/station/should_stop_pod(pod, from_dir)
|
|
return 1
|
|
|
|
|
|
|
|
// Called when a pod stops in this tube section.
|
|
/obj/structure/transit_tube/proc/pod_stopped(pod, from_dir)
|
|
return
|
|
|
|
|
|
|
|
/obj/structure/transit_tube/station/pod_stopped(obj/structure/transit_tube_pod/pod, from_dir)
|
|
pod_moving = 1
|
|
spawn(5)
|
|
open_animation()
|
|
sleep(OPEN_DURATION + 2)
|
|
pod_moving = 0
|
|
pod.mix_air()
|
|
|
|
if(automatic_launch_time)
|
|
var/const/wait_step = 5
|
|
var/i = 0
|
|
while(i < automatic_launch_time)
|
|
sleep(wait_step)
|
|
i += wait_step
|
|
|
|
if(pod_moving || icon_state != "open")
|
|
return
|
|
|
|
launch_pod()
|
|
|
|
|
|
|
|
// Returns a /list of directions this tube section can connect to.
|
|
// Tubes that have some sort of logic or changing direction might
|
|
// override it with additional logic.
|
|
/obj/structure/transit_tube/proc/directions()
|
|
return tube_dirs
|
|
|
|
|
|
|
|
/obj/structure/transit_tube/proc/has_entrance(from_dir)
|
|
from_dir = turn(from_dir, 180)
|
|
|
|
for(var/direction in directions())
|
|
if(direction == from_dir)
|
|
return 1
|
|
|
|
return 0
|
|
|
|
|
|
|
|
/obj/structure/transit_tube/proc/has_exit(in_dir)
|
|
for(var/direction in directions())
|
|
if(direction == in_dir)
|
|
return 1
|
|
|
|
return 0
|
|
|
|
|
|
|
|
// Searches for an exit direction within 45 degrees of the
|
|
// specified dir. Returns that direction, or 0 if none match.
|
|
/obj/structure/transit_tube/proc/get_exit(in_dir)
|
|
var/near_dir = 0
|
|
var/in_dir_cw = turn(in_dir, -45)
|
|
var/in_dir_ccw = turn(in_dir, 45)
|
|
|
|
for(var/direction in directions())
|
|
if(direction == in_dir)
|
|
return direction
|
|
|
|
else if(direction == in_dir_cw)
|
|
near_dir = direction
|
|
|
|
else if(direction == in_dir_ccw)
|
|
near_dir = direction
|
|
|
|
return near_dir
|
|
|
|
|
|
|
|
// Return how many BYOND ticks to wait before entering/exiting
|
|
// the tube section. Default action is to return the value of
|
|
// a var, which wouldn't need a proc, but it makes it possible
|
|
// for later tube types to interact in more interesting ways
|
|
// such as being very fast in one direction, but slow in others
|
|
/obj/structure/transit_tube/proc/exit_delay(pod, to_dir)
|
|
return exit_delay
|
|
|
|
/obj/structure/transit_tube/proc/enter_delay(pod, to_dir)
|
|
return enter_delay
|
|
|
|
|
|
|
|
/obj/structure/transit_tube_pod/proc/follow_tube()
|
|
if(moving)
|
|
return
|
|
|
|
moving = 1
|
|
|
|
spawn()
|
|
var/obj/structure/transit_tube/current_tube = null
|
|
var/next_dir
|
|
var/next_loc
|
|
var/last_delay = 0
|
|
var/exit_delay
|
|
|
|
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)
|
|
dir = next_dir
|
|
Move(get_step(loc, dir)) // Allow collisions when leaving the tubes.
|
|
break
|
|
|
|
last_delay = current_tube.enter_delay(src, next_dir)
|
|
sleep(last_delay)
|
|
dir = next_dir
|
|
loc = 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 = 1
|
|
|
|
// If the pod is no longer in a tube, move in a line until stopped or slowed to a halt.
|
|
// /turf/inertial_drift appears to only work on mobs, and re-implementing some of the
|
|
// logic allows a gradual slowdown and eventual stop when passing over non-space turfs.
|
|
if(!current_tube && last_delay <= 10)
|
|
do
|
|
sleep(last_delay)
|
|
|
|
if(!istype(loc, /turf/space))
|
|
last_delay++
|
|
|
|
if(last_delay > 10)
|
|
break
|
|
|
|
while(isturf(loc) && Move(get_step(loc, dir)))
|
|
|
|
moving = 0
|
|
|
|
|
|
// Should I return a copy here? If the caller edits or del()s the returned
|
|
// datum, there might be problems if I don't...
|
|
/obj/structure/transit_tube_pod/return_air()
|
|
var/datum/gas_mixture/GM = new()
|
|
GM.copy_from(air_contents)
|
|
return GM
|
|
|
|
// For now, copying what I found in an unused FEA file (and almost identical in a
|
|
// used ZAS file). Means that assume_air and remove_air don't actually alter the
|
|
// air contents.
|
|
/obj/structure/transit_tube_pod/assume_air(datum/gas_mixture/giver)
|
|
return air_contents.merge(giver)
|
|
|
|
/obj/structure/transit_tube_pod/remove_air(amount)
|
|
return air_contents.remove(amount)
|
|
|
|
|
|
|
|
// Called when a pod arrives at, and before a pod departs from a station,
|
|
// giving it a chance to mix its internal air supply with the turf it is
|
|
// currently on.
|
|
/obj/structure/transit_tube_pod/proc/mix_air()
|
|
var/datum/gas_mixture/environment = loc.return_air()
|
|
var/env_pressure = environment.return_pressure()
|
|
var/int_pressure = air_contents.return_pressure()
|
|
var/total_pressure = env_pressure + int_pressure
|
|
|
|
if(total_pressure == 0)
|
|
return
|
|
|
|
// Math here: Completely made up, not based on realistic equasions.
|
|
// Goal is to balance towards equal pressure, but ensure some gas
|
|
// transfer in both directions regardless.
|
|
// Feel free to rip this out and replace it with something better,
|
|
// I don't really know muhch about how gas transfer rates work in
|
|
// SS13.
|
|
var/transfer_in = max(0.1, 0.5 * (env_pressure - int_pressure) / total_pressure)
|
|
var/transfer_out = max(0.1, 0.3 * (int_pressure - env_pressure) / total_pressure)
|
|
|
|
var/datum/gas_mixture/from_env = loc.remove_air(environment.total_moles * transfer_in)
|
|
var/datum/gas_mixture/from_int = air_contents.remove(air_contents.total_moles * transfer_out)
|
|
|
|
loc.assume_air(from_int)
|
|
air_contents.merge(from_env)
|
|
|
|
|
|
|
|
// When the player moves, check if the pos is currently stopped at a station.
|
|
// if it is, check the direction. If the direction matches the direction of
|
|
// the station, try to exit. If the direction matches one of the station's
|
|
// tube directions, launch the pod in that direction.
|
|
/obj/structure/transit_tube_pod/relaymove(mob/mob, direction)
|
|
if(istype(mob, /mob) && mob.client)
|
|
// If the pod is not in a tube at all, you can get out at any time.
|
|
if(!(locate(/obj/structure/transit_tube) in loc))
|
|
mob.loc = loc
|
|
mob.client.Move(get_step(loc, direction), direction)
|
|
|
|
//if(moving && istype(loc, /turf/space))
|
|
// Todo: If you get out of a moving pod in space, you should move as well.
|
|
// Same direction as pod? Direcion you moved? Halfway between?
|
|
|
|
if(!moving)
|
|
for(var/obj/structure/transit_tube/station/station in loc)
|
|
if(dir in station.directions())
|
|
if(!station.pod_moving)
|
|
if(direction == station.dir)
|
|
if(station.icon_state == "open")
|
|
mob.loc = loc
|
|
mob.client.Move(get_step(loc, direction), direction)
|
|
|
|
else
|
|
station.open_animation()
|
|
|
|
else if(direction in station.directions())
|
|
dir = direction
|
|
station.launch_pod()
|
|
return
|
|
|
|
for(var/obj/structure/transit_tube/tube in loc)
|
|
if(dir in tube.directions())
|
|
if(tube.has_exit(direction))
|
|
dir = direction
|
|
return
|
|
|
|
|
|
|
|
// 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()
|
|
|
|
else
|
|
tube_dirs = parse_dirs(icon_state)
|
|
|
|
if(copytext(icon_state, 1, 3) == "D-" || findtextEx(icon_state, "Pass"))
|
|
density = 0
|
|
|
|
|
|
|
|
// 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))
|
|
|
|
|
|
|
|
// 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()
|
|
|
|
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
|
|
|
|
else if(turn(direction, 180) in tube.directions())
|
|
connected += direction
|
|
break
|
|
|
|
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)
|
|
|
|
|
|
|
|
// 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()
|
|
|
|
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]
|
|
|
|
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/proc/select_automatic_icon_state(directions)
|
|
if(length(directions) == 2)
|
|
icon_state = "[dir2text_short(directions[1])]-[dir2text_short(directions[2])]"
|
|
|
|
|
|
|
|
// 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 = text2list(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
|
|
else
|
|
return 0
|
|
|
|
|
|
|
|
// 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"
|
|
else
|
|
return
|