mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-10 17:04:36 +00:00
## About The Pull Request This is an expanding of https://github.com/tgstation/tgstation/pull/69708 Adds a config to not connect machines to a techweb at the start of a round Adds the ability to multitool a server to get its techweb in its buffer, which can then be used on machines to sync them. Adds support for some machines to not cry when they don't have a techweb linked to it, in case they actually don't. If the config to not have machines connected to the science server is enabled, research servers will make their own techwebs instead. This is barebones though and would need more work if this option is used. For misc stuff: - I replaced checking ``GLOB.machines`` for research servers, to instead check ``SSresearch.servers``, where we can use ``as anything``. - Removed unused vars on the RD server control - I renamed the operating computer's .dm file to remove the capitalized letter from it. It's now operating_computer instead of Operations. ## Why It's Good For The Game This is adding support for 2 different cases that can be used in the future: 1. Off-station roles, we can make roles like Oldstation have their own techweb so they don't ruin science's efforts, or use their advanced research to get things we don't want, or even possibly have some blacklist webs for ghost roles (like teleporters) so that way we don't need to have this dance where we have to give them a very specific amount of materials for them to do things while not being able to get a teleporter and leaving. I heard discussions that people wanted this a while back, and one of the main things preventing this from happening is the lack of support. Hopefully this is encouragement to make it a reality, because I think it would be a really cool expansion of ghost roles and a good way to prevent them from messing with the round in progress. 2. Downstreams who want to do different things with Science. Personally I made this PR with voidcrew(shiptest) in mind and think this would make their lives easier. I didn't expand too much on this because I'm leaving up mostly to the downstreams to figure out what they want to do with these systems. ## Changelog This generally isn't really player facing, since most of the changes would only come into effect if the config is enabled?? 🆑 fix: Research servers now only show servers connected to their techweb. /🆑
320 lines
12 KiB
Plaintext
320 lines
12 KiB
Plaintext
|
|
SUBSYSTEM_DEF(research)
|
|
name = "Research"
|
|
priority = FIRE_PRIORITY_RESEARCH
|
|
wait = 10
|
|
init_order = INIT_ORDER_RESEARCH
|
|
//TECHWEB STATIC
|
|
var/list/techweb_nodes = list() //associative id = node datum
|
|
var/list/techweb_designs = list() //associative id = node datum
|
|
var/list/datum/techweb/techwebs = list()
|
|
var/datum/techweb/science/science_tech
|
|
var/datum/techweb/admin/admin_tech
|
|
var/datum/techweb_node/error_node/error_node //These two are what you get if a node/design is deleted and somehow still stored in a console.
|
|
var/datum/design/error_design/error_design
|
|
|
|
///List of all research servers.
|
|
var/list/obj/machinery/rnd/server/servers = list()
|
|
|
|
//ERROR LOGGING
|
|
///associative id = number of times
|
|
var/list/invalid_design_ids = list()
|
|
///associative id = number of times
|
|
var/list/invalid_node_ids = list()
|
|
///associative id = error message
|
|
var/list/invalid_node_boost = list()
|
|
|
|
///associative id = TRUE
|
|
var/list/techweb_nodes_starting = list()
|
|
///category name = list(node.id = TRUE)
|
|
var/list/techweb_categories = list()
|
|
///associative double-layer path = list(id = list(point_type = point_discount))
|
|
var/list/techweb_boost_items = list()
|
|
///Node ids that should be hidden by default.
|
|
var/list/techweb_nodes_hidden = list()
|
|
///Node ids that are exclusive to the BEPIS.
|
|
var/list/techweb_nodes_experimental = list()
|
|
///path = list(point type = value)
|
|
var/list/techweb_point_items = list(
|
|
/obj/item/assembly/signaler/anomaly = list(TECHWEB_POINT_TYPE_GENERIC = 10000)
|
|
)
|
|
var/list/errored_datums = list()
|
|
var/list/point_types = list() //typecache style type = TRUE list
|
|
//----------------------------------------------
|
|
var/list/single_server_income = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_SINGLE_SERVER_INCOME)
|
|
var/last_income
|
|
//^^^^^^^^ ALL OF THESE ARE PER SECOND! ^^^^^^^^
|
|
|
|
/// A list of all master servers. If none of these have a source code HDD, research point generation is lowered.
|
|
var/list/obj/machinery/rnd/server/master/master_servers = list()
|
|
/// A multiplier applied to all research gain.
|
|
var/income_modifier = 1
|
|
|
|
//Aiming for 1.5 hours to max R&D
|
|
//[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(
|
|
/obj/item/assembly/signaler/anomaly/bluespace = MAX_CORES_BLUESPACE,
|
|
/obj/item/assembly/signaler/anomaly/pyro = MAX_CORES_PYRO,
|
|
/obj/item/assembly/signaler/anomaly/grav = MAX_CORES_GRAVITATIONAL,
|
|
/obj/item/assembly/signaler/anomaly/vortex = MAX_CORES_VORTEX,
|
|
/obj/item/assembly/signaler/anomaly/flux = MAX_CORES_FLUX,
|
|
/obj/item/assembly/signaler/anomaly/hallucination = MAX_CORES_HALLUCINATION,
|
|
/obj/item/assembly/signaler/anomaly/bioscrambler = MAX_CORES_BIOSCRAMBLER,
|
|
/obj/item/assembly/signaler/anomaly/dimensional = MAX_CORES_DIMENSIONAL,
|
|
)
|
|
|
|
/// Lookup list for ordnance briefers.
|
|
var/list/ordnance_experiments = list()
|
|
/// Lookup list for scipaper partners.
|
|
var/list/scientific_partners = list()
|
|
|
|
/datum/controller/subsystem/research/Initialize()
|
|
point_types = TECHWEB_POINT_TYPE_LIST_ASSOCIATIVE_NAMES
|
|
initialize_all_techweb_designs()
|
|
initialize_all_techweb_nodes()
|
|
populate_ordnance_experiments()
|
|
science_tech = new /datum/techweb/science
|
|
admin_tech = new /datum/techweb/admin
|
|
autosort_categories()
|
|
error_design = new
|
|
error_node = new
|
|
return SS_INIT_SUCCESS
|
|
|
|
/datum/controller/subsystem/research/fire()
|
|
var/list/bitcoins = list()
|
|
for(var/obj/machinery/rnd/server/miner as anything in servers)
|
|
if(miner.working)
|
|
bitcoins = single_server_income.Copy()
|
|
break //Just need one to work.
|
|
|
|
if (!isnull(last_income))
|
|
var/income_time_difference = world.time - last_income
|
|
science_tech.last_bitcoins = bitcoins // Doesn't take tick drift into account
|
|
for(var/i in bitcoins)
|
|
bitcoins[i] *= (income_time_difference / 10) * income_modifier
|
|
science_tech.add_point_list(bitcoins)
|
|
|
|
last_income = world.time
|
|
|
|
/datum/controller/subsystem/research/proc/calculate_server_coefficient() //Diminishing returns.
|
|
var/amt = servers.len
|
|
if(!amt)
|
|
return 0
|
|
var/coeff = 100
|
|
coeff = sqrt(coeff / amt)
|
|
return coeff
|
|
|
|
/datum/controller/subsystem/research/proc/autosort_categories()
|
|
for(var/i in techweb_nodes)
|
|
var/datum/techweb_node/I = techweb_nodes[i]
|
|
if(techweb_categories[I.category])
|
|
techweb_categories[I.category][I.id] = TRUE
|
|
else
|
|
techweb_categories[I.category] = list(I.id = TRUE)
|
|
|
|
/datum/controller/subsystem/research/proc/techweb_node_by_id(id)
|
|
return techweb_nodes[id] || error_node
|
|
|
|
/datum/controller/subsystem/research/proc/techweb_design_by_id(id)
|
|
return techweb_designs[id] || error_design
|
|
|
|
/datum/controller/subsystem/research/proc/on_design_deletion(datum/design/D)
|
|
for(var/i in techweb_nodes)
|
|
var/datum/techweb_node/TN = techwebs[i]
|
|
TN.on_design_deletion(TN)
|
|
for(var/i in techwebs)
|
|
var/datum/techweb/T = i
|
|
T.recalculate_nodes(TRUE)
|
|
|
|
/datum/controller/subsystem/research/proc/on_node_deletion(datum/techweb_node/TN)
|
|
for(var/i in techweb_nodes)
|
|
var/datum/techweb_node/TN2 = techwebs[i]
|
|
TN2.on_node_deletion(TN)
|
|
for(var/i in techwebs)
|
|
var/datum/techweb/T = i
|
|
T.recalculate_nodes(TRUE)
|
|
|
|
/datum/controller/subsystem/research/proc/initialize_all_techweb_nodes(clearall = FALSE)
|
|
if(islist(techweb_nodes) && clearall)
|
|
QDEL_LIST(techweb_nodes)
|
|
if(islist(techweb_nodes_starting && clearall))
|
|
techweb_nodes_starting.Cut()
|
|
var/list/returned = list()
|
|
for(var/path in subtypesof(/datum/techweb_node))
|
|
var/datum/techweb_node/TN = path
|
|
if(isnull(initial(TN.id)))
|
|
continue
|
|
TN = new path
|
|
if(returned[initial(TN.id)])
|
|
stack_trace("WARNING: Techweb node ID clash with ID [initial(TN.id)] detected! Path: [path]")
|
|
errored_datums[TN] = initial(TN.id)
|
|
continue
|
|
returned[initial(TN.id)] = TN
|
|
if(TN.starting_node)
|
|
techweb_nodes_starting[TN.id] = TRUE
|
|
for(var/id in techweb_nodes)
|
|
var/datum/techweb_node/TN = techweb_nodes[id]
|
|
TN.Initialize()
|
|
techweb_nodes = returned
|
|
if (!verify_techweb_nodes()) //Verify all nodes have ids and such.
|
|
stack_trace("Invalid techweb nodes detected")
|
|
calculate_techweb_nodes()
|
|
calculate_techweb_boost_list()
|
|
if (!verify_techweb_nodes()) //Verify nodes and designs have been crosslinked properly.
|
|
CRASH("Invalid techweb nodes detected")
|
|
|
|
/datum/controller/subsystem/research/proc/initialize_all_techweb_designs(clearall = FALSE)
|
|
if(islist(techweb_designs) && clearall)
|
|
QDEL_LIST(techweb_designs)
|
|
var/list/returned = list()
|
|
for(var/path in subtypesof(/datum/design))
|
|
var/datum/design/DN = path
|
|
if(isnull(initial(DN.id)))
|
|
stack_trace("WARNING: Design with null ID detected. Build path: [initial(DN.build_path)]")
|
|
continue
|
|
else if(initial(DN.id) == DESIGN_ID_IGNORE)
|
|
continue
|
|
DN = new path
|
|
if(returned[initial(DN.id)])
|
|
stack_trace("WARNING: Design ID clash with ID [initial(DN.id)] detected! Path: [path]")
|
|
errored_datums[DN] = initial(DN.id)
|
|
continue
|
|
DN.InitializeMaterials() //Initialize the materials in the design
|
|
returned[initial(DN.id)] = DN
|
|
techweb_designs = returned
|
|
verify_techweb_designs()
|
|
|
|
|
|
/datum/controller/subsystem/research/proc/verify_techweb_nodes()
|
|
. = TRUE
|
|
for(var/n in techweb_nodes)
|
|
var/datum/techweb_node/N = techweb_nodes[n]
|
|
if(!istype(N))
|
|
WARNING("Invalid research node with ID [n] detected and removed.")
|
|
techweb_nodes -= n
|
|
research_node_id_error(n)
|
|
. = FALSE
|
|
for(var/p in N.prereq_ids)
|
|
var/datum/techweb_node/P = techweb_nodes[p]
|
|
if(!istype(P))
|
|
WARNING("Invalid research prerequisite node with ID [p] detected in node [N.display_name]\[[N.id]\] removed.")
|
|
N.prereq_ids -= p
|
|
research_node_id_error(p)
|
|
. = FALSE
|
|
for(var/d in N.design_ids)
|
|
var/datum/design/D = techweb_designs[d]
|
|
if(!istype(D))
|
|
WARNING("Invalid research design with ID [d] detected in node [N.display_name]\[[N.id]\] removed.")
|
|
N.design_ids -= d
|
|
design_id_error(d)
|
|
. = FALSE
|
|
for(var/u in N.unlock_ids)
|
|
var/datum/techweb_node/U = techweb_nodes[u]
|
|
if(!istype(U))
|
|
WARNING("Invalid research unlock node with ID [u] detected in node [N.display_name]\[[N.id]\] removed.")
|
|
N.unlock_ids -= u
|
|
research_node_id_error(u)
|
|
. = FALSE
|
|
for(var/p in N.boost_item_paths)
|
|
if(!ispath(p))
|
|
N.boost_item_paths -= p
|
|
WARNING("[p] is not a valid path.")
|
|
node_boost_error(N.id, "[p] is not a valid path.")
|
|
. = FALSE
|
|
var/list/points = N.boost_item_paths[p]
|
|
if(islist(points))
|
|
for(var/i in points)
|
|
if(!isnum(points[i]))
|
|
WARNING("[points[i]] is not a valid number.")
|
|
node_boost_error(N.id, "[points[i]] is not a valid number.")
|
|
. = FALSE
|
|
else if(!point_types[i])
|
|
WARNING("[i] is not a valid point type.")
|
|
node_boost_error(N.id, "[i] is not a valid point type.")
|
|
. = FALSE
|
|
else if(!isnull(points))
|
|
N.boost_item_paths -= p
|
|
node_boost_error(N.id, "No valid list.")
|
|
WARNING("No valid list.")
|
|
. = FALSE
|
|
CHECK_TICK
|
|
|
|
/datum/controller/subsystem/research/proc/verify_techweb_designs()
|
|
for(var/d in techweb_designs)
|
|
var/datum/design/D = techweb_designs[d]
|
|
if(!istype(D))
|
|
stack_trace("WARNING: Invalid research design with ID [d] detected and removed.")
|
|
techweb_designs -= d
|
|
CHECK_TICK
|
|
|
|
/datum/controller/subsystem/research/proc/research_node_id_error(id)
|
|
if(invalid_node_ids[id])
|
|
invalid_node_ids[id]++
|
|
else
|
|
invalid_node_ids[id] = 1
|
|
|
|
/datum/controller/subsystem/research/proc/design_id_error(id)
|
|
if(invalid_design_ids[id])
|
|
invalid_design_ids[id]++
|
|
else
|
|
invalid_design_ids[id] = 1
|
|
|
|
/datum/controller/subsystem/research/proc/calculate_techweb_nodes()
|
|
for(var/design_id in techweb_designs)
|
|
var/datum/design/D = techweb_designs[design_id]
|
|
D.unlocked_by.Cut()
|
|
for(var/node_id in techweb_nodes)
|
|
var/datum/techweb_node/node = techweb_nodes[node_id]
|
|
node.unlock_ids = list()
|
|
for(var/i in node.design_ids)
|
|
var/datum/design/D = techweb_designs[i]
|
|
node.design_ids[i] = TRUE
|
|
D.unlocked_by += node.id
|
|
if(node.hidden)
|
|
techweb_nodes_hidden[node.id] = TRUE
|
|
if(node.experimental)
|
|
techweb_nodes_experimental[node.id] = TRUE
|
|
CHECK_TICK
|
|
generate_techweb_unlock_linking()
|
|
|
|
/datum/controller/subsystem/research/proc/generate_techweb_unlock_linking()
|
|
for(var/node_id in techweb_nodes) //Clear all unlock links to avoid duplication.
|
|
var/datum/techweb_node/node = techweb_nodes[node_id]
|
|
node.unlock_ids = list()
|
|
for(var/node_id in techweb_nodes)
|
|
var/datum/techweb_node/node = techweb_nodes[node_id]
|
|
for(var/prereq_id in node.prereq_ids)
|
|
var/datum/techweb_node/prereq_node = techweb_node_by_id(prereq_id)
|
|
prereq_node.unlock_ids[node.id] = node
|
|
|
|
/datum/controller/subsystem/research/proc/calculate_techweb_boost_list(clearall = FALSE)
|
|
if(clearall)
|
|
techweb_boost_items = list()
|
|
for(var/node_id in techweb_nodes)
|
|
var/datum/techweb_node/node = techweb_nodes[node_id]
|
|
for(var/path in node.boost_item_paths)
|
|
if(!ispath(path))
|
|
continue
|
|
if(length(techweb_boost_items[path]))
|
|
techweb_boost_items[path][node.id] = node.boost_item_paths[path]
|
|
else
|
|
techweb_boost_items[path] = list(node.id = node.boost_item_paths[path])
|
|
CHECK_TICK
|
|
|
|
/datum/controller/subsystem/research/proc/populate_ordnance_experiments()
|
|
for (var/datum/experiment/ordnance/experiment_path as anything in subtypesof(/datum/experiment/ordnance))
|
|
if (initial(experiment_path.experiment_proper))
|
|
ordnance_experiments += new experiment_path()
|
|
|
|
for(var/partner_path in subtypesof(/datum/scientific_partner))
|
|
var/datum/scientific_partner/partner = new partner_path
|
|
if(!partner.accepted_experiments.len)
|
|
for (var/datum/experiment/ordnance/ordnance_experiment as anything in ordnance_experiments)
|
|
partner.accepted_experiments += ordnance_experiment.type
|
|
scientific_partners += partner
|