Files
vgstation13/code/datums/shuttle.dm
ShiftyRail 409ae193c2 The Postman always ring twice (#30551)
* Revert "Revert "Revert "Lights out tonight (trouble in the Heartland) (#30526)" (#30527)" (#30529)"

This reverts commit 5ae655387f.

* fixes urgent problems

* fixes the problem with zooming out

* gliding

* on the beach

* suffer not the lag

* fixes mesons and fire
2021-09-07 09:58:07 +01:00

912 lines
30 KiB
Plaintext

#define NO_TRANSIT 0 //Don't use transit areas
#define TRANSIT_ACROSS_Z_LEVELS 1 //Only use transit areas if moving to another z-level
#define TRANSIT_ALWAYS 2 //Always use transit areas
//Whether this shuttle can be linked to a shuttle control console.
#define LINK_FREE 0
#define LINK_PASSWORD_ONLY 1
#define LINK_FORBIDDEN 2
//Whether the shuttle destroys stuff it collides with, or displaces it
#define COLLISION_DESTROY 0
#define COLLISION_DISPLACE 1 //this is same as COLLISION_DISPLACE as for now
//One of these values is returned at initialize()
#define INIT_SUCCESS 1 //everything is good
#define INIT_NO_AREA 2 //can't find starting area
#define INIT_NO_PORT 3 //can't find shuttle's docking port
#define INIT_NO_START 4 //shuttle's docking port isn't connected to a destination port
/datum/shuttle
var/name = "shuttle"
//List of ALL docking ports the shuttle can move to
var/list/docking_ports = list()
//The shuttle's main area - it contains the linked_port
var/area/linked_area
//The shuttle's linked shuttle docking port - essential
var/obj/docking_port/shuttle/linked_port
//The shuttle's current location
var/obj/docking_port/destination/current_port
//The shuttle's transit location
var/obj/docking_port/destination/transit_port
//The shuttle's destination
var/obj/docking_port/destination/destination_port
//List of ALL docking ports on the shuttle. Setup at initialize(), the shuttle can only move docking ports in this list
//(which means those which are placed in the shuttle's area on the map). This exists to prevent shuttles from moving on top
//of another docking port and then moving it away
var/list/docking_ports_aboard = list()
var/use_transit = TRANSIT_ACROSS_Z_LEVELS
var/dir = NORTH
var/can_rotate = 1
//This is the time it takes for the shuttle to depart (if there's a transit area) or to travel (if there are no transit areas)
var/pre_flight_delay = 50
//If there is a transit port, this is the time the shuttle spends in it
//If there isn't a transit port, this has no effect. Use the pre_flight_delay var instead
var/transit_delay = 100
//If the shuttle is currently moving
var/moving = 0
var/list/cant_leave_zlevel = list(
/obj/item/weapon/disk/nuclear = "The nuclear authentication disk can't be transported on a shuttle.",
)
//This list is transferred to all linked shuttle control consoles
var/list/req_access = list()
var/last_moved = 0
var/cooldown = 100
//When the shuttle moves, coordinates of its final location will be offset by rand(-innacuracy, innacuracy)
var/innacuracy = 0
//When the shuttle moves, if stable is 0 then all unbuckled mobs will be stunned
var/stable = 0
var/password = null
var/can_link_to_computer = LINK_FORBIDDEN
//Whether the shuttle gibs or displaces stuff. Change this to COLLISION_DISPLACE to make all shuttles displace stuff by default
var/collision_type = COLLISION_DESTROY
var/list/control_consoles = list()
var/lockdown = 0
var/destroy_everything = 0
/datum/shuttle/New(var/area/starting_area)
.=..()
if(starting_area)
if(ispath(starting_area))
linked_area = locate(starting_area)
else if(isarea(starting_area))
linked_area = starting_area
else
linked_area = starting_area
warning("Unable to find area [starting_area] in world - [src.type] ([src.name]) won't be able to function properly.")
if(istype(linked_area) && linked_area.contents.len) //Only add the shuttle to the list if its area exists and it has something in it
shuttles |= src
if(password)
password = rand(10000,99999)
//initialize() proc - called automatically in proc/setup_shuttles() below.
//Returns INIT_SUCCESS, INIT_NO_AREA, INIT_NO_START or INIT_NO_PORT, depending on whether there were any errors
/datum/shuttle/proc/initialize()
. = INIT_SUCCESS
src.docking_ports = list()
src.docking_ports_aboard = list()
src.transit_port = null
if(!linked_area || !istype(linked_area))
//No linked area - the shuttle doesn't exist (very bad)
return INIT_NO_AREA
//This line below used to cause weird bugs with some maps (https://github.com/d3athrow/vgstation13/issues/6773)
//var/obj/docking_port/shuttle/shuttle_docking_port = locate() in linked_area.contents.Copy()
//I have no idea what was causing it, but after replacing it with the following five lines everything started working as intended
var/obj/docking_port/shuttle/shuttle_docking_port
for(var/obj/docking_port/shuttle/S in linked_area)
shuttle_docking_port = S
break
//
if(shuttle_docking_port)
//In case this shuttle already has a shuttle docking port, unlink it
if(linked_port)
linked_port.unlink_from_shuttle(src)
shuttle_docking_port.link_to_shuttle(src)
//The following few lines ensure that if there's a docking port at the shuttle's starting location, the shuttle is docked to it
var/turf/check_turf = shuttle_docking_port.get_docking_turf()
if(check_turf)
for(var/obj/docking_port/P in check_turf.contents)
shuttle_docking_port.dock(P)
src.current_port = shuttle_docking_port.docked_with
break
if(!src.current_port)
//This isn't really a problem, but if the shuttle moves somewhere it won't be able to return to its starting location
. = INIT_NO_START
src.dir = turn(linked_port.dir, 180)
else
//No docking port - the shuttle can't be moved (bad but fixable with admin intervention)
. = INIT_NO_PORT
for(var/obj/docking_port/D in linked_area)
docking_ports_aboard |= D
return
/datum/shuttle/Destroy()
shuttles -= src
..()
/datum/shuttle/proc/get_transit_delay()
return transit_delay
/datum/shuttle/proc/get_pre_flight_delay()
return pre_flight_delay
/datum/shuttle/proc/get_cooldown()
return cooldown
//Shuttles like the emergency shuttle (which moves to pre-defined locations) and vox shuttle (which ends the round once moved to a pre-defined location)
//should have this proc return 1, so they can't be deleted.
/datum/shuttle/proc/is_special()
return 0
//Adds a docking port to list of travel destinations, accepts path or the port itself
/datum/shuttle/proc/add_dock(var/D)
if(ispath(D))
for(var/obj/docking_port/destination/dock in all_docking_ports)
if(istype(dock,D))
dock.link_to_shuttle(src)
return dock
else if(istype(D,/obj/docking_port/destination))
var/obj/docking_port/destination/dock = D
dock.link_to_shuttle(src)
return dock
return D
//The reverse
/datum/shuttle/proc/remove_dock(var/D)
if(ispath(D))
for(var/obj/docking_port/destination/dock in all_docking_ports)
if(istype(dock,D))
dock.unlink_from_shuttle(src)
return dock
else if(istype(D,/obj/docking_port/destination))
var/obj/docking_port/destination/dock = D
dock.unlink_from_shuttle(src)
return dock
return D
//Adds a docking port as a transit area, accepts path or the port itself
/datum/shuttle/proc/set_transit_dock(var/D)
if(ispath(D))
for(var/obj/docking_port/destination/dock in all_docking_ports)
if(istype(dock,D))
transit_port = dock
return dock
else if(istype(D,/obj/docking_port/destination))
transit_port = D
return D
/datum/shuttle/proc/can_move()
if(lockdown)
return 0
if(last_moved + cooldown < world.time)
return 1
//Checks the shuttle for any offending atoms
/datum/shuttle/proc/forbid_movement()
var/atom/A = linked_area.contains_atom_from_list(cant_leave_zlevel) //code/game/atoms.dm, 243
if(A)
return A
for(var/mob/living/M in get_contents_in_object(linked_area, /mob/living))
if(M.locked_to_z && M.locked_to_z != destination_port.z)
return M
return 0
//This is the proc you generally want to use when moving a shuttle. Runs all sorts of checks (cooldown, if already moving, etc)
//If you want to bypass it, set destination_port to something and call pre_flight()
//Alternatively, call move_to_dock(destination)
/datum/shuttle/proc/travel_to(var/obj/docking_port/D, var/obj/machinery/computer/shuttle_control/broadcast = null, var/mob/user)
if(!D)
return 0 //no docking port
if(!linked_port)
return 0 //no shuttle port
if(destination_port)
if(broadcast)
broadcast.announce( "The shuttle is currently in process of moving." )
else if(user)
to_chat(user, "The shuttle is currently moving")
return 0 //shuttle already travelling
if(lockdown)
if(broadcast)
broadcast.announce( "This shuttle is locked down." )
else if(user)
to_chat(user, "The shuttle can't move (locked down)")
return 0
if(!can_move())
if(broadcast)
broadcast.announce( "The engines are still cooling down from the previous trip." )
else if(user)
to_chat(user, "The shuttle can't move (on cooldown)")
return 0
if(D.docked_with)
if(broadcast)
broadcast.announce( "[capitalize(D.areaname)] is currently used by another shuttle. Please wait until the docking port is free, or select another destination." )
else if(user)
to_chat(user, "The shuttle can't move ([D.areaname] is used by another shuttle)")
return 0
if(D.require_admin_permission && !isAdminGhost(user))
if(broadcast)
broadcast.announce( "Currently requesting permission to reach [D.areaname]..." )
else if(user)
to_chat(user, "Waiting for permission...")
var/reason = input(user, "State your reasons for wanting to dock at [D.areaname].", "Docking Request", "")
message_admins("[key_name(user)] is requesting permission to fly their [name] to [D.areaname]. [reason ? "Reason:[reason]" : "They didn't give a reason"]. (<a href='?_src_=holder;shuttlepermission=1;shuttle=\ref[src];docking_port=\ref[D];broadcast=\ref[broadcast];user=\ref[user];answer=1'>ACCEPT</a>/<a href='?_src_=holder;shuttlepermission=1;shuttle=\ref[src];docking_port=\ref[D];broadcast=\ref[broadcast];user=\ref[user];answer=0'>DENY</a>)")
else
actually_travel_to(D, broadcast, user)
/datum/shuttle/proc/actually_travel_to(var/obj/docking_port/D, var/obj/machinery/computer/shuttle_control/broadcast = null, var/mob/user)
//Handle the message
var/time = "as soon as possible"
switch(pre_flight_delay)
if(0)
time = "immediately"
if(1 to 30)
time = "in a few seconds"
if(31 to 50)
time = "shortly"
if(51 to 80)
time = "after a short delay"
if(81 to INFINITY)
time = "in [max(round((pre_flight_delay) / 10, 1), 0)] seconds"
if(broadcast)
broadcast.announce("The shuttle has received your message and will be sent [time].")
destination_port = D
last_moved = world.time
moving = 1
log_game("[usr ? key_name(usr) : "Something"] sent [name] ([type]) to [D.areaname]")
if(get_pre_flight_delay())
spawn(max(1,get_pre_flight_delay()-5))
for(var/obj/structure/shuttle/engine/propulsion/P in linked_area)
spawn()
P.shoot_exhaust()
if(current_port)
current_port.start_warning_lights()
destination_port.start_warning_lights()
spawn(get_pre_flight_delay())
if(current_port)
current_port.stop_warning_lights()
if(destination_port)
destination_port.stop_warning_lights()
//If moving to another zlevel, check for items which can't leave the zlevel (nuke disk, primarily)
if(linked_port.z != D.z)
var/atom/A = forbid_movement()
if( A )
if(cant_leave_zlevel[A.type])
if(broadcast)
broadcast.announce("ERROR: [cant_leave_zlevel[A.type]]")
else if(user)
to_chat(user, cant_leave_zlevel[A.type])
else
if(broadcast)
broadcast.announce("ERROR: [A.name] is preventing the shuttle from departing.")
else if(user)
to_chat(user, "[A.name] is preventing the shuttle from departing.")
moving = 0
destination_port = null
return
for(var/atom/movable/AA in linked_area)
AA.invoke_event(/event/z_transition, list("user" = AA, "to_z" = D.z, "from_z" = linked_port.z))
if(transit_port && get_transit_delay())
if(broadcast)
broadcast.announce( "The shuttle has departed and is now moving towards [D.areaname]." )
else if(user)
to_chat(user, "The shuttle has departed towards [D.areaname]")
else
if(broadcast)
broadcast.announce( "The shuttle has arrived at [D.areaname]." )
else if(user)
to_chat(user, "The shuttle has arrived at [D.areaname]")
pre_flight()
return 1
/datum/shuttle/proc/pre_flight()
if(!destination_port)
return
if(transit_port && get_transit_delay())
if(use_transit == TRANSIT_ALWAYS || (use_transit == TRANSIT_ACROSS_Z_LEVELS && (linked_area.z != destination_port.z)))
move_to_dock(transit_port)
spawn(max(1,get_transit_delay()-5))
for(var/obj/structure/shuttle/engine/propulsion/P in linked_area)
spawn()
P.shoot_exhaust()
sleep(get_transit_delay())
if(destination_port)
move_to_dock(destination_port)
destination_port = null
moving = 0
//This is the proc you want to use to FORCE a shuttle to move. It always moves it, unless the shuttle or its area don't exist. Transit is skipped, after_flight() is called
/datum/shuttle/proc/move_to_dock(var/obj/docking_port/D, var/ignore_innacuracy = 0) //A direct proc with no bullshit
if(!D)
return
if(!linked_port)
return
//List of all shuttles docked to this shuttle. They will be moved together with their parent.
//In the list, shuttles are associated with the docking port they are docked to
var/list/docked_shuttles = list()
//To prevent two shuttles that are docked to each other from potentially breaking everything, all moved shuttles are added to this list
var/list/moved_shuttles = list()
moved_shuttles += src
//See all destination ports in current area
for(var/obj/docking_port/destination/dock in linked_area)
//If somebody is docked to it (and it isn't us (that would be weird but better be sure))
if(dock.docked_with && !(dock.docked_with == linked_port))
//Get the docking port that's docked to it, and then its shuttle
var/obj/docking_port/shuttle/S = dock.docked_with
if(!S || !S.linked_shuttle)
continue
docked_shuttles |= S.linked_shuttle
docked_shuttles[S.linked_shuttle]=dock
//******Handle rotation*********
var/rotate = 0
if(src.can_rotate)
if(linked_port.dir != turn(D.dir,180))
rotate = dir2angle(turn(D.dir,180)) - dir2angle(linked_port.dir)
if(rotate < 0)
rotate += 360
else if(rotate >= 360)
rotate -= 360
//******Get the turf to move to**
var/turf/target_turf = D.get_docking_turf()
if(!ignore_innacuracy && innacuracy) //Handle innacuracy
var/list/turf_list = list()
for(var/turf/T in orange(innacuracy,D.get_docking_turf()))
turf_list|=T
target_turf = pick(turf_list)
//****Finally, move the area***
if(move_area_to(get_turf(linked_port), target_turf, rotate))
linked_port.dock(D) //Dock our docking port with the destination
//****Move shuttles docked to us**
if(docked_shuttles.len)
for(var/datum/shuttle/S in docked_shuttles)
if(S in moved_shuttles)
continue
var/obj/docking_port/destination/our_moved_dock = docked_shuttles[S]
if(!our_moved_dock)
continue
moved_shuttles |= S
S.move_to_dock(our_moved_dock, ignore_innacuracy = 1)
log_game("[name] ([type]) moved to [D.areaname]")
current_port = D
after_flight() //Shake the shuttle, weaken unbuckled mobs, etc.
return 1
return
/datum/shuttle/proc/close_all_doors()
for(var/obj/machinery/door/unpowered/shuttle/D in linked_area)
spawn(0)
D.close()
/datum/shuttle/proc/open_all_doors()
for(var/obj/machinery/door/unpowered/shuttle/D in linked_area)
spawn(0)
D.open()
//Shakes cameras for mobs
/datum/shuttle/proc/after_flight()
for(var/atom/movable/AM in linked_area)
if(AM.anchored)
continue
if(istype(AM,/mob/living))
var/mob/living/M = AM
if(!M.locked_to)
shake_camera(M, 10, 1) // unbuckled, HOLY SHIT SHAKE THE ROOM
if(!src.stable)
if(istype(M, /mob/living/carbon))
M.Knockdown(3)
else
shake_camera(M, 3, 1) // buckled, not a lot of shaking
//Gibs or moves mobs and stuff
/datum/shuttle/proc/collide(var/atom/movable/AM as mob|obj)
AM.shuttle_act(src)
//This is awful
/datum/shuttle/proc/supercharge()
cooldown = 0
pre_flight_delay = 0
transit_delay = 0
//Like (input() in shuttles), but better
/proc/select_shuttle_from_all(var/mob/user, var/message = "Select a shuttle", var/title = "Shuttle selection", var/list/omit_shuttles = null, var/show_lockdown = 0, var/show_cooldown = 0)
if(!user)
return
var/list/shuttle_list = list()
for(var/datum/shuttle/S in shuttles)
if(omit_shuttles)
if(S.type in omit_shuttles)
continue
if(S in omit_shuttles)
continue
if(S.name in omit_shuttles)
continue
var/name = S.name
if(show_lockdown && S.lockdown)
name = "[name] (LOCKDOWN)"
else
if(show_cooldown && !S.can_move())
name = "[name] (ON COOLDOWN)"
shuttle_list += name
shuttle_list[name]=S
var/my_shuttle = input(usr, message, title) as null|anything in shuttle_list
if( my_shuttle && shuttle_list[my_shuttle] && istype(shuttle_list[my_shuttle], /datum/shuttle) )
return shuttle_list[my_shuttle]
/datum/shuttle/proc/move(var/mob/user) //a very simple proc which selects a random area and sends the shuttle there
var/list/possible_locations = list()
for(var/obj/docking_port/destination/S in src.docking_ports)
if(S == current_port)
continue
if(S.docked_with)
continue
possible_locations += S
if(!possible_locations.len)
return
var/obj/docking_port/destination/target = pick(possible_locations)
travel_to(target,,user)
/datum/shuttle/proc/get_occupants(var/find_stowaways)
var/list/occupants = list()
if(!find_stowaways)
for(var/mob/living/L in linked_area) //Yeah they could be hiding in lockers, but that's a stowaway not an occupant
occupants.Add(L)
else
for(var/mob/living/L in mob_list)
if(get_area(src) == linked_area)
occupants.Add(L)
return occupants
/proc/get_refill_area(var/obj/docking_port/destination/D)
if(ispath(D.refill_area))
return locate(D.refill_area)
else
return get_space_area()
//The proc that does most of the work
//RETURNS: 1 if everything is good, 0 if everything is bad
/datum/shuttle/proc/move_area_to(var/turf/our_center, var/turf/new_center, var/rotate = 0)
if(!our_center)
return
if(!new_center)
return
if((rotate % 90) != 0) //If not divisible by 90, make it
rotate += (rotate % 90)
var/datum/coords/our_center_coords = new(our_center.x,our_center.y)
var/datum/coords/new_center_coords = new(new_center.x,new_center.y)
var/datum/coords/offset = new_center_coords.subtract(our_center_coords)
//For displacing
var/throwy = world.maxy
var/obj/docking_port/destination/D = linked_port.docked_with
var/area/refill_area //the area that will be stamped over where the shuttle left
refill_area = get_refill_area(D)
if(!refill_area)
warning("Unable to find refill area for shuttle [src.type]")
//Make a list of coordinates of turfs to move, and associate the coordinates with the turfs they represent
var/list/turfs_to_move = list()
//Now here's the dumb part - since there's no fast way I know to check if a coord datum has a coord datum with the same values in a list,
//this coordinates list stores every coordinate of a moved turf as a string (example: "52;61").
var/list/our_own_turfs = list()
//Go through all turfs in our area
for(var/turf/T in linked_area.get_turfs())
var/datum/coords/C = new(T.x,T.y)
turfs_to_move += C
turfs_to_move[C] = T
our_own_turfs += "[T.x];[T.y];[T.z]"
var/cosine = cos(rotate)
var/sine = sin(rotate)
//Calculate new coordinates
var/list/new_turfs = list() //Coordinates of turfs that WILL be created
for(var/datum/coords/C in turfs_to_move)
var/datum/coords/new_coords = C.add(offset) //Get the coordinates of new turfs by adding offset
new_turfs += new_coords
new_turfs[new_coords] = C //Associate the old coordinates with the new ones for an easier time
if(rotate != 0)
//Oh god this works
var/newX = (cosine * (new_coords.x_pos - new_center.x)) + (sine * (new_coords.y_pos - new_center.y)) + new_center.x
var/newY = -(sine * (new_coords.x_pos - new_center.x)) + (cosine * (new_coords.y_pos - new_center.y)) + new_center.y
new_coords.x_pos = newX
new_coords.y_pos = newY
if(new_coords.y_pos < throwy)
throwy = new_coords.y_pos
var/area/A = get_area( locate(new_coords.x_pos, new_coords.y_pos, new_center.z) )
if(!A)
message_admins("<span class='notice'>WARNING: Unable to find an area at [new_coords.x_pos];[new_coords.y_pos];[new_center.z]. [src.name] ([src.type]) will not be moved.")
return
if(!destroy_everything && !A.shuttle_can_crush) //Breaking blueprint areas and space is fine, breaking the station is not. Breaking randomly generated vaults is fine, in case they spawn in a bad spot!
message_admins("<span class='notice'>WARNING: [src.name] ([src.type]) attempted to destroy [A] ([A.type]).</span> If you want [src.name] to be able to move freely and destroy areas, change its \"destroy_everything\" variable to 1.")
return
//If any of the new turfs are in the moved shuttle's current area, EMERGENCY ABORT (this leads to the shuttle destroying itself & potentially gibbing everybody inside)
if("[new_coords.x_pos];[new_coords.y_pos];[new_center.z]" in our_own_turfs)
warning("Shuttle ([src.name]; [src.type]) has attempted to move to a location which overlaps with its current position. Offending turf: [new_coords.x_pos];[new_coords.y_pos];[new_center.z]")
message_admins("WARNING: A shuttle ([src.name]; [src.type]) has attempted to move to a location which overlaps with its current position. The shuttle will not be moved.")
return
var/list/turfs_to_update = list()
//Move turfs
for(var/datum/coords/C in new_turfs)
//Get old turf type
var/datum/coords/old_C = new_turfs[C]
var/turf/old_turf = turfs_to_move[old_C]
var/turf/new_turf = locate(C.x_pos,C.y_pos,new_center.z)
var/add_underlay = 0
if(!old_turf)
message_admins("ERROR when moving [src.name] ([src.type]) - failed to get original turf at [old_C.x_pos];[old_C.y_pos];[our_center.z]")
continue
else if(old_turf.preserve_underlay == 0 && istype(old_turf,/turf/simulated/shuttle/wall)) //Varediting a turf's "preserve_underlay" to 1 will protect its underlay from being changed
if(old_turf.icon_state in transparent_icons)
add_underlay = 1
if(old_turf.underlays.len) //this list is in code/game/area/areas.dm
var/image/I = locate(/image) in old_turf.underlays //bandaid
if(I.icon == 'icons/turf/shuttle.dmi') //Don't change underlay to space if CURRENT underlay is a shuttle floor!
add_underlay = 0
if(!new_turf)
message_admins("ERROR when moving [src.name] ([src.type]) - failed to get new turf at [C.x_pos];[C.y_pos];[new_center.z]")
continue
var/turf/displace_to = locate(C.x_pos,throwy,new_center.z)
for(var/atom/movable/AM as mob|obj in new_turf.contents)
if(AM.anchored || src.collision_type == COLLISION_DESTROY)
src.collide(AM)
else
AM.forceMove(displace_to)
var/area/old_area = get_area(new_turf) //this is the area that is being replaced by shuttle area in the destination
if(!old_area)
old_area = get_space_area()
for(var/O in old_turf.overlays)
var/image/I = O
if(I.icon == 'icons/obj/projectiles.dmi')
old_turf.overlays.Remove(I) //remove beam overlays so they don't stay on the new turfs forever
//Get the turf's image before it's gone!
var/image/undlay
if(add_underlay)
undlay = image("icon"=new_turf.icon,"icon_state"=new_turf.icon_state,"dir"=new_turf.dir)
undlay.overlays = new_turf.overlays
//****Add the new turf to shuttle's area****
linked_area.contents.Add(new_turf)
new_turf.change_area(old_area,linked_area)
new_turf.ChangeTurf(old_turf.type, allow = 1)
new_turfs[C] = new_turf
//***Remove old turf from shuttle's area****
refill_area.contents.Add(old_turf)
old_turf.change_area(linked_area,refill_area)
//All objects which can't be moved by the shuttle have their area changed to refill_area!
for(var/atom/movable/AM in old_turf.contents)
if(!AM.can_shuttle_move(src))
AM.change_area(linked_area,refill_area)
if(old_turf.transform)
new_turf.transform = old_turf.transform
//****Prepare underlays**** (only do this if add_underlay is 1 -> see above)
if(add_underlay && undlay)
new_turf.underlays = list(undlay) //Remove all old underlays, add space
else
new_turf.underlays = old_turf.underlays
/*
if(ispath(replaced_turf_type,/turf/space))//including the transit hyperspace turfs
if(old_turf.underlays.len)
new_turf.underlays = old_turf.underlays
else
new_turf.underlays += undlay
else
new_turf.underlays += undlay*/
new_turf.dir = old_turf.dir
new_turf.icon_state = old_turf.icon_state
new_turf.icon = old_turf.icon
new_turf.plane = old_turf.plane
new_turf.layer = old_turf.layer
// Hack: transfer the ownership of old_turf's floor_tile to new_tile.
// Floor turfs create their `floor_tile` in New() if it's null.
// The better solution would be to not do that at all in New(), or use
// something like the map loader's atom preloader to transfer the
// floor_tile before New().
if(istype(old_turf, /turf/simulated/floor))
var/turf/simulated/floor/ancient = old_turf
var/turf/simulated/floor/modern = new_turf
modern.floor_tile = ancient.floor_tile
ancient.floor_tile = null
if(rotate)
new_turf.shuttle_rotate(rotate)
//*****Move air*****
var/turf/simulated/S_OLD = old_turf
if(istype(S_OLD) && S_OLD.zone)
var/turf/simulated/S_NEW = new_turf
if(!S_NEW.air)
S_NEW.make_air()
S_NEW.air.copy_from(S_OLD.zone.air)
S_OLD.zone.remove(S_OLD)
//*****Move objects and mobs*****
for(var/mob/M in old_turf) //mobs first
if(!M.can_shuttle_move(src))
continue
move_atom(M, new_turf, rotate)
for(var/atom/movable/AM in old_turf)
if(!AM.can_shuttle_move(src))
continue
move_atom(AM, new_turf, rotate)
//Move landmarks - for moving the arrivals shuttle
for(var/list/L in moved_landmarks) //moved_landmarks: code/game/area/areas.dm, 527 (above the move_contents_to proc)
if(old_turf in L)
L -= old_turf
L += new_turf
//Add the new turf to the list of turfs to update
turfs_to_update += new_turf
//Delete the old turf
var/replacing_turf_type = old_turf.get_underlying_turf()
if(D && istype(D))
replacing_turf_type = D.base_turf_type
old_turf.ChangeTurf(replacing_turf_type, allow = 1)
if(D && istype(D))
if(D.base_turf_icon)
old_turf.icon = D.base_turf_icon
if(D.base_turf_icon_state)
old_turf.icon_state = D.base_turf_icon_state
//Update doors
if(turfs_to_update.len)
for(var/turf/simulated/T1 in turfs_to_update)
for(var/obj/machinery/door/D2 in T1)
D2.update_nearby_tiles()
return 1
/datum/shuttle/proc/move_atom(var/atom/movable/AM, var/new_turf, var/rotate)
if(AM.bound_width > WORLD_ICON_SIZE || AM.bound_height > WORLD_ICON_SIZE) //If the moved object's bounding box is more than the default, move it after everything else (using spawn())
AM.forceMove(null) //Without this, ALL neighbouring turfs attempt to move this object too, resulting in the object getting shifted to north/east
spawn()
AM.forceMove(new_turf)
//TODO: Make this compactible with bound_x and bound_y.
else
AM.forceMove(new_turf)
if(rotate)
AM.shuttle_rotate(rotate)
/proc/setup_shuttles()
world.log << "Setting up all shuttles..."
var/all_count = 0
var/count = 0
for(var/datum/shuttle/S in shuttles)
switch(S.initialize())
if(INIT_NO_AREA)
if(S.is_special())
var/msg = S.linked_area ? "- \"[S.linked_area]\" was given as a starting area." : ""
warning("Invalid or missing starting area for [S.name] ([S.type]) [msg]")
else
var/msg = S.linked_area ? "- \"[S.linked_area]\" was given as a starting area." : ""
world.log << "Invalid or missing starting area for [S.name] ([S.type]) [msg]"
if(INIT_NO_PORT)
if(S.is_special())
warning("Couldn't find a shuttle docking port for [S.name] ([S.type]).")
else
world.log << "Couldn't find a shuttle docking port for [S.name] ([S.type])."
if(INIT_NO_START)
if(S.is_special())
warning("[S.name] ([S.type]) couldn't connect to a destination port on init - unless this is intended, there might be problems.")
else
world.log << "[S.name] ([S.type]) couldn't connect to a destination port on init - unless this is intended, there might be problems."
else
count++
all_count++
world.log << "[all_count] shuttles initialized, of them [count] were initialized properly."
//THE MOST IMPORTANT PIECE OF CODE HERE
emergency_shuttle.shuttle = escape_shuttle
if(!emergency_shuttle || !emergency_shuttle.shuttle)
warning("Emergency shuttle is broken.")
else
world.log << "Emergency shuttle has been successfully set up."
//Custom shuttles
/datum/shuttle/custom
name = "custom shuttle"
can_link_to_computer = LINK_FREE
/datum/shuttle/proc/show_outline(var/mob/user, var/turf/centered_at)
if(!user)
return
if(!centered_at)
var/turf/user_turf = get_turf(user)
if(!user_turf)
to_chat(user, "You must be standing on a turf!")
return
centered_at = get_step(user_turf,usr.dir)
var/turf/original_center = get_turf(linked_port)
if(!centered_at)
to_chat(user, "ERROR: Unable to find center turf!")
return
var/offsetX = centered_at.x - original_center.x
var/offsetY = centered_at.y - original_center.y
var/datum/coords/offset = new(offsetX,offsetY)
var/rotate = dir2angle(turn(user.dir,180)) - dir2angle(linked_port.dir)
var/list/original_coords = list()
for(var/turf/T in linked_area.get_turfs())
var/datum/coords/C = new(T.x,T.y)
original_coords += C
var/list/new_coords = list()
var/cosine = cos(rotate)
var/sine = sin(rotate)
for(var/datum/coords/C in original_coords)
var/datum/coords/NC = C.add(offset)
new_coords += NC
if(rotate)
var/newX = (cosine * (NC.x_pos - centered_at.x)) + (sine * (NC.y_pos - centered_at.y)) + centered_at.x
var/newY = -(sine * (NC.x_pos - centered_at.x)) + (cosine * (NC.y_pos - centered_at.y)) + centered_at.y
NC.x_pos = newX
NC.y_pos = newY
var/list/images = list()
for(var/datum/coords/C in new_coords)
var/turf/T = locate(C.x_pos,C.y_pos,centered_at.z)
if(!T)
continue
var/image/I = image('icons/turf/areas.dmi', icon_state="bluenew")
I.loc = T
images += I
user << I
var/image/center_img = image('icons/turf/areas.dmi', icon_state="blue") //This is actually RED, honk
center_img.loc = centered_at
images += center_img
user << center_img
alert(usr,"Press \"Ok\" to remove the images","Magic","Ok")
if(usr.client)
for(var/image/I in images)
usr.client.images -= I
return
#undef INIT_SUCCESS
#undef INIT_NO_AREA
#undef INIT_NO_PORT
#undef INIT_NO_START