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/machinery.dm b/code/__defines/machinery.dm index 3676071d7c..f7c2457b98 100644 --- a/code/__defines/machinery.dm +++ b/code/__defines/machinery.dm @@ -11,6 +11,11 @@ var/global/defer_powernet_rebuild = 0 // True if net rebuild will be called #define DOOR_CRUSH_DAMAGE 20 #define ALIEN_SELECT_AFK_BUFFER 1 // How many minutes that a person can be AFK before not being allowed to be an alien. +// Constants for machine's use_power +#define USE_POWER_OFF 0 // No continuous power use +#define USE_POWER_IDLE 1 // Machine is using power at its idle power level +#define USE_POWER_ACTIVE 2 // Machine is using power at its active power level + // Channel numbers for power. #define EQUIP 1 #define LIGHT 2 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/_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/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/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/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/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..46fcaad7df 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() @@ -71,21 +78,20 @@ proc/toggle_move_stars(zlevel, direction) 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]"] != gen_dir) + moving_levels["[zlevel]"] = gen_dir + var/list/spaceturfs = block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel)) + for(var/turf/space/T in spaceturfs) + if(!gen_dir) + T.icon_state = initial(T.icon_state) + else + T.icon_state = "speedspace_[gen_dir]_[rand(1,15)]" + for(var/atom/movable/AM in T) + if (AM.simulated && !AM.anchored) + AM.throw_at(get_step(T,reverse_direction(direction)), 5, 1) + CHECK_TICK + CHECK_TICK /* //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..a565c51d62 --- /dev/null +++ b/code/modules/overmap/overmap_shuttle.dm @@ -0,0 +1,179 @@ +#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) + 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..d53a2ce93b 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) @@ -117,7 +119,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..51ffef4924 100644 --- a/code/modules/overmap/ships/computers/helm.dm +++ b/code/modules/overmap/ships/computers/helm.dm @@ -1,151 +1,188 @@ -/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 + req_one_access = list(access_pilot) -/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 +194,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 +216,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..08167b73da --- /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 BS elements." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "sensors" + anchored = 1 + var/max_health = 200 + var/health = 200 + var/critical_heat = 50 // sparks and takes damage when active & above this heat + var/heat_reduction = 1.5 // mitigates this much heat per tick + var/heat = 0 + var/range = 1 + idle_power_usage = 5000 + +/obj/machinery/shipsensors/attackby(obj/item/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 = "Miniturized gravity scanner with various other sensors, used to detect irregularities in surrounding space. Can only run in vacuum to protect delicate quantum BS elements." \ No newline at end of file diff --git a/code/modules/overmap/ships/computers/ship.dm b/code/modules/overmap/ships/computers/ship.dm new file mode 100644 index 0000000000..4f2c88c13a --- /dev/null +++ b/code/modules/overmap/ships/computers/ship.dm @@ -0,0 +1,99 @@ +/* +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) + if(user.client) + user.client.view = 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() + if(user.client) + user.client.view = world.view + 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..4d6a048a3e --- /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 = 21600 //6 Wh per tick for default 2 capacitor. Gives them a reason to turn it off, really to nerf backup battery + + 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 = 36000 //10Wh for default 2 capacitor, chews through that battery power! Makes a trade off of fuel efficient vs energy efficient + 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..34e464eef8 --- /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 = 750 + 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..cc9d6dcb05 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) 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/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/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/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_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 524e0ee0c5..de0946d818 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" @@ -1948,6 +1949,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" @@ -2400,8 +2402,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" @@ -2676,7 +2690,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