diff --git a/aurorastation.dme b/aurorastation.dme index b608b3e2674..f1c09b12336 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -44,6 +44,7 @@ #include "code\__defines\mobs.dm" #include "code\__defines\modular_guns.dm" #include "code\__defines\obj.dm" +#include "code\__defines\overmap.dm" #include "code\__defines\psi.dm" #include "code\__defines\qdel.dm" #include "code\__defines\radio.dm" @@ -2209,6 +2210,21 @@ #include "code\modules\organs\subtypes\unbreakable.dm" #include "code\modules\organs\subtypes\vaurca.dm" #include "code\modules\organs\subtypes\xenos.dm" +#include "code\modules\overmap\_defines.dm" +#include "code\modules\overmap\overmap_object.dm" +#include "code\modules\overmap\overmap_shuttle.dm" +#include "code\modules\overmap\README.dm" +#include "code\modules\overmap\sectors.dm" +#include "code\modules\overmap\spacetravel.dm" +#include "code\modules\overmap\ships\landable.dm" +#include "code\modules\overmap\ships\ship.dm" +#include "code\modules\overmap\ships\computers\engine_control.dm" +#include "code\modules\overmap\ships\computers\helm.dm" +#include "code\modules\overmap\ships\computers\sensors.dm" +#include "code\modules\overmap\ships\computers\ship.dm" +#include "code\modules\overmap\ships\computers\shuttle.dm" +#include "code\modules\overmap\ships\engines\engine.dm" +#include "code\modules\overmap\ships\engines\gas_thruster.dm" #include "code\modules\paperwork\carbonpaper.dm" #include "code\modules\paperwork\clipboard.dm" #include "code\modules\paperwork\faxmachine.dm" @@ -2759,6 +2775,7 @@ #include "code\unit_tests\mob_tests.dm" #include "code\unit_tests\object_tests.dm" #include "code\unit_tests\observation_tests.dm" +#include "code\unit_tests\overmap_tests.dm" #include "code\unit_tests\spawner_tests.dm" #include "code\unit_tests\sql_tests.dm" #include "code\unit_tests\ss_test.dm" diff --git a/code/__defines/_macros.dm b/code/__defines/_macros.dm index e97ef70ad61..8366200602c 100644 --- a/code/__defines/_macros.dm +++ b/code/__defines/_macros.dm @@ -49,6 +49,8 @@ #define isobj(A) istype(A, /obj) +#define isspace(A) istype(A, /area/space) + #define isobserver(A) istype(A, /mob/abstract/observer) #define isorgan(A) istype(A, /obj/item/organ/external) @@ -88,4 +90,5 @@ #define isopenturf(target) istype(target, /turf/simulated/open) #define isweakref(target) istype(target, /datum/weakref) +#define isopenspace(A) istype(A, /turf/simulated/open) #define isdatum(target) istype(target, /datum) diff --git a/code/__defines/math_physics.dm b/code/__defines/math_physics.dm index 55eb4ecdea0..5779e845f64 100644 --- a/code/__defines/math_physics.dm +++ b/code/__defines/math_physics.dm @@ -44,3 +44,6 @@ #define round_duration_in_ticks (round_start_time ? world.time - round_start_time : 0) #define station_time_in_ticks (roundstart_hour HOURS + round_duration_in_ticks) + +#define SIMPLE_SIGN(X) ((X) < 0 ? -1 : 1) +#define SIGN(X) ((X) ? SIMPLE_SIGN(X) : 0) diff --git a/code/__defines/overmap.dm b/code/__defines/overmap.dm new file mode 100644 index 00000000000..06ab37a0580 --- /dev/null +++ b/code/__defines/overmap.dm @@ -0,0 +1,13 @@ +#define SHIP_SIZE_TINY 1 +#define SHIP_SIZE_SMALL 2 +#define SHIP_SIZE_LARGE 3 + +//multipliers for max_speed to find 'slow' and 'fast' speeds for the ship +#define SHIP_SPEED_SLOW 1/(40 SECONDS) +#define SHIP_SPEED_FAST 3/(20 SECONDS)// 15 speed + +#define OVERMAP_WEAKNESS_NONE 0 +#define OVERMAP_WEAKNESS_FIRE 1 +#define OVERMAP_WEAKNESS_EMP 2 +#define OVERMAP_WEAKNESS_MINING 4 +#define OVERMAP_WEAKNESS_EXPLOSIVE 8 \ No newline at end of file diff --git a/code/__defines/shuttle.dm b/code/__defines/shuttle.dm index a27b4e21d81..d6729701a32 100644 --- a/code/__defines/shuttle.dm +++ b/code/__defines/shuttle.dm @@ -5,6 +5,11 @@ #define SHUTTLE_FLAGS_ZERO_G 4 #define SHUTTLE_FLAGS_ALL (~SHUTTLE_FLAGS_NONE) +//Overmap landable shuttles. +#define SHIP_STATUS_LANDED 1 +#define SHIP_STATUS_TRANSIT 2 +#define SHIP_STATUS_OVERMAP 3 + //Landmarks. #define SLANDMARK_FLAG_AUTOSET 1 // If set, will set base area and turf type to same as where it was spawned at #define SLANDMARK_FLAG_ZERO_G 2 // Zero-G shuttles moved here will lose gravity unless the area has ambient gravity. diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm index 6dad2969e0d..6f1f9c60b2f 100644 --- a/code/_helpers/game.dm +++ b/code/_helpers/game.dm @@ -263,8 +263,6 @@ if (!objs[O]) objs[O] = TRUE -#define SIGN(X) ((X<0)?-1:1) - proc inLineOfSight(X1,Y1,X2,Y2,Z=1,PX1=16.5,PY1=16.5,PX2=16.5,PY2=16.5) var/turf/T @@ -295,7 +293,6 @@ proc if(T.opacity) return 0 return 1 -#undef SIGN proc/isInSight(var/atom/A, var/atom/B) var/turf/Aturf = get_turf(A) diff --git a/code/controllers/subsystems/processing/shuttle.dm b/code/controllers/subsystems/processing/shuttle.dm index 9fe5d7d5fa1..f623e32d563 100644 --- a/code/controllers/subsystems/processing/shuttle.dm +++ b/code/controllers/subsystems/processing/shuttle.dm @@ -7,6 +7,8 @@ var/datum/controller/subsystem/processing/shuttle/SSshuttle priority = SS_PRIORITY_SHUTTLE init_order = SS_INIT_MISC //Should be initialized after all maploading is over and atoms are initialized, to ensure that landmarks have been initialized. + var/overmap_halted = FALSE //Whether ships can move on the overmap; used for adminbus. + var/list/ships = list() //List of all ships. var/list/shuttles = list() //maps shuttle tags to shuttle datums, so that they can be looked up. var/list/process_shuttles = list() //simple list of shuttles, for processing var/list/registered_shuttle_landmarks = list() @@ -14,7 +16,10 @@ var/datum/controller/subsystem/processing/shuttle/SSshuttle var/list/docking_registry = list() //Docking controller tag -> docking controller program, mostly for init purposes. var/list/shuttle_areas = list() //All the areas of all shuttles. + var/list/landmarks_awaiting_sector = list() //Stores automatic landmarks that are waiting for a sector to finish loading. + var/list/landmarks_still_needed = list() //Stores landmark_tags that need to be assigned to the sector (landmark_tag = sector) when registered. var/list/shuttles_to_initialize = list() //A queue for shuttles to initialize at the appropriate time. + var/list/sectors_to_initialize //Used to find all sector objects at the appropriate time. var/block_queue = TRUE var/tmp/list/working_shuttles @@ -51,6 +56,7 @@ var/datum/controller/subsystem/processing/shuttle/SSshuttle if(block_queue) return initialize_shuttles() + initialize_sectors() /datum/controller/subsystem/processing/shuttle/proc/initialize_shuttles() var/list/shuttles_made = list() @@ -58,8 +64,14 @@ var/datum/controller/subsystem/processing/shuttle/SSshuttle var/shuttle = initialize_shuttle(shuttle_type) if(shuttle) shuttles_made += shuttle + hook_up_motherships(shuttles_made) shuttles_to_initialize = null +/datum/controller/subsystem/processing/shuttle/proc/initialize_sectors() + for(var/sector in sectors_to_initialize) + initialize_sector(sector) + sectors_to_initialize = null + /datum/controller/subsystem/processing/shuttle/proc/register_landmark(shuttle_landmark_tag, obj/effect/shuttle_landmark/shuttle_landmark) if (registered_shuttle_landmarks[shuttle_landmark_tag]) CRASH("Attempted to register shuttle landmark with tag [shuttle_landmark_tag], but it is already registered!") @@ -67,12 +79,78 @@ var/datum/controller/subsystem/processing/shuttle/SSshuttle registered_shuttle_landmarks[shuttle_landmark_tag] = shuttle_landmark last_landmark_registration_time = world.time + var/obj/effect/overmap/visitable/O = landmarks_still_needed[shuttle_landmark_tag] + if(O) //These need to be added to sectors, which we handle. + try_add_landmark_tag(shuttle_landmark_tag, O) + landmarks_still_needed -= shuttle_landmark_tag + else if(istype(shuttle_landmark, /obj/effect/shuttle_landmark/automatic)) //These find their sector automatically + O = map_sectors["[shuttle_landmark.z]"] + if(O) + O.add_landmark(shuttle_landmark, shuttle_landmark.shuttle_restricted) + else + landmarks_awaiting_sector += shuttle_landmark + /datum/controller/subsystem/processing/shuttle/proc/get_landmark(var/shuttle_landmark_tag) return registered_shuttle_landmarks[shuttle_landmark_tag] +//Checks if the given sector's landmarks have initialized; if so, registers them with the sector, if not, marks them for assignment after they come in. +//Also adds automatic landmarks that were waiting on their sector to spawn. +/datum/controller/subsystem/processing/shuttle/proc/initialize_sector(obj/effect/overmap/visitable/given_sector) + given_sector.populate_sector_objects() // This is a late init operation that sets up the sector's map_z and does non-overmap-related init tasks. + + for(var/landmark_tag in given_sector.initial_generic_waypoints) + if(!try_add_landmark_tag(landmark_tag, given_sector)) + landmarks_still_needed[landmark_tag] = given_sector + + for(var/shuttle_name in given_sector.initial_restricted_waypoints) + for(var/landmark_tag in given_sector.initial_restricted_waypoints[shuttle_name]) + if(!try_add_landmark_tag(landmark_tag, given_sector)) + landmarks_still_needed[landmark_tag] = given_sector + + var/landmarks_to_check = landmarks_awaiting_sector.Copy() + for(var/thing in landmarks_to_check) + var/obj/effect/shuttle_landmark/automatic/landmark = thing + if(landmark.z in given_sector.map_z) + given_sector.add_landmark(landmark, landmark.shuttle_restricted) + landmarks_awaiting_sector -= landmark + +/datum/controller/subsystem/processing/shuttle/proc/try_add_landmark_tag(landmark_tag, obj/effect/overmap/visitable/given_sector) + var/obj/effect/shuttle_landmark/landmark = get_landmark(landmark_tag) + if(!landmark) + return FALSE + + if(landmark.landmark_tag in given_sector.initial_generic_waypoints) + given_sector.add_landmark(landmark) + . = TRUE + for(var/shuttle_name in given_sector.initial_restricted_waypoints) + if(landmark.landmark_tag in given_sector.initial_restricted_waypoints[shuttle_name]) + given_sector.add_landmark(landmark, shuttle_name) + . = TRUE + /datum/controller/subsystem/processing/shuttle/proc/initialize_shuttle(var/shuttle_type) var/datum/shuttle/shuttle = shuttle_type if(initial(shuttle.category) != shuttle_type) shuttle = new shuttle() shuttle_areas |= shuttle.shuttle_area return shuttle + +/datum/controller/subsystem/processing/shuttle/proc/hook_up_motherships(shuttles_list) + for(var/datum/shuttle/S in shuttles_list) + if(S.mothershuttle && !S.motherdock) + var/datum/shuttle/mothership = shuttles[S.mothershuttle] + if(mothership) + S.motherdock = S.current_location.landmark_tag + mothership.shuttle_area |= S.shuttle_area + else + error("Shuttle [S] was unable to find mothership [mothership]!") + +/datum/controller/subsystem/processing/shuttle/proc/toggle_overmap(new_setting) + if(overmap_halted == new_setting) + return + overmap_halted = !overmap_halted + for(var/ship in ships) + var/obj/effect/overmap/visitable/ship/ship_effect = ship + overmap_halted ? ship_effect.halt() : ship_effect.unhalt() + +/datum/controller/subsystem/processing/shuttle/stat_entry() + ..("Shuttles:[shuttles.len], Ships:[ships.len], L:[registered_shuttle_landmarks.len][overmap_halted ? ", HALT" : ""]") diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 8940c29a571..8f874b29378 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -212,6 +212,10 @@ if(z in current_map.sealed_levels) return + if(current_map.use_overmap) + overmap_spacetravel(get_turf(src), src) + return + var/move_to_z = src.get_transit_zlevel() if(move_to_z) z = move_to_z diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index dcee2df81e6..2cf75d9ff4b 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -306,3 +306,7 @@ else if(integrity < 3) integrity++ + +/obj/item/tank/proc/remove_air_by_flag(flag, amount) + . = air_contents.remove_by_flag(flag, amount) + update_icon() \ No newline at end of file diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 6ca0b1ac7c2..d571f8b477a 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -260,6 +260,7 @@ /mob/proc/reset_view(atom/A) if (client) + A = A ? A : eyeobj if (istype(A, /atom/movable)) client.perspective = EYE_PERSPECTIVE client.eye = A diff --git a/code/modules/overmap/README.dm b/code/modules/overmap/README.dm new file mode 100644 index 00000000000..97d8272ccf4 --- /dev/null +++ b/code/modules/overmap/README.dm @@ -0,0 +1,87 @@ +/* +The overmap system allows adding new maps to the big 'galaxy' map. +There's overmap zlevel, that looks like a map. On it, token objects (overmap objects) are moved, representing ship movement etc. +No actual turfs are moved, you would need exploration shuttles or teleports to move atoms between different sectors/ships. +Unless stated otherwise, you just need to place any of things below somewhere on the map and they'll handle the rest. + +************************************************************* +# How to make new sector +************************************************************* +0. Map whatever. +1. Make /obj/effect/overmap/visitable/sector/[whatever] + If you want explorations shuttles be able to dock here, remember to set waypoints lists +2. Put /obj/effect/overmap/visitable/sector/[whatever] on the map. Even if it's multiz, only one is needed, on any z. +3. Done. + +************************************************************* +# How to make new ship +************************************************************* +0. Map whatever. +1. Make /obj/effect/overmap/visitable/ship/[whatever] + If you want explorations shuttles be able to dock here, remember to set waypoints lists +2. Put /obj/effect/overmap/visitable/ship/[whatever] on the map. If it's multiz, only one is needed, on any z. +3. Put Helm Console anywhere on the map. +4. Put Engines Control Console anywhere on the map. +5. Put some engines hooked up to gas supply anywhere on the map. +6. Done. + +************************************************************* +# Overmap object +************************************************************* +/obj/effect/overmap/visitable +### WHAT IT DOES +Lets overmap know this place should be represented on the map as a sector/ship. +If this zlevel (or any of connected ones for multiz) doesn't have this object, you won't be able to travel there by ovemap means. +### HOW TO USE +1. Create subtype for your ship/sector. Use /ship one for ships. +2. Put it anywhere on the ship/sector map. It will do the rest on its own during init. +If your thing is multiz, only one is needed per multiz sector/ship. + +If it's player's main base (e.g Exodus), set 'base' var to 1, so it adds itself to station_levels list. +If this place cannot be reached or left with EVA, set 'in_space' var to 0 +If you want exploration shuttles (look below) to be able to dock here, set up waypoints lists. +generic_waypoints is list of landmark_tags of waypoints any shttle should be able to visit. +restricted_waypoints is list of 'shuttle name = list(landmark_tags)' pairs for waypoints only those shuttles can visit + +************************************************************* +# Helm console +************************************************************* +/obj/machinery/computer/ship/helm +### WHAT IT DOES +Lets you steer ship around on overmap. +Lets you use autopilot. +### HOW TO USE +Just place it anywhere on the ship. + +************************************************************* +# Engines control console +************************************************************* +/obj/machinery/computer/ship/engines +### WHAT IT DOES +Lets use set thrust limits for engines of your ship. +Lets you shutdown/restart the engines. +Lets you check status of engines. +### HOW TO USE +Just place it anywhere on the ship. + +************************************************************* +# Thermal engines +************************************************************* +/obj/machinery/atmospherics/unary/engine +### WHAT IT DOES +Lets your ship move on the map at all. +### HOW TO USE +Put them on map, hook up to pipes with any gas. Heavier gas (CO2/plasma) + More pressure = more thrust. + +************************************************************* +# Exploration shuttle terminal +************************************************************* +/obj/machinery/computer/shuttle_control/explore +### WHAT IT DOES +Lets you control shuttles that can change destinations and visit other sectors/ships. +### HOW TO USE +1. Define starting shuttle landmark. +2. Define a /datum/shuttle/autodock/overmap for your shuttle. Same as normal shuttle, aside from 'range' var - how many squares on overmap it can travel on its own. +3. Place console anywhere on the ship/sector. Set shuttle_tag to shuttle's name. +4. Use. You can select destinations if you're in range (on same tile by defualt) on the map and sector has waypoints lists defined +*/ \ No newline at end of file diff --git a/code/modules/overmap/_defines.dm b/code/modules/overmap/_defines.dm new file mode 100644 index 00000000000..30c69c318f6 --- /dev/null +++ b/code/modules/overmap/_defines.dm @@ -0,0 +1,79 @@ +//How far from the edge of overmap zlevel could randomly placed objects spawn +#define OVERMAP_EDGE 2 +//Dimension of overmap (squares 4 lyfe) +var/global/list/map_sectors = list() + +/area/overmap/ + name = "System Map" + icon_state = "start" + requires_power = 0 + base_turf = /turf/unsimulated/map + +/turf/unsimulated/map + icon = 'icons/turf/space.dmi' + icon_state = "map" + permit_ao = FALSE + +/turf/unsimulated/map/edge + opacity = 1 + density = 1 + +/turf/unsimulated/map/Initialize(mapload) + . = ..() + name = "[x]-[y]" + var/list/numbers = list() + + if(x == 1 || x == current_map.overmap_size) + numbers += list("[round(y/10)]","[round(y%10)]") + if(y == 1 || y == current_map.overmap_size) + numbers += "-" + if(y == 1 || y == current_map.overmap_size) + numbers += list("[round(x/10)]","[round(x%10)]") + + for(var/i = 1 to numbers.len) + var/image/I = image('icons/effects/numbers.dmi',numbers[i]) + I.pixel_x = 5*i - 2 + I.pixel_y = world.icon_size/2 - 3 + if(y == 1) + I.pixel_y = 3 + I.pixel_x = 5*i + 4 + if(y == current_map.overmap_size) + I.pixel_y = world.icon_size - 9 + I.pixel_x = 5*i + 4 + if(x == 1) + I.pixel_x = 5*i - 2 + if(x == current_map.overmap_size) + I.pixel_x = 5*i + 2 + overlays += I + +//list used to track which zlevels are being 'moved' by the proc below +var/list/moving_levels = list() +//Proc to 'move' stars in spess +//yes it looks ugly, but it should only fire when state actually change. +//null direction stops movement +proc/toggle_move_stars(zlevel, direction) + if(!zlevel) + return + + var/gen_dir = null + if(direction & (NORTH|SOUTH)) + gen_dir += "ns" + else if(direction & (EAST|WEST)) + gen_dir += "ew" + if(!direction) + gen_dir = null + + if (moving_levels["[zlevel]"] != gen_dir) + moving_levels["[zlevel]"] = gen_dir + + var/list/spaceturfs = block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel)) + for(var/turf/space/T in spaceturfs) + if(!gen_dir) + T.icon_state = "white" + else + T.icon_state = "speedspace_[gen_dir]_[rand(1,15)]" + for(var/atom/movable/AM in T) + if (AM.simulated && !AM.anchored) + AM.throw_at(get_step(T,reverse_direction(direction)), 5, 1) + CHECK_TICK + CHECK_TICK diff --git a/code/modules/overmap/overmap_object.dm b/code/modules/overmap/overmap_object.dm new file mode 100644 index 00000000000..b9be6472e6e --- /dev/null +++ b/code/modules/overmap/overmap_object.dm @@ -0,0 +1,29 @@ +/obj/effect/overmap + name = "map object" + icon = 'icons/obj/overmap.dmi' + icon_state = "object" + color = "#fffffe" + + var/known = 1 //shows up on nav computers automatically + var/scannable //if set to TRUE will show up on ship sensors for detailed scans + +//Overlay of how this object should look on other skyboxes +/obj/effect/overmap/proc/get_skybox_representation() + return + +/obj/effect/overmap/proc/get_scan_data(mob/user) + return desc + +/obj/effect/overmap/Initialize() + . = ..() + if(!current_map.use_overmap) + return INITIALIZE_HINT_QDEL + + if(known) + layer = EFFECTS_ABOVE_LIGHTING_LAYER + for(var/obj/machinery/computer/ship/helm/H in SSmachinery.all_machines) + H.get_known_sectors() + update_icon() + +/obj/effect/overmap/update_icon() + filters = filter(type="drop_shadow", color = color + "F0", size = 2, offset = 1, x = 0, y = 0) diff --git a/code/modules/overmap/overmap_shuttle.dm b/code/modules/overmap/overmap_shuttle.dm new file mode 100644 index 00000000000..5a514dcc62a --- /dev/null +++ b/code/modules/overmap/overmap_shuttle.dm @@ -0,0 +1,159 @@ +#define waypoint_sector(waypoint) map_sectors["[waypoint.z]"] + +/datum/shuttle/autodock/overmap + warmup_time = 10 + + var/range = 0 //how many overmap tiles can shuttle go, for picking destinations and returning. + var/fuel_consumption = 0 //Amount of moles of gas consumed per trip; If zero, then shuttle is magic and does not need fuel + var/list/obj/structure/fuel_port/fuel_ports //the fuel ports of the shuttle (but usually just one) + + category = /datum/shuttle/autodock/overmap + +/datum/shuttle/autodock/overmap/New(var/_name, var/obj/effect/shuttle_landmark/start_waypoint) + ..(_name, start_waypoint) + refresh_fuel_ports_list() + +/datum/shuttle/autodock/overmap/proc/refresh_fuel_ports_list() //loop through all + fuel_ports = list() + for(var/area/A in shuttle_area) + for(var/obj/structure/fuel_port/fuel_port_in_area in A) + fuel_port_in_area.parent_shuttle = src + fuel_ports += fuel_port_in_area + +/datum/shuttle/autodock/overmap/fuel_check() + if(!src.try_consume_fuel()) //insufficient fuel + for(var/area/A in shuttle_area) + for(var/mob/living/M in A) + M.show_message(SPAN_WARNING("You hear the shuttle engines sputter... perhaps it doesn't have enough fuel?"), 2, + SPAN_WARNING("The shuttle shakes but fails to take off."), 1) + return 0 //failure! + return 1 //sucess, continue with launch + +/datum/shuttle/autodock/overmap/proc/can_go() + if(!next_location) + return FALSE + if(moving_status == SHUTTLE_INTRANSIT) + return FALSE //already going somewhere, current_location may be an intransit location instead of in a sector + return get_dist(waypoint_sector(current_location), waypoint_sector(next_location)) <= range + +/datum/shuttle/autodock/overmap/can_launch() + return ..() && can_go() + +/datum/shuttle/autodock/overmap/can_force() + return ..() && can_go() + +/datum/shuttle/autodock/overmap/get_travel_time() + var/distance_mod = get_dist(waypoint_sector(current_location),waypoint_sector(next_location)) + return move_time * (1 + distance_mod) + +/datum/shuttle/autodock/overmap/proc/set_destination(var/obj/effect/shuttle_landmark/A) + if(A != current_location) + next_location = A + +/datum/shuttle/autodock/overmap/proc/get_possible_destinations() + var/list/res = list() + for (var/obj/effect/overmap/visitable/S in range(get_turf(waypoint_sector(current_location)), range)) + var/list/waypoints = S.get_waypoints(name) + for(var/obj/effect/shuttle_landmark/LZ in waypoints) + if(LZ.is_valid(src)) + res["[waypoints[LZ]] - [LZ.name]"] = LZ + return res + +/datum/shuttle/autodock/overmap/get_location_name() + if(moving_status == SHUTTLE_INTRANSIT) + return "In transit" + return "[waypoint_sector(current_location)] - [current_location]" + +/datum/shuttle/autodock/overmap/get_destination_name() + if(!next_location) + return "None" + return "[waypoint_sector(next_location)] - [next_location]" + +/datum/shuttle/autodock/overmap/proc/try_consume_fuel() //returns 1 if sucessful, returns 0 if error (like insufficient fuel) + if(!fuel_consumption) + return 1 //shuttles with zero fuel consumption are magic and can always launch + if(!fuel_ports.len) + return 0 //Nowhere to get fuel from + var/list/obj/item/tank/fuel_tanks = list() + for(var/obj/structure/FP in fuel_ports) //loop through fuel ports and assemble list of all fuel tanks + var/obj/item/tank/FT = locate() in FP + if(FT) + fuel_tanks += FT + if(!fuel_tanks.len) + return 0 //can't launch if you have no fuel TANKS in the ports + var/total_flammable_gas_moles = 0 + for(var/obj/item/tank/FT in fuel_tanks) + total_flammable_gas_moles += FT.air_contents.get_by_flag(XGM_GAS_FUEL) + if(total_flammable_gas_moles < fuel_consumption) //not enough fuel + return 0 + // We are going to succeed if we got to here, so start consuming that fuel + var/fuel_to_consume = fuel_consumption + for(var/obj/item/tank/FT in fuel_tanks) //loop through tanks, consume their fuel one by one + var/fuel_available = FT.air_contents.get_by_flag(XGM_GAS_FUEL) + if(!fuel_available) // Didn't even have fuel. + continue + if(fuel_available >= fuel_to_consume) + FT.remove_air_by_flag(XGM_GAS_FUEL, fuel_to_consume) + return 1 //ALL REQUIRED FUEL HAS BEEN CONSUMED, GO FOR LAUNCH! + else //this tank doesn't have enough to launch shuttle by itself, so remove all its fuel, then continue loop + fuel_to_consume -= fuel_available + FT.remove_air_by_flag(XGM_GAS_FUEL, fuel_available) + +/obj/structure/fuel_port + name = "fuel port" + desc = "The fuel input port of the shuttle. Holds one fuel tank. Use a crowbar to open and close it." + icon = 'icons/turf/shuttle.dmi' + icon_state = "fuel_port" + density = 0 + anchored = 1 + var/icon_closed = "fuel_port" + var/icon_empty = "fuel_port_empty" + var/icon_full = "fuel_port_full" + var/opened = 0 + var/parent_shuttle + +/obj/structure/fuel_port/Initialize() + . = ..() + new /obj/item/tank/phoron(src) + +/obj/structure/fuel_port/attack_hand(mob/user) + if(!opened) + to_chat(user, SPAN_WARNING("\The [src] is secured tightly. You'll need to pry it open with a crowbar.")) + return + else if(contents.len > 0) + user.put_in_hands(contents[1]) + update_icon() + +/obj/structure/fuel_port/update_icon() + . = ..() + if(opened) + if(contents.len > 0) + icon_state = icon_full + else + icon_state = icon_empty + else + icon_state = icon_closed + +/obj/structure/fuel_port/attackby(obj/item/W, mob/user) + if(iscrowbar(W)) + if(opened) + to_chat(user, SPAN_NOTICE("You close \the [src].")) + playsound(src.loc, 'sound/effects/closet_close.ogg', 25, 0, -3) + opened = 0 + else + to_chat(user, SPAN_NOTICE("You pry \the [src] open.")) + playsound(src.loc, 'sound/effects/closet_open.ogg', 15, 1, -3) + opened = 1 + else if(istype(W,/obj/item/tank)) + if(!opened) + to_chat(user, SPAN_NOTICE("\The [src] isn't open!")) + return + if(contents.len == 0) + user.unEquip(W, src) + update_icon() + +// Walls hide stuff inside them, but we want to be visible. +/obj/structure/fuel_port/hide() + return + +#undef waypoint_sector \ No newline at end of file diff --git a/code/modules/overmap/sectors.dm b/code/modules/overmap/sectors.dm new file mode 100644 index 00000000000..e09d56cf5e2 --- /dev/null +++ b/code/modules/overmap/sectors.dm @@ -0,0 +1,133 @@ +//=================================================================================== +//Overmap object representing zlevel(s) +//=================================================================================== +var/global/area/overmap/map_overmap // Global object used to locate the overmap area. + +/obj/effect/overmap/visitable + name = "map object" + scannable = TRUE + + var/list/map_z = list() + + var/list/initial_generic_waypoints //store landmark_tag of landmarks that should be added to the actual lists below on init. + var/list/initial_restricted_waypoints //For use with non-automatic landmarks (automatic ones add themselves). + + var/list/generic_waypoints = list() //waypoints that any shuttle can use + var/list/restricted_waypoints = list() //waypoints for specific shuttles + + var/start_x //Coordinates for self placing + var/start_y //will use random values if unset + + var/base = 0 //starting sector, counts as station_levels + var/in_space = 1 //can be accessed via lucky EVA + + var/has_distress_beacon + +/obj/effect/overmap/visitable/Initialize() + . = ..() + if(. == INITIALIZE_HINT_QDEL) + return + + find_z_levels() // This populates map_z and assigns z levels to the ship. + register_z_levels() // This makes external calls to update global z level information. + + if(!current_map.overmap_z) + build_overmap() + + start_x = start_x || rand(OVERMAP_EDGE, current_map.overmap_size - OVERMAP_EDGE) + start_y = start_y || rand(OVERMAP_EDGE, current_map.overmap_size - OVERMAP_EDGE) + + forceMove(locate(start_x, start_y, current_map.overmap_z)) + + testing("Located sector \"[name]\" at [start_x],[start_y], containing Z [english_list(map_z)]") + + LAZYADD(SSshuttle.sectors_to_initialize, src) //Queued for further init. Will populate the waypoint lists; waypoints not spawned yet will be added in as they spawn. + SSshuttle.clear_init_queue() + +//This is called later in the init order by SSshuttle to populate sector objects. Importantly for subtypes, shuttles will be created by then. +/obj/effect/overmap/visitable/proc/populate_sector_objects() + +/obj/effect/overmap/visitable/proc/get_areas() + return get_filtered_areas(list(/proc/area_belongs_to_zlevels = map_z)) + +/obj/effect/overmap/visitable/proc/find_z_levels() + map_z = GetConnectedZlevels(z) + +/obj/effect/overmap/visitable/proc/register_z_levels() + for(var/zlevel in map_z) + map_sectors["[zlevel]"] = src + + current_map.player_levels |= map_z + if(!in_space) + current_map.sealed_levels |= map_z + if(base) + current_map.station_levels |= map_z + current_map.contact_levels |= map_z + current_map.map_levels |= map_z + +//Helper for init. +/obj/effect/overmap/visitable/proc/check_ownership(obj/object) + if((object.z in map_z) && !(get_area(object) in SSshuttle.shuttle_areas)) + return 1 + +//If shuttle_name is false, will add to generic waypoints; otherwise will add to restricted. Does not do checks. +/obj/effect/overmap/visitable/proc/add_landmark(obj/effect/shuttle_landmark/landmark, shuttle_name) + landmark.sector_set(src, shuttle_name) + if(shuttle_name) + LAZYADD(restricted_waypoints[shuttle_name], landmark) + else + generic_waypoints += landmark + +/obj/effect/overmap/visitable/proc/remove_landmark(obj/effect/shuttle_landmark/landmark, shuttle_name) + if(shuttle_name) + var/list/shuttles = restricted_waypoints[shuttle_name] + LAZYREMOVE(shuttles, landmark) + else + generic_waypoints -= landmark + +/obj/effect/overmap/visitable/proc/get_waypoints(var/shuttle_name) + . = list() + for(var/obj/effect/overmap/visitable/contained in src) + . += contained.get_waypoints(shuttle_name) + for(var/thing in generic_waypoints) + .[thing] = name + if(shuttle_name in restricted_waypoints) + for(var/thing in restricted_waypoints[shuttle_name]) + .[thing] = name + +/obj/effect/overmap/visitable/proc/generate_skybox() + return + +/obj/effect/overmap/visitable/sector + name = "generic sector" + desc = "Sector with some stuff in it." + icon_state = "sector" + anchored = 1 + +// Because of the way these are spawned, they will potentially have their invisibility adjusted by the turfs they are mapped on +// prior to being moved to the overmap. This blocks that. Use set_invisibility to adjust invisibility as needed instead. +/obj/effect/overmap/visitable/sector/hide() + +/proc/build_overmap() + if(!current_map.use_overmap) + return 1 + + testing("Building overmap...") + world.maxz++ + current_map.overmap_z = world.maxz + + testing("Putting overmap on [current_map.overmap_z]") + var/area/overmap/A = new + global.map_overmap = A + for (var/square in block(locate(1,1,current_map.overmap_z), locate(current_map.overmap_size,current_map.overmap_size,current_map.overmap_z))) + var/turf/T = square + if(T.x == current_map.overmap_size || T.y == current_map.overmap_size) + T = T.ChangeTurf(/turf/unsimulated/map/edge) + else + T = T.ChangeTurf(/turf/unsimulated/map) + ChangeArea(T, A) + + current_map.sealed_levels |= current_map.overmap_z + + testing("Overmap build complete.") + return 1 diff --git a/code/modules/overmap/ships/computers/engine_control.dm b/code/modules/overmap/ships/computers/engine_control.dm new file mode 100644 index 00000000000..bf0dc4a417a --- /dev/null +++ b/code/modules/overmap/ships/computers/engine_control.dm @@ -0,0 +1,94 @@ +//Engine control and monitoring console + +/obj/machinery/computer/ship/engines + name = "engine control console" + icon_screen = "enginecontrol" + var/display_state = "status" + +/obj/machinery/computer/ship/engines/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(!linked) + display_reconnect_dialog(user, "ship control systems") + return + + var/data[0] + data["state"] = display_state + data["global_state"] = linked.engines_state + data["global_limit"] = round(linked.thrust_limit*100) + var/total_thrust = 0 + + var/list/enginfo[0] + for(var/datum/ship_engine/E in linked.engines) + var/list/rdata[0] + rdata["eng_type"] = E.name + rdata["eng_on"] = E.is_on() + rdata["eng_thrust"] = E.get_thrust() + rdata["eng_thrust_limiter"] = round(E.get_thrust_limit()*100) + rdata["eng_status"] = E.get_status() + rdata["eng_reference"] = "\ref[E]" + total_thrust += E.get_thrust() + enginfo.Add(list(rdata)) + + data["engines_info"] = enginfo + data["total_thrust"] = total_thrust + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "engines_control.tmpl", "[linked.name] Engines Control", 390, 530) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/ship/engines/Topic(href, href_list) + if(..()) + return TOPIC_HANDLED + + if(href_list["state"]) + display_state = href_list["state"] + return TOPIC_REFRESH + + if(href_list["global_toggle"]) + linked.engines_state = !linked.engines_state + for(var/datum/ship_engine/E in linked.engines) + if(linked.engines_state == !E.is_on()) + E.toggle() + return TOPIC_REFRESH + + if(href_list["set_global_limit"]) + var/newlim = input("Input new thrust limit (0..100%)", "Thrust limit", linked.thrust_limit*100) as num + if(!CanInteract(usr, physical_state)) + return TOPIC_NOACTION + linked.thrust_limit = Clamp(newlim/100, 0, 1) + for(var/datum/ship_engine/E in linked.engines) + E.set_thrust_limit(linked.thrust_limit) + return TOPIC_REFRESH + + if(href_list["global_limit"]) + linked.thrust_limit = Clamp(linked.thrust_limit + text2num(href_list["global_limit"]), 0, 1) + for(var/datum/ship_engine/E in linked.engines) + E.set_thrust_limit(linked.thrust_limit) + return TOPIC_REFRESH + + if(href_list["engine"]) + if(href_list["set_limit"]) + var/datum/ship_engine/E = locate(href_list["engine"]) + var/newlim = input("Input new thrust limit (0..100)", "Thrust limit", E.get_thrust_limit()) as num + if(!CanInteract(usr, physical_state)) + return + var/limit = Clamp(newlim/100, 0, 1) + if(istype(E)) + E.set_thrust_limit(limit) + return TOPIC_REFRESH + if(href_list["limit"]) + var/datum/ship_engine/E = locate(href_list["engine"]) + var/limit = Clamp(E.get_thrust_limit() + text2num(href_list["limit"]), 0, 1) + if(istype(E)) + E.set_thrust_limit(limit) + return TOPIC_REFRESH + + if(href_list["toggle"]) + var/datum/ship_engine/E = locate(href_list["engine"]) + if(istype(E)) + E.toggle() + return TOPIC_REFRESH + return TOPIC_REFRESH + return TOPIC_NOACTION \ No newline at end of file diff --git a/code/modules/overmap/ships/computers/helm.dm b/code/modules/overmap/ships/computers/helm.dm new file mode 100644 index 00000000000..e1e86b2554f --- /dev/null +++ b/code/modules/overmap/ships/computers/helm.dm @@ -0,0 +1,263 @@ +/datum/computer_file/data/waypoint + var/list/fields = list() + +/obj/machinery/computer/ship/helm + name = "helm control console" + icon_screen = "helm" + light_color = "#7faaff" + var/autopilot = 0 + var/list/known_sectors = list() + var/dx //desitnation + var/dy //coordinates + var/speedlimit = 1/(20 SECONDS) //top speed for autopilot, 5 + var/accellimit = 0.001 //manual limiter for acceleration + +/obj/machinery/computer/ship/helm/Initialize() + . = ..() + get_known_sectors() + +/obj/machinery/computer/ship/helm/proc/get_known_sectors() + var/area/overmap/map = global.map_overmap + if(!map) + return + for(var/obj/effect/overmap/visitable/sector/S in map) + if (S.known) + var/datum/computer_file/data/waypoint/R = new() + R.fields["name"] = S.name + R.fields["x"] = S.x + R.fields["y"] = S.y + known_sectors[S.name] = R + +/obj/machinery/computer/ship/helm/process() + ..() + if (autopilot && dx && dy) + var/turf/T = locate(dx,dy,current_map.overmap_z) + if(linked.loc == T) + if(linked.is_still()) + autopilot = 0 + else + linked.decelerate() + else + var/brake_path = linked.get_brake_path() + var/direction = get_dir(linked.loc, T) + var/acceleration = min(linked.get_acceleration(), accellimit) + var/speed = linked.get_speed() + var/heading = linked.get_heading() + + // Destination is current grid or speedlimit is exceeded + if ((get_dist(linked.loc, T) <= brake_path) || speed > speedlimit) + linked.decelerate() + // Heading does not match direction + else if (heading & ~direction) + linked.accelerate(turn(heading & ~direction, 180), accellimit) + // All other cases, move toward direction + else if (speed + acceleration <= speedlimit) + linked.accelerate(direction, accellimit) + return + +/obj/machinery/computer/ship/helm/relaymove(var/mob/user, direction) + if(viewing_overmap(user) && linked) + linked.relaymove(user, direction, accellimit) + return 1 + +/obj/machinery/computer/ship/helm/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + var/data[0] + + if(!linked) + display_reconnect_dialog(user, "helm") + else + var/turf/T = get_turf(linked) + var/obj/effect/overmap/visitable/sector/current_sector = locate() in T + + data["sector"] = current_sector ? current_sector.name : "Deep Space" + data["sector_info"] = current_sector ? current_sector.desc : "Not Available" + data["landed"] = linked.get_landed_info() + data["s_x"] = linked.x + data["s_y"] = linked.y + data["dest"] = dy && dx + data["d_x"] = dx + data["d_y"] = dy + data["speedlimit"] = speedlimit ? speedlimit*1000 : "Halted" + data["accel"] = min(round(linked.get_acceleration()*1000, 0.01),accellimit*1000) + data["heading"] = linked.get_heading() ? dir2angle(linked.get_heading()) : 0 + data["autopilot"] = autopilot + data["manual_control"] = viewing_overmap(user) + data["canburn"] = linked.can_burn() + data["accellimit"] = accellimit*1000 + + var/speed = round(linked.get_speed()*1000, 0.01) + if(linked.get_speed() < SHIP_SPEED_SLOW) + speed = "[speed]" + if(linked.get_speed() > SHIP_SPEED_FAST) + speed = "[speed]" + data["speed"] = speed + + if(linked.get_speed()) + data["ETAnext"] = "[round(linked.ETA()/10)] seconds" + else + data["ETAnext"] = "N/A" + + var/list/locations[0] + for (var/key in known_sectors) + var/datum/computer_file/data/waypoint/R = known_sectors[key] + var/list/rdata[0] + rdata["name"] = R.fields["name"] + rdata["x"] = R.fields["x"] + rdata["y"] = R.fields["y"] + rdata["reference"] = "\ref[R]" + locations.Add(list(rdata)) + + data["locations"] = locations + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "helm.tmpl", "[linked.name] Helm Control", 565, 545) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/ship/helm/Topic(href, href_list) + if(..()) + return TOPIC_HANDLED + + if(!linked) + return TOPIC_HANDLED + + if (href_list["add"]) + var/datum/computer_file/data/waypoint/R = new() + var/sec_name = input("Input naviation entry name", "New navigation entry", "Sector #[known_sectors.len]") as text + if(!CanInteract(usr, physical_state)) + return TOPIC_NOACTION + if(!sec_name) + sec_name = "Sector #[known_sectors.len]" + R.fields["name"] = sec_name + if(sec_name in known_sectors) + to_chat(usr, "Sector with that name already exists, please input a different name.") + return TOPIC_REFRESH + switch(href_list["add"]) + if("current") + R.fields["x"] = linked.x + R.fields["y"] = linked.y + if("new") + var/newx = input("Input new entry x coordinate", "Coordinate input", linked.x) as num + if(!CanInteract(usr, physical_state)) + return TOPIC_REFRESH + var/newy = input("Input new entry y coordinate", "Coordinate input", linked.y) as num + if(!CanInteract(usr, physical_state)) + return TOPIC_NOACTION + R.fields["x"] = Clamp(newx, 1, world.maxx) + R.fields["y"] = Clamp(newy, 1, world.maxy) + known_sectors[sec_name] = R + + if (href_list["remove"]) + var/datum/computer_file/data/waypoint/R = locate(href_list["remove"]) + if(R) + known_sectors.Remove(R.fields["name"]) + qdel(R) + + if (href_list["setx"]) + var/newx = input("Input new destiniation x coordinate", "Coordinate input", dx) as num|null + if(!CanInteract(usr, physical_state)) + return + if (newx) + dx = Clamp(newx, 1, world.maxx) + + if (href_list["sety"]) + var/newy = input("Input new destiniation y coordinate", "Coordinate input", dy) as num|null + if(!CanInteract(usr, physical_state)) + return + if (newy) + dy = Clamp(newy, 1, world.maxy) + + if (href_list["x"] && href_list["y"]) + dx = text2num(href_list["x"]) + dy = text2num(href_list["y"]) + + if (href_list["reset"]) + dx = 0 + dy = 0 + + if (href_list["speedlimit"]) + var/newlimit = input("Input new speed limit for autopilot (0 to brake)", "Autopilot speed limit", speedlimit*1000) as num|null + if(newlimit) + speedlimit = Clamp(newlimit/1000, 0, 100) + if (href_list["accellimit"]) + var/newlimit = input("Input new acceleration limit", "Acceleration limit", accellimit*1000) as num|null + if(newlimit) + accellimit = max(newlimit/1000, 0) + + if (href_list["move"]) + var/ndir = text2num(href_list["move"]) + linked.relaymove(usr, ndir, accellimit) + + if (href_list["brake"]) + linked.decelerate() + + if (href_list["apilot"]) + autopilot = !autopilot + + if (href_list["manual"]) + viewing_overmap(usr) ? unlook(usr) : look(usr) + + add_fingerprint(usr) + updateUsrDialog() + + +/obj/machinery/computer/ship/navigation + name = "navigation console" + icon_screen = "nav" + +/obj/machinery/computer/ship/navigation/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(!linked) + display_reconnect_dialog(user, "Navigation") + return + + var/data[0] + + + var/turf/T = get_turf(linked) + var/obj/effect/overmap/visitable/sector/current_sector = locate() in T + + data["sector"] = current_sector ? current_sector.name : "Deep Space" + data["sector_info"] = current_sector ? current_sector.desc : "Not Available" + data["s_x"] = linked.x + data["s_y"] = linked.y + data["speed"] = round(linked.get_speed()*1000, 0.01) + data["accel"] = round(linked.get_acceleration()*1000, 0.01) + data["heading"] = linked.get_heading() ? dir2angle(linked.get_heading()) : 0 + data["viewing"] = viewing_overmap(user) + + if(linked.get_speed()) + data["ETAnext"] = "[round(linked.ETA()/10)] seconds" + else + data["ETAnext"] = "N/A" + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "nav.tmpl", "[linked.name] Navigation Screen", 380, 530) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/ship/navigation/Topic(href, href_list) + if(..()) + return TOPIC_HANDLED + + if (!linked) + return TOPIC_NOACTION + + if (href_list["viewing"]) + viewing_overmap(usr) ? unlook(usr) : look(usr) + return TOPIC_REFRESH + +/obj/machinery/computer/ship/navigation/telescreen //little hacky but it's only used on one ship so it should be okay + icon_state = "tele_nav" + density = 0 + +/obj/machinery/computer/ship/navigation/telescreen/update_icon() + if(stat & (NOPOWER|BROKEN)) + icon_state = "tele_off" + set_light(0) + else + icon_state = "tele_nav" + set_light(2, 2, "#33FAAA") diff --git a/code/modules/overmap/ships/computers/sensors.dm b/code/modules/overmap/ships/computers/sensors.dm new file mode 100644 index 00000000000..d66b49c2f81 --- /dev/null +++ b/code/modules/overmap/ships/computers/sensors.dm @@ -0,0 +1,220 @@ +/obj/machinery/computer/ship/sensors + name = "sensors console" + icon_screen = "sensors" + light_color = "#77fff8" + extra_view = 4 + var/obj/machinery/shipsensors/sensors + +/obj/machinery/computer/ship/sensors/attempt_hook_up(obj/effect/overmap/visitable/ship/sector) + . = ..() + if(!.) + return + find_sensors() + +/obj/machinery/computer/ship/sensors/proc/find_sensors() + if(!linked) + return + for(var/obj/machinery/shipsensors/S in SSmachinery.all_machines) + if(linked.check_ownership(S)) + sensors = S + break + +/obj/machinery/computer/ship/sensors/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(!linked) + display_reconnect_dialog(user, "sensors") + return + + var/data[0] + + data["viewing"] = viewing_overmap(user) + if(sensors) + data["on"] = sensors.use_power + data["range"] = sensors.range + data["health"] = sensors.health + data["max_health"] = sensors.max_health + data["heat"] = sensors.heat + data["critical_heat"] = sensors.critical_heat + if(sensors.health == 0) + data["status"] = "DESTROYED" + else if(!sensors.powered()) + data["status"] = "NO POWER" + else if(!sensors.in_vacuum()) + data["status"] = "VACUUM SEAL BROKEN" + else + data["status"] = "OK" + var/list/contacts = list() + for(var/obj/effect/overmap/O in view(7,linked)) + if(linked == O) + continue + if(!O.scannable) + continue + var/bearing = round(90 - Atan2(O.x - linked.x, O.y - linked.y),5) + if(bearing < 0) + bearing += 360 + contacts.Add(list(list("name"=O.name, "ref"="\ref[O]", "bearing"=bearing))) + if(contacts.len) + data["contacts"] = contacts + else + data["status"] = "MISSING" + data["range"] = "N/A" + data["on"] = 0 + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "shipsensors.tmpl", "[linked.name] Sensors Control", 420, 530, src) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/ship/sensors/Topic(href, href_list) + if (!linked) + return TOPIC_NOACTION + + if (href_list["viewing"]) + if(usr && !isAI(usr)) + viewing_overmap(usr) ? unlook(usr) : look(usr) + return TOPIC_REFRESH + + if (href_list["link"]) + find_sensors() + return TOPIC_REFRESH + + if(sensors) + if (href_list["range"]) + var/nrange = input("Set new sensors range", "Sensor range", sensors.range) as num|null + if(!CanInteract(usr, physical_state)) + return TOPIC_NOACTION + if (nrange) + sensors.set_range(Clamp(nrange, 1, world.view)) + return TOPIC_REFRESH + if (href_list["toggle"]) + sensors.toggle() + return TOPIC_REFRESH + + if (href_list["scan"]) + var/obj/effect/overmap/O = locate(href_list["scan"]) + if(istype(O) && !QDELETED(O) && (O in view(7,linked))) + playsound(loc, "sound/machines/dotprinter.ogg", 30, 1) + new/obj/item/paper/(get_turf(src), O.get_scan_data(usr), "paper (Sensor Scan - [O])") + return TOPIC_HANDLED + +/obj/machinery/computer/ship/sensors/process() + ..() + if(!linked) + return + if(sensors && sensors.use_power && sensors.powered()) + var/sensor_range = round(sensors.range*1.5) + 1 + linked.set_light(1, sensor_range, sensor_range+1) + else + linked.set_light(0) + +/obj/machinery/shipsensors + name = "sensors suite" + desc = "Long range gravity scanner with various other sensors, used to detect irregularities in surrounding space. Can only run in vacuum to protect delicate quantum BS elements." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "sensors" + anchored = 1 + var/max_health = 200 + var/health = 200 + var/critical_heat = 50 // sparks and takes damage when active & above this heat + var/heat_reduction = 1.5 // mitigates this much heat per tick + var/heat = 0 + var/range = 1 + idle_power_usage = 5000 + +/obj/machinery/shipsensors/attackby(obj/item/W, mob/user) + var/damage = max_health - health + if(damage && iswelder(W)) + + var/obj/item/weldingtool/WT = W + + if(!WT.isOn()) + return + + if(WT.remove_fuel(0,user)) + to_chat(user, "You start repairing the damage to [src].") + playsound(src, 'sound/items/Welder.ogg', 100, 1) + if(do_after(user, max(5, damage / 5), src) && WT && WT.isOn()) + to_chat(user, "You finish repairing the damage to [src].") + take_damage(-damage) + else + to_chat(user, "You need more welding fuel to complete this task.") + return + return + ..() + +/obj/machinery/shipsensors/proc/in_vacuum() + var/turf/T=get_turf(src) + if(istype(T)) + var/datum/gas_mixture/environment = T.return_air() + if(environment && environment.return_pressure() > MINIMUM_PRESSURE_DIFFERENCE_TO_SUSPEND) + return 0 + return 1 + +/obj/machinery/shipsensors/update_icon() + if(use_power) + icon_state = "sensors" + else + icon_state = "sensors_off" + +/obj/machinery/shipsensors/examine(mob/user) + . = ..() + if(health <= 0) + to_chat(user, "\The [src] is wrecked.") + else if(health < max_health * 0.25) + to_chat(user, "\The [src] looks like it's about to break!") + else if(health < max_health * 0.5) + to_chat(user, "\The [src] looks seriously damaged!") + else if(health < max_health * 0.75) + to_chat(user, "\The [src] shows signs of damage!") + +/obj/machinery/shipsensors/bullet_act(var/obj/item/projectile/Proj) + take_damage(Proj.get_structure_damage()) + ..() + +/obj/machinery/shipsensors/proc/toggle() + if(!use_power && (health == 0 || !in_vacuum())) + return // No turning on if broken or misplaced. + if(!use_power) //need some juice to kickstart + use_power(idle_power_usage*5) + update_use_power(!use_power) + queue_icon_update() + +/obj/machinery/shipsensors/process() + if(use_power) //can't run in non-vacuum + if(!in_vacuum()) + toggle() + if(heat > critical_heat) + src.visible_message("\The [src] violently spews out sparks!") + spark(src, 3, alldirs) + + take_damage(rand(10,50)) + toggle() + heat += idle_power_usage/15000 + + if (heat > 0) + heat = max(0, heat - heat_reduction) + +/obj/machinery/shipsensors/power_change() + . = ..() + if(use_power && !powered()) + toggle() + +/obj/machinery/shipsensors/proc/set_range(nrange) + range = nrange + active_power_usage = (1500 * (range**2)) //Exponential increase, also affects speed of overheating + +/obj/machinery/shipsensors/emp_act(severity) + if(!use_power) + return + take_damage(20/severity) + toggle() + +/obj/machinery/shipsensors/proc/take_damage(value) + health = min(max(health - value, 0),max_health) + if(use_power && health == 0) + toggle() + +/obj/machinery/shipsensors/weak + heat_reduction = 0.2 + desc = "Miniturized gravity scanner with various other sensors, used to detect irregularities in surrounding space. Can only run in vacuum to protect delicate quantum BS elements." \ No newline at end of file diff --git a/code/modules/overmap/ships/computers/ship.dm b/code/modules/overmap/ships/computers/ship.dm new file mode 100644 index 00000000000..f6f85171463 --- /dev/null +++ b/code/modules/overmap/ships/computers/ship.dm @@ -0,0 +1,97 @@ +/* +While these computers can be placed anywhere, they will only function if placed on either a non-space, non-shuttle turf +with an /obj/effect/overmap/visitable/ship present elsewhere on that z level, or else placed in a shuttle area with an /obj/effect/overmap/visitable/ship +somewhere on that shuttle. Subtypes of these can be then used to perform ship overmap movement functions. +*/ +/obj/machinery/computer/ship + var/obj/effect/overmap/visitable/ship/linked + var/list/viewers // Weakrefs to mobs in direct-view mode. + var/extra_view = 0 // how much the view is increased by when the mob is in overmap mode. + +// A late init operation called in SSshuttle, used to attach the thing to the right ship. +/obj/machinery/computer/ship/proc/attempt_hook_up(obj/effect/overmap/visitable/ship/sector) + if(!istype(sector)) + return + if(sector.check_ownership(src)) + linked = sector + return 1 + +/obj/machinery/computer/ship/proc/sync_linked() + var/obj/effect/overmap/visitable/ship/sector = map_sectors["[z]"] + if(!sector) + return + return attempt_hook_up_recursive(sector) + +/obj/machinery/computer/ship/proc/attempt_hook_up_recursive(obj/effect/overmap/visitable/ship/sector) + if(attempt_hook_up(sector)) + return sector + for(var/obj/effect/overmap/visitable/ship/candidate in sector) + if((. = .(candidate))) + return + +/obj/machinery/computer/ship/proc/display_reconnect_dialog(var/mob/user, var/flavor) + var/datum/browser/popup = new (user, "[src]", "[src]") + popup.set_content("