diff --git a/code/__DEFINES/research/anomalies.dm b/code/__DEFINES/research/anomalies.dm new file mode 100644 index 0000000000..2b23ca24c4 --- /dev/null +++ b/code/__DEFINES/research/anomalies.dm @@ -0,0 +1,18 @@ +// Anomaly core types +/// Bluespace cores +#define ANOMALY_CORE_BLUESPACE /obj/item/assembly/signaler/anomaly/bluespace +/// Gravitational cores +#define ANOMALY_CORE_GRAVITATIONAL /obj/item/assembly/signaler/anomaly/grav +/// Flux +#define ANOMALY_CORE_FLUX /obj/item/assembly/signaler/anomaly/flux +/// Vortex +#define ANOMALY_CORE_VORTEX /obj/item/assembly/signaler/anomaly/vortex +/// Pyro +#define ANOMALY_CORE_PYRO /obj/item/assembly/signaler/anomaly/pyro + +// Max amounts of cores you can make +#define MAX_CORES_BLUESPACE 8 +#define MAX_CORES_GRAVITATIONAL 8 +#define MAX_CORES_FLUX 8 +#define MAX_CORES_VORTEX 8 +#define MAX_CORES_PYRO 8 diff --git a/code/controllers/subsystem/research.dm b/code/controllers/subsystem/research.dm index 60c5dd5b99..ac9db60ccc 100644 --- a/code/controllers/subsystem/research.dm +++ b/code/controllers/subsystem/research.dm @@ -294,6 +294,17 @@ SUBSYSTEM_DEF(research) //[88nodes * 5000points/node] / [1.5hr * 90min/hr * 60s/min] //Around 450000 points max??? + /// The global list of raw anomaly types that have been refined, for hard limits. + var/list/created_anomaly_types = list() + /// The hard limits of cores created for each anomaly type. For faster code lookup without switch statements. + var/list/anomaly_hard_limit_by_type = list( + ANOMALY_CORE_BLUESPACE = MAX_CORES_BLUESPACE, + ANOMALY_CORE_PYRO = MAX_CORES_PYRO, + ANOMALY_CORE_GRAVITATIONAL = MAX_CORES_GRAVITATIONAL, + ANOMALY_CORE_VORTEX = MAX_CORES_VORTEX, + ANOMALY_CORE_FLUX = MAX_CORES_FLUX + ) + /datum/controller/subsystem/research/Initialize() point_types = TECHWEB_POINT_TYPE_LIST_ASSOCIATIVE_NAMES initialize_all_techweb_designs() diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index 32d9c02a27..8b0d46be44 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -289,3 +289,9 @@ . = TRUE update_icon() + +/** + * Returns if this is ready to be detonated. Checks if both tanks are in place. + */ +/obj/item/transfer_valve/proc/ready() + return tank_one && tank_two diff --git a/code/modules/cargo/packs/science.dm b/code/modules/cargo/packs/science.dm index fd6fee362d..4edf7f3bd2 100644 --- a/code/modules/cargo/packs/science.dm +++ b/code/modules/cargo/packs/science.dm @@ -192,3 +192,49 @@ crate_type = /obj/structure/closet/crate/secure/science dangerous = TRUE +//////// RAW ANOMALY CORES + +/datum/supply_pack/science/raw_flux_anomaly + name = "Raw Flux Anomaly" + desc = "The raw core of a flux anomaly, ready to be implosion-compressed into a powerful artifact." + cost = 5000 + access = ACCESS_TOXINS + contains = list(/obj/item/raw_anomaly_core/flux) + crate_name = "raw flux anomaly" + crate_type = /obj/structure/closet/crate/secure/science + +/datum/supply_pack/science/raw_grav_anomaly + name = "Raw Gravitational Anomaly" + desc = "The raw core of a gravitational anomaly, ready to be implosion-compressed into a powerful artifact." + cost = 5000 + access = ACCESS_TOXINS + contains = list(/obj/item/raw_anomaly_core/grav) + crate_name = "raw pyro anomaly" + crate_type = /obj/structure/closet/crate/secure/science + +/datum/supply_pack/science/raw_vortex_anomaly + name = "Raw Vortex Anomaly" + desc = "The raw core of a vortex anomaly, ready to be implosion-compressed into a powerful artifact." + cost = 5000 + access = ACCESS_TOXINS + contains = list(/obj/item/raw_anomaly_core/vortex) + crate_name = "raw vortex anomaly" + crate_type = /obj/structure/closet/crate/secure/science + +/datum/supply_pack/science/raw_bluespace_anomaly + name = "Raw Bluespace Anomaly" + desc = "The raw core of a bluespace anomaly, ready to be implosion-compressed into a powerful artifact." + cost = 5000 + access = ACCESS_TOXINS + contains = list(/obj/item/raw_anomaly_core/bluespace) + crate_name = "raw bluespace anomaly" + crate_type = /obj/structure/closet/crate/secure/science + +/datum/supply_pack/science/raw_pyro_anomaly + name = "Raw Pyro Anomaly" + desc = "The raw core of a pyro anomaly, ready to be implosion-compressed into a powerful artifact." + cost = 5000 + access = ACCESS_TOXINS + contains = list(/obj/item/raw_anomaly_core/pyro) + crate_name = "raw pyro anomaly" + crate_type = /obj/structure/closet/crate/secure/science diff --git a/code/modules/research/anomaly/explosive_compressor.dm b/code/modules/research/anomaly/explosive_compressor.dm new file mode 100644 index 0000000000..6927ac0dcf --- /dev/null +++ b/code/modules/research/anomaly/explosive_compressor.dm @@ -0,0 +1,145 @@ +#define MAX_RADIUS_REQUIRED 8000 //tritbomb +#define MIN_RADIUS_REQUIRED 20 //maxcap +/** + * # Explosive compressor machines + * + * The explosive compressor machine used in anomaly core production. + * + * Uses the standard toxins/tank explosion scaling to compress raw anomaly cores into completed ones. The required explosion radius increases as more cores of that type are created. + */ +/obj/machinery/research/explosive_compressor + name = "implosion compressor" + desc = "An advanced machine capable of implosion-compressing raw anomaly cores into finished artifacts." + icon = 'icons/obj/machines/research.dmi' + icon_state = "explosive_compressor" + density = TRUE + + /// The raw core inserted in the machine. + var/obj/item/raw_anomaly_core/inserted_core + /// The TTV inserted in the machine. + var/obj/item/transfer_valve/inserted_bomb + /// The last time we did say_requirements(), because someone will inevitably click spam this. + var/last_requirements_say = 0 + +/obj/machinery/research/explosive_compressor/examine(mob/user) + . = ..() + . += "Ctrl-Click to remove an inserted core." + . += "Click with an empty hand to gather information about the required radius of an inserted core. Insert a ready TTV to start the implosion process if a core is inserted." + +/obj/machinery/research/explosive_compressor/attack_hand(mob/living/user) + . = ..() + if(.) + return + if(!inserted_core) + to_chat(user, "There is no core inserted.") + return + if(last_requirements_say + 3 SECONDS > world.time) + return + last_requirements_say = world.time + say_requirements(inserted_core) + +/obj/machinery/research/explosive_compressor/CtrlClick(mob/living/user) + . = ..() + if(!istype(user) || !user.Adjacent(src) || !(user.mobility_flags & MOBILITY_USE)) + return + if(!inserted_core) + to_chat(user, "There is no core inserted.") + return + inserted_core.forceMove(get_turf(user)) + to_chat(user, "You remove [inserted_core] from [src].") + user.put_in_hands(inserted_core) + inserted_core = null + +/** + * Says (no, literally) the data of required explosive power for a certain anomaly type. + */ +/obj/machinery/research/explosive_compressor/proc/say_requirements(obj/item/raw_anomaly_core/C) + var/required = get_required_radius(C.anomaly_type) + if(isnull(required)) + say("Unfortunately, due to diminishing supplies of condensed anomalous matter, [C] and any cores of its type are no longer of a sufficient quality level to be compressed into a working core.") + else + say("[C] requires a minimum of a theoretical radius of [required] to successfully implode into a charged anomaly core.") + +/** + * Determines how much explosive power (last value, so light impact theoretical radius) is required to make a certain anomaly type. + * + * Returns null if the max amount has already been reached. + * + * Arguments: + * * anomaly_type - anomaly type define + */ +/obj/machinery/research/explosive_compressor/proc/get_required_radius(anomaly_type) + var/already_made = SSresearch.created_anomaly_types[anomaly_type] + var/hard_limit = SSresearch.anomaly_hard_limit_by_type[anomaly_type] + if(already_made >= hard_limit) + return //return null + // my crappy autoscale formula + // linear scaling. + var/radius_span = MAX_RADIUS_REQUIRED - MIN_RADIUS_REQUIRED + var/radius_increase_per_core = radius_span / hard_limit + var/radius = clamp(round(MIN_RADIUS_REQUIRED + radius_increase_per_core * already_made, 1), MIN_RADIUS_REQUIRED, MAX_RADIUS_REQUIRED) + return radius + +/obj/machinery/research/explosive_compressor/attackby(obj/item/I, mob/living/user, params) + . = ..() + if(istype(I, /obj/item/raw_anomaly_core)) + if(inserted_core) + to_chat(user, "There is already a core in [src].") + return + if(!user.transferItemToLoc(I, src)) + to_chat(user, "[I] is stuck to your hand.") + return + inserted_core = I + to_chat(user, "You insert [I] into [src].") + return + if(istype(I, /obj/item/transfer_valve)) + // If they don't have a bomb core inserted, don't let them insert this. If they do, insert and do implosion. + if(!inserted_core) + to_chat(user, "There is no core inserted in [src]. What would be the point of detonating an implosion without a core?") + return + var/obj/item/transfer_valve/valve = I + if(!valve.ready()) + to_chat(user, "[valve] is incomplete.") + return + if(!user.transferItemToLoc(I, src)) + to_chat(user, "[I] is stuck to your hand.") + return + inserted_bomb = I + to_chat(user, "You insert [I] and press the start button.") + do_implosion() + +/** + * The ""explosion"" proc. + */ +/obj/machinery/research/explosive_compressor/proc/do_implosion() + var/required_radius = get_required_radius(inserted_core.anomaly_type) + // By now, we should be sure that we have a core, a TTV, and that the TTV has both tanks in place. + var/datum/gas_mixture/mix1 = inserted_bomb.tank_one.air_contents + var/datum/gas_mixture/mix2 = inserted_bomb.tank_two.air_contents + // Snowflaked tank explosion + var/datum/gas_mixture/mix = new(70) // Standard tank volume, 70L + mix.merge(mix1) + mix.merge(mix2) + mix.react() + if(mix.return_pressure() < TANK_FRAGMENT_PRESSURE) + // They failed so miserably we're going to give them their bomb back. + inserted_bomb.forceMove(drop_location()) + inserted_bomb = null + inserted_core.forceMove(drop_location()) + inserted_core = null + say("Transfer valve resulted in negligible explosive power. Items ejected.") + return + mix.react() // build more pressure + var/pressure = mix.return_pressure() + var/range = (pressure - TANK_FRAGMENT_PRESSURE) / TANK_FRAGMENT_SCALE + QDEL_NULL(inserted_bomb) // bomb goes poof + if(range < required_radius) + inserted_bomb.forceMove(src) + say("Resultant detonation failed to produce enough implosive power to compress [inserted_core]. Core ejected.") + return + inserted_core.create_core(drop_location(), TRUE, TRUE) + inserted_core = null + say("Success. Resultant detonation has theoretical range of [range]. Required radius was [required_radius]. Core production complete.") + +#undef MAX_RADIUS_REQUIRED +#undef MIN_RADIUS_REQUIRED diff --git a/code/modules/research/anomaly/raw_anomaly.dm b/code/modules/research/anomaly/raw_anomaly.dm new file mode 100644 index 0000000000..2342edd28f --- /dev/null +++ b/code/modules/research/anomaly/raw_anomaly.dm @@ -0,0 +1,73 @@ +/** + * # Raw Anomaly Cores + * + * The current precursor to anomaly cores, these are manufactured into 'finished' anomaly cores for use in research, items, and more. + * + * The current amounts created is stored in SSresearch.created_anomaly_types[ANOMALY_CORE_TYPE_DEFINE] = amount + * The hard limits are in code/__DEFINES/anomalies.dm + */ +/obj/item/raw_anomaly_core + name = "raw anomaly core" + desc = "You shouldn't be seeing this. Someone screwed up." + icon = 'icons/obj/assemblies/new_assemblies.dmi' + icon_state = "broken_state" + + /// Anomaly type + var/anomaly_type + +/obj/item/raw_anomaly_core/bluespace + name = "raw bluespace core" + desc = "The raw core of a bluespace anomaly, glowing and full of potential." + anomaly_type = /obj/item/assembly/signaler/anomaly/bluespace + icon_state = "rawcore_bluespace" + +/obj/item/raw_anomaly_core/vortex + name = "raw vortex core" + desc = "The raw core of a vortex anomaly. Feels heavy to the touch." + anomaly_type = /obj/item/assembly/signaler/anomaly/vortex + icon_state = "rawcore_vortex" + +/obj/item/raw_anomaly_core/grav + name = "raw gravity core" + desc = "The raw core of a gravity anomaly. The air seems attracted to it." + anomaly_type = /obj/item/assembly/signaler/anomaly/grav + icon_state = "rawcore_grav" + +/obj/item/raw_anomaly_core/pyro + desc = "The raw core of a pyro anomaly. It is warm to the touch." + name = "raw pyro core" + anomaly_type = /obj/item/assembly/signaler/anomaly/pyro + icon_state = "rawcore_pyro" + +/obj/item/raw_anomaly_core/flux + name = "raw flux core" + desc = "The raw core of a flux anomaly, faintly crackling with energy." + anomaly_type = /obj/item/assembly/signaler/anomaly/flux + icon_state = "rawcore_flux" + +/obj/item/raw_anomaly_core/random + name = "random raw core" + desc = "You should not see this!" + icon_state = "rawcore_bluespace" + +/obj/item/raw_anomaly_core/random/Initialize(mapload) + . = ..() + var/path = pick(subtypesof(/obj/item/raw_anomaly_core)) + new path(loc) + return INITIALIZE_HINT_QDEL + +/** + * Created the resulting core after being "made" into it. + * + * Arguments: + * * newloc - Where the new core will be created + * * del_self - should we qdel(src) + * * count_towards_limit - should we increment the amount of created cores on SSresearch + */ +/obj/item/raw_anomaly_core/proc/create_core(newloc, del_self = FALSE, count_towards_limit = FALSE) + . = new anomaly_type(newloc) + if(count_towards_limit) + var/existing = SSresearch.created_anomaly_types[anomaly_type] || 0 + SSresearch.created_anomaly_types[anomaly_type] = existing + 1 + if(del_self) + qdel(src) diff --git a/tgstation.dme b/tgstation.dme index 11f8b22fdd..8d1dfddd40 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -139,6 +139,7 @@ #include "code\__DEFINES\mapping\maploader.dm" #include "code\__DEFINES\material\worth.dm" #include "code\__DEFINES\mobs\slowdowns.dm" +#include "code\__DEFINES\research\anomalies.dm" #include "code\__DEFINES\research\stock_parts.dm" #include "code\__DEFINES\skills\defines.dm" #include "code\__DEFINES\skills\helpers.dm" @@ -3109,6 +3110,8 @@ #include "code\modules\research\server.dm" #include "code\modules\research\stock_parts.dm" #include "code\modules\research\anomaly\anomaly_core.dm" +#include "code\modules\research\anomaly\explosive_compressor.dm" +#include "code\modules\research\anomaly\raw_anomaly.dm" #include "code\modules\research\designs\AI_module_designs.dm" #include "code\modules\research\designs\autobotter_designs.dm" #include "code\modules\research\designs\autoylathe_designs.dm"