diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm index 24b8b041aebd..8ea0a76c5d77 100644 --- a/code/controllers/configuration/entries/game_options.dm +++ b/code/controllers/configuration/entries/game_options.dm @@ -401,3 +401,10 @@ /datum/config_entry/number/engine_type config_entry_value = 3 + +//Shuttle size limiter +/datum/config_entry/number/max_shuttle_count + config_entry_value = 6 + +/datum/config_entry/number/max_shuttle_size + config_entry_value = 250 diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm index c2c4b9f35ced..252aa7bd95f8 100644 --- a/code/controllers/subsystem/shuttle.dm +++ b/code/controllers/subsystem/shuttle.dm @@ -8,6 +8,7 @@ SUBSYSTEM_DEF(shuttle) var/list/mobile = list() var/list/stationary = list() + var/list/beacons = list() var/list/transit = list() var/list/transit_requesters = list() diff --git a/code/game/area/areas/shuttles.dm b/code/game/area/areas/shuttles.dm index 15b626434753..8ff9140009a3 100644 --- a/code/game/area/areas/shuttles.dm +++ b/code/game/area/areas/shuttles.dm @@ -106,6 +106,10 @@ /area/shuttle/custom name = "Custom player shuttle" +/area/shuttle/custom/powered + name = "Custom Powered player shuttle" + requires_power = FALSE + /area/shuttle/arrival name = "Arrival Shuttle" unique = TRUE // SSjob refers to this area for latejoiners diff --git a/code/game/machinery/shuttle/custom_shuttle.dm b/code/game/machinery/shuttle/custom_shuttle.dm new file mode 100644 index 000000000000..7c83cade69f3 --- /dev/null +++ b/code/game/machinery/shuttle/custom_shuttle.dm @@ -0,0 +1,33 @@ +/obj/machinery/shuttle + name = "shuttle component" + desc = "Something for shuttles." + density = TRUE + obj_integrity = 250 + max_integrity = 250 + icon = 'icons/turf/shuttle.dmi' + icon_state = "burst_plasma" + idle_power_usage = 150 + circuit = /obj/item/circuitboard/machine/shuttle/engine + var/icon_state_closed = "burst_plasma" + var/icon_state_open = "burst_plasma_open" + var/icon_state_off = "burst_plasma_off" + +/obj/machinery/shuttle/Initialize() + . = ..() + GLOB.custom_shuttle_machines += src + +/obj/machinery/shuttle/Destroy() + . = ..() + GLOB.custom_shuttle_machines -= src + +/obj/machinery/shuttle/attackby(obj/item/I, mob/living/user, params) + if(default_deconstruction_screwdriver(user, icon_state_open, icon_state_closed, I)) + return + if(default_pry_open(I)) + return + if(panel_open) + if(default_change_direction_wrench(user, I)) + return + if(default_deconstruction_crowbar(I)) + return + return ..() diff --git a/code/game/machinery/shuttle/shuttle_engine.dm b/code/game/machinery/shuttle/shuttle_engine.dm new file mode 100644 index 000000000000..98e324b3d020 --- /dev/null +++ b/code/game/machinery/shuttle/shuttle_engine.dm @@ -0,0 +1,140 @@ +//----------------------------------------------- +//-------------Engine Thrusters------------------ +//----------------------------------------------- + +#define ENGINE_HEAT_TARGET 600 +#define ENGINE_HEATING_POWER 5000000 + +/obj/machinery/shuttle/engine + name = "shuttle thruster" + desc = "A thruster for shuttles." + density = TRUE + obj_integrity = 250 + max_integrity = 250 + icon = 'icons/turf/shuttle.dmi' + icon_state = "burst_plasma" + idle_power_usage = 150 + circuit = /obj/item/circuitboard/machine/shuttle/engine + var/thrust = 0 + var/fuel_use = 0 + var/bluespace_capable = TRUE + var/cooldown = 0 + var/thruster_active = FALSE + var/datum/weakref/attached_heater + +/obj/machinery/shuttle/engine/plasma + name = "plasma thruster" + desc = "A thruster that burns plasma stored in an adjacent plasma thruster heater." + icon_state = "burst_plasma" + icon_state_off = "burst_plasma_off" + + idle_power_usage = 0 + circuit = /obj/item/circuitboard/machine/shuttle/engine/plasma + thrust = 25 + fuel_use = 0.24 + bluespace_capable = FALSE + cooldown = 45 + +/obj/machinery/shuttle/engine/void + name = "void thruster" + desc = "A thruster using technology to breach voidspace for propulsion." + icon_state = "burst_void" + icon_state_off = "burst_void" + icon_state_closed = "burst_void" + icon_state_open = "burst_void_open" + idle_power_usage = 0 + circuit = /obj/item/circuitboard/machine/shuttle/engine/void + thrust = 400 + fuel_use = 0 + bluespace_capable = TRUE + cooldown = 90 + +/obj/machinery/shuttle/engine/Initialize() + . = ..() + check_setup() + +/obj/machinery/shuttle/engine/on_construction() + . = ..() + check_setup() + +/obj/machinery/shuttle/engine/proc/check_setup() + var/heater_turf + switch(dir) + if(NORTH) + heater_turf = get_offset_target_turf(src, 0, 1) + if(SOUTH) + heater_turf = get_offset_target_turf(src, 0, -1) + if(EAST) + heater_turf = get_offset_target_turf(src, 1, 0) + if(WEST) + heater_turf = get_offset_target_turf(src, -1, 0) + if(!heater_turf) + attached_heater = null + update_engine() + return + attached_heater = null + for(var/obj/machinery/atmospherics/components/unary/shuttle/heater/as_heater in heater_turf) + if(as_heater.dir != dir) + continue + if(as_heater.panel_open) + continue + if(!as_heater.anchored) + continue + attached_heater = WEAKREF(as_heater) + break + update_engine() + return + +/obj/machinery/shuttle/engine/proc/update_engine() + if(!attached_heater) + icon_state = icon_state_off + thruster_active = FALSE + return + var/obj/machinery/atmospherics/components/unary/shuttle/heater/resolved_heater = attached_heater.resolve() + if(panel_open) + thruster_active = FALSE + else if(resolved_heater?.hasFuel(1)) + icon_state = icon_state_closed + thruster_active = TRUE + else + thruster_active = FALSE + icon_state = icon_state_off + return + +/obj/machinery/shuttle/engine/void/update_engine() + if(panel_open) + thruster_active = FALSE + return + thruster_active = TRUE + icon_state = icon_state_closed + return + +//Thanks to spaceheater.dm for inspiration :) +/obj/machinery/shuttle/engine/proc/fireEngine() + var/turf/heatTurf = loc + if(!heatTurf) + return + var/datum/gas_mixture/env = heatTurf.return_air() + var/heat_cap = env.heat_capacity() + var/req_power = abs(env.return_temperature() - ENGINE_HEAT_TARGET) * heat_cap + req_power = min(req_power, ENGINE_HEATING_POWER) + var/deltaTemperature = 0 + if(!heat_cap == 0) + deltaTemperature = req_power / heat_cap + if(deltaTemperature < 0) + return + env.set_temperature(env.return_temperature() + deltaTemperature) + air_update_turf() + +/obj/machinery/shuttle/engine/attackby(obj/item/I, mob/living/user, params) + check_setup() + if(default_deconstruction_screwdriver(user, icon_state_open, icon_state_closed, I)) + return + if(default_pry_open(I)) + return + if(panel_open) + if(default_change_direction_wrench(user, I)) + return + if(default_deconstruction_crowbar(I)) + return + return ..() diff --git a/code/game/machinery/shuttle/shuttle_heater.dm b/code/game/machinery/shuttle/shuttle_heater.dm new file mode 100644 index 000000000000..80c46fde7374 --- /dev/null +++ b/code/game/machinery/shuttle/shuttle_heater.dm @@ -0,0 +1,134 @@ +//----------------------------------------------- +//--------------Engine Heaters------------------- +//This uses atmospherics, much like a thermomachine, +//but instead of changing temp, it stores plasma and uses +//it for the engine. +//----------------------------------------------- +/obj/machinery/atmospherics/components/unary/shuttle + name = "shuttle atmospherics device" + desc = "This does something to do with shuttle atmospherics" + icon_state = "heater" + icon = 'icons/turf/shuttle.dmi' + +/obj/machinery/atmospherics/components/unary/shuttle/heater + name = "engine heater" + desc = "Directs energy into compressed particles in order to power an attached thruster." + icon_state = "heater_pipe" + var/icon_state_closed = "heater_pipe" + var/icon_state_open = "heater_pipe_open" + var/icon_state_off = "heater_pipe" + idle_power_usage = 50 + circuit = /obj/item/circuitboard/machine/shuttle/heater + + density = TRUE + max_integrity = 400 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 30) + layer = OBJ_LAYER + showpipe = TRUE + + pipe_flags = PIPING_ONE_PER_TURF | PIPING_DEFAULT_LAYER_ONLY + + var/gas_type = /datum/gas/plasma + var/efficiency_multiplier = 1 + var/gas_capacity = 0 + +/obj/machinery/atmospherics/components/unary/shuttle/heater/New() + . = ..() + GLOB.custom_shuttle_machines += src + SetInitDirections() + update_adjacent_engines() + updateGasStats() + +/obj/machinery/atmospherics/components/unary/shuttle/heater/Destroy() + . = ..() + update_adjacent_engines() + GLOB.custom_shuttle_machines -= src + +/obj/machinery/atmospherics/components/unary/shuttle/heater/on_construction() + ..(dir, dir) + SetInitDirections() + update_adjacent_engines() + +/obj/machinery/atmospherics/components/unary/shuttle/heater/default_change_direction_wrench(mob/user, obj/item/I) + if(!..()) + return FALSE + SetInitDirections() + var/obj/machinery/atmospherics/node = nodes[1] + if(node) + node.disconnect(src) + nodes[1] = null + if(!parents[1]) + return + nullifyPipenet(parents[1]) + + atmosinit() + node = nodes[1] + if(node) + node.atmosinit() + node.addMember(src) + build_network() + return TRUE + +/obj/machinery/atmospherics/components/unary/shuttle/heater/RefreshParts() + var/cap = 0 + var/eff = 0 + for(var/obj/item/stock_parts/matter_bin/M in component_parts) + cap += M.rating + for(var/obj/item/stock_parts/micro_laser/L in component_parts) + eff += L.rating + gas_capacity = 5000 * ((cap - 1) ** 2) + 1000 + efficiency_multiplier = round(((eff / 2) / 2.8) ** 2, 0.1) + updateGasStats() + +/obj/machinery/atmospherics/components/unary/shuttle/heater/examine(mob/user) + . = ..() + var/datum/gas_mixture/air_contents = airs[1] + . += "The engine heater's gas dial reads [air_contents.get_moles(gas_type)] moles of gas.
" //This probably has issues [air_contents.get_moles] + +/obj/machinery/atmospherics/components/unary/shuttle/heater/proc/updateGasStats() + var/datum/gas_mixture/air_contents = airs[1] + if(!air_contents) + return + air_contents.set_volume(gas_capacity) + air_contents.set_temperature(T20C) + if(gas_type) + air_contents.set_moles(gas_type) + +/obj/machinery/atmospherics/components/unary/shuttle/heater/proc/hasFuel(var/required) + var/datum/gas_mixture/air_contents = airs[1] + var/moles = air_contents.total_moles() + return moles >= required + +/obj/machinery/atmospherics/components/unary/shuttle/heater/proc/consumeFuel(var/amount) + var/datum/gas_mixture/air_contents = airs[1] + air_contents.remove(amount) + return + +/obj/machinery/atmospherics/components/unary/shuttle/heater/attackby(obj/item/I, mob/living/user, params) + update_adjacent_engines() + if(default_deconstruction_screwdriver(user, icon_state_open, icon_state_closed, I)) + return + if(default_pry_open(I)) + return + if(panel_open) + if(default_change_direction_wrench(user, I)) + return + if(default_deconstruction_crowbar(I)) + return + return ..() + +/obj/machinery/atmospherics/components/unary/shuttle/heater/proc/update_adjacent_engines() + var/engine_turf + switch(dir) + if(NORTH) + engine_turf = get_offset_target_turf(src, 0, -1) + if(SOUTH) + engine_turf = get_offset_target_turf(src, 0, 1) + if(EAST) + engine_turf = get_offset_target_turf(src, -1, 0) + if(WEST) + engine_turf = get_offset_target_turf(src, 1, 0) + if(!engine_turf) + return + for(var/obj/machinery/shuttle/engine/E in engine_turf) + E.check_setup() diff --git a/code/game/objects/items/circuitboards/computer_circuitboards.dm b/code/game/objects/items/circuitboards/computer_circuitboards.dm index 81284c9b9cd9..d8ab95c987e1 100644 --- a/code/game/objects/items/circuitboards/computer_circuitboards.dm +++ b/code/game/objects/items/circuitboards/computer_circuitboards.dm @@ -377,6 +377,14 @@ name = "circuit board (Xenobiology Console)" icon_state = "science" build_path = /obj/machinery/computer/camera_advanced/xenobio + +/obj/item/circuitboard/computer/shuttle/flight_control + name = "Shuttle Flight Control (Computer Board)" + build_path = /obj/machinery/computer/custom_shuttle + +/obj/item/circuitboard/computer/shuttle/docker + name = "Shuttle Navigation Computer (Computer Board)" + build_path = /obj/machinery/computer/camera_advanced/shuttle_docker/custom //Security diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm index 0b66104d6cd6..b81ed386e172 100644 --- a/code/game/objects/items/circuitboards/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm @@ -1230,3 +1230,28 @@ /obj/item/stack/sheet/mineral/silver = 1) needs_anchored = FALSE + +/obj/item/circuitboard/machine/shuttle/engine + name = "Thruster (Machine Board)" + build_path = /obj/machinery/shuttle/engine + req_components = list() + +/obj/item/circuitboard/machine/shuttle/engine/plasma + name = "Plasma Thruster (Machine Board)" + build_path = /obj/machinery/shuttle/engine/plasma + req_components = list(/obj/item/stock_parts/capacitor = 2, + /obj/item/stack/cable_coil = 5, + /obj/item/stock_parts/micro_laser = 1) + +/obj/item/circuitboard/machine/shuttle/engine/void + name = "Void Thruster (Machine Board)" + build_path = /obj/machinery/shuttle/engine/void + req_components = list(/obj/item/stock_parts/capacitor/quadratic = 2, + /obj/item/stack/cable_coil = 5, + /obj/item/stock_parts/micro_laser/quadultra = 1) + +/obj/item/circuitboard/machine/shuttle/heater + name = "Electronic Engine Heater (Machine Board)" + build_path = /obj/machinery/atmospherics/components/unary/shuttle/heater + req_components = list(/obj/item/stock_parts/micro_laser = 2, + /obj/item/stock_parts/matter_bin = 1) diff --git a/code/game/objects/items/shuttle_creator.dm b/code/game/objects/items/shuttle_creator.dm new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/code/game/objects/items/shuttle_upgrades.dm b/code/game/objects/items/shuttle_upgrades.dm new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 96ba894f1e8e..2797bcc65051 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -219,3 +219,5 @@ /// A mock client, provided by tests and friends var/datum/client_interface/mock_client + + var/create_area_cooldown diff --git a/code/modules/research/designs/comp_board_designs.dm b/code/modules/research/designs/comp_board_designs.dm index 83400fc18f2b..c6cda870418c 100644 --- a/code/modules/research/designs/comp_board_designs.dm +++ b/code/modules/research/designs/comp_board_designs.dm @@ -285,3 +285,19 @@ build_path = /obj/item/circuitboard/computer/nanite_cloud_controller category = list("Computer Boards") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/shuttle/flight_control + name = "Computer Design (Shuttle Flight Controller)" + desc = "Allows for the construction of circuit boards used to build a console that enables shuttle flight" + id = "shuttle_control" + build_path = /obj/item/circuitboard/computer/shuttle/flight_control + category = list("Computer Boards", "Shuttle Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/board/shuttle/shuttle_docker + name = "Computer Design (Shuttle Zoning Designator)" + desc = "Allows for the construction of circuit boards used to build a console that enables the targetting of custom flight locations" + id = "shuttle_docker" + build_path = /obj/item/circuitboard/computer/shuttle/docker + category = list("Computer Boards", "Shuttle Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING diff --git a/code/modules/research/designs/machine_designs.dm b/code/modules/research/designs/machine_designs.dm index 0360b7dbfa97..a21214d5e40f 100644 --- a/code/modules/research/designs/machine_designs.dm +++ b/code/modules/research/designs/machine_designs.dm @@ -2,6 +2,38 @@ //////////////MISC Boards/////////////// //////////////////////////////////////// +/datum/design/board/shuttle/engine/plasma + name = "Machine Design (Plasma Thruster Board)" + desc = "The circuit board for a plasma thruster." + id = "engine_plasma" + build_path = /obj/item/circuitboard/machine/shuttle/engine/plasma + category = list ("Shuttle Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/shuttle/engine/void + name = "Machine Design (Void Thruster Board)" + desc = "The circuit board for a void thruster." + id = "engine_void" + build_path = /obj/item/circuitboard/machine/shuttle/engine/void + category = list ("Shuttle Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/shuttle/engine/heater + name = "Machine Design (Engine Heater Board)" + desc = "The circuit board for an engine heater." + id = "engine_heater" + build_path = /obj/item/circuitboard/machine/shuttle/heater + category = list ("Shuttle Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/spaceship_navigation_beacon + name = "Machine Design (Bluespace Navigation Gigabeacon)" + desc = "The circuit board for a Bluespace Navigation Gigabeacon." + id = "spaceship_navigation_beacon" + build_path = /obj/item/circuitboard/machine/spaceship_navigation_beacon + category = list ("Shuttle Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE + /datum/design/board/smes name = "Machine Design (SMES Board)" desc = "The circuit board for an SMES." diff --git a/code/modules/research/designs/misc_designs.dm b/code/modules/research/designs/misc_designs.dm index 99924c91b004..d50cd0f7d6b4 100644 --- a/code/modules/research/designs/misc_designs.dm +++ b/code/modules/research/designs/misc_designs.dm @@ -3,6 +3,36 @@ /////////////////HUDs//////////////////// ///////////////////////////////////////// +/datum/design/shuttle_speed_upgrade + name = "Shuttle Route Optimisation Upgrade" + desc = "A disk that allows for calculating shorter routes when inserted into a flight control console." + id = "disk_shuttle_route" + build_type = PROTOLATHE + materials = list(/datum/material/iron = 1000, /datum/material/glass = 1000) + build_path = /obj/item/shuttle_route_optimisation + category = list("Equipment") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/shuttle_speed_upgrade_hyper + name = "Shuttle Bluespace Hyperlane Optimisation Upgrade" + desc = "A disk that allows for calculating shorter routes when inserted into a flight control console. This one abuses bluespace hyperlanes for increased efficiency." + id = "disk_shuttle_route_hyper" + build_type = PROTOLATHE + materials = list(/datum/material/iron = 1000, /datum/material/glass = 1000) + build_path = /obj/item/shuttle_route_optimisation/hyperlane + category = list("Equipment") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/shuttle_speed_upgrade_void + name = "Shuttle Voidspace Optimisation Upgrade" + desc = "A disk that allows for calculating shorter routes when inserted into a flight control console. This one access voidspace for increased efficiency." + id = "disk_shuttle_route_void" + build_type = PROTOLATHE + materials = list(/datum/material/iron = 1000, /datum/material/glass = 1000) + build_path = /obj/item/shuttle_route_optimisation/void + category = list("Equipment") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING + /datum/design/health_hud name = "Health Scanner HUD" desc = "A heads-up display that scans the humans in view and provides accurate data about their health status." diff --git a/code/modules/research/designs/tool_designs.dm b/code/modules/research/designs/tool_designs.dm index ffc14a6bb941..4a983f4a3619 100644 --- a/code/modules/research/designs/tool_designs.dm +++ b/code/modules/research/designs/tool_designs.dm @@ -23,6 +23,16 @@ category = list("Tool Designs") departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING +/datum/design/shuttlecreator + name = "Rapid Shuttle Designator" + desc = "An advanced device capable of defining areas for use in the creation of shuttles" + id = "shuttle_creator" + build_path = /obj/item/shuttle_creator + build_type = PROTOLATHE + materials = list(/datum/material/iron = 8000, /datum/material/titanium = 5000, /datum/material/bluespace = 5000) + category = list("Tool Designs") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING + /datum/design/exwelder name = "Experimental Welding Tool" desc = "An experimental welder capable of self-fuel generation." diff --git a/code/modules/research/machinery/circuit_imprinter.dm b/code/modules/research/machinery/circuit_imprinter.dm index 948dad61db94..8415564f6d09 100644 --- a/code/modules/research/machinery/circuit_imprinter.dm +++ b/code/modules/research/machinery/circuit_imprinter.dm @@ -14,7 +14,8 @@ "Subspace Telecomms", "Research Machinery", "Misc. Machinery", - "Computer Parts" + "Computer Parts", + "Shuttle Machinery" ) production_animation = "circuit_imprinter_ani" allowed_buildtypes = IMPRINTER diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index 10a89987c13b..5a2caf65a5b5 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -272,6 +272,43 @@ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 +/////////////////////////shuttle tech///////////////////////// +/datum/techweb_node/basic_shuttle_tech + id = "basic_shuttle" + display_name = "Basic Shuttle Research" + description = "Research the technology required to create and use basic shuttles." + prereq_ids = list("bluespace_travel", "adv_engi") + design_ids = list("shuttle_creator", "engine_plasma", "engine_heater", "shuttle_control", "shuttle_docker") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 10000) + export_price = 5000 + +/datum/techweb_node/shuttle_route_upgrade + id = "shuttle_route_upgrade" + display_name = "Route Optimisation Upgrade" + description = "Research into bluespace tunnelling, allowing us to reduce flight times by up to 20%!" + prereq_ids = list("basic_shuttle") + design_ids = list("disk_shuttle_route") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) + export_price = 2500 + +/datum/techweb_node/shuttle_route_upgrade_hyper + id = "shuttle_route_upgrade_hyper" + display_name = "Hyperlane Optimisation Upgrade" + description = "Research into bluespace hyperlane, allowing us to reduce flight times by up to 40%!" + prereq_ids = list("shuttle_route_upgrade", "micro_bluespace") + design_ids = list("disk_shuttle_route_hyper") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000) + export_price = 5000 + +/datum/techweb_node/shuttle_route_upgrade_void + id = "shuttle_route_upgrade_void" + display_name = "Nullspace Breaching Upgrade" + description = "Research into voidspace tunnelling, allowing us to significantly reduce flight times." + prereq_ids = list("shuttle_route_upgrade_hyper", "alientech") + design_ids = list("disk_shuttle_route_void", "engine_void") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 12500) + export_price = 5000 + /////////////////////////robotics tech///////////////////////// /datum/techweb_node/robotics id = "robotics" diff --git a/code/modules/research/techweb/layout.dm b/code/modules/research/techweb/layout.dm index 13caacb682b4..a2e777139efe 100644 --- a/code/modules/research/techweb/layout.dm +++ b/code/modules/research/techweb/layout.dm @@ -8,505 +8,525 @@ // In addition, hovering over a node will show the coordinates if do_node_drag is set /datum/techweb_node/base - ui_x = 32 - ui_y = -128 + ui_x = 32 + ui_y = -128 /datum/techweb_node/mmi - ui_x = -32 - ui_y = 96 + ui_x = -32 + ui_y = 96 /datum/techweb_node/cyborg - ui_x = -96 - ui_y = 96 + ui_x = -96 + ui_y = 96 /datum/techweb_node/mech - ui_x = -448 - ui_y = 0 + ui_x = -448 + ui_y = 0 /datum/techweb_node/mech_tools - ui_x = -448 - ui_y = -64 + ui_x = -448 + ui_y = -64 /datum/techweb_node/basic_tools - ui_x = -96 - ui_y = -384 + ui_x = -96 + ui_y = -384 /datum/techweb_node/biotech - ui_x = 160 - ui_y = -128 + ui_x = 160 + ui_y = -128 /datum/techweb_node/datatheory - ui_x = 160 - ui_y = -256 + ui_x = 160 + ui_y = -256 /datum/techweb_node/engineering - ui_x = 32 - ui_y = -256 + ui_x = 32 + ui_y = -256 /datum/techweb_node/bluespace_basic - ui_x = -96 - ui_y = -192 + ui_x = -96 + ui_y = -192 /datum/techweb_node/robotics - ui_x = -160 - ui_y = 96 + ui_x = -160 + ui_y = 96 /datum/techweb_node/emp_basic - ui_x = -96 - ui_y = -128 + ui_x = -96 + ui_y = -128 /datum/techweb_node/clown - ui_x = 256 - ui_y = -576 + ui_x = 256 + ui_y = -576 /datum/techweb_node/sec_basic - ui_x = -512 - ui_y = -672 + ui_x = -512 + ui_y = -672 /datum/techweb_node/odysseus - ui_x = 352 - ui_y = -384 + ui_x = 352 + ui_y = -384 -/datum/techweb_node/spacepod_basic - ui_x = -160 - ui_y = -448 +/datum/techweb_node/spacevehicle_basic + ui_x = -32 + ui_y = -736 /datum/techweb_node/adv_biotech - ui_x = 288 - ui_y = -96 + ui_x = 288 + ui_y = -96 /datum/techweb_node/bio_process - ui_x = 224 - ui_y = -224 + ui_x = 224 + ui_y = -224 /datum/techweb_node/imp_wt_surgery - ui_x = 224 - ui_y = 64 + ui_x = 224 + ui_y = 64 /datum/techweb_node/neural_programming - ui_x = 544 - ui_y = -160 + ui_x = 544 + ui_y = -160 /datum/techweb_node/cloning - ui_x = 288 - ui_y = -256 + ui_x = 288 + ui_y = -256 /datum/techweb_node/cryotech - ui_x = 160 - ui_y = -192 + ui_x = 160 + ui_y = -192 /datum/techweb_node/subdermal_implants - ui_x = 288 - ui_y = -32 + ui_x = 288 + ui_y = -32 /datum/techweb_node/botany - ui_x = 320 - ui_y = -224 + ui_x = 320 + ui_y = -224 /datum/techweb_node/nanite_bio - ui_x = 480 - ui_y = -288 + ui_x = 480 + ui_y = -288 /datum/techweb_node/alientech - ui_x = 288 - ui_y = 320 + ui_x = 288 + ui_y = 320 /datum/techweb_node/high_efficiency - ui_x = -32 - ui_y = -320 + ui_x = -32 + ui_y = -320 /datum/techweb_node/comptech - ui_x = 192 - ui_y = -320 + ui_x = 192 + ui_y = -320 /datum/techweb_node/cyber_implants - ui_x = 352 - ui_y = -32 + ui_x = 352 + ui_y = -32 /datum/techweb_node/nanite_base - ui_x = 480 - ui_y = -224 + ui_x = 480 + ui_y = -224 /datum/techweb_node/adv_engi - ui_x = -32 - ui_y = -256 + ui_x = -32 + ui_y = -256 /datum/techweb_node/adv_power - ui_x = -128 - ui_y = -320 + ui_x = -128 + ui_y = -320 /datum/techweb_node/practical_bluespace - ui_x = -192 - ui_y = -224 + ui_x = -192 + ui_y = -224 /datum/techweb_node/basic_plasma - ui_x = 32 - ui_y = -320 + ui_x = 32 + ui_y = -320 /datum/techweb_node/cyborg_upg_util - ui_x = -96 - ui_y = 160 + ui_x = -96 + ui_y = 160 /datum/techweb_node/basic_mining - ui_x = 96 - ui_y = -384 + ui_x = 96 + ui_y = -384 /datum/techweb_node/weaponry - ui_x = -576 - ui_y = -416 + ui_x = -576 + ui_y = -416 /datum/techweb_node/smartmine - ui_x = -512 - ui_y = -800 + ui_x = -512 + ui_y = -800 /datum/techweb_node/nanite_mesh - ui_x = 416 - ui_y = -288 + ui_x = 416 + ui_y = -288 /datum/techweb_node/spacepod_lock - ui_x = -224 - ui_y = -448 + ui_x = 32 + ui_y = -768 /datum/techweb_node/telecomms - ui_x = 256 - ui_y = -320 + ui_x = 256 + ui_y = -320 /datum/techweb_node/adv_robotics - ui_x = -160 - ui_y = 160 + ui_x = -160 + ui_y = 160 /datum/techweb_node/ai - ui_x = 608 - ui_y = -96 + ui_x = 608 + ui_y = -96 /datum/techweb_node/emp_adv - ui_x = -160 - ui_y = -128 + ui_x = -160 + ui_y = -128 /datum/techweb_node/integrated_HUDs - ui_x = -96 - ui_y = -64 + ui_x = -96 + ui_y = -64 /datum/techweb_node/electric_weapons - ui_x = -480 - ui_y = -480 + ui_x = -480 + ui_y = -480 /datum/techweb_node/camera_theory - ui_x = 96 - ui_y = -320 + ui_x = 96 + ui_y = -320 /datum/techweb_node/landmine - ui_x = -512 - ui_y = -736 + ui_x = -512 + ui_y = -736 + +/datum/techweb_node/basic_shuttle_tech + ui_x = -96 + ui_y = -736 + +/datum/techweb_node/shuttle_route_upgrade + ui_x = -160 + ui_y = -736 /datum/techweb_node/spacepod_disabler - ui_x = -64 - ui_y = -576 + ui_x = 32 + ui_y = -704 /datum/techweb_node/spacepod_pseat - ui_x = -160 - ui_y = -512 - -/datum/techweb_node/xenology - ui_x = 480 - ui_y = -96 + ui_x = 32 + ui_y = -832 /datum/techweb_node/cyborg_upg_med - ui_x = 352 - ui_y = -160 - -/datum/techweb_node/cyborg_upg_surgkit - ui_x = 416 - ui_y = -160 + ui_x = 352 + ui_y = -160 /datum/techweb_node/cyber_organs - ui_x = 352 - ui_y = -96 + ui_x = 352 + ui_y = -96 /datum/techweb_node/medical_weapons - ui_x = -480 - ui_y = -544 + ui_x = -480 + ui_y = -544 /datum/techweb_node/med_mech_tools - ui_x = 288 - ui_y = -160 + ui_x = 288 + ui_y = -160 /datum/techweb_node/alien_bio - ui_x = 224 - ui_y = 256 + ui_x = 224 + ui_y = 256 /datum/techweb_node/adv_surgery - ui_x = 288 - ui_y = 64 + ui_x = 288 + ui_y = 64 /datum/techweb_node/posibrain - ui_x = 608 - ui_y = -160 + ui_x = 608 + ui_y = -160 /datum/techweb_node/adv_cyber_implants - ui_x = 416 - ui_y = -32 + ui_x = 416 + ui_y = -32 /datum/techweb_node/nanite_synaptic - ui_x = 576 - ui_y = -352 + ui_x = 576 + ui_y = -352 /datum/techweb_node/nanite_neural - ui_x = 512 - ui_y = -352 + ui_x = 512 + ui_y = -352 /datum/techweb_node/nanite_harmonic - ui_x = 448 - ui_y = -352 + ui_x = 448 + ui_y = -352 /datum/techweb_node/alien_surgery - ui_x = 352 - ui_y = 256 + ui_x = 352 + ui_y = 256 + +/datum/techweb_node/shuttle_route_upgrade_void + ui_x = 352 + ui_y = 320 /datum/techweb_node/nanite_hazard - ui_x = 288 - ui_y = 256 + ui_x = 288 + ui_y = 256 /datum/techweb_node/alien_engi - ui_x = 224 - ui_y = 320 + ui_x = 224 + ui_y = 320 /datum/techweb_node/micro_bluespace - ui_x = -320 - ui_y = -224 + ui_x = -320 + ui_y = -224 /datum/techweb_node/combat_cyber_implants - ui_x = 480 - ui_y = -32 + ui_x = 480 + ui_y = -32 /datum/techweb_node/spacepod_storage - ui_x = -160 - ui_y = -576 + ui_x = 96 + ui_y = -832 /datum/techweb_node/spacepod_lockbuster - ui_x = -224 - ui_y = -512 + ui_x = 96 + ui_y = -768 /datum/techweb_node/spacepod_iarmor - ui_x = -224 - ui_y = -576 + ui_x = 160 + ui_y = -832 /datum/techweb_node/computer_hardware_basic - ui_x = 256 - ui_y = -448 + ui_x = 256 + ui_y = -448 /datum/techweb_node/computer_board_gaming - ui_x = 192 - ui_y = -384 + ui_x = 192 + ui_y = -384 /datum/techweb_node/comp_recordkeeping - ui_x = 256 - ui_y = -384 + ui_x = 256 + ui_y = -384 /datum/techweb_node/nanite_smart - ui_x = 544 - ui_y = -288 + ui_x = 544 + ui_y = -288 /datum/techweb_node/anomaly - ui_x = -224 - ui_y = -288 + ui_x = -224 + ui_y = -288 /datum/techweb_node/NVGtech - ui_x = -160 - ui_y = -64 + ui_x = -160 + ui_y = -64 /datum/techweb_node/adv_mining - ui_x = 96 - ui_y = -448 + ui_x = 96 + ui_y = -448 /datum/techweb_node/janitor - ui_x = -320 - ui_y = -160 + ui_x = -320 + ui_y = -160 /datum/techweb_node/exp_tools - ui_x = -96 - ui_y = -448 + ui_x = -96 + ui_y = -448 /datum/techweb_node/rcd_upgrade - ui_x = -32 - ui_y = -448 + ui_x = -32 + ui_y = -448 /datum/techweb_node/adv_weaponry - ui_x = -576 - ui_y = -576 + ui_x = -576 + ui_y = -576 /datum/techweb_node/advmine - ui_x = -448 - ui_y = -800 + ui_x = -448 + ui_y = -800 /datum/techweb_node/radioactive_weapons - ui_x = -704 - ui_y = -736 + ui_x = -704 + ui_y = -736 /datum/techweb_node/syndicate_basic - ui_x = -384 - ui_y = -672 + ui_x = -384 + ui_y = -672 /datum/techweb_node/bluespace_power - ui_x = -192 - ui_y = -352 + ui_x = -192 + ui_y = -352 /datum/techweb_node/mech_tesla - ui_x = -576 - ui_y = -256 + ui_x = -576 + ui_y = -256 /datum/techweb_node/bluespace_travel - ui_x = -256 - ui_y = -224 + ui_x = -256 + ui_y = -224 /datum/techweb_node/adv_plasma - ui_x = 32 - ui_y = -384 + ui_x = 32 + ui_y = -384 /datum/techweb_node/spacepod_ka - ui_x = -64 - ui_y = -512 + ui_x = 32 + ui_y = -640 /datum/techweb_node/ballistic_weapons - ui_x = -736 - ui_y = -416 + ui_x = -736 + ui_y = -416 /datum/techweb_node/gygax - ui_x = -672 - ui_y = -160 + ui_x = -672 + ui_y = -160 /datum/techweb_node/phazon - ui_x = -736 - ui_y = -128 + ui_x = -736 + ui_y = -128 /datum/techweb_node/adv_mecha - ui_x = -576 - ui_y = -96 + ui_x = -576 + ui_y = -96 /datum/techweb_node/emp_super - ui_x = -224 - ui_y = -128 + ui_x = -224 + ui_y = -128 /datum/techweb_node/mech_ion - ui_x = -672 - ui_y = -256 + ui_x = -672 + ui_y = -256 /datum/techweb_node/spacepod_lasers - ui_x = 0 - ui_y = -576 + ui_x = 96 + ui_y = -704 + +/datum/techweb_node/shuttle_route_upgrade_hyper + ui_x = -224 + ui_y = -736 + +/datum/techweb_node/cyborg_upg_surgkit + ui_x = 416 + ui_y = -160 /datum/techweb_node/cyber_organs_upgraded - ui_x = 416 - ui_y = -96 + ui_x = 416 + ui_y = -96 /datum/techweb_node/exp_surgery - ui_x = 352 - ui_y = 64 + ui_x = 352 + ui_y = 64 /datum/techweb_node/nanite_combat - ui_x = -320 - ui_y = -672 + ui_x = -320 + ui_y = -672 /datum/techweb_node/advanced_bluespace - ui_x = -384 - ui_y = -160 + ui_x = -384 + ui_y = -160 /datum/techweb_node/mech_teleporter - ui_x = -416 - ui_y = -256 + ui_x = -416 + ui_y = -256 /datum/techweb_node/mech_diamond_drill - ui_x = 128 - ui_y = -512 + ui_x = 96 + ui_y = -512 /datum/techweb_node/spacepod_advmining - ui_x = 0 - ui_y = -512 + ui_x = 96 + ui_y = -640 + +/datum/techweb_node/adv_rcd_upgrade + ui_x = 32 + ui_y = -448 /datum/techweb_node/beam_weapons - ui_x = -640 - ui_y = -736 + ui_x = -640 + ui_y = -736 /datum/techweb_node/explosive_weapons - ui_x = -736 - ui_y = -576 + ui_x = -736 + ui_y = -576 /datum/techweb_node/exotic_ammo - ui_x = -704 - ui_y = -672 + ui_x = -704 + ui_y = -672 /datum/techweb_node/gravity_gun - ui_x = -320 - ui_y = -288 + ui_x = -320 + ui_y = -288 /datum/techweb_node/durand - ui_x = -672 - ui_y = -96 + ui_x = -672 + ui_y = -96 /datum/techweb_node/unregulated_bluespace - ui_x = -384 - ui_y = -608 + ui_x = -384 + ui_y = -608 /datum/techweb_node/syndicate_surgery - ui_x = -320 - ui_y = -608 + ui_x = -320 + ui_y = -608 /datum/techweb_node/mech_modules - ui_x = -640 - ui_y = -32 + ui_x = -640 + ui_y = -32 /datum/techweb_node/mech_wormhole_gen - ui_x = -416 - ui_y = -320 + ui_x = -416 + ui_y = -320 /datum/techweb_node/spacepod_advplasmacutter - ui_x = 64 - ui_y = -512 + ui_x = 160 + ui_y = -640 /datum/techweb_node/mech_scattershot - ui_x = -800 - ui_y = -352 + ui_x = -800 + ui_y = -352 /datum/techweb_node/mech_carbine - ui_x = -800 - ui_y = -416 + ui_x = -800 + ui_y = -416 /datum/techweb_node/mech_lmg - ui_x = -736 - ui_y = -352 + ui_x = -736 + ui_y = -352 /datum/techweb_node/adv_mecha_tools - ui_x = -576 - ui_y = -32 + ui_x = -576 + ui_y = -32 + +/datum/techweb_node/xenology + ui_x = 480 + ui_y = -96 /datum/techweb_node/adv_beam_weapons - ui_x = -640 - ui_y = -832 + ui_x = -640 + ui_y = -832 /datum/techweb_node/mech_laser - ui_x = -704 - ui_y = -896 + ui_x = -704 + ui_y = -896 /datum/techweb_node/mech_disabler - ui_x = -448 - ui_y = -224 + ui_x = -448 + ui_y = -224 /datum/techweb_node/mech_grenade_launcher - ui_x = -800 - ui_y = -512 + ui_x = -800 + ui_y = -512 /datum/techweb_node/mech_missile_rack - ui_x = -800 - ui_y = -640 + ui_x = -800 + ui_y = -640 /datum/techweb_node/clusterbang_launcher - ui_x = -800 - ui_y = -576 + ui_x = -800 + ui_y = -576 /datum/techweb_node/mech_laser_heavy - ui_x = -576 - ui_y = -896 + ui_x = -576 + ui_y = -896 /datum/techweb_node/mech_xray - ui_x = -640 - ui_y = -896 \ No newline at end of file + ui_x = -640 + ui_y = -896 diff --git a/code/modules/shuttle/custom_shuttle.dm b/code/modules/shuttle/custom_shuttle.dm new file mode 100644 index 000000000000..03cb5cfbfe74 --- /dev/null +++ b/code/modules/shuttle/custom_shuttle.dm @@ -0,0 +1,272 @@ +#define Z_DIST 500 +#define CUSTOM_ENGINES_START_TIME 65 +#define CALCULATE_STATS_COOLDOWN 2 + +/obj/machinery/computer/custom_shuttle + name = "nanotrasen shuttle flight controller" + desc = "A terminal used to fly shuttles defined by the Shuttle Zoning Designator" + circuit = /obj/item/circuitboard/computer/shuttle/flight_control + icon_screen = "shuttle" + icon_keyboard = "tech_key" + light_color = LIGHT_COLOR_CYAN + req_access = list( ) + var/shuttleId + var/possible_destinations = "whiteship_home" + var/admin_controlled + var/no_destination_swap = 0 + var/calculated_mass = 0 + var/calculated_dforce = 0 + var/calculated_speed = 0 + var/calculated_engine_count = 0 + var/calculated_consumption = 0 + var/calculated_cooldown = 0 + var/calculated_non_operational_thrusters = 0 + var/calculated_fuel_less_thrusters = 0 + var/target_fuel_cost = 0 + var/targetLocation + var/datum/browser/popup + + var/stat_calc_cooldown = 0 + + //Upgrades + var/distance_multiplier = 1 + +/obj/machinery/computer/custom_shuttle/examine(mob/user) + . = ..() + . += distance_multiplier < 1 ? "Bluespace shortcut module installed. Route is [distance_multiplier]x the original length." : "" + +/obj/machinery/computer/custom_shuttle/ui_interact(mob/user) + var/list/options = params2list(possible_destinations) + var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId) + var/dat = "[M ? "Current Location : [M.getStatusText()]" : "Shuttle link required."]

" + if(M) + dat += "Run Flight Calculations
" + dat += "Shuttle Data
" + dat += "Shuttle Mass: [calculated_mass/10]tons
" + dat += "Engine Force: [calculated_dforce]kN ([calculated_engine_count] engines)
" + dat += "Sublight Speed: [calculated_speed]ms-1
" + dat += calculated_speed < 1 ? "INSUFFICIENT ENGINE POWER
" : "" + dat += calculated_non_operational_thrusters > 0 ? "Warning: [calculated_non_operational_thrusters] thrusters offline.
" : "" + dat += "Fuel Consumption: [calculated_consumption]units per distance
" + dat += "Engine Cooldown: [calculated_cooldown]s
" + var/destination_found + for(var/obj/docking_port/stationary/S in SSshuttle.stationary) + if(!options.Find(S.id)) + continue + if(!M.check_dock(S, silent=TRUE)) + continue + if(calculated_speed == 0) + break + destination_found = TRUE + var/dist = round(calculateDistance(S)) + dat += "Target [S.name] (Dist: [dist] | Fuel Cost: [round(dist * calculated_consumption)] | Time: [round(dist / calculated_speed)])
" + if(!destination_found) + dat += "No valid destinations
" + dat += "
[targetLocation ? "Target Location : [targetLocation]" : "No Target Location"]" + dat += "
Initate Flight
" + dat += "Close" + + popup = new(user, "computer", M ? M.name : "shuttle", 350, 450) + popup.set_content("
[dat]
") + //popup.set_title_image(usr.browse_rsc_icon(src.icon, src.icon_state)) + popup.open() + +/obj/machinery/computer/custom_shuttle/Topic(href, href_list) + if(..()) + return + usr.set_machine(src) + src.add_fingerprint(usr) + if(!allowed(usr)) + to_chat(usr, "Access denied.") + return + + if(href_list["calculate"]) + calculateStats() + ui_interact(usr) + return + var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId) + if(!M) + return + if(M.launch_status == ENDGAME_LAUNCHED) + return + if(href_list["setloc"]) + SetTargetLocation(href_list["setloc"]) + ui_interact(usr) + return + else if(href_list["fly"]) + Fly() + ui_interact(usr) + return + +/obj/machinery/computer/custom_shuttle/proc/calculateDistance(var/obj/port) + var/deltaX = port.x - x + var/deltaY = port.y - y + var/deltaZ = (port.z - z) * Z_DIST + return sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ) * distance_multiplier + +/obj/machinery/computer/custom_shuttle/proc/linkShuttle(var/new_id) + shuttleId = new_id + possible_destinations = "whiteship_home;shuttle[new_id]_custom" + +/obj/machinery/computer/custom_shuttle/proc/calculateStats(var/useFuel = FALSE, var/dist = 0, var/ignore_cooldown = FALSE) + if(!ignore_cooldown && stat_calc_cooldown >= world.time) + to_chat(usr, "You are using this too fast, please slow down") + return + stat_calc_cooldown = world.time + CALCULATE_STATS_COOLDOWN + var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId) + if(!M) + return FALSE + //Reset data + calculated_mass = 0 + calculated_dforce = 0 + calculated_speed = 0 + calculated_engine_count = 0 + calculated_consumption = 0 + calculated_cooldown = 0 + calculated_fuel_less_thrusters = 0 + calculated_non_operational_thrusters = 0 + //Calculate all the data + var/list/areas = M.shuttle_areas + for(var/shuttleArea in areas) + calculated_mass += length(get_area_turfs(shuttleArea)) + for(var/obj/machinery/shuttle/engine/E in shuttleArea) + E.check_setup() + if(!E.thruster_active) //Skipover thrusters with no valid heater + calculated_non_operational_thrusters ++ + continue + if(E.attached_heater) + var/obj/machinery/atmospherics/components/unary/shuttle/heater/resolvedHeater = E.attached_heater.resolve() + if(resolvedHeater && !resolvedHeater.hasFuel(dist * E.fuel_use) && useFuel) + calculated_fuel_less_thrusters ++ + continue + calculated_engine_count++ + calculated_dforce += E.thrust + calculated_consumption += E.fuel_use + calculated_cooldown = max(calculated_cooldown, E.cooldown) + //This should really be accelleration, but its a 2d spessman game so who cares + if(calculated_mass == 0) + return FALSE + calculated_speed = (calculated_dforce*1000) / (calculated_mass*100) + return TRUE + +/obj/machinery/computer/custom_shuttle/proc/consumeFuel(var/dist) + var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId) + if(!M) + return FALSE + //Calculate all the data + for(var/obj/machinery/shuttle/engine/shuttle_machine in GLOB.custom_shuttle_machines) + shuttle_machine.check_setup() + if(!shuttle_machine.thruster_active) + continue + if(get_area(M) != get_area(shuttle_machine)) + continue + if(shuttle_machine.attached_heater) + var/obj/machinery/atmospherics/components/unary/shuttle/heater/resolvedHeater = shuttle_machine.attached_heater.resolve() + if(resolvedHeater && !resolvedHeater.hasFuel(dist * shuttle_machine.fuel_use)) + continue + resolvedHeater?.consumeFuel(dist * shuttle_machine.fuel_use) + shuttle_machine.fireEngine() + +/obj/machinery/computer/custom_shuttle/proc/SetTargetLocation(var/newTarget) + if(!(newTarget in params2list(possible_destinations))) + log_admin("[usr] attempted to href dock exploit on [src] with target location \"[newTarget]\"") + message_admins("[usr] just attempted to href dock exploit on [src] with target location \"[newTarget]\"") + return + targetLocation = newTarget + say("Shuttle route calculated.") + return + +/obj/machinery/computer/custom_shuttle/proc/Fly() + if(!targetLocation) + return + var/obj/docking_port/mobile/linkedShuttle = SSshuttle.getShuttle(shuttleId) + if(!linkedShuttle) + return + if(linkedShuttle.mode != SHUTTLE_IDLE) + return + if(!calculateStats(TRUE, 0, TRUE)) + return + if(calculated_fuel_less_thrusters > 0) + say("Warning, [calculated_fuel_less_thrusters] do not have enough fuel for this journey, engine output may be limitted.") + if(calculated_speed < 1) + say("Insufficient engine power, shuttle requires [calculated_mass / 10]kN of thrust.") + return + var/obj/docking_port/stationary/targetPort = SSshuttle.getDock(targetLocation) + if(!targetPort) + return + var/dist = calculateDistance(targetPort) + var/time = min(max(round(dist / calculated_speed), 10), 90) + linkedShuttle.callTime = time * 10 + linkedShuttle.rechargeTime = calculated_cooldown + linkedShuttle.ignitionTime = CUSTOM_ENGINES_START_TIME + linkedShuttle.count_engines() + linkedShuttle.hyperspace_sound(HYPERSPACE_WARMUP) + var/throwForce = clamp((calculated_speed / 2) - 5, 0, 10) + linkedShuttle.movement_force = list("KNOCKDOWN" = calculated_speed > 5 ? 3 : 0, "THROW" = throwForce) + if(!(targetLocation in params2list(possible_destinations))) + log_admin("[usr] attempted to launch a shuttle that has been affected by href dock exploit on [src] with target location \"[targetLocation]\"") + message_admins("[usr] attempted to launch a shuttle that has been affected by href dock exploit on [src] with target location \"[targetLocation]\"") + return + switch(SSshuttle.moveShuttle(shuttleId, targetLocation, 1)) + if(0) + consumeFuel(dist) + say("Shuttle departing. Please stand away from the doors.") + if(1) + to_chat(usr, "Invalid shuttle requested.") + else + to_chat(usr, "Unable to comply.") + return + +/obj/machinery/computer/custom_shuttle/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE) + if(port && (shuttleId == initial(shuttleId) || override)) + linkShuttle(port.id) + +//Custom shuttle docker locations +/obj/machinery/computer/camera_advanced/shuttle_docker/custom + name = "Shuttle Zoning Designator" + desc = "Used to designate a precise transit location for private ships." + lock_override = NONE + whitelist_turfs = list(/turf/open/space, + /turf/open/lava, + /turf/open/floor/plating/beach, + /turf/open/floor/plating/ashplanet, + /turf/open/floor/plating/asteroid, + /turf/open/floor/plating/lavaland_baseturf) + jumpto_ports = list("whiteship_home" = 1) + view_range = 12 + designate_time = 100 + circuit = /obj/item/circuitboard/computer/shuttle/docker + +/obj/machinery/computer/camera_advanced/shuttle_docker/custom/Initialize() + . = ..() + GLOB.jam_on_wardec += src + +/obj/machinery/computer/camera_advanced/shuttle_docker/custom/Destroy() + GLOB.jam_on_wardec -= src + return ..() + +/obj/machinery/computer/camera_advanced/shuttle_docker/custom/placeLandingSpot() + if(!shuttleId) + return //Only way this would happen is if someone else delinks the console while in use somehow + var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId) + if(M?.mode != SHUTTLE_IDLE) + to_chat(usr, "You cannot target locations while in transit.") + return + ..() + +/obj/machinery/computer/camera_advanced/shuttle_docker/custom/attack_hand(mob/user) + if(!shuttleId) + to_chat(user, "You must link the console to a shuttle first.") + return + return ..() + +/obj/machinery/computer/camera_advanced/shuttle_docker/custom/proc/linkShuttle(new_id) + shuttleId = new_id + shuttlePortId = "shuttle[new_id]_custom" + + //Take info from connected port and calculate amendments + var/obj/docking_port/mobile/M = SSshuttle.getShuttle(new_id) + var/list/shuttlebounds = M.return_coords() + view_range = min(round(max(M.width, M.height)*0.5), 15) + x_offset = round((shuttlebounds[1] + shuttlebounds[3])*0.5) - M.x + y_offset = round((shuttlebounds[2] + shuttlebounds[4])*0.5) - M.y diff --git a/code/modules/shuttle/navigation_computer.dm b/code/modules/shuttle/navigation_computer.dm index e5225d3fcbab..bebc478c2a1a 100644 --- a/code/modules/shuttle/navigation_computer.dm +++ b/code/modules/shuttle/navigation_computer.dm @@ -11,9 +11,11 @@ var/list/jumpto_ports = list() //hashset of ports to jump to and ignore for collision purposes var/obj/docking_port/stationary/my_port //the custom docking port placed by this console var/obj/docking_port/mobile/shuttle_port //the mobile docking port of the connected shuttle + var/list/locked_traits = list(ZTRAIT_RESERVED, ZTRAIT_CENTCOM, ZTRAIT_AWAY, ZTRAIT_REEBE) //traits forbided for custom docking var/view_range = 0 var/x_offset = 0 var/y_offset = 0 + var/list/whitelist_turfs = list(/turf/open/space, /turf/open/floor/plating, /turf/open/lava) var/space_turfs_only = TRUE var/see_hidden = FALSE var/designate_time = 0 @@ -29,6 +31,7 @@ var/obj/docking_port/stationary/S = V if(jumpto_ports[S.id]) z_lock |= S.z + whitelist_turfs = typecacheof(whitelist_turfs) /obj/machinery/computer/camera_advanced/shuttle_docker/Destroy() . = ..() @@ -162,7 +165,7 @@ if(current_user.client) current_user.client.images += the_eye.placed_images to_chat(current_user, span_notice("Transit location designated")) - return + return TRUE /obj/machinery/computer/camera_advanced/shuttle_docker/proc/canDesignateTarget() if(!designating_target_loc || !current_user || (eyeobj.loc != designating_target_loc) || (stat & (NOPOWER|BROKEN)) ) @@ -190,7 +193,7 @@ var/turf/eyeturf = get_turf(the_eye) if(!eyeturf) return SHUTTLE_DOCKER_BLOCKED - if(z_lock.len && !(eyeturf.z in z_lock)) + if(!eyeturf.z || SSmapping.level_has_any_trait(eyeturf.z, locked_traits)) return SHUTTLE_DOCKER_BLOCKED . = SHUTTLE_DOCKER_LANDING_CLEAR @@ -228,12 +231,17 @@ if(hidden_turf_info) . = SHUTTLE_DOCKER_BLOCKED_BY_HIDDEN_PORT + if(length(whitelist_turfs)) + var/turf_type = hidden_turf_info ? hidden_turf_info[2] : T.type + if(!is_type_in_typecache(turf_type, whitelist_turfs)) + return SHUTTLE_DOCKER_BLOCKED +/* if(space_turfs_only) var/turf_type = hidden_turf_info ? hidden_turf_info[2] : T.type var/area/A = get_area(T) if(!ispath(turf_type, /turf/open/space) && !ispath(A.type, /area/icemoon)) return SHUTTLE_DOCKER_BLOCKED - +*/ // Checking for overlapping dock boundaries for(var/i in 1 to overlappers.len) var/obj/docking_port/port = overlappers[i] @@ -324,13 +332,25 @@ var/list/L = list() for(var/V in SSshuttle.stationary) if(!V) + stack_trace("SSshuttle.stationary have null entry!") continue var/obj/docking_port/stationary/S = V if(console.z_lock.len && !(S.z in console.z_lock)) continue if(console.jumpto_ports[S.id]) - L[S.name] = S + L["([L.len])[S.name]"] = S + for(var/V in SSshuttle.beacons) + if(!V) + stack_trace("SSshuttle.beacons have null entry!") + continue + var/obj/machinery/spaceship_navigation_beacon/nav_beacon = V + if(!nav_beacon.z || SSmapping.level_has_any_trait(nav_beacon.z, console.locked_traits)) + break + if(!nav_beacon.locked) + L["([L.len]) [nav_beacon.name] located: [nav_beacon.x] [nav_beacon.y] [nav_beacon.z]"] = nav_beacon + else + L["([L.len]) [nav_beacon.name] locked"] = null playsound(console, 'sound/machines/terminal_prompt.ogg', 25, 0) var/selected = input("Choose location to jump to", "Locations", null) as null|anything in L if(QDELETED(src) || QDELETED(target) || !isliving(target)) @@ -345,4 +365,4 @@ C.overlay_fullscreen("flash", /obj/screen/fullscreen/flash/static) C.clear_fullscreen("flash", 3) else - playsound(console, 'sound/machines/terminal_prompt_deny.ogg', 25, 0) \ No newline at end of file + playsound(console, 'sound/machines/terminal_prompt_deny.ogg', 25, 0) diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index 11aa65c70b88..8b59f0e7fcc9 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -826,12 +826,17 @@ /obj/docking_port/mobile/proc/count_engines() . = 0 + engine_list.Cut() for(var/thing in shuttle_areas) var/area/shuttle/areaInstance = thing for(var/obj/structure/shuttle/engine/E in areaInstance.contents) if(!QDELETED(E)) engine_list += E . += E.engine_power + for(var/obj/machinery/shuttle/engine/E in areaInstance.contents) + if(!QDELETED(E)) + engine_list += E + . += E.thruster_active ? 1 : 0 // Double initial engines to get to 0.5 minimum // Lose all initial engines to get to 2 diff --git a/code/modules/shuttle/shuttle_creation/shuttle_creator.dm b/code/modules/shuttle/shuttle_creation/shuttle_creator.dm new file mode 100644 index 000000000000..d4bd4178117b --- /dev/null +++ b/code/modules/shuttle/shuttle_creation/shuttle_creator.dm @@ -0,0 +1,401 @@ +#define SHUTTLE_CREATOR_MAX_SIZE CONFIG_GET(number/max_shuttle_size) +#define CUSTOM_SHUTTLE_LIMIT CONFIG_GET(number/max_shuttle_count) +#define CARDINAL_DIRECTIONS_X list(1, 0, -1, 0) +#define CARDINAL_DIRECTIONS_Y list(0, 1, 0, -1) + +GLOBAL_VAR_INIT(custom_shuttle_count, 0) //The amount of custom shuttles created to prevent creating hundreds +GLOBAL_LIST_EMPTY(custom_shuttle_machines) //Machines that require updating (Heaters, engines) + +//============ Shuttle Creator Object ============ +/obj/item/shuttle_creator + name = "Rapid Shuttle Designator" + icon = 'icons/obj/tools.dmi' + icon_state = "rsd" + lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' + desc = "A device used to define the area required for custom ships. Uses bluespace crystals to create bluespace-capable ships." + density = FALSE + anchored = FALSE + flags_1 = CONDUCT_1 + item_flags = NOBLUDGEON + force = 0 + throwforce = 8 + throw_speed = 3 + throw_range = 5 + w_class = WEIGHT_CLASS_NORMAL + req_access_txt = "11" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) + resistance_flags = FIRE_PROOF + var/ready = TRUE + //pre-designation + var/override_max_shuttles = FALSE + var/obj/machinery/computer/camera_advanced/shuttle_creator/internal_shuttle_creator + var/ignore_max_shuttle_size = FALSE + var/ignore_area = FALSE + //During designation + var/overwritten_area = /area/space + var/list/loggedTurfs = list() + var/loggedOldArea + var/recorded_shuttle_area + var/datum/shuttle_creator_overlay_holder/overlay_holder + //After designation + var/linkedShuttleId + +/obj/item/shuttle_creator/Initialize() + . = ..() + internal_shuttle_creator = new() + internal_shuttle_creator.owner_rsd = src + overlay_holder = new() + +/obj/item/shuttle_creator/Destroy() + . = ..() + if(internal_shuttle_creator) + internal_shuttle_creator.owner_rsd = null + QDEL_NULL(internal_shuttle_creator) + if(overlay_holder) + QDEL_NULL(overlay_holder) + +/obj/item/shuttle_creator/attack_self(mob/user) + ..() + if(linkedShuttleId) + select_preferred_direction(user) + return + if(GLOB.custom_shuttle_count > CUSTOM_SHUTTLE_LIMIT && !override_max_shuttles) + to_chat(user, "Too many shuttles have been created.") + message_admins("[ADMIN_FLW(user)] attempted to create a shuttle, however [CUSTOM_SHUTTLE_LIMIT] have already been created.") + return + if(!internal_shuttle_creator) + return + overlay_holder.add_client(user.client) + internal_shuttle_creator.attack_hand(user) + +/obj/item/shuttle_creator/afterattack(atom/target, mob/user, proximity_flag) + . = ..() + if(!ready) + to_chat(user, "You need to define a shuttle area first.") + return + if(!proximity_flag) + return + if(istype(target, /obj/machinery/computer/custom_shuttle)) + if(!linkedShuttleId) + to_chat(user, "Error, no defined shuttle linked to device") + return + var/obj/machinery/computer/custom_shuttle/console = target + console.linkShuttle(linkedShuttleId) + to_chat(user, "Console linked successfully!") + return + else if(istype(target, /obj/machinery/computer/camera_advanced/shuttle_docker/custom)) + if(!linkedShuttleId) + to_chat(user, "Error, no defined shuttle linked to device") + return + var/obj/machinery/computer/camera_advanced/shuttle_docker/custom/console = target + console.linkShuttle(linkedShuttleId) + to_chat(user, "Console linked successfully!") + return + to_chat(user, "The [src] bleeps. Select an airlock to create a docking port, or a valid machine to link.") + return + +//=========== shuttle designation actions ============ +/obj/item/shuttle_creator/proc/calculate_bounds(obj/docking_port/mobile/port) + if(!port || !istype(port, /obj/docking_port/mobile)) + return FALSE + //Heights is the distance away from the port + //width is the distance perpendicular to the port + var/minX = INFINITY + var/maxX = 0 + var/minY = INFINITY + var/maxY = 0 + for(var/turf/T in loggedTurfs) + minX = min(T.x, minX) + maxX = max(T.x, maxX) + minY = min(T.y, minY) + maxY = max(T.y, maxY) + //Make sure shuttle was actually found. + if(maxX == INFINITY || maxY == INFINITY) + return FALSE + minX-- + minY-- + var/width = maxX - minX + var/height = maxY - minY + var/offset_x = port.x - minX + var/offset_y = port.y - minY + switch(port.dir) //Source: code/datums/shuttles.dm line 77 (14/03/2020) :) + if(NORTH) + port.width = width + port.height = height + port.dwidth = offset_x - 1 + port.dheight = offset_y - 1 + if(EAST) + port.width = height + port.height = width + port.dwidth = height - offset_y + port.dheight = offset_x - 1 + if(SOUTH) + port.width = width + port.height = height + port.dwidth = width - offset_x + port.dheight = height - offset_y + if(WEST) + port.width = height + port.height = width + port.dwidth = offset_y - 1 + port.dheight = width - offset_x + return TRUE + +//Go through all the all_turfs and check which direction doesn't have the shuttle +/obj/item/shuttle_creator/proc/getNonShuttleDirection(turf/targetTurf) + var/position = null + if(!(get_offset_target_turf(targetTurf, 0, 1) in loggedTurfs)) + if(position != null) + return null + position = NORTH + if(!(get_offset_target_turf(targetTurf, 0, -1) in loggedTurfs)) + if(position != null) + return null + position = SOUTH + if(!(get_offset_target_turf(targetTurf, 1, 0) in loggedTurfs)) + if(position != null) + return null + position = EAST + if(!(get_offset_target_turf(targetTurf, -1, 0) in loggedTurfs)) + if(position != null) + return null + position = WEST + return position + +/obj/item/shuttle_creator/proc/invertDir(var/input_dir) + if(input_dir == NORTH) + return SOUTH + else if(input_dir == SOUTH) + return NORTH + else if(input_dir == EAST) + return WEST + else if(input_dir == WEST) + return EAST + return null + +/obj/item/shuttle_creator/proc/shuttle_create_docking_port(atom/target, mob/user) + + if(loggedTurfs.len == 0 || !recorded_shuttle_area) + to_chat(user, "Invalid shuttle, restarting bluespace systems...") + return FALSE + + var/datum/map_template/shuttle/new_shuttle = new /datum/map_template/shuttle() + + var/obj/docking_port/mobile/port = new /obj/docking_port/mobile(get_turf(target)) + var/obj/docking_port/stationary/stationary_port = new /obj/docking_port/stationary(get_turf(target)) + port.callTime = 50 + port.dir = 1 //Point away from space. + port.id = "custom_[GLOB.custom_shuttle_count]" + linkedShuttleId = port.id + port.ignitionTime = 25 + port.name = "Custom Shuttle" + port.port_direction = 2 + port.preferred_direction = EAST + port.preferred_direction = 4 + port.area_type = recorded_shuttle_area + + var/portDirection = getNonShuttleDirection(get_turf(port)) + var/invertedDir = invertDir(portDirection) + if(!portDirection || !invertedDir) + to_chat(usr, "Shuttle creation aborted, docking airlock must be on an external wall. Please select a new airlock.") + port.Destroy() + stationary_port.Destroy() + linkedShuttleId = null + return FALSE + port.dir = invertedDir + port.port_direction = portDirection + + if(!calculate_bounds(port)) + to_chat(usr, "Bluespace calculations failed, please select a new airlock.") + port.Destroy() + stationary_port.Destroy() + linkedShuttleId = null + return FALSE + + port.shuttle_areas = list() + //var/list/all_turfs = port.return_ordered_turfs(port.x, port.y, port.z, port.dir) + var/list/all_turfs = loggedTurfs + for(var/i in 1 to all_turfs.len) + var/turf/curT = all_turfs[i] + var/area/cur_area = curT.loc + //Add the area to the shuttle <3 + if(istype(cur_area, recorded_shuttle_area)) + if(istype(curT, /turf/open/space)) + continue + if(length(curT.baseturfs) < 2) + continue + //Add the shuttle base shit to the shuttle + curT.baseturfs.Insert(3, /turf/baseturf_skipover/shuttle) + port.shuttle_areas[cur_area] = TRUE + + port.linkup(new_shuttle, stationary_port) + + port.movement_force = list("KNOCKDOWN" = 0, "THROW" = 0) + port.initiate_docking(stationary_port) + + port.mode = SHUTTLE_IDLE + port.timer = 0 + + port.register() + + icon_state = initial(icon_state) + "_used" + + //Select shuttle fly direction. + select_preferred_direction(user) + + //Clear highlights + overlay_holder.clear_highlights() + GLOB.custom_shuttle_count ++ + message_admins("[ADMIN_LOOKUPFLW(user)] created a new shuttle with a [src] at [ADMIN_VERBOSEJMP(user)] ([GLOB.custom_shuttle_count] custom shuttles, limit is [CUSTOM_SHUTTLE_LIMIT])") + log_game("[key_name(user)] created a new shuttle with a [src] at [AREACOORD(user)] ([GLOB.custom_shuttle_count] custom shuttles, limit is [CUSTOM_SHUTTLE_LIMIT])") + return TRUE + +/obj/item/shuttle_creator/proc/create_shuttle_area(mob/user) + //Check to see if the user can make a new area to prevent spamming + if(user) + if(user.create_area_cooldown >= world.time) + to_chat(user, "Smoke vents from the [src], maybe you should let it cooldown before using it again.") + return FALSE + user.create_area_cooldown = world.time + 10 + if(!loggedTurfs) + return FALSE + //Create the new area + var/area/shuttle/custom/powered/newS + var/area/oldA = loggedOldArea + var/str = stripped_input(user, "Shuttle Name:", "Blueprint Editing", "", MAX_NAME_LEN) + if(!str || !length(str)) + return FALSE + if(length(str) > 50) + to_chat(user, "The provided ship name is too long, blares the [src]") + return FALSE +//Yogs Start: Runs the name through the petty filter. If they trip it, it will cause the shuttle creation to fail, messages the admins, and put the RSD on cooldown. + if(isnotpretty(str)) + to_chat(user, "Nanotrasen prohibited words are in use in this shuttle name, blares the [src] in a slightly offended tone.") + message_admins("[ADMIN_LOOKUPFLW(user)] attempted to created a new shuttle with a [src] at [ADMIN_VERBOSEJMP(user)], but failed because of not passing the pretty filter") + user.create_area_cooldown = world.time + 10 + return FALSE +//Yogs End + newS = new /area/shuttle/custom/powered() + newS.setup(str) + newS.set_dynamic_lighting() + //Shuttles always have gravity + newS.has_gravity = TRUE + newS.requires_power = TRUE + //Record the area for use when creating the docking port + recorded_shuttle_area = newS + + for(var/i in 1 to loggedTurfs.len) + var/turf/turf_holder = loggedTurfs[i] + var/area/old_area = turf_holder.loc + newS.contents += turf_holder + turf_holder.change_area(old_area, newS) + + newS.reg_in_areas_in_z() + + var/list/firedoors = oldA.firedoors + for(var/door in firedoors) + var/obj/machinery/door/firedoor/FD = door + FD.CalculateAffectingAreas() + return TRUE + +//Select shuttle fly direction. +/obj/item/shuttle_creator/proc/select_preferred_direction(mob/user) + var/obj/docking_port/mobile/port = SSshuttle.getShuttle(linkedShuttleId) + if(!port || !istype(port, /obj/docking_port/mobile)) + return FALSE + var/static/list/choice = list("NORTH" = NORTH, "SOUTH" = SOUTH, "EAST" = EAST, "WEST" = WEST) + var/Pdir = input(user, "Shuttle Fly Direction:", "Blueprint Editing", "NORTH") as null|anything in list("NORTH", "SOUTH", "EAST", "WEST") + if(Pdir) + port.preferred_direction = choice[Pdir] + +//Checks an area to ensure that the turfs provided are valid to be made into a shuttle +/obj/item/shuttle_creator/proc/check_area(list/turfs) + if(!turfs) + to_chat(usr, "Shuttles must be created in an airtight space, ensure that the shuttle is airtight, including corners.") + return FALSE + if(turfs.len > SHUTTLE_CREATOR_MAX_SIZE && !ignore_max_shuttle_size) + to_chat(usr, "The [src]'s internal cooling system wizzes violently and a message appears on the screen, \"Caution, this device can only handle the creation of shuttles up to [SHUTTLE_CREATOR_MAX_SIZE] units in size. Please reduce your shuttle by [turfs.len-SHUTTLE_CREATOR_MAX_SIZE]. Sorry for the inconvinience\"") + return FALSE + //Check to see if it's a valid shuttle + for(var/i in 1 to turfs.len) + var/area/place = get_area(turfs[i]) + //If any of the turfs are on station / not in space, a shuttle cannot be forced there + if(!place) + to_chat(usr, "You can't seem to overpower the bluespace harmonics in this location, try somewhere else.") + return FALSE + if(istype(place, /area/space)) + overwritten_area = /area/space + else if(istype(place, /area/lavaland/surface/outdoors)) + overwritten_area = /area/lavaland/surface/outdoors + else if(ignore_area) + overwritten_area = place + else + to_chat(usr, "Caution, shuttle must not use any material connected to the station. Your shuttle is currenly overlapping with [place.name]") + return FALSE + //Finally, check to see if the area is actually attached + if(!LAZYLEN(loggedTurfs)) + return TRUE + for(var/turf/T in turfs) + if(turf_connected_to_saved_turfs(T)) + return TRUE + CHECK_TICK + to_chat(usr, "Caution, new areas of the shuttle must be connected to the other areas of the shuttle.") + return FALSE + +/obj/item/shuttle_creator/proc/turf_connected_to_saved_turfs(turf/T) + for(var/i in 1 to 4) + var/turf/adjacentT = get_offset_target_turf(T, CARDINAL_DIRECTIONS_X[i], CARDINAL_DIRECTIONS_Y[i]) + if(adjacentT in loggedTurfs) + return TRUE + return FALSE + +/obj/item/shuttle_creator/proc/turf_in_list(turf/T) + return loggedTurfs.Find(T) + +/obj/item/shuttle_creator/proc/add_single_turf(turf/T) + if(!check_area(list(T))) + return FALSE + loggedTurfs |= T + loggedOldArea = get_area(T) + overlay_holder.highlight_turf(T) + +/obj/item/shuttle_creator/proc/add_saved_area(mob/user) + var/static/area_or_turf_fail_types = typecacheof(list( + /turf/open/space, + /area/shuttle + )) + //Detect the turfs connected in the curerrent enclosed area + var/list/turfs = detect_room(get_turf(user), area_or_turf_fail_types) + if(!check_area(turfs)) + return FALSE + loggedOldArea = get_area(get_turf(user)) + loggedTurfs |= turfs + overlay_holder.highlight_area(turfs) + //TODO READD THIS SHIT: icon_state = "rsd_used" + to_chat(user, "You add the area into the buffer of the [src], you made add more areas or select an airlock to act as a docking port to complete the shuttle.") + return turfs + +/obj/item/shuttle_creator/proc/remove_single_turf(turf/T) + if(!turf_in_list(T)) + return + loggedTurfs -= T + loggedOldArea = get_area(T) + overlay_holder.unhighlight_turf(T) + +/obj/item/shuttle_creator/proc/reset_saved_area() + overlay_holder.clear_highlights() + loggedTurfs.Cut() + to_chat(usr, "You reset the area buffer on the [src].") + +//Yogs: Admin RSD for bussing and messing around. I am not liable for any crashed servers this may cause. This one removes the size and area limits, allowing you to turn the entire station into a shuttle if you want. +/obj/item/shuttle_creator/admin + name = "Admin Rapid Shuttle Designator" + icon_state = "arsd" + desc = "An experimental RSD with the size and area limits disabled. It is covered in warning of what not to do." + override_max_shuttles = TRUE + ignore_max_shuttle_size = TRUE + ignore_area = TRUE + +#undef CARDINAL_DIRECTIONS_X +#undef CARDINAL_DIRECTIONS_Y diff --git a/code/modules/shuttle/shuttle_creation/shuttle_creator_actions.dm b/code/modules/shuttle/shuttle_creation/shuttle_creator_actions.dm new file mode 100644 index 000000000000..0cf06721aff7 --- /dev/null +++ b/code/modules/shuttle/shuttle_creation/shuttle_creator_actions.dm @@ -0,0 +1,101 @@ +//============ Actions ============ +/datum/action/innate/shuttle_creator + icon_icon = 'icons/mob/actions/actions_shuttle.dmi' + var/mob/living/C + var/mob/camera/aiEye/remote/shuttle_creation/remote_eye + var/obj/item/shuttle_creator/shuttle_creator + +/datum/action/innate/shuttle_creator/Activate() + if(!target) + return TRUE + C = owner + remote_eye = C.remote_control + var/obj/machinery/computer/camera_advanced/shuttle_creator/internal_console = target + shuttle_creator = internal_console.owner_rsd + +//Add an area +/datum/action/innate/shuttle_creator/designate_area + name = "Designate Room" + button_icon_state = "designate_area" + +/datum/action/innate/shuttle_creator/designate_area/Activate() + if(..()) + return + shuttle_creator.add_saved_area(remote_eye) + +//Add a single turf +/datum/action/innate/shuttle_creator/designate_turf + name = "Designate Turf" + button_icon_state = "designate_turf" + +/datum/action/innate/shuttle_creator/designate_turf/Activate() + if(..()) + return + var/turf/T = get_turf(remote_eye) + if(istype(T, /turf/open/space)) + var/connectors_exist = FALSE + for(var/obj/structure/lattice/lattice in T) + connectors_exist = TRUE + break + if(!connectors_exist) + to_chat(usr, "This turf requires support, build some catwalks or lattices.") + return + if(!shuttle_creator.check_area(list(T))) + return + if(shuttle_creator.turf_in_list(T)) + return + shuttle_creator.add_single_turf(T) + +//Clear a single entire area +/datum/action/innate/shuttle_creator/clear_turf + name = "Clear Turf" + button_icon_state = "clear_turf" + +/datum/action/innate/shuttle_creator/clear_turf/Activate() + if(..()) + return + shuttle_creator.remove_single_turf(get_turf(remote_eye)) + +//Clear the entire area +/datum/action/innate/shuttle_creator/reset + name = "Reset Buffer" + button_icon_state = "clear_area" + +/datum/action/innate/shuttle_creator/reset/Activate() + if(..()) + return + shuttle_creator.reset_saved_area() + +//Finish the shuttle +/datum/action/innate/shuttle_creator/airlock + name = "Select Docking Airlock" + button_icon_state = "select_airlock" + +/datum/action/innate/shuttle_creator/airlock/Activate() + if(..()) + return + var/turf/T = get_turf(remote_eye) + for(var/obj/machinery/door/airlock/A in T) + if(get_area(A) != shuttle_creator.loggedOldArea) + to_chat(C, "Caution, airlock must be on the shuttle to function as a dock.") + return + if(shuttle_creator.linkedShuttleId) + return + if(GLOB.custom_shuttle_count > CUSTOM_SHUTTLE_LIMIT) + to_chat(C, "Shuttle limit reached, sorry.") + return + if(shuttle_creator.loggedTurfs.len > SHUTTLE_CREATOR_MAX_SIZE && !shuttle_creator.ignore_max_shuttle_size) + to_chat(C, "This shuttle is too large!") + return + if(!shuttle_creator.getNonShuttleDirection(T)) + to_chat(C, "Docking port must be on an external wall, with only 1 side exposed to space.") + return + if(!shuttle_creator.create_shuttle_area(C)) + return + if(shuttle_creator.shuttle_create_docking_port(A, C)) + to_chat(C, "Shuttle created!") + //Remove eye control + var/obj/machinery/computer/camera_advanced/shuttle_creator/internal_console = target + internal_console.remove_eye_control(C) + qdel(internal_console) + return diff --git a/code/modules/shuttle/shuttle_creation/shuttle_creator_console.dm b/code/modules/shuttle/shuttle_creation/shuttle_creator_console.dm new file mode 100644 index 000000000000..6945c934279f --- /dev/null +++ b/code/modules/shuttle/shuttle_creation/shuttle_creator_console.dm @@ -0,0 +1,93 @@ +//============The internal camera console used for designating the area============= +/obj/machinery/computer/camera_advanced/shuttle_creator + name = "internal shuttle creator console" + desc = "You should not have access to this, please report this as a bug" + networks = list() + var/obj/item/shuttle_creator/owner_rsd + var/datum/action/innate/shuttle_creator/designate_area/area_action = new + var/datum/action/innate/shuttle_creator/designate_turf/turf_action = new + var/datum/action/innate/shuttle_creator/clear_turf/clear_turf_action = new + var/datum/action/innate/shuttle_creator/reset/reset_action = new + var/datum/action/innate/shuttle_creator/airlock/airlock_action = new + +/obj/machinery/computer/camera_advanced/shuttle_creator/check_eye(mob/user) + if(user.eye_blind || user.incapacitated()) + user.unset_machine() + +/obj/machinery/computer/camera_advanced/shuttle_creator/CreateEye() + eyeobj = new /mob/camera/aiEye/remote/shuttle_creation(get_turf(owner_rsd)) + eyeobj.origin = src + eyeobj.use_static = USE_STATIC_NONE + +/obj/machinery/computer/camera_advanced/shuttle_creator/is_operational() + return TRUE + +/obj/machinery/computer/camera_advanced/shuttle_creator/can_interact(mob/user) + if(!isliving(user)) + return FALSE + var/mob/living/L = user + if(L.incapacitated()) + return FALSE + return TRUE + +/obj/machinery/computer/camera_advanced/shuttle_creator/GrantActions(mob/living/user) + ..(user) + eyeobj.invisibility = SEE_INVISIBLE_LIVING + if(area_action) + area_action.target = src + area_action.Grant(user) + actions += area_action + if(turf_action) + turf_action.target = src + turf_action.Grant(user) + actions += turf_action + if(clear_turf_action) + clear_turf_action.target = src + clear_turf_action.Grant(user) + actions += clear_turf_action + if(reset_action) + reset_action.target = src + reset_action.Grant(user) + actions += reset_action + if(airlock_action) + airlock_action.target = src + airlock_action.Grant(user) + actions += airlock_action + +/obj/machinery/computer/camera_advanced/shuttle_creator/remove_eye_control(mob/living/user) + . = ..() + owner_rsd.overlay_holder.remove_client() + eyeobj.invisibility = INVISIBILITY_MAXIMUM + if(user.client) + user.client.images -= eyeobj.user_image + +/obj/machinery/computer/camera_advanced/shuttle_creator/attack_hand(mob/user) + if(!is_operational()) //you cant use broken machine you chumbis + return + if(current_user) + to_chat(user, "The console is already in use!") + return + var/mob/living/L = user + if(!can_use(user)) + return + if(!eyeobj) + CreateEye() + if(!eyeobj.eye_initialized) + var/camera_location = get_turf(owner_rsd) + if(camera_location) + eyeobj.eye_initialized = TRUE + give_eye_control(L) + eyeobj.setLoc(camera_location) + var/mob/camera/aiEye/remote/shuttle_creation/shuttle_eye = eyeobj + shuttle_eye.source_turf = get_turf(user) + else + user.unset_machine() + else + var/camera_location = get_turf(owner_rsd) + var/mob/camera/aiEye/remote/shuttle_creation/eye = eyeobj + give_eye_control(L) + if(camera_location) + eye.source_turf = camera_location + eyeobj.setLoc(camera_location) + else + eyeobj.setLoc(eyeobj.loc) diff --git a/code/modules/shuttle/shuttle_creation/shuttle_creator_eye.dm b/code/modules/shuttle/shuttle_creation/shuttle_creator_eye.dm new file mode 100644 index 000000000000..4eea6bfe8856 --- /dev/null +++ b/code/modules/shuttle/shuttle_creation/shuttle_creator_eye.dm @@ -0,0 +1,55 @@ +//===============Camera Eye================ +/mob/camera/aiEye/remote/shuttle_creation + name = "shuttle holo-drone" + icon = 'icons/obj/mining.dmi' + icon_state = "construction_drone" + visible_icon = FALSE + acceleration = 0 + var/turf/source_turf + var/max_range = 12 + +/mob/camera/aiEye/remote/shuttle_creation/Initialize() + . = ..() + setLoc(get_turf(source_turf)) + icon_state = "construction_drone" + +/mob/camera/aiEye/remote/shuttle_creation/update_remote_sight(mob/living/user) + user.sight = BLIND|SEE_TURFS + user.lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE + user.sync_lighting_plane_alpha() + return TRUE + +/mob/camera/aiEye/remote/shuttle_creation/relaymove(mob/user, direct) + dir = direct //This camera eye is visible as a drone, and needs to keep the dir updated + var/initial = initial(sprint) + var/max_sprint = 50 + + if(cooldown && cooldown < world.timeofday) // 3 seconds + sprint = initial + + for(var/i = 0; i < max(sprint, initial); i += 20) + var/turf/step = get_turf(get_step(src, direct)) + if(step && can_move_to(step)) + setLoc(step) + + cooldown = world.timeofday + 5 + if(acceleration) + sprint = min(sprint + 0.5, max_sprint) + else + sprint = initial + +/mob/camera/aiEye/remote/shuttle_creation/proc/can_move_to(var/turf/T) + var/origin_x = source_turf.x + var/origin_y = source_turf.y + var/change_X = abs(origin_x - T.x) + var/change_Y = abs(origin_y - T.y) + return (change_X < max_range && change_Y < max_range) + +/mob/camera/aiEye/remote/shuttle_creation/setLoc(T) + ..() + if(eye_user?.client) + eye_user.client.images -= user_image + var/image/I = image(icon, loc, icon_state, FLY_LAYER, dir) + I.plane = MASSIVE_OBJ_LAYER + user_image = I + eye_user.client.images += user_image diff --git a/code/modules/shuttle/shuttle_creation/shuttle_creator_overlay.dm b/code/modules/shuttle/shuttle_creation/shuttle_creator_overlay.dm new file mode 100644 index 000000000000..919b1f02211e --- /dev/null +++ b/code/modules/shuttle/shuttle_creation/shuttle_creator_overlay.dm @@ -0,0 +1,52 @@ +/* + * Manages the overlays for the shuttle creator drone. +*/ + +/datum/shuttle_creator_overlay_holder + var/client/holder + var/list/images = list() + var/list/turfs = list() + +/datum/shuttle_creator_overlay_holder/proc/add_client(client/C) + holder = C + holder.images += images + +/datum/shuttle_creator_overlay_holder/proc/remove_client() + holder.images -= images + holder = null + +/datum/shuttle_creator_overlay_holder/proc/clear_highlights() + if(holder) + holder.images -= images + images.Cut() + turfs.Cut() + +/datum/shuttle_creator_overlay_holder/proc/create_hightlight(turf/T) + if(T in turfs) + return + var/image/I = image('icons/turf/overlays.dmi', T, "greenOverlay") + I.plane = ABOVE_LIGHTING_PLANE + images += I + holder.images += I + turfs += T + +/datum/shuttle_creator_overlay_holder/proc/remove_hightlight(turf/T) + if(!(T in turfs)) + return + turfs -= T + holder.images -= images + for(var/image/I in images) + if(get_turf(I) != T) + continue + images -= I + holder.images += images + +/datum/shuttle_creator_overlay_holder/proc/highlight_area(list/turfs) + for(var/turf/T in turfs) + highlight_turf(T) + +/datum/shuttle_creator_overlay_holder/proc/highlight_turf(turf/T) + create_hightlight(T) + +/datum/shuttle_creator_overlay_holder/proc/unhighlight_turf(turf/T) + remove_hightlight(T) diff --git a/code/modules/shuttle/shuttle_creation/shuttle_upgrades.dm b/code/modules/shuttle/shuttle_creation/shuttle_upgrades.dm new file mode 100644 index 000000000000..c64f643b6dc7 --- /dev/null +++ b/code/modules/shuttle/shuttle_creation/shuttle_upgrades.dm @@ -0,0 +1,39 @@ +/obj/item/shuttle_route_optimisation + name = "Route Optimisation Upgrade" + desc = "Used on a custom shuttle control console to calculate more efficient routes." + icon = 'icons/obj/module.dmi' + icon_state = "shuttledisk" + force = 0 + throwforce = 8 + throw_speed = 3 + throw_range = 5 + density = FALSE + anchored = FALSE + item_flags = NOBLUDGEON + var/upgrade_amount = 0.8 + +/obj/item/shuttle_route_optimisation/hyperlane + name = "Bluespace Hyperlane Calculator" + desc = "Used on a custom shuttle control console to allow for the following of bluespace hyperlanes, increasing the efficiency of the shuttle." + icon_state = "shuttledisk_better" + upgrade_amount = 0.6 + +/obj/item/shuttle_route_optimisation/void + name = "Voidspace Route Calculator" + desc = "Used on a custom shuttle control console to allow it to navigate into voidspace, making the routes almost instant." + icon_state = "shuttledisk_void" + upgrade_amount = 0.2 + +/obj/item/shuttle_route_optimisation/attack_obj(obj/O, mob/living/user) + . = ..() + if(!istype(O, /obj/machinery/computer)) + return + if(!istype(O, /obj/machinery/computer/custom_shuttle)) + to_chat(user, "This upgrade only works on a custom shuttle flight console.") + return + if (!user.transferItemToLoc(src, get_turf(O))) + return + var/obj/machinery/computer/custom_shuttle/link_comp = O + link_comp.distance_multiplier = clamp(link_comp.distance_multiplier, 0, upgrade_amount) //I have no clue what clamp even does, but I think this should work + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0) + to_chat(usr, "You insert the disk into the flight computer, allowing for routes to be [upgrade_amount]x the original distance.") diff --git a/code/modules/shuttle/spaceship_navigation_beacon.dm b/code/modules/shuttle/spaceship_navigation_beacon.dm new file mode 100644 index 000000000000..89e02920a732 --- /dev/null +++ b/code/modules/shuttle/spaceship_navigation_beacon.dm @@ -0,0 +1,63 @@ +/obj/item/circuitboard/machine/spaceship_navigation_beacon + name = "Bluespace Navigation Gigabeacon (Machine Board)" + build_path = /obj/machinery/spaceship_navigation_beacon + req_components = list() + + +/obj/machinery/spaceship_navigation_beacon + name = "Bluespace Navigation Gigabeacon" + desc = "A device that creates a bluespace anchor that allow ships jump near to it." + icon = 'icons/obj/abductor.dmi' + icon_state = "core" + use_power = IDLE_POWER_USE + idle_power_usage = 0 + density = TRUE + circuit = /obj/item/circuitboard/machine/spaceship_navigation_beacon + + var/locked = FALSE //Locked beacons don't allow to jump to it. + + +/obj/machinery/spaceship_navigation_beacon/Initialize() + . = ..() + SSshuttle.beacons |= src + +obj/machinery/spaceship_navigation_beacon/emp_act() + locked = TRUE + +/obj/machinery/spaceship_navigation_beacon/Destroy() + SSshuttle.beacons -= src + return ..() + +// update the icon_state +/obj/machinery/spaceship_navigation_beacon/update_icon() + if(powered()) + icon_state = "core" + else + icon_state = "core-open" + +/obj/machinery/spaceship_navigation_beacon/power_change() + . = ..() + update_icon() + +/obj/machinery/spaceship_navigation_beacon/multitool_act(mob/living/user, obj/item/multitool/I) + if(panel_open) + var/new_name = "Beacon_[input("Enter the custom name for this beacon", "It be Beacon ..your input..") as text]" + if(new_name && Adjacent(user)) + name = new_name + to_chat(user, "You change beacon name to [name].") + else + locked =!locked + to_chat(user, "You [locked ? "" : "un"]lock [src].") + return TRUE + +/obj/machinery/spaceship_navigation_beacon/examine() + .=..() + . += "Status: [locked ? "LOCKED" : "Stable"] " + +/obj/machinery/spaceship_navigation_beacon/attackby(obj/item/W, mob/user, params) + if(default_deconstruction_screwdriver(user, "core-open", "core", W)) + return + if(default_deconstruction_crowbar(W)) + return + + return ..() diff --git a/config/config.txt b/config/config.txt index e2f736b11a2e..964dfb7c07a6 100644 --- a/config/config.txt +++ b/config/config.txt @@ -445,3 +445,7 @@ CENTCOM_BAN_DB https://centcom.melonmesa.com/ban/search ## Uncomment to disable hard deletes entirely, even things that explicitly request. This is not recommended unless you have a need for it during events or other high pop times where performance is key. #DISABLE_ALL_HARD_DELETES + +## Custom shuttle spam prevention. Changine these numbers allows you to change the maxsize and amount of custom shuttles. +MAX_SHUTTLE_COUNT 6 +MAX_SHUTTLE_SIZE 300 diff --git a/icons/mob/actions/actions_shuttle.dmi b/icons/mob/actions/actions_shuttle.dmi new file mode 100644 index 000000000000..399f50941504 Binary files /dev/null and b/icons/mob/actions/actions_shuttle.dmi differ diff --git a/icons/obj/module.dmi b/icons/obj/module.dmi index c0523d86fe1f..a26cfca979c6 100644 Binary files a/icons/obj/module.dmi and b/icons/obj/module.dmi differ diff --git a/icons/obj/tools.dmi b/icons/obj/tools.dmi index ff31df768418..e21b6cfb6cfe 100644 Binary files a/icons/obj/tools.dmi and b/icons/obj/tools.dmi differ diff --git a/icons/turf/shuttle.dmi b/icons/turf/shuttle.dmi index a09cb7a84737..7c96dcf1b4b4 100644 Binary files a/icons/turf/shuttle.dmi and b/icons/turf/shuttle.dmi differ diff --git a/yogstation.dme b/yogstation.dme index b002e518256d..1e3be75185d6 100644 --- a/yogstation.dme +++ b/yogstation.dme @@ -811,6 +811,9 @@ #include "code\game\machinery\porta_turret\portable_turret.dm" #include "code\game\machinery\porta_turret\portable_turret_construct.dm" #include "code\game\machinery\porta_turret\portable_turret_cover.dm" +#include "code\game\machinery\shuttle\custom_shuttle.dm" +#include "code\game\machinery\shuttle\shuttle_engine.dm" +#include "code\game\machinery\shuttle\shuttle_heater.dm" #include "code\game\machinery\telecomms\broadcasting.dm" #include "code\game\machinery\telecomms\machine_interactions.dm" #include "code\game\machinery\telecomms\telecomunications.dm" @@ -981,6 +984,8 @@ #include "code\game\objects\items\sharpener.dm" #include "code\game\objects\items\shields.dm" #include "code\game\objects\items\shooting_range.dm" +#include "code\game\objects\items\shuttle_creator.dm" +#include "code\game\objects\items\shuttle_upgrades.dm" #include "code\game\objects\items\signs.dm" #include "code\game\objects\items\singularityhammer.dm" #include "code\game\objects\items\stunbaton.dm" @@ -2909,6 +2914,7 @@ #include "code\modules\shuttle\arrivals.dm" #include "code\modules\shuttle\assault_pod.dm" #include "code\modules\shuttle\computer.dm" +#include "code\modules\shuttle\custom_shuttle.dm" #include "code\modules\shuttle\docking.dm" #include "code\modules\shuttle\elevator.dm" #include "code\modules\shuttle\emergency.dm" @@ -2920,10 +2926,17 @@ #include "code\modules\shuttle\ripple.dm" #include "code\modules\shuttle\shuttle.dm" #include "code\modules\shuttle\shuttle_rotate.dm" +#include "code\modules\shuttle\spaceship_navigation_beacon.dm" #include "code\modules\shuttle\special.dm" #include "code\modules\shuttle\supply.dm" #include "code\modules\shuttle\syndicate.dm" #include "code\modules\shuttle\white_ship.dm" +#include "code\modules\shuttle\shuttle_creation\shuttle_creator.dm" +#include "code\modules\shuttle\shuttle_creation\shuttle_creator_actions.dm" +#include "code\modules\shuttle\shuttle_creation\shuttle_creator_console.dm" +#include "code\modules\shuttle\shuttle_creation\shuttle_creator_eye.dm" +#include "code\modules\shuttle\shuttle_creation\shuttle_creator_overlay.dm" +#include "code\modules\shuttle\shuttle_creation\shuttle_upgrades.dm" #include "code\modules\spells\spell.dm" #include "code\modules\spells\spell_types\aimed.dm" #include "code\modules\spells\spell_types\area_teleport.dm" diff --git a/yogstation/code/modules/research/techweb/all_nodes.dm b/yogstation/code/modules/research/techweb/all_nodes.dm index d5229ff2ff74..6136be1c7d39 100644 --- a/yogstation/code/modules/research/techweb/all_nodes.dm +++ b/yogstation/code/modules/research/techweb/all_nodes.dm @@ -13,14 +13,16 @@ /datum/techweb_node/basic_tools prereq_ids = list("base") -/datum/techweb_node/spacepod_basic - id = "spacepod_basic" + +/////////////////////////space vehicle tech///////////////////////// +/datum/techweb_node/spacevehicle_basic + id = "spacevehicle_basic" display_name = "Spacepod Construction" description = "Basic stuff to construct Spacepods. Don't crash your first spacepod into the station, especially while going more than 10 m/s." research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 2500 prereq_ids = list("base") - design_ids = list("podcore", "podarmor_civ", "podarmor_dark", "spacepod_main") + design_ids = list("podcore", "podarmor_civ", "podarmor_dark", "spacepod_main", "spaceship_navigation_beacon") /datum/techweb_node/spacepod_lock id = "spacepod_lock" @@ -28,7 +30,7 @@ description = "Keeps greytiders out of your spacepods." research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2750) export_price = 2750 - prereq_ids = list("spacepod_basic", "engineering") + prereq_ids = list("spacevehicle_basic", "engineering") design_ids = list("podlock_keyed", "podkey", "podmisc_tracker") /datum/techweb_node/spacepod_disabler @@ -37,7 +39,7 @@ description = "For a bit of pew pew space battles" research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3500) export_price = 3500 - prereq_ids = list("spacepod_basic", "weaponry") + prereq_ids = list("spacevehicle_basic", "weaponry") design_ids = list("podgun_disabler") /datum/techweb_node/spacepod_lasers @@ -82,7 +84,7 @@ description = "For bringing along victims as you fly off into the far reaches of space" research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3750) export_price = 3750 - prereq_ids = list("spacepod_basic", "adv_engi") + prereq_ids = list("spacevehicle_basic", "adv_engi") design_ids = list("podcargo_seat") /datum/techweb_node/spacepod_storage @@ -112,6 +114,43 @@ prereq_ids = list("spacepod_storage", "high_efficiency") design_ids = list("podarmor_industiral", "podarmor_sec", "podarmor_gold") +/datum/techweb_node/basic_shuttle_tech + id = "basic_shuttle_tech" + display_name = "Basic Shuttle Research" + description = "Research the technology required to create and use basic shuttles." + prereq_ids = list("bluespace_travel", "adv_engi", "spacevehicle_basic") + design_ids = list("shuttle_creator", "engine_plasma", "engine_heater", "shuttle_control", "shuttle_docker") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000) + export_price = 5000 + +/datum/techweb_node/shuttle_route_upgrade + id = "shuttle_route_upgrade" + display_name = "Route Optimisation Upgrade" + description = "Research into bluespace tunnelling, allowing us to reduce flight times by up to 20%!" + prereq_ids = list("spacevehicle_basic") + design_ids = list("disk_shuttle_route") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) + export_price = 2500 + +/datum/techweb_node/shuttle_route_upgrade_hyper + id = "shuttle_route_upgrade_hyper" + display_name = "Hyperlane Optimisation Upgrade" + description = "Research into bluespace hyperlane, allowing us to reduce flight times by up to 40%!" + prereq_ids = list("shuttle_route_upgrade", "micro_bluespace") + design_ids = list("disk_shuttle_route_hyper") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000) + export_price = 5000 + +/datum/techweb_node/shuttle_route_upgrade_void + id = "shuttle_route_upgrade_void" + display_name = "Nullspace Breaching Upgrade" + description = "Research into voidspace tunnelling, allowing us to significantly reduce flight times." + prereq_ids = list("shuttle_route_upgrade_hyper", "alientech") + design_ids = list("engine_void", "disk_shuttle_route_void") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) + export_price = 2500 + + /datum/techweb_node/syndicate_surgery id = "syndicate_surgery" display_name = "Syndicate Surgery"