Files
VOREStation/code/modules/research/tg/rdconsole.dm
T
Will 0c8488b0ae Move autolathe design datums to techweb datums (#19202)
* testing makes me feel good

* invalid lists

* get rid of those boards

* show expected mats

* oop

* error

* wip techweb porting

* more techweb entries

* ported too

* disable those for now

* okay nevermind i guess

* already exists

* assemblies

* moar

* welding helmet

* many entries

* lol tg

* fix test

* again

* fixagain

* more moved

* update techweb test

* catch that too

* screeee

* more fixes

* missing announcement

* orphaned sleevecard

* some more fixing

* test

* more

* oops

* glasses

* illegal icons

* non starting

* it's starting

* messed up

* cleanup

* more generic stuff

* morrrreee

* the plastic set

* oops

* correct value

* barbed wires

* more cleaning

* many more

* gone

* cleanup

* no longer

* do not allow duped designs

* it's own node

* proper order no dupes

* last of the broken nodes

* fix bad path

* cleanup

* more test more limits

* gone

* almost done

* these are gone

* syringe gun

* bioregenerator design

* all moved

* organized and webbed

* more fixes

* oops

* cleaning up

* organization

* clean up

* organize

* organized

* didn't save

* wrong mat

* adds many missing ammo types

* svd doesn't exist

* 10mm pistol added

* magazines not casings

* these are pistols

* this is used by like, one weapon

* m1 is ancient

* this is hunting

* extremely botched untested lathe

* disk junk

* rough foundations

* slowly converting

* slowly building this

* local materials

* almost

* big reorganization

* more cleanup

* more cleaning

* lathe stuff

* small fixes

* oops

* many fixes

* mat fix

* more fixes

* actually isolate the hacked designs

* protolathe hacking

* imprinter hacking

* missing ammos

* sheet printing for autolathe

* fixs

* again

* fixed test

* test test

* fix

* fix

* WHY

* fix it...

* here we go

* material print

* on turf

* body disk

* consistant name

* Many weapons to hacked lathe

* missing node

* wrong section

* fixes

* this list should be unified

* ammo boxes

* don't do single shells anymore

* wat

* specifics

* snowflake

* not needed

* already available

* pickaxe too

* tweak

* removed unneeded node

* missing surgery items

* Apply suggestion from @Cameron-The-Raven

* Adds RMS

* typo

* illegal needs hacked

* disabled material weapons

* circuit testing

* STRING TEST

* test

* fixing some things

* huh?

* hidden circuits

* some flakey boards

* grammar

* fixes

* more hidden

* wrong board

* security cams

* alert consoles

* spaceheater is simple enough to not have one

* pcus

* cargo vendors

* tcomms stuff

* solar tracker circuit

* artifact stuff

* SM stuff

* illegal machines

* med machines

* no longer needed

* more stuff

* these are not standard

* moreeee

* doppler and more

* organized

* finishing

* oop

* use the define

* inheret

* dopper fixes

* space heater

* doing some organization

* explain the var

* some tweaks

* wrong one

* fixed test

* material test too

* simple roto gens

* use the define

* emergency power node

* tgui fixes for icons

* not needed

* color blended material designs

* icons for large research

* oops

* unshit that

* use stack here

* center math

* .

* .

* .

* .

* .

* unifi

* fix these

* NO COPYPASTA

* ILLEEGGALLL

* mandate descriptions

* agony

* actually lets do this

* use macros

* description

* desc

* desc

* desc

* desc

* desc

* desc

* gps desc

* telecomms component desc

---------

Co-authored-by: Cameron Lennox <killer65311@gmail.com>
Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
2026-03-20 19:38:00 -04:00

389 lines
14 KiB
Plaintext

#define RND_TECH_DISK "tech"
#define RND_DESIGN_DISK "design"
/*
Research and Development (R&D) Console
This is the main work horse of the R&D system. It contains the menus/controls for the Destructive Analyzer, Protolathe, and Circuit
imprinter.
Basic use: When it first is created, it will attempt to link up to related devices within 3 squares. It'll only link up if they
aren't already linked to another console. Any consoles it cannot link up with (either because all of a certain type are already
linked or there aren't any in range), you'll just not have access to that menu. In the settings menu, there are menu options that
allow a player to attempt to re-sync with nearby consoles. You can also force it to disconnect from a specific console.
The only thing that requires ordnance access is locking and unlocking the console on the settings menu.
Nothing else in the console has ID requirements.
*/
/obj/machinery/computer/rdconsole_tg
name = "R&D Console"
desc = "A console used to interface with R&D tools."
icon_screen = "rdcomp"
icon_keyboard = "rd_key"
circuit = /obj/item/circuitboard/rdconsole
req_access = list(ACCESS_RESEARCH) // Locking and unlocking the console requires research access
/// Reference to global science techweb
var/datum/techweb/stored_research
/// The stored technology disk, if present
var/obj/item/disk/tech_disk/t_disk
/// The stored design disk, if present
var/obj/item/disk/design_disk/d_disk
/// Determines if the console is locked, and consequently if actions can be performed with it
var/locked = FALSE
/// Used for compressing data sent to the UI via static_data as payload size is of concern
var/id_cache = list()
/// Sequence var for the id cache
var/id_cache_seq = 1
/// Cooldown that prevents hanging the MC when tech disks are copied
STATIC_COOLDOWN_DECLARE(cooldowncopy)
/obj/machinery/computer/rdconsole_tg/Initialize(mapload)
. = ..()
if(!stored_research)
CONNECT_TO_RND_SERVER_ROUNDSTART(stored_research, src)
if(stored_research)
stored_research.consoles_accessing += src
/obj/machinery/computer/rdconsole_tg/Destroy()
if(stored_research)
stored_research.consoles_accessing -= src
stored_research = null
if(t_disk)
t_disk.forceMove(get_turf(src))
t_disk = null
if(d_disk)
d_disk.forceMove(get_turf(src))
d_disk = null
return ..()
/obj/machinery/computer/rdconsole_tg/attackby(obj/item/D, mob/user, list/modifiers, list/attack_modifiers)
//Loading a disk into it.
if(istype(D, /obj/item/disk))
if(istype(D, /obj/item/disk/tech_disk))
if(t_disk)
to_chat(user, span_warning("A technology disk is already loaded!"))
return
if(!user.unEquip(D, target = src))
to_chat(user, span_warning("[D] is stuck to your hand!"))
return
t_disk = D
else if (istype(D, /obj/item/disk/design_disk))
if(d_disk)
to_chat(user, span_warning("A design disk is already loaded!"))
return
if(!user.unEquip(D, target = src))
to_chat(user, span_warning("[D] is stuck to your hand!"))
return
d_disk = D
else
to_chat(user, span_warning("Machine cannot accept disks in that format."))
return
to_chat(user, span_notice("You insert [D] into \the [src]!"))
return
return ..()
/obj/machinery/computer/rdconsole_tg/proc/enqueue_node(id, mob/user)
if(!stored_research || !stored_research.available_nodes[id] || stored_research.researched_nodes[id])
atom_say("Node enqueue failed: Either no techweb is found, node is already researched or is not available!")
return FALSE
stored_research.enqueue_node(id, user)
return TRUE
/obj/machinery/computer/rdconsole_tg/proc/dequeue_node(id, mob/user)
if(!stored_research || !stored_research.available_nodes[id] || stored_research.researched_nodes[id])
atom_say("Node dequeue failed: Either no techweb is found, node is already researched or is not available!")
return FALSE
stored_research.dequeue_node(id, user)
return TRUE
/obj/machinery/computer/rdconsole_tg/proc/research_node(id, mob/user)
if(!stored_research || !stored_research.available_nodes[id] || stored_research.researched_nodes[id])
atom_say("Node unlock failed: Either no techweb is found, node is already researched or is not available!")
return FALSE
var/datum/techweb_node/TN = SSresearch.techweb_node_by_id(id)
if(!istype(TN))
atom_say("Node unlock failed: Unknown error.")
return FALSE
var/list/price = TN.get_price(stored_research)
if(stored_research.can_afford(price))
user.investigate_log("researched [id]([json_encode(price)]) on techweb id [stored_research.id].", INVESTIGATE_RESEARCH)
// if(istype(stored_research, /datum/techweb/science))
// SSblackbox.record_feedback("associative", "science_techweb_unlock", 1, list("id" = "[id]", "name" = TN.display_name, "price" = "[json_encode(price)]", "time" = ISOtime()))
if(stored_research.research_node_id(id, research_source = src))
atom_say("Successfully researched [TN.display_name].")
var/logname = "Unknown"
if(isAI(user))
logname = "AI [user.name]"
if(isrobot(user))
logname = "CYBORG [user.name]"
if(iscarbon(user))
var/obj/item/card/id/idcard = user.get_active_hand()
if(istype(idcard))
logname = "[idcard.registered_name]"
if(ishuman(user))
var/mob/living/carbon/human/H = user
var/obj/item/I = H.wear_id
if(istype(I))
var/obj/item/card/id/ID = I.GetID()
if(istype(ID))
logname = "[ID.registered_name]"
stored_research.research_logs += list(list(
"node_name" = TN.display_name,
"node_cost" = price[TECHWEB_POINT_TYPE_GENERIC],
"node_researcher" = logname,
"node_research_location" = "[get_area(src)] ([src.x],[src.y],[src.z])",
))
return TRUE
else
atom_say("Failed to research node: Internal database error!")
return FALSE
atom_say("Not enough research points...")
return FALSE
/obj/machinery/computer/rdconsole_tg/attack_hand(mob/user as mob)
if(stat & (BROKEN|NOPOWER))
return
tgui_interact(user)
/obj/machinery/computer/rdconsole_tg/tgui_interact(mob/user, datum/tgui/ui = null)
. = ..()
ui = SStgui.try_update_ui(user, src, ui)
if (!ui)
ui = new(user, src, "Techweb", name)
ui.open()
/obj/machinery/computer/rdconsole_tg/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/spritesheet_batched/research_designs),
)
/obj/machinery/computer/rdconsole_tg/tgui_data(mob/user)
var/list/data = list()
data["stored_research"] = !!stored_research
data["locked"] = locked
if(!stored_research) //lack of a research node is all we care about.
return data
data += list(
"nodes" = list(),
"queue_nodes" = stored_research.research_queue_nodes,
"experiments" = list(),
"researched_designs" = stored_research.researched_designs,
"points" = stored_research.research_points,
"points_last_tick" = stored_research.last_bitcoins,
"web_org" = stored_research.organization,
"sec_protocols" = FALSE, // !(obj_flags & EMAGGED),
"t_disk" = null,
"d_disk" = null,
)
if(t_disk)
data["t_disk"] = list (
"stored_research" = t_disk.stored_research.researched_nodes,
)
if(d_disk)
data["d_disk"] = list("blueprints" = list())
for (var/datum/design_techweb/D in d_disk.blueprints)
data["d_disk"]["blueprints"] += D.id
// Serialize all nodes to display
for(var/v in stored_research.tiers)
var/datum/techweb_node/n = SSresearch.techweb_node_by_id(v)
var/enqueued_by_user = FALSE
if((v in stored_research.research_queue_nodes) && stored_research.research_queue_nodes[v] == user)
enqueued_by_user = TRUE
// Ensure node is supposed to be visible
if (stored_research.hidden_nodes[v])
continue
data["nodes"] += list(list(
"id" = n.id,
"is_free" = n.is_free(stored_research),
"can_unlock" = stored_research.can_unlock_node(n),
"have_experiments_done" = stored_research.have_experiments_for_node(n),
"tier" = stored_research.tiers[n.id],
"enqueued_by_user" = enqueued_by_user
))
// Get experiments and serialize them
var/list/exp_to_process = stored_research.available_experiments.Copy()
for (var/e in stored_research.completed_experiments)
exp_to_process += stored_research.completed_experiments[e]
for (var/e in exp_to_process)
var/datum/experiment/ex = e
data["experiments"][ex.type] = list(
"name" = ex.name,
"description" = ex.description,
"tag" = ex.exp_tag,
"progress" = ex.check_progress(),
"completed" = ex.completed,
"performance_hint" = ex.performance_hint,
)
return data
/**
* Compresses an ID to an integer representation using the id_cache, used for deduplication
* in sent JSON payloads
*
* Arguments:
* * id - the ID to compress
*/
/obj/machinery/computer/rdconsole_tg/proc/compress_id(id)
if (!id_cache[id])
id_cache[id] = id_cache_seq
id_cache_seq += 1
return id_cache[id]
/obj/machinery/computer/rdconsole_tg/tgui_static_data(mob/user)
. = list(
"static_data" = list(),
"point_types_abbreviations" = SSresearch.point_types,
)
// Build node cache...
// Note this looks a bit ugly but its to reduce the size of the JSON payload
// by the greatest amount that we can, as larger JSON payloads result in
// hanging when the user opens the UI
var/node_cache = list()
for (var/node_id in SSresearch.techweb_nodes)
var/datum/techweb_node/node = SSresearch.techweb_nodes[node_id] || SSresearch.error_node
var/compressed_id = "[compress_id(node.id)]"
node_cache[compressed_id] = list(
"name" = node.display_name,
"description" = node.description
)
if (LAZYLEN(node.research_costs))
node_cache[compressed_id]["costs"] = list()
for (var/node_cost in node.research_costs)
node_cache[compressed_id]["costs"]["[compress_id(node_cost)]"] = node.research_costs[node_cost]
if (LAZYLEN(node.prereq_ids))
node_cache[compressed_id]["prereq_ids"] = list()
for (var/prerequisite_node in node.prereq_ids)
node_cache[compressed_id]["prereq_ids"] += compress_id(prerequisite_node)
if (LAZYLEN(node.design_ids))
node_cache[compressed_id]["design_ids"] = list()
for (var/unlocked_design in node.design_ids)
node_cache[compressed_id]["design_ids"] += compress_id(unlocked_design)
if (LAZYLEN(node.unlock_ids))
node_cache[compressed_id]["unlock_ids"] = list()
for (var/unlocked_node in node.unlock_ids)
node_cache[compressed_id]["unlock_ids"] += compress_id(unlocked_node)
if (LAZYLEN(node.required_experiments))
node_cache[compressed_id]["required_experiments"] = node.required_experiments
if (LAZYLEN(node.discount_experiments))
node_cache[compressed_id]["discount_experiments"] = node.discount_experiments
// Build design cache
var/design_cache = list()
var/datum/asset/spritesheet_batched/research_designs/spritesheet = get_asset_datum(/datum/asset/spritesheet_batched/research_designs)
var/size32x32 = "[spritesheet.name]32x32"
for (var/design_id in SSresearch.techweb_designs)
var/datum/design_techweb/design = SSresearch.techweb_designs[design_id] || SSresearch.error_design
var/compressed_id = "[compress_id(design.id)]"
var/css_id = sanitize_css_class_name(design.id)
var/size = spritesheet.icon_size_id(css_id)
design_cache[compressed_id] = list(
design.name,
"[size == size32x32 ? "" : "[size] "][css_id]"
)
// Ensure id cache is included for decompression
var/flat_id_cache = list()
for (var/id in id_cache)
flat_id_cache += id
.["static_data"] = list(
"node_cache" = node_cache,
"design_cache" = design_cache,
"id_cache" = flat_id_cache,
)
/obj/machinery/computer/rdconsole_tg/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
. = ..()
if (.)
return
add_fingerprint(usr)
// Check if the console is locked to block any actions occuring
if (locked && action != "toggleLock")
atom_say("Console is locked, cannot perform further actions.")
return TRUE
switch (action)
if ("toggleLock")
// if(flags & EMAGGED)
// to_chat(usr, span_boldwarning("Security protocol error: Unable to access locking protocols."))
// return TRUE
if(allowed(usr))
locked = !locked
else
to_chat(usr, span_boldwarning("Unauthorized Access."))
return TRUE
if ("researchNode")
research_node(params["node_id"], usr)
return TRUE
if ("enqueueNode")
enqueue_node(params["node_id"], usr)
return TRUE
if ("dequeueNode")
dequeue_node(params["node_id"], usr)
return TRUE
if("ejectDisk")
eject_disk(params["type"])
return TRUE
if("uploadDisk")
if(params["type"] == RND_DESIGN_DISK)
if(QDELETED(d_disk))
atom_say("No design disk inserted!")
return TRUE
for(var/D in d_disk.blueprints)
if(D)
stored_research.add_design(D, TRUE)
atom_say("Uploading blueprints from disk.")
d_disk.on_upload(stored_research, src)
return TRUE
if(params["type"] == RND_TECH_DISK)
if(!COOLDOWN_FINISHED(src, cooldowncopy)) // prevents MC hang
atom_say("Servers busy!")
return
if (QDELETED(t_disk))
atom_say("No tech disk inserted!")
return TRUE
COOLDOWN_START(src, cooldowncopy, 5 SECONDS)
atom_say("Uploading technology disk.")
t_disk.stored_research.copy_research_to(stored_research)
return TRUE
//Tech disk-only action.
if("loadTech")
if(!COOLDOWN_FINISHED(src, cooldowncopy)) // prevents MC hang
atom_say("Servers busy!")
return
if(QDELETED(t_disk))
atom_say("No tech disk inserted!")
return
COOLDOWN_START(src, cooldowncopy, 5 SECONDS)
atom_say("Downloading to technology disk.")
stored_research.copy_research_to(t_disk.stored_research)
return TRUE
/obj/machinery/computer/rdconsole_tg/proc/eject_disk(type)
if(type == RND_DESIGN_DISK && d_disk)
d_disk.forceMove(get_turf(src))
d_disk = null
if(type == RND_TECH_DISK && t_disk)
t_disk.forceMove(get_turf(src))
t_disk = null
#undef RND_TECH_DISK
#undef RND_DESIGN_DISK