diff --git a/code/__defines/_planes+layers.dm b/code/__defines/_planes+layers.dm index 01d890b152..c36e238329 100644 --- a/code/__defines/_planes+layers.dm +++ b/code/__defines/_planes+layers.dm @@ -42,6 +42,8 @@ What is the naming convention for planes or layers? #define SPACE_PLANE -82 // Reserved for use in space/parallax #define PARALLAX_PLANE -80 // Reserved for use in space/parallax +#define SKYBOX_PLANE -79 // Skybox parallax +#define DUST_PLANE -78 // For dust overlay on space turfs. Should be above skybox for parallax effect. // OPENSPACE_PLANE reserves all planes between OPENSPACE_PLANE_START and OPENSPACE_PLANE_END inclusive #define OPENSPACE_PLANE -75 // /turf/simulated/open will use OPENSPACE_PLANE + z (Valid z's being 2 thru 17) @@ -50,7 +52,7 @@ What is the naming convention for planes or layers? #define OVER_OPENSPACE_PLANE -57 // Turf Planes -#define SPACE_PLANE -43 // Space turfs themselves +#define SPACE_PLANE -82 // Space turfs themselves #define PLATING_PLANE -44 // Plating #define DISPOSAL_LAYER 2.1 // Under objects, even when planeswapped #define PIPES_LAYER 2.2 // Under objects, even when planeswapped diff --git a/code/__defines/atmos.dm b/code/__defines/atmos.dm index 66348344f2..56055273e4 100644 --- a/code/__defines/atmos.dm +++ b/code/__defines/atmos.dm @@ -29,6 +29,7 @@ #define MINIMUM_AIR_TO_SUSPEND (MOLES_CELLSTANDARD * MINIMUM_AIR_RATIO_TO_SUSPEND) // Minimum amount of air that has to move before a group processing can be suspended #define MINIMUM_MOLES_DELTA_TO_MOVE (MOLES_CELLSTANDARD * MINIMUM_AIR_RATIO_TO_SUSPEND) // Either this must be active #define MINIMUM_TEMPERATURE_TO_MOVE (T20C + 100) // or this (or both, obviously) +#define MINIMUM_PRESSURE_DIFFERENCE_TO_SUSPEND (MINIMUM_AIR_TO_SUSPEND*R_IDEAL_GAS_EQUATION*T20C)/CELL_VOLUME // Minimum pressure difference between zones to suspend #define MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND 0.012 // Minimum temperature difference before group processing is suspended. #define MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND 4 diff --git a/code/__defines/construction.dm b/code/__defines/construction.dm index 2bae550a22..f148803322 100644 --- a/code/__defines/construction.dm +++ b/code/__defines/construction.dm @@ -38,6 +38,7 @@ #define CONNECT_TYPE_SUPPLY 2 #define CONNECT_TYPE_SCRUBBER 4 #define CONNECT_TYPE_HE 8 +#define CONNECT_TYPE_FUEL 16 // TODO - Implement this! Its piping so better ask Leshana // We are based on the three named layers of supply, regular, and scrubber. #define PIPING_LAYER_SUPPLY 1 diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index b96c2cca84..3c466e306d 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -270,6 +270,7 @@ #define WORLD_ICON_SIZE 32 //Needed for the R-UST port #define PIXEL_MULTIPLIER WORLD_ICON_SIZE/32 //Needed for the R-UST port +#define MAX_CLIENT_VIEW 34 // Maximum effective value of client.view (According to DM references) // Maploader bounds indices #define MAP_MINX 1 diff --git a/code/modules/shuttles/_defines.dm b/code/__defines/shuttle.dm similarity index 68% rename from code/modules/shuttles/_defines.dm rename to code/__defines/shuttle.dm index 22d57fe9cb..9bf7dcad6f 100644 --- a/code/modules/shuttles/_defines.dm +++ b/code/__defines/shuttle.dm @@ -9,6 +9,11 @@ #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. +// Overmap landable shuttles (/obj/effect/overmap/visitable/ship/landable on a /datum/shuttle/autodock/overmap) +#define SHIP_STATUS_LANDED 1 // Ship is at any other shuttle landmark. +#define SHIP_STATUS_TRANSIT 2 // Ship is at it's shuttle datum's transition shuttle landmark. +#define SHIP_STATUS_OVERMAP 3 // Ship is at its "overmap" shuttle landmark (allowed to move on overmap now) + // Ferry shuttle location constants #define FERRY_LOCATION_STATION 0 #define FERRY_LOCATION_OFFSITE 1 diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm index ae7b77e214..3e8b6a2cd2 100644 --- a/code/__defines/subsystems.dm +++ b/code/__defines/subsystems.dm @@ -52,10 +52,11 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G // Subsystem init_order, from highest priority to lowest priority // Subsystems shutdown in the reverse of the order they initialize in // The numbers just define the ordering, they are meaningless otherwise. -#define INIT_ORDER_SQLITE 19 -#define INIT_ORDER_CHEMISTRY 18 -#define INIT_ORDER_MAPPING 17 -#define INIT_ORDER_DECALS 16 +#define INIT_ORDER_SQLITE 40 +#define INIT_ORDER_CHEMISTRY 35 +#define INIT_ORDER_SKYBOX 30 +#define INIT_ORDER_MAPPING 25 +#define INIT_ORDER_DECALS 20 #define INIT_ORDER_ATOMS 15 #define INIT_ORDER_MACHINES 10 #define INIT_ORDER_SHUTTLES 3 diff --git a/code/_helpers/_lists.dm b/code/_helpers/_lists.dm index 48e9d1ba64..e34333f72e 100644 --- a/code/_helpers/_lists.dm +++ b/code/_helpers/_lists.dm @@ -206,6 +206,20 @@ proc/listclearnulls(list/list) result = first - second return result +/* +Two lists may be different (A!=B) even if they have the same elements. +This actually tests if they have the same entries and values. +*/ +/proc/same_entries(var/list/first, var/list/second) + if(!islist(first) || !islist(second)) + return 0 + if(length(first) != length(second)) + return 0 + for(var/entry in first) + if(!(entry in second) || (first[entry] != second[entry])) + return 0 + return 1 + /* * Returns list containing entries that are in either list but not both. * If skipref = 1, repeated elements are treated as one. diff --git a/code/_macros.dm b/code/_macros.dm index d5878c6259..6faa7ce430 100644 --- a/code/_macros.dm +++ b/code/_macros.dm @@ -2,6 +2,12 @@ #define get_turf(A) get_step(A,0) +#define get_x(A) (get_step(A, 0)?.x || 0) + +#define get_y(A) (get_step(A, 0)?.y || 0) + +#define get_z(A) (get_step(A, 0)?.z || 0) + #define RANDOM_BLOOD_TYPE pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+") #define to_chat(target, message) target << message diff --git a/code/_onclick/hud/skybox.dm b/code/_onclick/hud/skybox.dm new file mode 100644 index 0000000000..492a11e588 --- /dev/null +++ b/code/_onclick/hud/skybox.dm @@ -0,0 +1,67 @@ +#define SKYBOX_PADDING 4 // How much larger we want the skybox image to be than client's screen (in turfs) +#define SKYBOX_PIXELS 736 // Size of skybox image in pixels +#define SKYBOX_TURFS (SKYBOX_PIXELS/WORLD_ICON_SIZE) + +// Skybox screen object. +/obj/skybox + name = "skybox" + mouse_opacity = 0 + anchored = TRUE + simulated = FALSE + screen_loc = "CENTER,CENTER" + plane = SKYBOX_PLANE + blend_mode = BLEND_MULTIPLY // You actually need to do it this way or you see it in occlusion. + +// Adjust transform property to scale for client's view var. We assume the skybox is 736x736 px +/obj/skybox/proc/scale_to_view(var/view) + var/matrix/M = matrix() + // Translate to center the icon over us! + M.Translate(-(SKYBOX_PIXELS - WORLD_ICON_SIZE) / 2) + // Scale appropriately based on view size. (7 results in scale of 1) + view = text2num(view) || 7 // Sanitize + M.Scale(((min(MAX_CLIENT_VIEW, view) + SKYBOX_PADDING) * 2 + 1) / SKYBOX_TURFS) + src.transform = M + +/client + var/obj/skybox/skybox + +/client/proc/update_skybox(rebuild) + if(!skybox) + skybox = new() + skybox.scale_to_view(src.view) + screen += skybox + rebuild = 1 + + var/turf/T = get_turf(eye) + if(T) + if(rebuild) + skybox.cut_overlays() + skybox.add_overlay(SSskybox.get_skybox(T.z)) + screen |= skybox + skybox.screen_loc = "CENTER:[(world.maxx>>1) - T.x],CENTER:[(world.maxy>>1) - T.y]" + +/mob/Login() + . = ..() + client.update_skybox(TRUE) + +/mob/Move() + var/old_z = get_z(src) + . = ..() + if(. && client) + client.update_skybox(old_z != get_z(src)) + +/mob/forceMove() + var/old_z = get_z(src) + . = ..() + if(. && client) + client.update_skybox(old_z != get_z(src)) + +/mob/set_viewsize() + . = ..() + if (. && client) + client.update_skybox() + client.skybox?.scale_to_view(client.view) + +#undef SKYBOX_BORDER +#undef SKYBOX_PIXELS +#undef SKYBOX_TURFS diff --git a/code/controllers/subsystems/shuttles.dm b/code/controllers/subsystems/shuttles.dm index 35a93a5eac..27bbbae027 100644 --- a/code/controllers/subsystems/shuttles.dm +++ b/code/controllers/subsystems/shuttles.dm @@ -15,7 +15,6 @@ SUBSYSTEM_DEF(shuttles) flags = SS_KEEP_TIMING|SS_NO_TICK_CHECK runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME - // TODO OVERMAP - These two are unused for now var/overmap_halted = FALSE // Whether ships can move on the overmap; used for adminbus. var/list/ships = list() // List of all ships. @@ -99,14 +98,13 @@ SUBSYSTEM_DEF(shuttles) registered_shuttle_landmarks[shuttle_landmark_tag] = shuttle_landmark last_landmark_registration_time = world.time - // TODO - Uncomment once overmap sectors are ported - //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]"] - // O ? O.add_landmark(shuttle_landmark, shuttle_landmark.shuttle_restricted) : (landmarks_awaiting_sector += shuttle_landmark) + 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]"] + O ? O.add_landmark(shuttle_landmark, shuttle_landmark.shuttle_restricted) : (landmarks_awaiting_sector += shuttle_landmark) /datum/controller/subsystem/shuttles/proc/get_landmark(var/shuttle_landmark_tag) return registered_shuttle_landmarks[shuttle_landmark_tag] @@ -114,39 +112,37 @@ SUBSYSTEM_DEF(shuttles) //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/shuttles/proc/initialize_sector(obj/effect/overmap/visitable/given_sector) - return // TODO - Uncomment once overmap sectors are ported -// 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. + 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 // Landmark isn't registered yet, queue it to be added once it is. + 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 // Landmark isn't registered yet, queue it to be added once it is. -// 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 // Landmark isn't registered yet, queue it to be added once it is. + 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 // Landmark isn't registered yet, queue it to be added once it is. -// 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 + 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 -// TODO - Uncomment once overmap sectors are ported -//// Attempts to add a landmark instance with a sector (returns false if landmark isn't registered yet) -///datum/controller/subsystem/shuttles/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 +// Attempts to add a landmark instance with a sector (returns false if landmark isn't registered yet) +/datum/controller/subsystem/shuttles/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 -// if(landmark.landmark_tag in given_sector.initial_generic_waypoints) -// given_sector.add_landmark(landmark) -// . = 1 -// 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) -// . = 1 + if(landmark.landmark_tag in given_sector.initial_generic_waypoints) + given_sector.add_landmark(landmark) + . = 1 + 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) + . = 1 /datum/controller/subsystem/shuttles/proc/initialize_shuttle(var/shuttle_type) var/datum/shuttle/shuttle = shuttle_type @@ -170,13 +166,13 @@ SUBSYSTEM_DEF(shuttles) error("Shuttle [S] was unable to find mothership [mothership]!") // Admin command to halt/resume overmap -// /datum/controller/subsystem/shuttles/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/shuttles/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/shuttles/stat_entry() ..("Shuttles:[process_shuttles.len]/[shuttles.len], Ships:[ships.len], L:[registered_shuttle_landmarks.len][overmap_halted ? ", HALT" : ""]") diff --git a/code/controllers/subsystems/skybox.dm b/code/controllers/subsystems/skybox.dm new file mode 100644 index 0000000000..ccf4427d99 --- /dev/null +++ b/code/controllers/subsystems/skybox.dm @@ -0,0 +1,82 @@ + +//Exists to handle a few global variables that change enough to justify this. Technically a parallax, but it exhibits a skybox effect. +SUBSYSTEM_DEF(skybox) + name = "Space skybox" + init_order = INIT_ORDER_SKYBOX + flags = SS_NO_FIRE + var/list/skybox_cache = list() + +/datum/controller/subsystem/skybox/Initialize() + . = ..() + +/datum/controller/subsystem/skybox/Recover() + skybox_cache = SSskybox.skybox_cache + +/datum/controller/subsystem/skybox/proc/get_skybox(z) + if(!skybox_cache["[z]"]) + skybox_cache["[z]"] = generate_skybox(z) + if(global.using_map.use_overmap) + var/obj/effect/overmap/visitable/O = map_sectors["[z]"] + if(istype(O)) + for(var/zlevel in O.map_z) + skybox_cache["[zlevel]"] = skybox_cache["[z]"] + return skybox_cache["[z]"] + +/datum/controller/subsystem/skybox/proc/generate_skybox(z) + var/datum/skybox_settings/settings = global.using_map.get_skybox_datum(z) + + var/image/res = image(settings.icon) + res.appearance_flags = KEEP_TOGETHER + + var/image/base = image(settings.icon, settings.icon_state) + base.color = settings.color + + if(settings.use_stars) + var/image/stars = image(settings.icon, settings.star_state) + stars.appearance_flags = RESET_COLOR + base.overlays += stars + + res.overlays += base + + if(global.using_map.use_overmap && settings.use_overmap_details) + var/obj/effect/overmap/visitable/O = map_sectors["[z]"] + if(istype(O)) + var/image/overmap = image(settings.icon) + overmap.overlays += O.generate_skybox() + for(var/obj/effect/overmap/visitable/other in O.loc) + if(other != O) + overmap.overlays += other.get_skybox_representation() + overmap.appearance_flags = RESET_COLOR + res.overlays += overmap + + // TODO - Allow events to apply custom overlays to skybox! (Awesome!) + //for(var/datum/event/E in SSevent.active_events) + // if(E.has_skybox_image && E.isRunning && (z in E.affecting_z)) + // res.overlays += E.get_skybox_image() + + return res + +/datum/controller/subsystem/skybox/proc/rebuild_skyboxes(var/list/zlevels) + for(var/z in zlevels) + skybox_cache["[z]"] = generate_skybox(z) + + for(var/client/C) + C.update_skybox(1) + +// Settings datum that maps can override to play with their skyboxes +/datum/skybox_settings + var/icon = 'icons/skybox/skybox.dmi' //Path to our background. Lets us use anything we damn well please. Skyboxes need to be 736x736 + var/icon_state = "dyable" + var/color + var/random_color = FALSE + + var/use_stars = TRUE + var/star_icon = 'icons/skybox/skybox.dmi' + var/star_state = "stars" + + var/use_overmap_details = TRUE //Do we try to draw overmap visitables in our sector on the map? + +/datum/skybox_settings/New() + ..() + if(random_color) + color = rgb(rand(0,255), rand(0,255), rand(0,255)) diff --git a/code/datums/observation/helpers.dm b/code/datums/observation/helpers.dm index 9116026700..12feeba16d 100644 --- a/code/datums/observation/helpers.dm +++ b/code/datums/observation/helpers.dm @@ -9,6 +9,9 @@ /atom/proc/recursive_dir_set(var/atom/a, var/old_dir, var/new_dir) set_dir(new_dir) +/datum/proc/qdel_self() + qdel(src) + /proc/register_all_movement(var/event_source, var/listener) GLOB.moved_event.register(event_source, listener, /atom/movable/proc/recursive_move) GLOB.dir_set_event.register(event_source, listener, /atom/proc/recursive_dir_set) diff --git a/code/game/machinery/embedded_controller/docking_program.dm b/code/game/machinery/embedded_controller/docking_program.dm index 44a34fddec..86876abe28 100644 --- a/code/game/machinery/embedded_controller/docking_program.dm +++ b/code/game/machinery/embedded_controller/docking_program.dm @@ -117,7 +117,7 @@ if(docking_codes) var/code = signal.data["code"] if(code != docking_codes) - testing("Controller [id_tag] got request_dock but code:[code] != docking_codes:[docking_codes]") + log_debug("Controller [id_tag] got request_dock but code:[code] != docking_codes:[docking_codes]") return control_mode = MODE_SERVER diff --git a/code/game/mecha/combat/gorilla.dm b/code/game/mecha/combat/gorilla.dm index a33cd963e2..e413fc892a 100644 --- a/code/game/mecha/combat/gorilla.dm +++ b/code/game/mecha/combat/gorilla.dm @@ -178,10 +178,10 @@ src.log_message("Toggled zoom mode.") src.occupant_message("Zoom mode [zoom?"en":"dis"]abled.") if(zoom) - src.occupant.client.view = 12 + src.occupant.set_viewsize(12) playsound(src.occupant, 'sound/mecha/imag_enh.ogg',50) else - src.occupant.client.view = world.view//world.view - default mob view size + src.occupant.set_viewsize() // Reset to default return diff --git a/code/game/mecha/combat/marauder.dm b/code/game/mecha/combat/marauder.dm index 8b2471d953..2b383379ad 100644 --- a/code/game/mecha/combat/marauder.dm +++ b/code/game/mecha/combat/marauder.dm @@ -180,10 +180,10 @@ src.log_message("Toggled zoom mode.") src.occupant_message("Zoom mode [zoom?"en":"dis"]abled.") if(zoom) - src.occupant.client.view = 12 + src.occupant.set_viewsize(12) src.occupant << sound('sound/mecha/imag_enh.ogg',volume=50) else - src.occupant.client.view = world.view//world.view - default mob view size + src.occupant.set_viewsize() // Reset to default return diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 02161a4db6..26a0a6478e 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -663,7 +663,7 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. if(!zoom && !cannotzoom) if(H.hud_used.hud_shown) H.toggle_zoom_hud() // If the user has already limited their HUD this avoids them having a HUD when they zoom in - H.client.view = viewsize + H.set_viewsize(viewsize) zoom = 1 var/tilesize = 32 @@ -689,7 +689,7 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. H.handle_vision() else - H.client.view = world.view + H.set_viewsize() // Reset to default if(!H.hud_used.hud_shown) H.toggle_zoom_hud() zoom = 0 diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index 6230350851..8943c0b86b 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -329,6 +329,9 @@ var/list/global/tank_gauge_cache = list() /obj/item/weapon/tank/remove_air(amount) return air_contents.remove(amount) +/obj/item/weapon/tank/proc/remove_air_by_flag(flag, amount) + return air_contents.remove_by_flag(flag, amount) + /obj/item/weapon/tank/return_air() return air_contents diff --git a/code/game/turfs/space/cracked_asteroid.dm b/code/game/turfs/space/cracked_asteroid.dm index 75cf25fd3f..ca77a8cd2c 100644 --- a/code/game/turfs/space/cracked_asteroid.dm +++ b/code/game/turfs/space/cracked_asteroid.dm @@ -7,6 +7,7 @@ icon_state = "asteroid_cracked" dynamic_lighting = TRUE keep_sprite = TRUE + plane = TURF_PLANE //It's not really space /turf/space/cracked_asteroid/is_space() // So people don't start floating when standing on it. return FALSE diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index a3bc0494ca..afdaab404b 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -1,20 +1,89 @@ /turf/space icon = 'icons/turf/space.dmi' name = "\proper space" - icon_state = "0" + icon_state = "default" dynamic_lighting = 0 + plane = SPACE_PLANE temperature = T20C thermal_conductivity = OPEN_HEAT_TRANSFER_COEFFICIENT can_build_into_floor = TRUE var/keep_sprite = FALSE // heat_capacity = 700000 No. + var/static/list/dust_cache + var/static/list/speedspace_cache + var/static/list/phase_shift_by_x + var/static/list/phase_shift_by_y + +/turf/space/proc/build_dust_cache() + //Static + LAZYINITLIST(dust_cache) + for (var/i in 0 to 25) + var/image/im = image('icons/turf/space_dust.dmi', "[i]") + im.plane = DUST_PLANE + im.alpha = 128 //80 + im.blend_mode = BLEND_ADD + dust_cache["[i]"] = im + //Moving + LAZYINITLIST(speedspace_cache) + for (var/i in 0 to 14) + // NORTH/SOUTH + var/image/im = image('icons/turf/space_dust_transit.dmi', "speedspace_ns_[i]") + im.plane = DUST_PLANE + im.blend_mode = BLEND_ADD + speedspace_cache["NS_[i]"] = im + // EAST/WEST + im = image('icons/turf/space_dust_transit.dmi', "speedspace_ew_[i]") + im.plane = DUST_PLANE + im.blend_mode = BLEND_ADD + speedspace_cache["EW_[i]"] = im /turf/space/Initialize() . = ..() if(!keep_sprite) - icon_state = "[((x + y) ^ ~(x * y) + z) % 25]" + icon_state = "white" update_starlight() + if (!dust_cache) + build_dust_cache() + toggle_transit() //add static dust + +/turf/space/proc/toggle_transit(var/direction) + cut_overlays() + + if(!direction) + add_overlay(dust_cache["[((x + y) ^ ~(x * y) + z) % 25]"]) + return + + if(direction & (NORTH|SOUTH)) + if(!phase_shift_by_x) + phase_shift_by_x = get_cross_shift_list(15) + var/x_shift = phase_shift_by_x[src.x % (phase_shift_by_x.len - 1) + 1] + var/transit_state = ((direction & SOUTH ? world.maxy - src.y : src.y) + x_shift)%15 + add_overlay(speedspace_cache["NS_[transit_state]"]) + else if(direction & (EAST|WEST)) + if(!phase_shift_by_y) + phase_shift_by_y = get_cross_shift_list(15) + var/y_shift = phase_shift_by_y[src.y % (phase_shift_by_y.len - 1) + 1] + var/transit_state = ((direction & WEST ? world.maxx - src.x : src.x) + y_shift)%15 + add_overlay(speedspace_cache["EW_[transit_state]"]) + + for(var/atom/movable/AM in src) + if (AM.simulated && !AM.anchored) + AM.throw_at(get_step(src,reverse_direction(direction)), 5, 1) + +//generates a list used to randomize transit animations so they aren't in lockstep +/turf/space/proc/get_cross_shift_list(var/size) + var/list/result = list() + + result += rand(0, 14) + for(var/i in 2 to size) + var/shifts = list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) + shifts -= result[i - 1] //consecutive shifts should not be equal + if(i == size) + shifts -= result[1] //because shift list is a ring buffer + result += pick(shifts) + + return result /turf/space/is_space() return 1 diff --git a/code/game/turfs/space/transit.dm b/code/game/turfs/space/transit.dm index 0bfcb4e0f6..02c589e966 100644 --- a/code/game/turfs/space/transit.dm +++ b/code/game/turfs/space/transit.dm @@ -1,5 +1,4 @@ /turf/space/transit - keep_sprite = TRUE can_build_into_floor = FALSE var/pushdirection // push things that get caught in the transit tile this direction @@ -7,87 +6,26 @@ /turf/space/transit/attackby(obj/O as obj, mob/user as mob) return -//generates a list used to randomize transit animations so they aren't in lockstep -/turf/space/transit/proc/get_cross_shift_list(var/size) - var/list/result = list() - - result += rand(0, 14) - for(var/i in 2 to size) - var/shifts = list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) - shifts -= result[i - 1] //consecutive shifts should not be equal - if(i == size) - shifts -= result[1] //because shift list is a ring buffer - result += pick(shifts) - - return result +/turf/space/transit/Initialize() + . = ..() + toggle_transit(reverse_dir[pushdirection]) //------------------------ /turf/space/transit/north // moving to the north icon_state = "arrow-north" pushdirection = SOUTH // south because the space tile is scrolling south - var/static/list/phase_shift_by_x - -/turf/space/transit/north/New() - ..() - if(!phase_shift_by_x) - phase_shift_by_x = get_cross_shift_list(15) - - var/x_shift = phase_shift_by_x[src.x % (phase_shift_by_x.len - 1) + 1] - var/transit_state = (world.maxy - src.y + x_shift)%15 + 1 - - icon_state = "speedspace_ns_[transit_state]" -//------------------------ /turf/space/transit/south // moving to the south icon_state = "arrow-south" pushdirection = SOUTH // south because the space tile is scrolling south - var/static/list/phase_shift_by_x - -/turf/space/transit/south/New() - ..() - if(!phase_shift_by_x) - phase_shift_by_x = get_cross_shift_list(15) - - var/x_shift = phase_shift_by_x[src.x % (phase_shift_by_x.len - 1) + 1] - var/transit_state = (world.maxy - src.y + x_shift)%15 + 1 - - var/icon/I = new(icon, "speedspace_ns_[transit_state]") - I.Flip(SOUTH) - icon = I -//------------------------ /turf/space/transit/east // moving to the east icon_state = "arrow-east" pushdirection = WEST - var/static/list/phase_shift_by_y - -/turf/space/transit/east/New() - ..() - if(!phase_shift_by_y) - phase_shift_by_y = get_cross_shift_list(15) - - var/y_shift = phase_shift_by_y[src.y % (phase_shift_by_y.len - 1) + 1] - var/transit_state = (world.maxx - src.x + y_shift)%15 + 1 - - icon_state = "speedspace_ew_[transit_state]" -//------------------------ /turf/space/transit/west // moving to the west icon_state = "arrow-west" pushdirection = WEST - var/static/list/phase_shift_by_y - -/turf/space/transit/west/New() - ..() - if(!phase_shift_by_y) - phase_shift_by_y = get_cross_shift_list(15) - - var/y_shift = phase_shift_by_y[src.y % (phase_shift_by_y.len - 1) + 1] - var/transit_state = (world.maxx - src.x + y_shift)%15 + 1 - - var/icon/I = new(icon, "speedspace_ew_[transit_state]") - I.Flip(WEST) - icon = I //------------------------ \ No newline at end of file diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 1eb22f1201..6b2170452a 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -812,10 +812,12 @@ Traitors and the like can also be revived with the previous role mostly intact. if(!holder) return + var/view = src.view if(view == world.view) view = input("Select view range:", "FUCK YE", 7) in list(1,2,3,4,5,6,7,8,9,10,11,12,13,14,128) else view = world.view + mob.set_viewsize(view) log_admin("[key_name(usr)] changed their view range to [view].") //message_admins("[key_name_admin(usr)] changed their view range to [view].", 1) //why? removed by order of XSI diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm index e9e532e3d5..e45e1ae2ad 100644 --- a/code/modules/holodeck/HolodeckObjects.dm +++ b/code/modules/holodeck/HolodeckObjects.dm @@ -58,8 +58,9 @@ /turf/simulated/floor/holofloor/space icon = 'icons/turf/space.dmi' + plane = SPACE_PLANE name = "\proper space" - icon_state = "0" + icon_state = "white" /turf/simulated/floor/holofloor/reinforced icon = 'icons/turf/flooring/tiles.dmi' @@ -67,9 +68,6 @@ name = "reinforced holofloor" icon_state = "reinforced" -/turf/simulated/floor/holofloor/space/New() - icon_state = "[((x + y) ^ ~(x * y) + z) % 25]" - /turf/simulated/floor/holofloor/beach desc = "Uncomfortably gritty for a hologram." base_desc = "Uncomfortably gritty for a hologram." diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 323f6d2bfa..6ce6f21e13 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1077,6 +1077,13 @@ mob/proc/yank_out_object() /mob/proc/setEarDamage() return +// Set client view distance (size of client's screen). Returns TRUE if anything changed. +/mob/proc/set_viewsize(var/new_view = world.view) + if (client && new_view != client.view) + client.view = new_view + return TRUE + return FALSE + //Throwing stuff /mob/proc/toggle_throw_mode() diff --git a/code/modules/mob/skillset.dm b/code/modules/mob/skillset.dm new file mode 100644 index 0000000000..2877372491 --- /dev/null +++ b/code/modules/mob/skillset.dm @@ -0,0 +1,14 @@ + +// We don't actually have a skills system, so return max skill for everything. +/mob/proc/get_skill_value(skill_path) + return SKILL_EXPERT + +// A generic way of modifying success probabilities via skill values. Higher factor means skills have more effect. fail_chance is the chance at SKILL_NONE. +/mob/proc/skill_fail_chance(skill_path, fail_chance, no_more_fail = SKILL_EXPERT, factor = 1) + var/points = get_skill_value(skill_path) + if(points >= no_more_fail) + return 0 + else + return fail_chance * 2 ** (factor*(SKILL_BASIC - points)) + + return FALSE // We don't actually have a skills system, so never fail. diff --git a/code/modules/overmap/README.dm b/code/modules/overmap/README.dm index 14a7a955a5..ac9fc8890e 100644 --- a/code/modules/overmap/README.dm +++ b/code/modules/overmap/README.dm @@ -53,6 +53,12 @@ Changes desitnation area depending on current sector ship is in. Currently updating is called in attack_hand(), until a better place is found. Currently no modifications were made to interface to display availability of landing area in sector. +************************************************************* +Landable Ships +************************************************************* +Ship - Vessel that can move around on the overmap. It's entire z-level(s) "move" conceptually. +Shuttles - Vessel that can jump to shuttle landmarks. Its areas move by transition_turfs. +Landable Ship - Vessel that can do both. Sits at a special shuttle landmark for overmap movement mode. ************************************************************* Guide to how make new sector diff --git a/code/modules/overmap/_defines.dm b/code/modules/overmap/_defines.dm index a272ab7591..bf967eb3b9 100644 --- a/code/modules/overmap/_defines.dm +++ b/code/modules/overmap/_defines.dm @@ -1,9 +1,19 @@ -//Zlevel where overmap objects should be -#define OVERMAP_ZLEVEL 1 //How far from the edge of overmap zlevel could randomly placed objects spawn -#define OVERMAP_EDGE 7 +#define OVERMAP_EDGE 2 +#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 //Dimension of overmap (squares 4 lyfe) var/global/list/map_sectors = list() @@ -17,6 +27,7 @@ var/global/list/map_sectors = list() /turf/unsimulated/map icon = 'icons/turf/space.dmi' icon_state = "map" + initialized = FALSE // TODO - Fix unsimulated turf initialization so this override is not necessary! /turf/unsimulated/map/edge opacity = 1 @@ -48,11 +59,7 @@ var/global/list/map_sectors = list() I.pixel_x = 5*i - 2 if(x == global.using_map.overmap_size) I.pixel_x = 5*i + 2 - overlays += I - - - - + add_overlay(I) //list used to track which zlevels are being 'moved' by the proc below var/list/moving_levels = list() @@ -63,29 +70,13 @@ 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 - for(var/x = 1 to world.maxx) - for(var/y = 1 to world.maxy) - var/turf/space/T = locate(x,y,zlevel) - if (istype(T)) - if(!gen_dir) - T.icon_state = "[((T.x + T.y) ^ ~(T.x * T.y) + T.z) % 25]" - else - T.icon_state = "speedspace_[gen_dir]_[rand(1,15)]" - for(var/atom/movable/AM in T) - if (!AM.anchored) - AM.throw_at(get_step(T,reverse_direction(direction)), 5, 1) - + if (moving_levels["[zlevel]"] != direction) + moving_levels["[zlevel]"] = direction + var/list/spaceturfs = block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel)) + for(var/turf/space/T in spaceturfs) + T.toggle_transit(direction) + CHECK_TICK /* //list used to cache empty zlevels to avoid nedless map bloat var/list/cached_space = list() diff --git a/code/modules/overmap/overmap_object.dm b/code/modules/overmap/overmap_object.dm index 9db73eff63..fd2798e7d6 100644 --- a/code/modules/overmap/overmap_object.dm +++ b/code/modules/overmap/overmap_object.dm @@ -21,11 +21,9 @@ if(known) //layer = ABOVE_LIGHTING_LAYER plane = PLANE_LIGHTING_ABOVE - // TODO - Leshana HELM - // for(var/obj/machinery/computer/ship/helm/H in global.machines) - // H.get_known_sectors() -/* -TODO - Leshana - No need for this, we don't have skyboxes + for(var/obj/machinery/computer/ship/helm/H in global.machines) + H.get_known_sectors() + /obj/effect/overmap/Crossed(var/obj/effect/overmap/visitable/other) if(istype(other)) for(var/obj/effect/overmap/visitable/O in loc) @@ -36,4 +34,3 @@ TODO - Leshana - No need for this, we don't have skyboxes SSskybox.rebuild_skyboxes(other.map_z) for(var/obj/effect/overmap/visitable/O in loc) SSskybox.rebuild_skyboxes(O.map_z) -*/ \ No newline at end of file diff --git a/code/modules/overmap/overmap_shuttle.dm b/code/modules/overmap/overmap_shuttle.dm new file mode 100644 index 0000000000..c3195c99de --- /dev/null +++ b/code/modules/overmap/overmap_shuttle.dm @@ -0,0 +1,180 @@ +#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) + var/obj/effect/overmap/visitable/ship/landable/myship //my overmap ship object + + category = /datum/shuttle/autodock/overmap + var/skill_needed = SKILL_BASIC + var/operator_skill = SKILL_BASIC + +/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/Destroy() + . = ..() + myship = null + +/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("You hear the shuttle engines sputter... perhaps it doesn't have enough fuel?", 1, + "The shuttle shakes but fails to take off.", 2) + 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 + var/our_sector = waypoint_sector(current_location) + if(!our_sector && myship?.landmark && next_location == myship.landmark) + return TRUE //We're not on the overmap yet (admin spawned probably), and we're trying to hook up with our openspace sector + return get_dist(our_sector, 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)) + var/skill_mod = 0.2*(skill_needed - operator_skill) + return move_time * (1 + distance_mod + skill_mod) + +/datum/shuttle/autodock/overmap/process_launch() + if(prob(10*max(0, skill_needed - operator_skill))) + var/places = get_possible_destinations() + var/place = pick(places) + set_destination(places[place]) + ..() + +/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() + var/our_sector = waypoint_sector(current_location) + if(!our_sector && myship?.landmark) + res["Perform Test Jump"] = myship.landmark + return res //We're not on the overmap, maybe an admin spawned us on a non-sector map. We're broken until we connect to our space z-level. + for (var/obj/effect/overmap/visitable/S in range(get_turf(our_sector), 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/weapon/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/weapon/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/weapon/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/weapon/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_parts.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/weapon/tank/phoron(src) + +/obj/structure/fuel_port/attack_hand(mob/user as mob) + if(!opened) + to_chat(user, "The door is secured tightly. You'll need a crowbar to open it.") + 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/weapon/W as obj, mob/user as mob) + if(W.is_crowbar()) + if(opened) + to_chat(user, "You tightly shut \the [src] door.") + playsound(src.loc, 'sound/effects/locker_close.ogg', 25, 0, -3) + opened = 0 + else + to_chat(user, "You open up \the [src] door.") + playsound(src.loc, 'sound/effects/locker_open.ogg', 15, 1, -3) + opened = 1 + else if(istype(W,/obj/item/weapon/tank)) + if(!opened) + to_chat(user, "\The [src] door is still closed!") + return + if(contents.len == 0) + user.unEquip(W, src) + W.forceMove(src) + update_icon() + +// Walls hide stuff inside them, but we want to be visible. +/obj/structure/fuel_port/hide() + return \ No newline at end of file diff --git a/code/modules/overmap/sectors.dm b/code/modules/overmap/sectors.dm index 15393a5be1..cab58ec8eb 100644 --- a/code/modules/overmap/sectors.dm +++ b/code/modules/overmap/sectors.dm @@ -50,9 +50,11 @@ //This is called later in the init order by SSshuttles to populate sector objects. Importantly for subtypes, shuttles will be created by then. /obj/effect/overmap/visitable/proc/populate_sector_objects() -// TODO - Leshana - Implement -///obj/effect/overmap/visitable/proc/get_areas() -// return get_filtered_areas(list(/proc/area_belongs_to_zlevels = map_z)) +/obj/effect/overmap/visitable/proc/get_areas() + . = list() + for(var/area/A) + if (A.z in map_z) + . += A /obj/effect/overmap/visitable/proc/find_z_levels() map_z = GetConnectedZlevels(z) @@ -102,6 +104,21 @@ /obj/effect/overmap/visitable/proc/generate_skybox() return +/obj/effect/overmap/visitable/MouseEntered(location, control, params) + openToolTip(user = usr, tip_src = src, params = params, title = name) + + ..() + +/obj/effect/overmap/visitable/MouseDown() + closeToolTip(usr) //No reason not to, really + + ..() + +/obj/effect/overmap/visitable/MouseExited() + closeToolTip(usr) //No reason not to, really + + ..() + /obj/effect/overmap/visitable/sector name = "generic sector" desc = "Sector with some stuff in it." @@ -117,7 +134,7 @@ return 1 testing("Building overmap...") - world.maxz++ + world.increment_max_z() global.using_map.overmap_z = world.maxz testing("Putting overmap on [global.using_map.overmap_z]") diff --git a/code/modules/overmap/ships/computers/computer_shims.dm b/code/modules/overmap/ships/computers/computer_shims.dm new file mode 100644 index 0000000000..c685acc49d --- /dev/null +++ b/code/modules/overmap/ships/computers/computer_shims.dm @@ -0,0 +1,104 @@ +/* +** +** HELLO! DON'T COPY THINGS FROM HERE - READ THIS! +** +** The ship machines/computers ported from baystation expect certain procs and infrastruture that we don't have. +** I /could/ just port those computers to our code, but I actually *like* that infrastructure. But I +** don't have time (yet) to implement it fully in our codebase, so I'm shimming it here experimentally as a test +** bed for later implementing it on /obj/machinery and /obj/machinery/computer for everything. ~Leshana (March 2020) +*/ + +// +// Power +// + +// This will have this machine have its area eat this much power next tick, and not afterwards. Do not use for continued power draw. +/obj/machinery/proc/use_power_oneoff(var/amount, var/chan = -1) + return use_power(amount, chan) + +// Change one of the power consumption vars +/obj/machinery/proc/change_power_consumption(new_power_consumption, use_power_mode = USE_POWER_IDLE) + switch(use_power_mode) + if(USE_POWER_IDLE) + idle_power_usage = new_power_consumption + if(USE_POWER_ACTIVE) + active_power_usage = new_power_consumption + // No need to do anything else in our power scheme. + +// Defining directly here to avoid conflicts with existing set_broken procs in our codebase that behave differently. +/obj/machinery/atmospherics/unary/engine/proc/set_broken(var/new_state, var/cause) + if(!(stat & BROKEN) == !new_state) + return // Nothing changed + stat ^= BROKEN + update_icon() + + +// +// Compoenents +// + +/obj/machinery/proc/total_component_rating_of_type(var/part_type) + . = 0 + for(var/thing in component_parts) + if(istype(thing, part_type)) + var/obj/item/weapon/stock_parts/part = thing + . += part.rating + // Now isn't THIS a cool idea? + // for(var/path in uncreated_component_parts) + // if(ispath(path, part_type)) + // var/obj/item/weapon/stock_parts/comp = path + // . += initial(comp.rating) * uncreated_component_parts[path] + +// +// Skills +// +/obj/machinery/computer/ship + var/core_skill = /datum/skill/devices //The skill used for skill checks for this machine (mostly so subtypes can use different skills). + +// +// Topic +// + +/obj/machinery/computer/ship/proc/DefaultTopicState() + return global.default_state + +/obj/machinery/computer/ship/Topic(var/href, var/href_list = list(), var/datum/topic_state/state) + if((. = ..())) + return + state = state || DefaultTopicState() || global.default_state + if(CanUseTopic(usr, state, href_list) == STATUS_INTERACTIVE) + CouldUseTopic(usr) + return OnTopic(usr, href_list, state) + CouldNotUseTopic(usr) + return TRUE + +/obj/machinery/computer/ship/proc/OnTopic(var/mob/user, var/href_list, var/datum/topic_state/state) + return TOPIC_NOACTION + +// +// Interaction +// + +// If you want to have interface interactions handled for you conveniently, use this. +// Return TRUE for handled. +// If you perform direct interactions in here, you are responsible for ensuring that full interactivity checks have been made (i.e CanInteract). +// The checks leading in to here only guarantee that the user should be able to view a UI. +/obj/machinery/computer/ship/proc/interface_interact(var/mob/user) + ui_interact(user) + return TRUE + +/obj/machinery/computer/ship/attack_ai(mob/user) + if(CanUseTopic(user, DefaultTopicState()) > STATUS_CLOSE) + return interface_interact(user) + +// After a recent rework this should mostly be safe. +/obj/machinery/computer/ship/attack_ghost(mob/user) + interface_interact(user) + +// If you don't call parent in this proc, you must make all appropriate checks yourself. +// If you do, you must respect the return value. +/obj/machinery/computer/ship/attack_hand(mob/user) + if((. = ..())) + return + if(CanUseTopic(user, DefaultTopicState()) > STATUS_CLOSE) + return interface_interact(user) diff --git a/code/modules/overmap/ships/computers/engine_control.dm b/code/modules/overmap/ships/computers/engine_control.dm index 01920ee2e0..9c77e1b995 100644 --- a/code/modules/overmap/ships/computers/engine_control.dm +++ b/code/modules/overmap/ships/computers/engine_control.dm @@ -1,46 +1,24 @@ //Engine control and monitoring console -/obj/machinery/computer/engines +/obj/machinery/computer/ship/engines name = "engine control console" icon_keyboard = "tech_key" - icon_screen = "id" - var/state = "status" - var/list/engines = list() - var/obj/effect/map/ship/linked + icon_screen = "engines" + var/display_state = "status" -/obj/machinery/computer/engines/Initialize() - . = ..() - linked = map_sectors["[z]"] - if (linked) - if (!linked.eng_control) - linked.eng_control = src - testing("Engines console at level [z] found a corresponding overmap object '[linked.name]'.") - else - testing("Engines console at level [z] was unable to find a corresponding overmap object.") - - for(var/datum/ship_engine/E in engines) - if (E.zlevel == z && !(E in engines)) - engines += E - -/obj/machinery/computer/engines/attack_hand(var/mob/user as mob) - if(..()) - user.unset_machine() - return - - if(!isAI(user)) - user.set_machine(src) - - ui_interact(user) - -/obj/machinery/computer/engines/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) +/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"] = state + 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 engines) + for(var/datum/ship_engine/E in linked.engines) var/list/rdata[0] rdata["eng_type"] = E.name rdata["eng_on"] = E.is_on() @@ -48,54 +26,70 @@ 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", 380, 530) + 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/engines/Topic(href, href_list) +/obj/machinery/computer/ship/engines/OnTopic(var/mob/user, var/list/href_list, state) if(..()) - return 1 + return ..() if(href_list["state"]) - state = 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(user, 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(user, state)) + return var/limit = CLAMP(newlim/100, 0, 1) - if(E) + 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(E) + 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(E) + if(istype(E)) E.toggle() - - add_fingerprint(usr) - updateUsrDialog() - -/obj/machinery/computer/engines/proc/burn() - if(engines.len == 0) - return 0 - var/res = 0 - for(var/datum/ship_engine/E in engines) - res |= E.burn() - return res - -/obj/machinery/computer/engines/proc/get_total_thrust() - for(var/datum/ship_engine/E in engines) - . += E.get_thrust() + 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 index 0249fae482..1ba9cd875e 100644 --- a/code/modules/overmap/ships/computers/helm.dm +++ b/code/modules/overmap/ships/computers/helm.dm @@ -1,151 +1,187 @@ -/obj/machinery/computer/helm +// LEGACY_RECORD_STRUCTURE(all_waypoints, waypoint) +GLOBAL_LIST_EMPTY(all_waypoints) +/datum/computer_file/data/waypoint + var/list/fields + filetype = "WPT" + +/datum/computer_file/data/waypoint/New() + ..() + fields = list() + GLOB.all_waypoints.Add(src) + +/datum/computer_file/data/waypoint/Destroy() + . = ..() + GLOB.all_waypoints.Remove(src); +// End LEGACY_RECORD_STRUCTURE(all_waypoints, waypoint) + +/obj/machinery/computer/ship/helm name = "helm control console" - icon_keyboard = "med_key" - icon_screen = "id" - var/state = "status" - var/obj/effect/map/ship/linked //connected overmap object + icon_keyboard = "teleport_key" + icon_screen = "helm" + light_color = "#7faaff" + core_skill = /datum/skill/pilot var/autopilot = 0 - var/manual_control = 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/helm/Initialize() +/obj/machinery/computer/ship/helm/Initialize() . = ..() - linked = map_sectors["[z]"] - if (linked) - if(!linked.nav_control) - linked.nav_control = src - testing("Helm console at level [z] found a corresponding overmap object '[linked.name]'.") - else - testing("Helm console at level [z] was unable to find a corresponding overmap object.") + get_known_sectors() - for(var/level in map_sectors) - var/obj/effect/map/sector/S = map_sectors["[level]"] - if (istype(S) && S.always_known) - var/datum/data/record/R = new() +/obj/machinery/computer/ship/helm/proc/get_known_sectors() + var/area/overmap/map = locate() in world + 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 += R + known_sectors[S.name] = R -/obj/machinery/computer/helm/process() +/obj/machinery/computer/ship/helm/process() ..() if (autopilot && dx && dy) - var/turf/T = locate(dx,dy,1) + var/turf/T = locate(dx,dy,global.using_map.overmap_z) if(linked.loc == T) if(linked.is_still()) autopilot = 0 else linked.decelerate() - - var/brake_path = linked.get_brake_path() - - if(get_dist(linked.loc, T) > brake_path) - linked.accelerate(get_dir(linked.loc, T)) else - linked.decelerate() + 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) + linked.operator_skill = null//if this is on you can't dodge meteors return -/obj/machinery/computer/helm/relaymove(var/mob/user, direction) - if(manual_control && linked) - linked.relaymove(user,direction) +/obj/machinery/computer/ship/helm/relaymove(var/mob/user, direction) + if(viewing_overmap(user) && linked) + if(prob(user.skill_fail_chance(/datum/skill/pilot, 50, linked.skill_needed, factor = 1))) + direction = turn(direction,pick(90,-90)) + linked.relaymove(user, direction, accellimit) return 1 -/obj/machinery/computer/helm/check_eye(var/mob/user as mob) - if (!manual_control) - return -1 - if (!get_dist(user, src) > 1 || user.blinded || !linked ) - return -1 - return 0 - -/obj/machinery/computer/helm/attack_hand(var/mob/user as mob) - if(..()) - user.unset_machine() - manual_control = 0 - return - - if(!isAI(user)) - user.set_machine(src) - if(linked) - user.reset_view(linked) - - ui_interact(user) - -/obj/machinery/computer/helm/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(!linked) - return - +/obj/machinery/computer/ship/helm/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] - data["state"] = state - data["sector"] = linked.current_sector ? linked.current_sector.name : "Deep Space" - data["sector_info"] = linked.current_sector ? linked.current_sector.desc : "Not Available" - data["s_x"] = linked.x - data["s_y"] = linked.y - data["dest"] = dy && dx - data["d_x"] = dx - data["d_y"] = dy - data["speed"] = linked.get_speed() - data["accel"] = round(linked.get_acceleration()) - data["heading"] = linked.get_heading() ? dir2angle(linked.get_heading()) : 0 - data["autopilot"] = autopilot - data["manual_control"] = manual_control + 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 - var/list/locations[0] - for (var/datum/data/record/R in known_sectors) - 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["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_degrees() + data["autopilot"] = autopilot + data["manual_control"] = viewing_overmap(user) + data["canburn"] = linked.can_burn() + data["accellimit"] = accellimit*1000 - data["locations"] = locations + 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 - 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", 380, 530) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + if(linked.get_speed()) + data["ETAnext"] = "[round(linked.ETA()/10)] seconds" + else + data["ETAnext"] = "N/A" -/obj/machinery/computer/helm/Topic(href, href_list) + 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/OnTopic(var/mob/user, var/list/href_list, state) if(..()) - return 1 + return TOPIC_HANDLED - if (!linked) - return + if(!linked) + return TOPIC_HANDLED if (href_list["add"]) - var/datum/data/record/R = new() + 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(user,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(user, "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 - R.fields["x"] = CLAMP(newx, 1, world.maxx) + if(!CanInteract(user,state)) + return TOPIC_REFRESH var/newy = input("Input new entry y coordinate", "Coordinate input", linked.y) as num + if(!CanInteract(user,state)) + return TOPIC_NOACTION + R.fields["x"] = CLAMP(newx, 1, world.maxx) R.fields["y"] = CLAMP(newy, 1, world.maxy) - known_sectors += R + known_sectors[sec_name] = R if (href_list["remove"]) - var/datum/data/record/R = locate(href_list["remove"]) - known_sectors.Remove(R) + 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(user,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(user,state)) + return if (newy) dy = CLAMP(newy, 1, world.maxy) @@ -157,9 +193,20 @@ 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) + if(prob(user.skill_fail_chance(/datum/skill/pilot, 50, linked.skill_needed, factor = 1))) + ndir = turn(ndir,pick(90,-90)) + linked.relaymove(user, ndir, accellimit) if (href_list["brake"]) linked.decelerate() @@ -168,10 +215,71 @@ autopilot = !autopilot if (href_list["manual"]) - manual_control = !manual_control + viewing_overmap(user) ? unlook(user) : look(user) - if (href_list["state"]) - state = href_list["state"] - add_fingerprint(usr) + add_fingerprint(user) updateUsrDialog() + +/obj/machinery/computer/ship/navigation + name = "navigation console" + icon_keyboard = "generic_key" + icon_screen = "helm" + +/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_degrees() + 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/OnTopic(var/mob/user, var/list/href_list) + if(..()) + return TOPIC_HANDLED + + if (!linked) + return TOPIC_NOACTION + + if (href_list["viewing"]) + viewing_overmap(user) ? unlook(user) : look(user) + 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" + icon_keyboard = null + icon_screen = null + density = 0 + +/obj/machinery/computer/ship/navigation/telescreen/update_icon() + if(stat & NOPOWER || stat & BROKEN) + icon_state = "tele_off" + set_light(0) + else + icon_state = "tele_nav" + set_light(light_range_on, light_power_on) + ..() diff --git a/code/modules/overmap/ships/computers/sensors.dm b/code/modules/overmap/ships/computers/sensors.dm new file mode 100644 index 0000000000..ded9c5d978 --- /dev/null +++ b/code/modules/overmap/ships/computers/sensors.dm @@ -0,0 +1,226 @@ +/obj/machinery/computer/ship/sensors + name = "sensors console" + icon_keyboard = "teleport_key" + icon_screen = "teleport" + 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 global.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/OnTopic(var/mob/user, var/list/href_list, state) + if(..()) + return TOPIC_HANDLED + + if (!linked) + return TOPIC_NOACTION + + if (href_list["viewing"]) + if(user && !isAI(user)) + viewing_overmap(user) ? unlook(user) : look(user) + 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(user,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/weapon/paper/(get_turf(src), O.get_scan_data(user), "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(sensor_range + 0.5, 4) + 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 bluespace 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/weapon/W, mob/user) + var/damage = max_health - health + if(damage && istype(W, /obj/item/weapon/weldingtool)) + + var/obj/item/weapon/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_oneoff(idle_power_usage*5) + update_use_power(!use_power) + update_icon() + +/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!") + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(3, 1, src) + s.start() + + 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 + change_power_consumption(1500 * (range**2), USE_POWER_IDLE) //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/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 = "Miniaturized gravity scanner with various other sensors, used to detect irregularities in surrounding space. Can only run in vacuum to protect delicate quantum bluespace 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 0000000000..4ab9d241df --- /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 SSshuttles, 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("
Error
Unable to connect to [flavor].
Reconnect
") + popup.open() + +// In computer_shims for now - we had to define it. +// /obj/machinery/computer/ship/interface_interact(var/mob/user) +// ui_interact(user) +// return TRUE + +/obj/machinery/computer/ship/OnTopic(var/mob/user, var/list/href_list) + if(..()) + return TOPIC_HANDLED + if(href_list["sync"]) + sync_linked() + return TOPIC_REFRESH + if(href_list["close"]) + unlook(user) + user.unset_machine() + return TOPIC_HANDLED + return TOPIC_NOACTION + +// Management of mob view displacement. look to shift view to the ship on the overmap; unlook to shift back. + +/obj/machinery/computer/ship/proc/look(var/mob/user) + if(linked) + user.reset_view(linked) + user.set_viewsize(world.view + extra_view) + GLOB.moved_event.register(user, src, /obj/machinery/computer/ship/proc/unlook) + // TODO GLOB.stat_set_event.register(user, src, /obj/machinery/computer/ship/proc/unlook) + LAZYDISTINCTADD(viewers, weakref(user)) + +/obj/machinery/computer/ship/proc/unlook(var/mob/user) + user.reset_view() + user.set_viewsize() // reset to default + GLOB.moved_event.unregister(user, src, /obj/machinery/computer/ship/proc/unlook) + // TODO GLOB.stat_set_event.unregister(user, src, /obj/machinery/computer/ship/proc/unlook) + LAZYREMOVE(viewers, weakref(user)) + +/obj/machinery/computer/ship/proc/viewing_overmap(mob/user) + return (weakref(user) in viewers) + +/obj/machinery/computer/ship/CouldNotUseTopic(mob/user) + . = ..() + unlook(user) + +/obj/machinery/computer/ship/CouldUseTopic(mob/user) + . = ..() + if(viewing_overmap(user)) + look(user) + +/obj/machinery/computer/ship/check_eye(var/mob/user) + if (!get_dist(user, src) > 1 || user.blinded || !linked ) + unlook(user) + return -1 + else + return 0 + +/obj/machinery/computer/ship/sensors/Destroy() + sensors = null + if(LAZYLEN(viewers)) + for(var/weakref/W in viewers) + var/M = W.resolve() + if(M) + unlook(M) + . = ..() \ No newline at end of file diff --git a/code/modules/overmap/ships/computers/shuttle.dm b/code/modules/overmap/ships/computers/shuttle.dm index 09bf47512d..726c339178 100644 --- a/code/modules/overmap/ships/computers/shuttle.dm +++ b/code/modules/overmap/ships/computers/shuttle.dm @@ -1,139 +1,45 @@ //Shuttle controller computer for shuttles going between sectors -/datum/shuttle/ferry/var/range = 0 //how many overmap tiles can shuttle go, for picking destinatiosn and returning. /obj/machinery/computer/shuttle_control/explore - name = "exploration shuttle console" - shuttle_tag = "Exploration" - req_access = list() - var/landing_type //area for shuttle ship-side - var/obj/effect/map/destination //current destination - var/obj/effect/map/home //current destination + name = "general shuttle control console" + ui_template = "shuttle_control_console_exploration.tmpl" -/obj/machinery/computer/shuttle_control/explore/Initialize() +/obj/machinery/computer/shuttle_control/explore/get_ui_data(var/datum/shuttle/autodock/overmap/shuttle) . = ..() - home = map_sectors["[z]"] - shuttle_tag = "[shuttle_tag]-[z]" - if(!shuttle_controller.shuttles[shuttle_tag]) - var/datum/shuttle/ferry/shuttle = new() - shuttle.warmup_time = 10 - shuttle.area_station = locate(landing_type) - shuttle.area_offsite = shuttle.area_station - shuttle_controller.shuttles[shuttle_tag] = shuttle - shuttle_controller.process_shuttles += shuttle - testing("Exploration shuttle '[shuttle_tag]' at z-level [z] successfully added.") + if(istype(shuttle)) + var/total_gas = 0 + for(var/obj/structure/fuel_port/FP in shuttle.fuel_ports) //loop through fuel ports + var/obj/item/weapon/tank/fuel_tank = locate() in FP + if(fuel_tank) + total_gas += fuel_tank.air_contents.total_moles -//Sets destination to new sector. Can be null. -/obj/machinery/computer/shuttle_control/explore/proc/update_destination(var/obj/effect/map/D) - destination = D - if(destination && shuttle_controller.shuttles[shuttle_tag]) - var/datum/shuttle/ferry/shuttle = shuttle_controller.shuttles[shuttle_tag] - shuttle.area_offsite = destination.shuttle_landing - testing("Shuttle controller [shuttle_tag] now sends shuttle to [destination]") - shuttle_controller.shuttles[shuttle_tag] = shuttle + var/fuel_span = "good" + if(total_gas < shuttle.fuel_consumption * 2) + fuel_span = "bad" -//Gets all sectors with landing zones in shuttle's range -/obj/machinery/computer/shuttle_control/explore/proc/get_possible_destinations() - var/list/res = list() - var/datum/shuttle/ferry/shuttle = shuttle_controller.shuttles[shuttle_tag] - for (var/obj/effect/map/S in orange(shuttle.range, home)) - if(S.shuttle_landing) - res += S - return res + . += list( + "destination_name" = shuttle.get_destination_name(), + "can_pick" = shuttle.moving_status == SHUTTLE_IDLE, + "fuel_usage" = shuttle.fuel_consumption * 100, + "remaining_fuel" = round(total_gas, 0.01) * 100, + "fuel_span" = fuel_span + ) -//Checks if current destination is still reachable -/obj/machinery/computer/shuttle_control/explore/proc/check_destination() - var/datum/shuttle/ferry/shuttle = shuttle_controller.shuttles[shuttle_tag] - return shuttle && destination && get_dist(home, destination) <= shuttle.range +/obj/machinery/computer/shuttle_control/explore/handle_topic_href(var/datum/shuttle/autodock/overmap/shuttle, var/list/href_list) + if(ismob(usr)) + var/mob/user = usr + shuttle.operator_skill = user.get_skill_value(/datum/skill/pilot) -/obj/machinery/computer/shuttle_control/explore/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - var/data[0] - var/datum/shuttle/ferry/shuttle = shuttle_controller.shuttles[shuttle_tag] - if (!istype(shuttle)) - return - - //If we are already there, or can't reach place anymore, reset destination - if(!shuttle.location && !check_destination()) - destination = null - - //check if shuttle can fly at all - var/can_go = !isnull(destination) - var/current_destination = destination ? destination.name : "None" - //shuttle doesn't need destination set to return home, as long as it's in range. - if(shuttle.location) - current_destination = "Return" - var/area/offsite = shuttle.area_offsite - var/obj/effect/map/cur_loc = map_sectors["[offsite.z]"] - can_go = (get_dist(home,cur_loc) <= shuttle.range) - - //disable picking locations if there are none, or shuttle is already off-site - var/list/possible_d = get_possible_destinations() - var/can_pick = !shuttle.location && possible_d.len - - var/shuttle_state - switch(shuttle.moving_status) - if(SHUTTLE_IDLE) shuttle_state = "idle" - if(SHUTTLE_WARMUP) shuttle_state = "warmup" - if(SHUTTLE_INTRANSIT) shuttle_state = "in_transit" - - var/shuttle_status - switch (shuttle.process_state) - if(IDLE_STATE) - if (shuttle.in_use) - shuttle_status = "Busy." - else if (!shuttle.location) - shuttle_status = "Standing-by at station." - else - shuttle_status = "Standing-by at offsite location." - if(WAIT_LAUNCH, FORCE_LAUNCH) - shuttle_status = "Shuttle has received command and will depart shortly." - if(WAIT_ARRIVE) - shuttle_status = "Proceeding to destination." - if(WAIT_FINISH) - shuttle_status = "Arriving at destination now." - - data = list( - "destination_name" = current_destination, - "can_pick" = can_pick, - "shuttle_status" = shuttle_status, - "shuttle_state" = shuttle_state, - "has_docking" = shuttle.docking_controller? 1 : 0, - "docking_status" = shuttle.docking_controller? shuttle.docking_controller.get_docking_status() : null, - "docking_override" = shuttle.docking_controller? shuttle.docking_controller.override_enabled : null, - "can_launch" = can_go && shuttle.can_launch(), - "can_cancel" = can_go && shuttle.can_cancel(), - "can_force" = can_go && shuttle.can_force(), - ) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - - if (!ui) - ui = new(user, src, ui_key, "shuttle_control_console_exploration.tmpl", "[shuttle_tag] Shuttle Control", 470, 310) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/computer/shuttle_control/explore/Topic(href, href_list) - if(..()) - return 1 - - usr.set_machine(src) - src.add_fingerprint(usr) - - var/datum/shuttle/ferry/shuttle = shuttle_controller.shuttles[shuttle_tag] - if (!istype(shuttle)) + if((. = ..()) != null) return if(href_list["pick"]) - var/obj/effect/map/self = map_sectors["[z]"] - if(self) - var/list/possible_d = get_possible_destinations() - var/obj/effect/map/D - if(possible_d.len) - D = input("Choose shuttle destination", "Shuttle Destination") as null|anything in possible_d - update_destination(D) - - if(href_list["move"]) - shuttle.launch(src) - if(href_list["force"]) - shuttle.force_launch(src) - else if(href_list["cancel"]) - shuttle.cancel_launch(src) \ No newline at end of file + var/list/possible_d = shuttle.get_possible_destinations() + var/D + if(possible_d.len) + D = input("Choose shuttle destination", "Shuttle Destination") as null|anything in possible_d + else + to_chat(usr,"No valid landing sites in range.") + possible_d = shuttle.get_possible_destinations() + if(CanInteract(usr, global.default_state) && (D in possible_d)) + shuttle.set_destination(possible_d[D]) + return TOPIC_REFRESH diff --git a/code/modules/overmap/ships/engines/engine.dm b/code/modules/overmap/ships/engines/engine.dm index 6e927e6431..a5d3bc7016 100644 --- a/code/modules/overmap/ships/engines/engine.dm +++ b/code/modules/overmap/ships/engines/engine.dm @@ -3,58 +3,43 @@ var/list/ship_engines = list() /datum/ship_engine var/name = "ship engine" - var/obj/machinery/engine //actual engine object - var/zlevel = 0 + var/obj/machinery/holder //actual engine object -/datum/ship_engine/New(var/obj/machinery/holder) - engine = holder - zlevel = holder.z - for(var/obj/machinery/computer/engines/E in machines) - if (E.z == zlevel && !(src in E.engines)) - E.engines += src - break +/datum/ship_engine/New(var/obj/machinery/_holder) + ..() + holder = _holder + ship_engines += src -//Tries to fire the engine. If successfull, returns 1 +/datum/ship_engine/proc/can_burn() + return 0 + +//Tries to fire the engine. Returns thrust /datum/ship_engine/proc/burn() - if(!engine) - die() - return 1 + return 0 //Returns status string for this engine /datum/ship_engine/proc/get_status() - if(!engine) - die() return "All systems nominal" /datum/ship_engine/proc/get_thrust() - if(!engine) - die() - return 100 + return 1 //Sets thrust limiter, a number between 0 and 1 /datum/ship_engine/proc/set_thrust_limit(var/new_limit) - if(!engine) - die() return 1 /datum/ship_engine/proc/get_thrust_limit() - if(!engine) - die() return 1 /datum/ship_engine/proc/is_on() - if(!engine) - die() return 1 /datum/ship_engine/proc/toggle() - if(!engine) - die() return 1 -/datum/ship_engine/proc/die() - for(var/obj/machinery/computer/engines/E in machines) - if (E.z == zlevel) - E.engines -= src - break - qdel(src) \ No newline at end of file +/datum/ship_engine/Destroy() + ship_engines -= src + for(var/obj/effect/overmap/visitable/ship/S in SSshuttles.ships) + S.engines -= src + holder = null + . = ..() \ No newline at end of file diff --git a/code/modules/overmap/ships/engines/gas_thruster.dm b/code/modules/overmap/ships/engines/gas_thruster.dm new file mode 100644 index 0000000000..04101984c4 --- /dev/null +++ b/code/modules/overmap/ships/engines/gas_thruster.dm @@ -0,0 +1,218 @@ +//Gas nozzle engine +/datum/ship_engine/gas_thruster + name = "gas thruster" + var/obj/machinery/atmospherics/unary/engine/nozzle + +/datum/ship_engine/gas_thruster/New(var/obj/machinery/_holder) + ..() + nozzle = _holder + +/datum/ship_engine/gas_thruster/Destroy() + nozzle = null + . = ..() + +/datum/ship_engine/gas_thruster/get_status() + return nozzle.get_status() + +/datum/ship_engine/gas_thruster/get_thrust() + return nozzle.get_thrust() + +/datum/ship_engine/gas_thruster/burn() + return nozzle.burn() + +/datum/ship_engine/gas_thruster/set_thrust_limit(var/new_limit) + nozzle.thrust_limit = new_limit + +/datum/ship_engine/gas_thruster/get_thrust_limit() + return nozzle.thrust_limit + +/datum/ship_engine/gas_thruster/is_on() + if(nozzle.use_power && nozzle.operable()) + if(nozzle.next_on > world.time) + return -1 + else + return 1 + return 0 + +/datum/ship_engine/gas_thruster/toggle() + if(nozzle.use_power) + nozzle.update_use_power(USE_POWER_OFF) + else + if(nozzle.blockage) + if(nozzle.check_blockage()) + return + nozzle.update_use_power(USE_POWER_IDLE) + if(nozzle.stat & NOPOWER)//try again + nozzle.power_change() + if(nozzle.is_on())//if everything is in working order, start booting! + nozzle.next_on = world.time + nozzle.boot_time + +/datum/ship_engine/gas_thruster/can_burn() + return nozzle.is_on() && nozzle.check_fuel() + +//Actual thermal nozzle engine object + +/obj/machinery/atmospherics/unary/engine + name = "rocket nozzle" + desc = "Simple rocket nozzle, expelling gas at hypersonic velocities to propell the ship." + icon = 'icons/turf/shuttle_parts.dmi' + icon_state = "nozzle" + opacity = 1 + density = 1 + can_atmos_pass = ATMOS_PASS_NO + connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_FUEL + + // construct_state = /decl/machine_construction/default/panel_closed + // maximum_component_parts = list(/obj/item/weapon/stock_parts = 6)//don't want too many, let upgraded component shine + // uncreated_component_parts = list(/obj/item/weapon/stock_parts/power/apc/buildable = 1) + + use_power = USE_POWER_OFF + power_channel = EQUIP + idle_power_usage = 1000 + + var/datum/ship_engine/gas_thruster/controller + var/thrust_limit = 1 //Value between 1 and 0 to limit the resulting thrust + var/volume_per_burn = 15 //20 litres(with bin) + var/charge_per_burn = 3600 + var/boot_time = 35 + var/next_on + var/blockage + +/obj/machinery/atmospherics/unary/engine/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) + return 0 + +/obj/machinery/atmospherics/unary/engine/Initialize() + . = ..() + controller = new(src) + update_nearby_tiles(need_rebuild=1) + + for(var/ship in SSshuttles.ships) + var/obj/effect/overmap/visitable/ship/S = ship + if(S.check_ownership(src)) + S.engines |= controller + if(dir != S.fore_dir) + set_broken(TRUE) + break + +/obj/machinery/atmospherics/unary/engine/Destroy() + QDEL_NULL(controller) + update_nearby_tiles() + . = ..() + +/obj/machinery/atmospherics/unary/engine/proc/get_status() + . = list() + .+= "Location: [get_area(src)]." + if(stat & NOPOWER) + .+= "Insufficient power to operate." + if(!check_fuel()) + .+= "Insufficient fuel for a burn." + if(stat & BROKEN) + .+= "Inoperable engine configuration." + if(blockage) + .+= "Obstruction of airflow detected." + + .+= "Propellant total mass: [round(air_contents.get_mass(),0.01)] kg." + .+= "Propellant used per burn: [round(air_contents.get_mass() * volume_per_burn * thrust_limit / air_contents.volume,0.01)] kg." + .+= "Propellant pressure: [round(air_contents.return_pressure()/1000,0.1)] MPa." + . = jointext(.,"
") + +/obj/machinery/atmospherics/unary/engine/power_change() + . = ..() + if(stat & NOPOWER) + update_use_power(USE_POWER_OFF) + +/obj/machinery/atmospherics/unary/engine/proc/is_on() + return use_power && operable() && (next_on < world.time) + +/obj/machinery/atmospherics/unary/engine/proc/check_fuel() + return air_contents.total_moles > 5 // minimum fuel usage is five moles, for EXTREMELY hot mix or super low pressure + +/obj/machinery/atmospherics/unary/engine/proc/get_thrust() + if(!is_on() || !check_fuel()) + return 0 + var/used_part = volume_per_burn * thrust_limit / air_contents.volume + . = calculate_thrust(air_contents, used_part) + return + +/obj/machinery/atmospherics/unary/engine/proc/check_blockage() + blockage = FALSE + var/exhaust_dir = reverse_direction(dir) + var/turf/A = get_step(src, exhaust_dir) + var/turf/B = A + while(isturf(A) && !(istype(A, /turf/space) || isopenspace(A))) + if((B.c_airblock(A)) & AIR_BLOCKED) + blockage = TRUE + break + B = A + A = get_step(A, exhaust_dir) + return blockage + +/obj/machinery/atmospherics/unary/engine/proc/burn() + if(!is_on()) + return 0 + if(!check_fuel() || (0 < use_power_oneoff(charge_per_burn)) || check_blockage()) + audible_message(src,"[src] coughs once and goes silent!") + update_use_power(USE_POWER_OFF) + return 0 + + var/datum/gas_mixture/removed = air_contents.remove_ratio(volume_per_burn * thrust_limit / air_contents.volume) + if(!removed) + return 0 + . = calculate_thrust(removed) + playsound(loc, 'sound/machines/thruster.ogg', 100 * thrust_limit, 0, world.view * 4, 0.1) + if(network) + network.update = 1 + + var/exhaust_dir = reverse_direction(dir) + var/turf/T = get_step(src,exhaust_dir) + if(T) + T.assume_air(removed) + new/obj/effect/engine_exhaust(T, exhaust_dir, air_contents.check_combustability() && air_contents.temperature >= PHORON_MINIMUM_BURN_TEMPERATURE) + +/obj/machinery/atmospherics/unary/engine/proc/calculate_thrust(datum/gas_mixture/propellant, used_part = 1) + return round(sqrt(propellant.get_mass() * used_part * sqrt(air_contents.return_pressure()/200)),0.1) + +/obj/machinery/atmospherics/unary/engine/RefreshParts() + ..() + //allows them to upgrade the max limit of fuel intake (which only gives diminishing returns) for increase in max thrust but massive reduction in fuel economy + var/bin_upgrade = 5 * CLAMP(total_component_rating_of_type(/obj/item/weapon/stock_parts/matter_bin), 0, 6)//5 litre per rank + volume_per_burn = bin_upgrade ? initial(volume_per_burn) + bin_upgrade : 2 //Penalty missing part: 10% fuel use, no thrust + boot_time = bin_upgrade ? initial(boot_time) - bin_upgrade : initial(boot_time) * 2 + //energy cost - thb all of this is to limit the use of back up batteries + var/energy_upgrade = CLAMP(total_component_rating_of_type(/obj/item/weapon/stock_parts/capacitor), 0.1, 6) + charge_per_burn = initial(charge_per_burn) / energy_upgrade + change_power_consumption(initial(idle_power_usage) / energy_upgrade, USE_POWER_IDLE) + +//Exhaust effect +/obj/effect/engine_exhaust + name = "engine exhaust" + icon = 'icons/effects/effects.dmi' + icon_state = "smoke" + light_color = "#ed9200" + anchored = 1 + +/obj/effect/engine_exhaust/New(var/turf/nloc, var/ndir, var/flame) + ..(nloc) + if(flame) + icon_state = "exhaust" + nloc.hotspot_expose(1000,125) + set_light(0.5, 3) + set_dir(ndir) + QDEL_IN(src, 20) + +/obj/item/weapon/circuitboard/unary_atmos/engine //why don't we move this elsewhere? + name = T_BOARD("gas thruster") + icon_state = "mcontroller" + build_path = /obj/machinery/atmospherics/unary/engine + origin_tech = list(TECH_POWER = 1, TECH_ENGINEERING = 2) + req_components = list( + /obj/item/stack/cable_coil = 30, + /obj/item/pipe = 2, + /obj/item/weapon/stock_parts/matter_bin = 1, + /obj/item/weapon/stock_parts/capacitor = 2) + +// Not Implemented - Variant that pulls power from cables. Too complicated without bay's power components. +// /obj/machinery/atmospherics/unary/engine/terminal +// base_type = /obj/machinery/atmospherics/unary/engine +// stock_part_presets = list(/decl/stock_part_preset/terminal_setup) +// uncreated_component_parts = list(/obj/item/weapon/stock_parts/power/terminal/buildable = 1) diff --git a/code/modules/overmap/ships/engines/ion_thruster.dm b/code/modules/overmap/ships/engines/ion_thruster.dm new file mode 100644 index 0000000000..3252159391 --- /dev/null +++ b/code/modules/overmap/ships/engines/ion_thruster.dm @@ -0,0 +1,86 @@ +/datum/ship_engine/ion + name = "ion thruster" + var/obj/machinery/ion_engine/thruster + +/datum/ship_engine/ion/New(var/obj/machinery/_holder) + ..() + thruster = _holder + +/datum/ship_engine/ion/Destroy() + thruster = null + . = ..() + +/datum/ship_engine/ion/get_status() + return thruster.get_status() + +/datum/ship_engine/ion/get_thrust() + return thruster.get_thrust() + +/datum/ship_engine/ion/burn() + return thruster.burn() + +/datum/ship_engine/ion/set_thrust_limit(var/new_limit) + thruster.thrust_limit = new_limit + +/datum/ship_engine/ion/get_thrust_limit() + return thruster.thrust_limit + +/datum/ship_engine/ion/is_on() + return thruster.on && thruster.powered() + +/datum/ship_engine/ion/toggle() + thruster.on = !thruster.on + +/datum/ship_engine/ion/can_burn() + return thruster.on && thruster.powered() + +/obj/machinery/ion_engine + name = "ion propulsion device" + desc = "An advanced ion propulsion device, using energy and minutes amount of gas to generate thrust." + icon = 'icons/turf/shuttle_parts.dmi' + icon_state = "nozzle" + power_channel = ENVIRON + idle_power_usage = 100 + anchored = TRUE + // construct_state = /decl/machine_construction/default/panel_closed + var/datum/ship_engine/ion/controller + var/thrust_limit = 1 + var/on = 1 + var/burn_cost = 7500 + var/generated_thrust = 2.5 + +/obj/machinery/ion_engine/Initialize() + . = ..() + controller = new(src) + +/obj/machinery/ion_engine/Destroy() + QDEL_NULL(controller) + . = ..() + +/obj/machinery/ion_engine/proc/get_status() + . = list() + .+= "Location: [get_area(src)]." + if(!powered()) + .+= "Insufficient power to operate." + + . = jointext(.,"
") + +/obj/machinery/ion_engine/proc/burn() + if(!on && !powered()) + return 0 + use_power_oneoff(burn_cost) + . = thrust_limit * generated_thrust + +/obj/machinery/ion_engine/proc/get_thrust() + return thrust_limit * generated_thrust * on + +/obj/item/weapon/circuitboard/engine/ion + name = T_BOARD("ion propulsion device") + board_type = "machine" + icon_state = "mcontroller" + build_path = /obj/machinery/ion_engine + origin_tech = list(TECH_POWER = 1, TECH_ENGINEERING = 2) + req_components = list( + /obj/item/stack/cable_coil = 2, + /obj/item/weapon/stock_parts/matter_bin = 1, + /obj/item/weapon/stock_parts/capacitor = 2) \ No newline at end of file diff --git a/code/modules/overmap/ships/landable.dm b/code/modules/overmap/ships/landable.dm new file mode 100644 index 0000000000..967fdeb571 --- /dev/null +++ b/code/modules/overmap/ships/landable.dm @@ -0,0 +1,173 @@ +// These come with shuttle functionality. Need to be assigned a (unique) shuttle datum name. +// Mapping location doesn't matter, so long as on a map loaded at the same time as the shuttle areas. +// Multiz shuttles currently not supported. Non-autodock shuttles currently not supported. + +/obj/effect/overmap/visitable/ship/landable + var/shuttle // Name of associated shuttle. Must be autodock. + var/obj/effect/shuttle_landmark/ship/landmark // Record our open space landmark for easy reference. + var/multiz = 0 // Index of multi-z levels, starts at 0 + var/status = SHIP_STATUS_LANDED + icon_state = "shuttle" + moving_state = "shuttle_moving" + +/obj/effect/overmap/visitable/ship/landable/Destroy() + GLOB.shuttle_moved_event.unregister(SSshuttles.shuttles[shuttle], src) + return ..() + +/obj/effect/overmap/visitable/ship/landable/can_burn() + if(status != SHIP_STATUS_OVERMAP) + return 0 + return ..() + +/obj/effect/overmap/visitable/ship/landable/burn() + if(status != SHIP_STATUS_OVERMAP) + return 0 + return ..() + +/obj/effect/overmap/visitable/ship/landable/check_ownership(obj/object) + var/datum/shuttle/shuttle_datum = SSshuttles.shuttles[shuttle] + if(!shuttle_datum) + return + var/list/areas = shuttle_datum.find_childfree_areas() + if(get_area(object) in areas) + return 1 + +// We autobuild our z levels. +/obj/effect/overmap/visitable/ship/landable/find_z_levels() + for(var/i = 0 to multiz) + world.increment_max_z() + map_z += world.maxz + + var/turf/center_loc = locate(round(world.maxx/2), round(world.maxy/2), world.maxz) + landmark = new (center_loc, shuttle) + add_landmark(landmark, shuttle) + + var/visitor_dir = fore_dir + for(var/landmark_name in list("FORE", "PORT", "AFT", "STARBOARD")) + var/turf/visitor_turf = get_ranged_target_turf(center_loc, visitor_dir, round(min(world.maxx/4, world.maxy/4))) + var/obj/effect/shuttle_landmark/visiting_shuttle/visitor_landmark = new (visitor_turf, landmark, landmark_name) + add_landmark(visitor_landmark) + visitor_dir = turn(visitor_dir, 90) + + if(multiz) + new /obj/effect/landmark/map_data(center_loc, (multiz + 1)) + +/obj/effect/overmap/visitable/ship/landable/get_areas() + var/datum/shuttle/shuttle_datum = SSshuttles.shuttles[shuttle] + if(!shuttle_datum) + return list() + return shuttle_datum.find_childfree_areas() + +/obj/effect/overmap/visitable/ship/landable/populate_sector_objects() + ..() + var/datum/shuttle/shuttle_datum = SSshuttles.shuttles[shuttle] + if(istype(shuttle_datum,/datum/shuttle/autodock/overmap)) + var/datum/shuttle/autodock/overmap/oms = shuttle_datum + oms.myship = src + GLOB.shuttle_moved_event.register(shuttle_datum, src, .proc/on_shuttle_jump) + on_landing(landmark, shuttle_datum.current_location) // We "land" at round start to properly place ourselves on the overmap. + +/obj/effect/shuttle_landmark/ship + name = "Open Space" + landmark_tag = "ship" + flags = SLANDMARK_FLAG_AUTOSET | SLANDMARK_FLAG_ZERO_G + var/shuttle_name + var/list/visitors // landmark -> visiting shuttle stationed there + +/obj/effect/shuttle_landmark/ship/Initialize(mapload, shuttle_name) + landmark_tag += "_[shuttle_name]" + src.shuttle_name = shuttle_name + . = ..() + +/obj/effect/shuttle_landmark/ship/Destroy() + var/obj/effect/overmap/visitable/ship/landable/ship = map_sectors["[z]"] + if(istype(ship) && ship.landmark == src) + ship.landmark = null + . = ..() + +/obj/effect/shuttle_landmark/ship/cannot_depart(datum/shuttle/shuttle) + if(LAZYLEN(visitors)) + return "Grappled by other shuttle; cannot manouver." + +/obj/effect/shuttle_landmark/visiting_shuttle + flags = SLANDMARK_FLAG_AUTOSET | SLANDMARK_FLAG_ZERO_G + var/obj/effect/shuttle_landmark/ship/core_landmark + +/obj/effect/shuttle_landmark/visiting_shuttle/Initialize(mapload, obj/effect/shuttle_landmark/ship/master, _name) + core_landmark = master + name = _name + landmark_tag = master.shuttle_name + _name + GLOB.destroyed_event.register(master, src, /datum/proc/qdel_self) + . = ..() + +/obj/effect/shuttle_landmark/visiting_shuttle/Destroy() + GLOB.destroyed_event.unregister(core_landmark, src) + LAZYREMOVE(core_landmark.visitors, src) + core_landmark = null + . = ..() + +/obj/effect/shuttle_landmark/visiting_shuttle/is_valid(datum/shuttle/shuttle) + . = ..() + if(!.) + return + var/datum/shuttle/boss_shuttle = SSshuttles.shuttles[core_landmark.shuttle_name] + if(boss_shuttle.current_location != core_landmark) + return FALSE // Only available when our governing shuttle is in space. + if(shuttle == boss_shuttle) // Boss shuttle only lands on main landmark + return FALSE + +/obj/effect/shuttle_landmark/visiting_shuttle/shuttle_arrived(datum/shuttle/shuttle) + LAZYSET(core_landmark.visitors, src, shuttle) + GLOB.shuttle_moved_event.register(shuttle, src, .proc/shuttle_left) + +/obj/effect/shuttle_landmark/visiting_shuttle/proc/shuttle_left(datum/shuttle/shuttle, obj/effect/shuttle_landmark/old_landmark, obj/effect/shuttle_landmark/new_landmark) + if(old_landmark == src) + GLOB.shuttle_moved_event.unregister(shuttle, src) + LAZYREMOVE(core_landmark.visitors, src) + +/obj/effect/overmap/visitable/ship/landable/proc/on_shuttle_jump(datum/shuttle/given_shuttle, obj/effect/shuttle_landmark/from, obj/effect/shuttle_landmark/into) + if(given_shuttle != SSshuttles.shuttles[shuttle]) + return + var/datum/shuttle/autodock/auto = given_shuttle + if(into == auto.landmark_transition) + status = SHIP_STATUS_TRANSIT + on_takeoff(from, into) + return + if(into == landmark) + status = SHIP_STATUS_OVERMAP + on_takeoff(from, into) + return + status = SHIP_STATUS_LANDED + on_landing(from, into) + +/obj/effect/overmap/visitable/ship/landable/proc/on_landing(obj/effect/shuttle_landmark/from, obj/effect/shuttle_landmark/into) + var/obj/effect/overmap/visitable/target = map_sectors["[into.z]"] + var/datum/shuttle/shuttle_datum = SSshuttles.shuttles[shuttle] + if(into.landmark_tag == shuttle_datum.motherdock) // If our motherdock is a landable ship, it won't be found properly here so we need to find it manually. + for(var/obj/effect/overmap/visitable/ship/landable/landable in SSshuttles.ships) + if(landable.shuttle == shuttle_datum.mothershuttle) + target = landable + break + if(!target || target == src) + return + forceMove(target) + halt() + +/obj/effect/overmap/visitable/ship/landable/proc/on_takeoff(obj/effect/shuttle_landmark/from, obj/effect/shuttle_landmark/into) + if(!isturf(loc)) + forceMove(get_turf(loc)) + unhalt() + +/obj/effect/overmap/visitable/ship/landable/get_landed_info() + switch(status) + if(SHIP_STATUS_LANDED) + var/obj/effect/overmap/visitable/location = loc + if(istype(loc, /obj/effect/overmap/visitable/sector)) + return "Landed on \the [location.name]. Use secondary thrust to get clear before activating primary engines." + if(istype(loc, /obj/effect/overmap/visitable/ship)) + return "Docked with \the [location.name]. Use secondary thrust to get clear before activating primary engines." + return "Docked with an unknown object." + if(SHIP_STATUS_TRANSIT) + return "Maneuvering under secondary thrust." + if(SHIP_STATUS_OVERMAP) + return "In open space." \ No newline at end of file diff --git a/code/modules/overmap/ships/ship.dm b/code/modules/overmap/ships/ship.dm index 0abd450a69..44a2d8e45e 100644 --- a/code/modules/overmap/ships/ship.dm +++ b/code/modules/overmap/ships/ship.dm @@ -1,116 +1,234 @@ -/obj/effect/map/ship +#define SHIP_MOVE_RESOLUTION 0.00001 +#define MOVING(speed) abs(speed) >= min_speed +#define SANITIZE_SPEED(speed) SIGN(speed) * CLAMP(abs(speed), 0, max_speed) +#define CHANGE_SPEED_BY(speed_var, v_diff) \ + v_diff = SANITIZE_SPEED(v_diff);\ + if(!MOVING(speed_var + v_diff)) \ + {speed_var = 0};\ + else \ + {speed_var = SANITIZE_SPEED((speed_var + v_diff)/(1 + speed_var*v_diff/(max_speed ** 2)))} +// Uses Lorentzian dynamics to avoid going too fast. + +/obj/effect/overmap/visitable/ship name = "generic ship" desc = "Space faring vessel." - icon_state = "sheet-sandstone" - var/vessel_mass = 9000 //tonnes, random number - var/default_delay = 60 - var/list/speed = list(0,0) - var/last_burn = 0 - var/list/last_movement = list(0,0) - var/fore_dir = NORTH - var/rotate = 1 //For proc rotate + icon_state = "ship" + var/moving_state = "ship_moving" - var/obj/effect/map/current_sector - var/obj/machinery/computer/helm/nav_control - var/obj/machinery/computer/engines/eng_control + var/vessel_mass = 10000 //tonnes, arbitrary number, affects acceleration provided by engines + var/vessel_size = SHIP_SIZE_LARGE //arbitrary number, affects how likely are we to evade meteors + var/max_speed = 1/(1 SECOND) //"speed of light" for the ship, in turfs/tick. + var/min_speed = 1/(2 MINUTES) // Below this, we round speed to 0 to avoid math errors. -/obj/effect/map/ship/Initialize() + var/list/speed = list(0,0) //speed in x,y direction + var/last_burn = 0 //worldtime when ship last acceleated + var/burn_delay = 1 SECOND //how often ship can do burns + var/list/last_movement = list(0,0) //worldtime when ship last moved in x,y direction + var/fore_dir = NORTH //what dir ship flies towards for purpose of moving stars effect procs + + var/list/engines = list() + var/engines_state = 0 //global on/off toggle for all engines + var/thrust_limit = 1 //global thrust limit for all engines, 0..1 + var/halted = 0 //admin halt or other stop. + var/skill_needed = SKILL_ADEPT //piloting skill needed to steer it without going in random dir + var/operator_skill + +/obj/effect/overmap/visitable/ship/Initialize() . = ..() - for(var/obj/machinery/computer/engines/E in machines) - if (E.z == map_z) - eng_control = E - break - for(var/obj/machinery/computer/helm/H in machines) - if (H.z == map_z) - nav_control = H - break + min_speed = round(min_speed, SHIP_MOVE_RESOLUTION) + max_speed = round(max_speed, SHIP_MOVE_RESOLUTION) + SSshuttles.ships += src START_PROCESSING(SSobj, src) -/obj/effect/map/ship/relaymove(mob/user, direction) - accelerate(direction) +/obj/effect/overmap/visitable/ship/Destroy() + STOP_PROCESSING(SSobj, src) + SSshuttles.ships -= src + . = ..() -/obj/effect/map/ship/proc/is_still() - return !(speed[1] || speed[2]) +/obj/effect/overmap/visitable/ship/relaymove(mob/user, direction, accel_limit) + accelerate(direction, accel_limit) + operator_skill = user.get_skill_value(/datum/skill/pilot) -/obj/effect/map/ship/proc/get_acceleration() - return eng_control.get_total_thrust()/vessel_mass +/obj/effect/overmap/visitable/ship/proc/is_still() + return !MOVING(speed[1]) && !MOVING(speed[2]) -/obj/effect/map/ship/proc/get_speed() - return round(sqrt(speed[1]*speed[1] + speed[2]*speed[2])) +/obj/effect/overmap/visitable/ship/get_scan_data(mob/user) + . = ..() + if(!is_still()) + . += "
Heading: [get_heading_degrees()], speed [get_speed() * 1000]" -/obj/effect/map/ship/proc/get_heading() +//Projected acceleration based on information from engines +/obj/effect/overmap/visitable/ship/proc/get_acceleration() + return round(get_total_thrust()/get_vessel_mass(), SHIP_MOVE_RESOLUTION) + +//Does actual burn and returns the resulting acceleration +/obj/effect/overmap/visitable/ship/proc/get_burn_acceleration() + return round(burn() / get_vessel_mass(), SHIP_MOVE_RESOLUTION) + +/obj/effect/overmap/visitable/ship/proc/get_vessel_mass() + . = vessel_mass + for(var/obj/effect/overmap/visitable/ship/ship in src) + . += ship.get_vessel_mass() + +/obj/effect/overmap/visitable/ship/proc/get_speed() + return round(sqrt(speed[1] ** 2 + speed[2] ** 2), SHIP_MOVE_RESOLUTION) + +// Get heading in BYOND dir bits +/obj/effect/overmap/visitable/ship/proc/get_heading() var/res = 0 - if(speed[1]) + if(MOVING(speed[1])) if(speed[1] > 0) res |= EAST else res |= WEST - if(speed[2]) + if(MOVING(speed[2])) if(speed[2] > 0) res |= NORTH else res |= SOUTH return res -/obj/effect/map/ship/proc/adjust_speed(n_x, n_y) - speed[1] = CLAMP(speed[1] + n_x, -default_delay, default_delay) - speed[2] = CLAMP(speed[2] + n_y, -default_delay, default_delay) - if(is_still()) - toggle_move_stars(map_z) - else - toggle_move_stars(map_z, fore_dir) +// Get heading in degrees (like a compass heading) +/obj/effect/overmap/visitable/ship/proc/get_heading_degrees() + return (ATAN2(speed[2], speed[1]) + 360) % 360 // Yes ATAN2(y, x) is correct to get clockwise degrees -/obj/effect/map/ship/proc/can_burn() - if (!eng_control) - return 0 - if (world.time < last_burn + 10) - return 0 - if (!eng_control.burn()) - return 0 - return 1 +/obj/effect/overmap/visitable/ship/proc/adjust_speed(n_x, n_y) + CHANGE_SPEED_BY(speed[1], n_x) + CHANGE_SPEED_BY(speed[2], n_y) + for(var/zz in map_z) + if(is_still()) + toggle_move_stars(zz) + else + toggle_move_stars(zz, fore_dir) + update_icon() -/obj/effect/map/ship/proc/get_brake_path() +/obj/effect/overmap/visitable/ship/proc/get_brake_path() if(!get_acceleration()) return INFINITY - return get_speed()/get_acceleration() + if(is_still()) + return 0 + if(!burn_delay) + return 0 + if(!get_speed()) + return 0 + var/num_burns = get_speed()/get_acceleration() + 2 //some padding in case acceleration drops form fuel usage + var/burns_per_grid = 1/ (burn_delay * get_speed()) + return round(num_burns/burns_per_grid) -#define SIGN(X) (X == 0 ? 0 : (X > 0 ? 1 : -1)) -/obj/effect/map/ship/proc/decelerate() - if(!is_still() && can_burn()) +/obj/effect/overmap/visitable/ship/proc/decelerate() + if(((speed[1]) || (speed[2])) && can_burn()) if (speed[1]) - adjust_speed(-SIGN(speed[1]) * min(get_acceleration(),abs(speed[1])), 0) + adjust_speed(-SIGN(speed[1]) * min(get_burn_acceleration(),abs(speed[1])), 0) if (speed[2]) - adjust_speed(0, -SIGN(speed[2]) * min(get_acceleration(),abs(speed[2]))) + adjust_speed(0, -SIGN(speed[2]) * min(get_burn_acceleration(),abs(speed[2]))) last_burn = world.time -/obj/effect/map/ship/proc/accelerate(direction) +/obj/effect/overmap/visitable/ship/proc/accelerate(direction, accel_limit) if(can_burn()) last_burn = world.time - + var/acceleration = min(get_burn_acceleration(), accel_limit) if(direction & EAST) - adjust_speed(get_acceleration(), 0) + adjust_speed(acceleration, 0) if(direction & WEST) - adjust_speed(-get_acceleration(), 0) + adjust_speed(-acceleration, 0) if(direction & NORTH) - adjust_speed(0, get_acceleration()) + adjust_speed(0, acceleration) if(direction & SOUTH) - adjust_speed(0, -get_acceleration()) + adjust_speed(0, -acceleration) - -/obj/effect/map/ship/proc/rotate(var/direction) - var/matrix/M = matrix() - M.Turn(dir2angle(direction)) - src.transform = M //Rotate ship - -/obj/effect/map/ship/process() - if(!is_still()) +/obj/effect/overmap/visitable/ship/process() + if(!halted && !is_still()) var/list/deltas = list(0,0) for(var/i=1, i<=2, i++) - if(speed[i] && world.time > last_movement[i] + default_delay - abs(speed[i])) - deltas[i] = speed[i] > 0 ? 1 : -1 + if(MOVING(speed[i]) && world.time > last_movement[i] + 1/abs(speed[i])) + deltas[i] = SIGN(speed[i]) last_movement[i] = world.time var/turf/newloc = locate(x + deltas[1], y + deltas[2], z) if(newloc) Move(newloc) - if(rotate) - rotate(get_heading()) + handle_wraparound() + update_icon() + +/obj/effect/overmap/visitable/ship/update_icon() + if(!is_still()) + icon_state = moving_state + dir = get_heading() + else + icon_state = initial(icon_state) + ..() + +/obj/effect/overmap/visitable/ship/proc/burn() + for(var/datum/ship_engine/E in engines) + . += E.burn() + +/obj/effect/overmap/visitable/ship/proc/get_total_thrust() + for(var/datum/ship_engine/E in engines) + . += E.get_thrust() + +/obj/effect/overmap/visitable/ship/proc/can_burn() + if(halted) + return 0 + if (world.time < last_burn + burn_delay) + return 0 + for(var/datum/ship_engine/E in engines) + . |= E.can_burn() + +//deciseconds to next step +/obj/effect/overmap/visitable/ship/proc/ETA() + . = INFINITY + for(var/i=1, i<=2, i++) + if(MOVING(speed[i])) + . = min(last_movement[i] - world.time + 1/abs(speed[i]), .) + . = max(.,0) + +/obj/effect/overmap/visitable/ship/proc/handle_wraparound() + var/nx = x + var/ny = y + var/low_edge = 1 + var/high_edge = global.using_map.overmap_size - 1 + + if((dir & WEST) && x == low_edge) + nx = high_edge + else if((dir & EAST) && x == high_edge) + nx = low_edge + if((dir & SOUTH) && y == low_edge) + ny = high_edge + else if((dir & NORTH) && y == high_edge) + ny = low_edge + if((x == nx) && (y == ny)) + return //we're not flying off anywhere + + var/turf/T = locate(nx,ny,z) + if(T) + forceMove(T) + +/obj/effect/overmap/visitable/ship/proc/halt() + adjust_speed(-speed[1], -speed[2]) + halted = 1 + +/obj/effect/overmap/visitable/ship/proc/unhalt() + if(!SSshuttles.overmap_halted) + halted = 0 + +/obj/effect/overmap/visitable/ship/Bump(var/atom/A) + if(istype(A,/turf/unsimulated/map/edge)) + handle_wraparound() + ..() + +/obj/effect/overmap/visitable/ship/proc/get_helm_skill()//delete this mover operator skill to overmap obj + return operator_skill + +/obj/effect/overmap/visitable/ship/populate_sector_objects() + ..() + for(var/obj/machinery/computer/ship/S in global.machines) + S.attempt_hook_up(src) + for(var/datum/ship_engine/E in ship_engines) + if(check_ownership(E.holder)) + engines |= E + +/obj/effect/overmap/visitable/ship/proc/get_landed_info() + return "This ship cannot land." + +#undef MOVING +#undef SANITIZE_SPEED +#undef CHANGE_SPEED_BY \ No newline at end of file diff --git a/code/modules/shuttles/shuttle_console.dm b/code/modules/shuttles/shuttle_console.dm index 80086b1b47..f8c2a8210d 100644 --- a/code/modules/shuttles/shuttle_console.dm +++ b/code/modules/shuttles/shuttle_console.dm @@ -121,7 +121,7 @@ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) - ui = new(user, src, ui_key, ui_template, "[shuttle_tag] Shuttle Control", 470, 310) + ui = new(user, src, ui_key, ui_template, "[shuttle_tag] Shuttle Control", 470, 360) ui.set_initial_data(data) ui.open() ui.set_auto_update(1) @@ -142,3 +142,30 @@ /obj/machinery/computer/shuttle_control/emp_act() return + + +GLOBAL_LIST_BOILERPLATE(papers_dockingcode, /obj/item/weapon/paper/dockingcodes) +/hook/roundstart/proc/populate_dockingcodes() + for(var/paper in global.papers_dockingcode) + var/obj/item/weapon/paper/dockingcodes/dcp = paper + dcp.populate_info() + return TRUE + +/obj/item/weapon/paper/dockingcodes + name = "Docking Codes" + var/codes_from_z = null //So you can put codes from the station other places to give to antags or whatever + +/obj/item/weapon/paper/dockingcodes/proc/populate_info() + var/dockingcodes = null + var/z_to_check = codes_from_z ? codes_from_z : z + if(using_map.use_overmap) + var/obj/effect/overmap/visitable/location = map_sectors["[z_to_check]"] + if(location && location.docking_codes) + dockingcodes = location.docking_codes + + if(!dockingcodes) + info = "

Daily Docking Codes


The docking security system is down for maintenance. Please exercise caution when shuttles dock and depart." + else + info = "

Daily Docking Codes


The docking codes for this shift are '[dockingcodes]'.
These codes are secret, as they will allow hostile shuttles to dock with impunity if discovered.
" + info_links = info + icon_state = "paper_words" diff --git a/code/modules/shuttles/shuttle_ferry.dm b/code/modules/shuttles/shuttle_ferry.dm index a5876f55dd..cad706c800 100644 --- a/code/modules/shuttles/shuttle_ferry.dm +++ b/code/modules/shuttles/shuttle_ferry.dm @@ -48,3 +48,9 @@ /datum/shuttle/autodock/ferry/process_arrived() ..() next_location = get_location_waypoint(!location) + +// Ferry shuttles should generally always be able to dock. So read the docking codes off of the target. +/datum/shuttle/autodock/ferry/update_docking_target(var/obj/effect/shuttle_landmark/location) + ..() + if(active_docking_controller && active_docking_controller.docking_codes) + set_docking_codes(active_docking_controller.docking_codes) diff --git a/code/modules/shuttles/shuttle_supply.dm b/code/modules/shuttles/shuttle_supply.dm index 53286ac949..8b4de51588 100644 --- a/code/modules/shuttles/shuttle_supply.dm +++ b/code/modules/shuttles/shuttle_supply.dm @@ -79,9 +79,3 @@ /datum/shuttle/autodock/ferry/supply/proc/eta_minutes() var/ticksleft = arrive_time - world.time return round(ticksleft/600,1) - -// Read the docking codes off of the target to make sure we can always dock. -/datum/shuttle/autodock/ferry/supply/update_docking_target(var/obj/effect/shuttle_landmark/location) - ..() - if(active_docking_controller && active_docking_controller.docking_codes) - set_docking_codes(active_docking_controller.docking_codes) diff --git a/code/modules/xgm/xgm_gas_mixture.dm b/code/modules/xgm/xgm_gas_mixture.dm index 7fe1a96d0d..6036b7381a 100644 --- a/code/modules/xgm/xgm_gas_mixture.dm +++ b/code/modules/xgm/xgm_gas_mixture.dm @@ -277,6 +277,12 @@ return removed +//Returns the amount of gas that has the given flag, in moles +/datum/gas_mixture/proc/get_by_flag(flag) + . = 0 + for(var/g in gas) + if(gas_data.flags[g] & flag) + . += gas[g] //Copies gas and temperature from another gas_mixture. /datum/gas_mixture/proc/copy_from(const/datum/gas_mixture/sample) @@ -478,3 +484,7 @@ gasmix.multiply(gasmix.volume) return 1 + +/datum/gas_mixture/proc/get_mass() + for(var/g in gas) + . += gas[g] * gas_data.molar_mass[g] * group_multiplier diff --git a/html/changelogs/Leshana - vplk-overmap-skybox.yml b/html/changelogs/Leshana - vplk-overmap-skybox.yml new file mode 100644 index 0000000000..7afda30167 --- /dev/null +++ b/html/changelogs/Leshana - vplk-overmap-skybox.yml @@ -0,0 +1,6 @@ +author: Leshana +delete-after: True +changes: + - rscadd: "Added skybox parallax background for space." + - rscadd: "Baystation12 style Overmap" + - tweak: "Make transit turfs (shuttle transit) actually look like they move in the right direction." diff --git a/icons/obj/computer.dmi b/icons/obj/computer.dmi index b3f6a15d3b..408264594c 100644 Binary files a/icons/obj/computer.dmi and b/icons/obj/computer.dmi differ diff --git a/icons/obj/overmap.dmi b/icons/obj/overmap.dmi index 859cd6c3af..20523513b3 100644 Binary files a/icons/obj/overmap.dmi and b/icons/obj/overmap.dmi differ diff --git a/icons/skybox/electrobox.dmi b/icons/skybox/electrobox.dmi new file mode 100644 index 0000000000..52e9f5d8cc Binary files /dev/null and b/icons/skybox/electrobox.dmi differ diff --git a/icons/skybox/ionbox.dmi b/icons/skybox/ionbox.dmi new file mode 100644 index 0000000000..7f5c5b15d6 Binary files /dev/null and b/icons/skybox/ionbox.dmi differ diff --git a/icons/skybox/planet.dmi b/icons/skybox/planet.dmi new file mode 100644 index 0000000000..45497b6e5f Binary files /dev/null and b/icons/skybox/planet.dmi differ diff --git a/icons/skybox/planet_rings.dmi b/icons/skybox/planet_rings.dmi new file mode 100644 index 0000000000..b122605206 Binary files /dev/null and b/icons/skybox/planet_rings.dmi differ diff --git a/icons/skybox/radbox.dmi b/icons/skybox/radbox.dmi new file mode 100644 index 0000000000..8b44c21257 Binary files /dev/null and b/icons/skybox/radbox.dmi differ diff --git a/icons/skybox/rockbox.dmi b/icons/skybox/rockbox.dmi new file mode 100644 index 0000000000..3a4d97595c Binary files /dev/null and b/icons/skybox/rockbox.dmi differ diff --git a/icons/skybox/skybox.dmi b/icons/skybox/skybox.dmi new file mode 100644 index 0000000000..2a16a2f81d Binary files /dev/null and b/icons/skybox/skybox.dmi differ diff --git a/icons/skybox/skybox_rock_128.dmi b/icons/skybox/skybox_rock_128.dmi new file mode 100644 index 0000000000..732eaf1136 Binary files /dev/null and b/icons/skybox/skybox_rock_128.dmi differ diff --git a/icons/skybox/skybox_rocks.dmi b/icons/skybox/skybox_rocks.dmi new file mode 100644 index 0000000000..b917bc9eaa Binary files /dev/null and b/icons/skybox/skybox_rocks.dmi differ diff --git a/icons/turf/shuttle_parts.dmi b/icons/turf/shuttle_parts.dmi index f7f1308d70..0604db6d7d 100644 Binary files a/icons/turf/shuttle_parts.dmi and b/icons/turf/shuttle_parts.dmi differ diff --git a/icons/turf/space.dmi b/icons/turf/space.dmi index 3e3afc355b..361dd4d180 100644 Binary files a/icons/turf/space.dmi and b/icons/turf/space.dmi differ diff --git a/icons/turf/space_dust.dmi b/icons/turf/space_dust.dmi new file mode 100644 index 0000000000..8328b3dba9 Binary files /dev/null and b/icons/turf/space_dust.dmi differ diff --git a/icons/turf/space_dust_transit.dmi b/icons/turf/space_dust_transit.dmi new file mode 100644 index 0000000000..db36a50efb Binary files /dev/null and b/icons/turf/space_dust_transit.dmi differ diff --git a/maps/southern_cross/southern_cross_defines.dm b/maps/southern_cross/southern_cross_defines.dm index 7cf3ac4dc7..c1d099af29 100644 --- a/maps/southern_cross/southern_cross_defines.dm +++ b/maps/southern_cross/southern_cross_defines.dm @@ -79,8 +79,10 @@ ) usable_email_tlds = list("freemail.nt") allowed_spawns = list("Arrivals Shuttle","Gateway", "Cryogenic Storage", "Cyborg Storage") - unit_test_exempt_areas = list(/area/ninja_dojo, /area/ninja_dojo/firstdeck, /area/ninja_dojo/arrivals_dock) + default_skybox = /datum/skybox_settings/southern_cross + + unit_test_exempt_areas = list(/area/ninja_dojo, /area/ninja_dojo/firstdeck, /area/ninja_dojo/arrivals_dock) unit_test_exempt_from_atmos = list(/area/tcomm/chamber) planet_datums_to_make = list(/datum/planet/sif) @@ -127,6 +129,11 @@ // Todo: Forest generation. return 1 +// Skybox Settings +/datum/skybox_settings/southern_cross + icon_state = "dyable" + random_color = TRUE + // For making the 6-in-1 holomap, we calculate some offsets #define SOUTHERN_CROSS_MAP_SIZE 160 // Width and height of compiled in Southern Cross z levels. #define SOUTHERN_CROSS_HOLOMAP_CENTER_GUTTER 40 // 40px central gutter between columns diff --git a/maps/~map_system/maps.dm b/maps/~map_system/maps.dm index a5298f09a0..107271df09 100644 --- a/maps/~map_system/maps.dm +++ b/maps/~map_system/maps.dm @@ -89,6 +89,8 @@ var/list/all_maps = list() var/overmap_z = 0 // If 0 will generate overmap zlevel on init. Otherwise will populate the zlevel provided. var/overmap_event_areas = 0 // How many event "clouds" will be generated + var/datum/skybox_settings/default_skybox // What skybox do we use if a zlevel doesn't have a custom one? Provide a type. + var/lobby_icon = 'icons/misc/title.dmi' // The icon which contains the lobby image(s) var/list/lobby_screens = list("mockingjay00") // The list of lobby screen to pick() from. If left unset the first icon state is always selected. @@ -113,6 +115,10 @@ var/list/all_maps = list() map_levels = station_levels.Copy() if(!allowed_jobs || !allowed_jobs.len) allowed_jobs = subtypesof(/datum/job) + if(default_skybox) //Type was specified + default_skybox = new default_skybox() + else + default_skybox = new() /datum/map/proc/setup_map() return @@ -164,6 +170,33 @@ var/list/all_maps = list() var/datum/map_z_level/Z = zlevels["[index]"] return Z.name +// Access check is of the type requires one. These have been carefully selected to avoid allowing the janitor to see channels he shouldn't +// This list needs to be purged but people insist on adding more cruft to the radio. +/datum/map/proc/default_internal_channels() + return list( + num2text(PUB_FREQ) = list(), + num2text(AI_FREQ) = list(access_synth), + num2text(ENT_FREQ) = list(), + num2text(ERT_FREQ) = list(access_cent_specops), + num2text(COMM_FREQ) = list(access_heads), + num2text(ENG_FREQ) = list(access_engine_equip, access_atmospherics), + num2text(MED_FREQ) = list(access_medical_equip), + num2text(MED_I_FREQ) = list(access_medical_equip), + num2text(SEC_FREQ) = list(access_security), + num2text(SEC_I_FREQ) = list(access_security), + num2text(SCI_FREQ) = list(access_tox,access_robotics,access_xenobiology), + num2text(SUP_FREQ) = list(access_cargo), + num2text(SRV_FREQ) = list(access_janitor, access_hydroponics), + ) + +/datum/map/proc/get_skybox_datum(z) + if(map_levels["[z]"]) + var/datum/map_z_level/picked = map_levels["[z]"] + if(picked.custom_skybox) + return picked.custom_skybox + + return default_skybox + // Another way to setup the map datum that can be convenient. Just declare all your zlevels as subtypes of a common // subtype of /datum/map_z_level and set zlevel_datum_type on /datum/map to have the lists auto-initialized. @@ -181,6 +214,9 @@ var/list/all_maps = list() var/holomap_legend_x = 96 // x position of the holomap legend for this z var/holomap_legend_y = 96 // y position of the holomap legend for this z +// Skybox + var/datum/skybox_settings/custom_skybox // Can override skybox type here for this z + // Default constructor applies itself to the parent map datum /datum/map_z_level/New(var/datum/map/map) if(!z) return @@ -212,6 +248,8 @@ var/list/all_maps = list() LIST_NUMERIC_SET(map.holomap_offset_y, z, holomap_offset_y) LIST_NUMERIC_SET(map.holomap_legend_x, z, holomap_legend_x) LIST_NUMERIC_SET(map.holomap_legend_y, z, holomap_legend_y) + if(custom_skybox) + custom_skybox = new custom_skybox() /datum/map_z_level/Destroy(var/force) crash_with("Attempt to delete a map_z_level instance [log_info_line(src)]") @@ -220,22 +258,3 @@ var/list/all_maps = list() if (using_map.zlevels["[z]"] == src) using_map.zlevels -= "[z]" return ..() - -// Access check is of the type requires one. These have been carefully selected to avoid allowing the janitor to see channels he shouldn't -// This list needs to be purged but people insist on adding more cruft to the radio. -/datum/map/proc/default_internal_channels() - return list( - num2text(PUB_FREQ) = list(), - num2text(AI_FREQ) = list(access_synth), - num2text(ENT_FREQ) = list(), - num2text(ERT_FREQ) = list(access_cent_specops), - num2text(COMM_FREQ) = list(access_heads), - num2text(ENG_FREQ) = list(access_engine_equip, access_atmospherics), - num2text(MED_FREQ) = list(access_medical_equip), - num2text(MED_I_FREQ) = list(access_medical_equip), - num2text(SEC_FREQ) = list(access_security), - num2text(SEC_I_FREQ) = list(access_security), - num2text(SCI_FREQ) = list(access_tox,access_robotics,access_xenobiology), - num2text(SUP_FREQ) = list(access_cargo), - num2text(SRV_FREQ) = list(access_janitor, access_hydroponics), - ) diff --git a/nano/templates/engines_control.tmpl b/nano/templates/engines_control.tmpl index 726edf27ac..2933b1cbe6 100644 --- a/nano/templates/engines_control.tmpl +++ b/nano/templates/engines_control.tmpl @@ -1,8 +1,35 @@
- {{:helper.link('Overall status', 'note', {'state' :'status'}, null, data.state == 'status' ? 'selected' : null)}} + {{:helper.link('Overall info', 'note', {'state' :'status'}, null, data.state == 'status' ? 'selected' : null)}} {{:helper.link('Details', 'note', {'state' : 'engines'}, null, data.state == 'engines' ? 'selected' : null)}}
- +
+
+
+ Global controls: +
+
+ {{:helper.link(data.global_state ? 'Shut all down' : 'Power all up', 'power', {'global_toggle' : 1}, null, data.global_state ? 'selected' : null)}} +
+
+
+
+ Volume limit: +
+
+ {{:helper.link('', 'circle-plus', { 'global_limit' : 0.1}, null, null)}} + {{:helper.link(data.global_limit+'%', null, { 'set_global_limit' : 1 }, null, null)}} + {{:helper.link('', 'circle-minus', { 'global_limit' : -0.1}, null, null)}} +
+
+
+
+ Total thrust: +
+
+ {{:data.total_thrust}} +
+
+
{{if data.state == "engines"}} {{if data.engines_info}} {{for data.engines_info}} @@ -11,9 +38,8 @@ Engine #{{:(index + 1)}}:
- {{:helper.link(value.eng_on ? 'Shutdown' : 'Power up', 'power', { 'toggle' : 1, 'engine' : value.eng_reference }, null, value.eng_on ? 'selected' : null)}} + {{:helper.link(value.eng_on ? 'Shutdown' : 'Power up', 'power', { 'toggle' : 1, 'engine' : value.eng_reference }, null, value.eng_on ? value.eng_on == 1 ? 'linkOn' : 'yellowButton' : null)}}
-
@@ -28,7 +54,7 @@ Status:
- {{:value.eng_on ? 'Online' : 'Offline'}}
+ {{:value.eng_on ? value.eng_on == 1 ? 'Online' : 'Booting' : 'Offline'}}
{{:value.eng_status}}
@@ -42,7 +68,7 @@
- Thrust limit: + Volume limit:
{{:helper.link('', 'circle-plus', { 'limit' : 0.1, 'engine' : value.eng_reference }, null, null)}} @@ -63,14 +89,14 @@ Engine #{{:(index + 1)}}:
- {{:helper.link(value.eng_on ? 'Shutdown' : 'Power up', 'power', { 'toggle' : 1, 'engine' : value.eng_reference }, null, value.eng_on ? 'selected' : null)}} + {{:helper.link(value.eng_on ? 'Shutdown' : 'Power up', 'power', { 'toggle' : 1, 'engine' : value.eng_reference }, null, value.eng_on ? value.eng_on == 1 ? 'linkOn' : 'yellowButton' : null)}}
Thrust:
- Thrust limit: + Volume limit:
{{:value.eng_thrust}} diff --git a/nano/templates/helm.tmpl b/nano/templates/helm.tmpl index c72d0921b8..2d371b1154 100644 --- a/nano/templates/helm.tmpl +++ b/nano/templates/helm.tmpl @@ -1,114 +1,159 @@ -
-

Sector information

-
- {{:data.sector}} -
- Coordinates: {{:data.s_x}} : {{:data.s_y}} -
- Additional information: {{:data.sector_info}} -
-
-
-

Flight data

-
+ +
+
+ Flight data
-
- Speed: +
+ ETA to next grid:
- {{:data.speed}} + {{:data.ETAnext}}
-
- Acceleration: +
+ Speed:
- {{:data.accel}} + {{:data.speed}} Gm/h
-
- Heading: +
+ Acceleration:
- {{:data.heading}} + {{:data.accel}} Gm/h +
+
+
+
+ Heading: +
+
+ {{:data.heading}}°
-
+
+
+ Acceleration limiter: +
+
+ {{:helper.link(data.accellimit, null, { 'accellimit' : 1}, null, null)}} Gm/h +
+
+
- - -

Manual control

-
+ +
+
+ Manual control
-
-
- {{:helper.link('', 'triangle-1-nw', { 'move' : 9 }, null, null)}} - {{:helper.link('', 'triangle-1-n', { 'move' : 1 }, null, null)}} - {{:helper.link('', 'triangle-1-ne', { 'move' : 5 }, null, null)}} -
-
- {{:helper.link('', 'triangle-1-w', { 'move' : 8 }, null, null)}} - {{:helper.link('', 'circle-close', { 'brake' : 1 }, null, null)}} - {{:helper.link('', 'triangle-1-e', { 'move' : 4 }, null, null)}} -
-
- {{:helper.link('', 'triangle-1-sw', { 'move' : 10 }, null, null)}} - {{:helper.link('', 'triangle-1-s', { 'move' : 2 }, null, null)}} - {{:helper.link('', 'triangle-1-se', { 'move' : 6 }, null, null)}} -
+
+ {{:helper.link('', 'triangle-1-nw', { 'move' : 9 }, data.canburn ? null : 'disabled', null)}} + {{:helper.link('', 'triangle-1-n', { 'move' : 1 }, data.canburn ? null : 'disabled', null)}} + {{:helper.link('', 'triangle-1-ne', { 'move' : 5 }, data.canburn ? null : 'disabled', null)}} +
+
+ {{:helper.link('', 'triangle-1-w', { 'move' : 8 }, data.canburn ? null : 'disabled', null)}} + {{:helper.link('', 'circle-close', { 'brake' : 1 }, data.canburn ? null : 'disabled', null)}} + {{:helper.link('', 'triangle-1-e', { 'move' : 4 }, data.canburn ? null : 'disabled', null)}} +
+
+ {{:helper.link('', 'triangle-1-sw', { 'move' : 10 }, data.canburn ? null : 'disabled', null)}} + {{:helper.link('', 'triangle-1-s', { 'move' : 2 }, data.canburn ? null : 'disabled', null)}} + {{:helper.link('', 'triangle-1-se', { 'move' : 6 }, data.canburn ? null : 'disabled', null)}}
-
-
- Direct control - {{:helper.link(data.manual_control ? 'Engaged' : 'Disengaged', 'shuffle', { 'manual' : 1 }, data.manual_control ? 'selected' : null, null)}} -
+
+ Direct control +
+ {{:helper.link(data.manual_control ? 'Engaged' : 'Disengaged', 'shuffle', { 'manual' : 1 }, null, data.manual_control ? 'selected' : null)}}
+
- -
-
-

Autopilot

-
-
+ +
+
+ Autopilot +
+
+ Target: +
+
+ {{if data.dest}} + {{:helper.link(data.d_x, null, { 'setx' : 1 }, null, null)}} {{:helper.link(data.d_y, null, { 'sety' : 1 }, null, null)}} + {{else}} + {{:helper.link('None', null, { 'sety' : 1, 'setx' : 1 }, null, null)}} + {{/if}} +
+
+
+
+ Speed limit: +
+
+ {{:helper.link(data.speedlimit, null, { 'speedlimit' : 1 }, null, null)}} Gm/h +
+
+
{{:helper.link(data.autopilot ? 'Engaged' : 'Disengaged', 'gear', { 'apilot' : 1 }, data.dest ? null : 'disabled', data.autopilot ? 'selected' : null)}}
-
-
-
- Target coordinates -
-
- {{if data.dest}} - {{:helper.link(data.d_x, null, { 'setx' : 1 }, null, null)}} {{:helper.link(data.d_y, null, { 'sety' : 1 }, null, null)}} - {{else}} - {{:helper.link('None', null, { 'sety' : 1, 'setx' : 1 }, null, null)}} - {{/if}} -
-
- -

Navigation data

-
- {{:helper.link('Save current position', 'disk', { 'add' : 'current' }, null)}} - {{:helper.link('Add new entry', 'document', { 'add' : 'new' }, null)}} +
- -
- {{if data.locations}} + +
+

Navigation data

+
+
+ Location: +
+
+ {{:data.sector}} +
+
+
+
+ Coordinates: +
+
+ {{:data.s_x}} : {{:data.s_y}} +
+
+
+
+ Scan data: +
+
+ {{:data.sector_info}} +
+
+
+
+ Status: +
+
+ {{:data.landed}} +
+
+
+ {{:helper.link('Save current position', 'disk', { 'add' : 'current' }, null)}} + {{:helper.link('Add new entry', 'document', { 'add' : 'new' }, null)}} +
+
+ +
NameCoordinatesActions {{for data.locations}} -
- {{:value.name}}: - {{:value.x}} : {{:value.y}} -
-
- {{:helper.link('Plot course', 'arrowreturnthick-1-e', { 'x' : value.x, 'y' : value.y }, null, null)}} - {{:helper.link('Remove entry', 'close', { 'remove' : value.reference }, null, null)}} -
+
{{:value.name}} + {{:value.x}} : {{:value.y}} + {{:helper.link('Plot course', 'arrowreturnthick-1-e', { 'x' : value.x, 'y' : value.y }, null, null)}} + {{:helper.link('Remove', 'close', { 'remove' : value.reference }, null, null)}} {{/for}} - {{/if}} - +
+
+
+ + \ No newline at end of file diff --git a/nano/templates/nav.tmpl b/nano/templates/nav.tmpl new file mode 100644 index 0000000000..6b5f5a9628 --- /dev/null +++ b/nano/templates/nav.tmpl @@ -0,0 +1,59 @@ +

Navigation

+
+
+
+
+ Map view + {{:helper.link(data.viewing ? 'Engaged' : 'Disengaged', 'shuffle', { 'viewing' : 1 }, null, data.viewing ? 'selected' : null)}} +
+
+
+
+
+

Sector information

+
+ {{:data.sector}} +
+ Coordinates: {{:data.s_x}} : {{:data.s_y}} +
+ Additional information: {{:data.sector_info}} +
+
+ +
+

Flight data

+
+
+
+ ETA to next grid: +
+
+ {{:data.ETAnext}} +
+
+
+
+ Speed: +
+
+ {{:data.speed}} Gm/h +
+
+
+
+ Acceleration: +
+
+ {{:data.accel}} Gm/h +
+
+
+
+ Heading: +
+
+ {{:data.heading}}° +
+
+
+
\ No newline at end of file diff --git a/nano/templates/shipsensors.tmpl b/nano/templates/shipsensors.tmpl new file mode 100644 index 0000000000..5931fd01b2 --- /dev/null +++ b/nano/templates/shipsensors.tmpl @@ -0,0 +1,90 @@ +

Sensors control console

+
+ {{:helper.link(data.on ? 'Switch off' : 'Switch on', 'gear', { 'toggle' : 1 }, data.status != 'MISSING' ? null : 'disabled', data.on ? 'selected' : null)}} +
+
+ Status: +
+
+ {{:data.status}} +
+
+
+
+ Range: +
+
+ {{:helper.link(data.range, null, { 'range' : 1 }, null, null)}} +
+
+
+
+
+
+ Integrity: +
+
+ {{if data.health < (data.max_health * 0.25)}} + {{:helper.displayBar(data.health, 0, data.max_health, 'bad')}} +
{{:data.health}}/{{:data.max_health}} + {{else data.health < data.max_health *.75}} + {{:helper.displayBar(data.health, 0, data.max_health, 'average')}} +
{{:data.health}}/{{:data.max_health}} + {{else}} + {{:helper.displayBar(data.health, 0, data.max_health, 'good')}} +
{{:data.health}}/{{:data.max_health}} + {{/if}} +
+
+
+
+ Temperature: +
+
+ {{if data.heat < (data.critical_heat * 0.5)}} + {{:helper.displayBar(data.heat, 0, data.critical_heat, 'good')}} + {{else data.heat < (data.critical_heat * 0.75)}} + {{:helper.displayBar(data.heat, 0, data.critical_heat, 'average')}} + {{else}} + {{:helper.displayBar(data.heat, 0, data.critical_heat, 'bad')}} + {{/if}} +
+
+ {{if data.heat < (data.critical_heat * 0.5)}} + Temperature low. + {{else data.heat < (data.critical_heat * 0.75)}} + Sensor temperature high! + {{else}} + TEMPERATURE CRITICAL: Disable or reduce power immediately! + {{/if}} +
+
+
+
+
+
+ Sector map view + {{:helper.link(data.viewing ? 'Engaged' : 'Disengaged', 'shuffle', { 'viewing' : 1 }, null, data.viewing ? 'selected' : null)}} +
+
+
+

Sensor contacts

+
+{{if data.contacts}} + + {{for data.contacts}} + +
+
+ + + + {{/for}} +
{{:helper.link('Scan', 'search' ,{ 'scan' : value.ref }, null, null)}}{{:value.name}}, bearing {{:value.bearing}}
+{{/if}} +
+{{if data.status == 'MISSING'}} +
+ {{:helper.link('Link up with the sensor suite', 'gear', { 'link' : 1 }, data.status == 'MISSING' ? null : 'disabled', null)}} +
+{{/if}} diff --git a/nano/templates/shuttle_control_console.tmpl b/nano/templates/shuttle_control_console.tmpl index ccc4df70aa..05cae4ef09 100644 --- a/nano/templates/shuttle_control_console.tmpl +++ b/nano/templates/shuttle_control_console.tmpl @@ -50,6 +50,12 @@ {{/if}}
+
+ Docking Codes: +
+
+ {{:helper.link(data.docking_codes ? data.docking_codes : 'Not set', null, {'set_codes' : '1'}, null , null)}} +
{{/if}} diff --git a/nano/templates/shuttle_control_console_exploration.tmpl b/nano/templates/shuttle_control_console_exploration.tmpl index fe765246d2..17f5a0b2d2 100644 --- a/nano/templates/shuttle_control_console_exploration.tmpl +++ b/nano/templates/shuttle_control_console_exploration.tmpl @@ -7,7 +7,7 @@
- Drive: + Engines:
{{if data.shuttle_state == "idle"}} @@ -61,6 +61,22 @@ {{:helper.link('Choose Destination', 'arrowreturn-1-s', {'pick' : '1'}, data.can_pick ? null : 'disabled' , null)}}
+{{if data.fuel_usage}} +
+
+ Est. Delta-V Budget: +
+
+ {{:data.remaining_fuel}} m/s +
+
+ Avg. Delta-V Per Maneuver: +
+
+ {{:data.fuel_usage}} m/s +
+
+{{/if}}

Shuttle Control

diff --git a/polaris.dme b/polaris.dme index dc21b622fe..3b348ca91c 100644 --- a/polaris.dme +++ b/polaris.dme @@ -55,6 +55,7 @@ #include "code\__defines\process_scheduler.dm" #include "code\__defines\qdel.dm" #include "code\__defines\research.dm" +#include "code\__defines\shuttle.dm" #include "code\__defines\sound.dm" #include "code\__defines\species_languages.dm" #include "code\__defines\sqlite_defines.dm" @@ -131,6 +132,7 @@ #include "code\_onclick\hud\radial_persistent.dm" #include "code\_onclick\hud\robot.dm" #include "code\_onclick\hud\screen_objects.dm" +#include "code\_onclick\hud\skybox.dm" #include "code\_onclick\hud\spell_screen_objects.dm" #include "code\ATMOSPHERICS\_atmos_setup.dm" #include "code\ATMOSPHERICS\_atmospherics_helpers.dm" @@ -216,6 +218,7 @@ #include "code\controllers\subsystems\planets.dm" #include "code\controllers\subsystems\radiation.dm" #include "code\controllers\subsystems\shuttles.dm" +#include "code\controllers\subsystems\skybox.dm" #include "code\controllers\subsystems\sqlite.dm" #include "code\controllers\subsystems\sun.dm" #include "code\controllers\subsystems\time_track.dm" @@ -1948,6 +1951,7 @@ #include "code\modules\mob\mob_planes.dm" #include "code\modules\mob\mob_transformation_simple.dm" #include "code\modules\mob\say.dm" +#include "code\modules\mob\skillset.dm" #include "code\modules\mob\transform_procs.dm" #include "code\modules\mob\typing_indicator.dm" #include "code\modules\mob\update_icons.dm" @@ -2401,8 +2405,20 @@ #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\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\computer_shims.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\overmap\ships\engines\ion_thruster.dm" #include "code\modules\paperwork\adminpaper.dm" #include "code\modules\paperwork\carbonpaper.dm" #include "code\modules\paperwork\clipboard.dm" @@ -2677,7 +2693,6 @@ #include "code\modules\shieldgen\shield_diffuser.dm" #include "code\modules\shieldgen\shield_gen.dm" #include "code\modules\shieldgen\shield_gen_external.dm" -#include "code\modules\shuttles\_defines.dm" #include "code\modules\shuttles\antagonist.dm" #include "code\modules\shuttles\departmental.dm" #include "code\modules\shuttles\escape_pods.dm" diff --git a/sound/effects/locker_close.ogg b/sound/effects/locker_close.ogg new file mode 100644 index 0000000000..86913e51a2 Binary files /dev/null and b/sound/effects/locker_close.ogg differ diff --git a/sound/effects/locker_open.ogg b/sound/effects/locker_open.ogg new file mode 100644 index 0000000000..4a254c2b0b Binary files /dev/null and b/sound/effects/locker_open.ogg differ diff --git a/sound/machines/thruster.ogg b/sound/machines/thruster.ogg new file mode 100644 index 0000000000..7564bde67a Binary files /dev/null and b/sound/machines/thruster.ogg differ