mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-16 03:56:20 +00:00
## About The Pull Request This PR does many things and I expect to be asked to atomize some stuff. ### Oldstation Additions Oldstation now has their own research server, generating their own points. To help alleviate major concerns, they have a few stuff to help with this: - They now have a pre-built operating computer - They now have an Autopsy scanner - They now have access to Experimental Dissection Experimental Dissection is the old dissection, giving research points in the form of paper notes on completion. They must be turned in to the RND server (only works on the Oldstation one, so you can't abuse this) for points. This was an idea I've had for some time, as Oldstation is used somewhat as a representation of how ss13 used to be (through its use of directional windows (before they got removed, but I'd like to bring them back), old IDs, RTGs, and old engines before they got removed from the game fully) Considering there are 11 alien mobs in Oldstation, there is 27.5k research points to get from alien corpses, enough to bring them up to speed with whatever they wish to do. This is their 'alternative' for experiments (which they can still do if they want, but it is very hard to do, outside of dissection which is needed for the node). This surgery isn't repeatable, isn't upgradable, and isn't removed by being healed. It is not mutually exclusive with autopsy (so you can't ruin yourself doing the wrong surgery). ### Other stuff - Ninjas now drain RND points from the server they drain from, rather than always hitting science - Syncing machines to research roundstart is now a macro, and now immediately syncs to a server on your Z-level. Machines will automatically connect to the Science servers if none else are available. ### non-player facing - Science, Admin, and Oldstation techwebs are now no longer vars on research, but stored in research's list of techwebs. - ``get_available_servers`` and ``find_valid_servers`` are now procs on the research subsystem, rather than the experisci component. - Oldstation code has been split into separate files. ## Why It's Good For The Game Oldstation is one of my favorite ruins, but it is also one of the largest complaints for RND (along with Golems) because they use the station's Science nodes & points (I recently tried de-hardcoding Science stuff to help prepare for this, but I didn't get everything in advance). The complaint stems from these ghost roles, who are meant to be a fun activity to do while waiting for the next round, using the station's research points for their own stuff, completely untrackable unless someone goes out of their way to grief a ghost spawn just for using points to get things they need. These roles make their own servers to drain the station, and I find that unfun and quite boring for everyone- it's also not very flavorful, why would Charliestation know of the station's RND to take advantage of it? This hopes to fix those issues, make Charliestation more worthwhile, and more flavorful. ## Changelog 🆑 fix: Getting a node researched now properly makes it no longer hidden. fix: Ninjas draining RD servers now drains it from the connected techweb, rather than sniping Science. balance: Machines will first try to connect to a techweb with servers on their z-level, with the Science techweb remaining as fallback. add: Oldstation RND, comes with their own Techweb and special surgery to gain research points through dissecting Xenomorphs. /🆑
229 lines
8.8 KiB
Plaintext
229 lines
8.8 KiB
Plaintext
/datum/computer_file/program/science
|
|
filename = "experi_track"
|
|
filedesc = "Nanotrasen Science Hub"
|
|
category = PROGRAM_CATEGORY_SCI
|
|
program_icon_state = "research"
|
|
extended_desc = "Connect to the internal science server in order to assist in station research efforts."
|
|
requires_ntnet = TRUE
|
|
size = 10
|
|
tgui_id = "NtosTechweb"
|
|
program_icon = "atom"
|
|
required_access = list(ACCESS_COMMAND, ACCESS_RESEARCH)
|
|
transfer_access = list(ACCESS_RESEARCH)
|
|
/// Reference to global science techweb
|
|
var/datum/techweb/stored_research
|
|
/// Access needed to lock/unlock the console
|
|
var/lock_access = ACCESS_RESEARCH
|
|
/// 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
|
|
|
|
/datum/computer_file/program/science/on_start(mob/living/user)
|
|
. = ..()
|
|
if(!CONFIG_GET(flag/no_default_techweb_link) && !stored_research)
|
|
CONNECT_TO_RND_SERVER_ROUNDSTART(stored_research, src)
|
|
|
|
/datum/computer_file/program/science/application_attackby(obj/item/attacking_item, mob/living/user)
|
|
if(!istype(attacking_item, /obj/item/multitool))
|
|
return FALSE
|
|
var/obj/item/multitool/attacking_tool = attacking_item
|
|
if(!QDELETED(attacking_tool.buffer) && istype(attacking_tool.buffer, /datum/techweb))
|
|
stored_research = attacking_tool.buffer
|
|
return TRUE
|
|
|
|
/datum/computer_file/program/science/ui_assets(mob/user)
|
|
return list(
|
|
get_asset_datum(/datum/asset/spritesheet/research_designs)
|
|
)
|
|
|
|
// heavy data from this proc should be moved to static data when possible
|
|
/datum/computer_file/program/science/ui_data(mob/user)
|
|
var/list/data = list()
|
|
data["stored_research"] = !!stored_research
|
|
if(!stored_research) //lack of a research node is all we care about.
|
|
return data
|
|
data += list(
|
|
"nodes" = list(),
|
|
"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" = !(computer.obj_flags & EMAGGED),
|
|
"t_disk" = null, //Not doing disk operations on the app, use the console for that.
|
|
"d_disk" = null, //See above.
|
|
"locked" = locked,
|
|
)
|
|
|
|
// Serialize all nodes to display
|
|
for(var/tier in stored_research.tiers)
|
|
var/datum/techweb_node/node = SSresearch.techweb_node_by_id(tier)
|
|
|
|
// Ensure node is supposed to be visible
|
|
if (stored_research.hidden_nodes[tier])
|
|
continue
|
|
|
|
data["nodes"] += list(list(
|
|
"id" = node.id,
|
|
"can_unlock" = stored_research.can_unlock_node(node),
|
|
"tier" = stored_research.tiers[node.id]
|
|
))
|
|
|
|
// Get experiments and serialize them
|
|
var/list/exp_to_process = stored_research.available_experiments.Copy()
|
|
for (var/comp_experi in stored_research.completed_experiments)
|
|
exp_to_process += stored_research.completed_experiments[comp_experi]
|
|
for (var/process_experi in exp_to_process)
|
|
var/datum/experiment/unf_experi = process_experi
|
|
data["experiments"][unf_experi.type] = list(
|
|
"name" = unf_experi.name,
|
|
"description" = unf_experi.description,
|
|
"tag" = unf_experi.exp_tag,
|
|
"progress" = unf_experi.check_progress(),
|
|
"completed" = unf_experi.completed,
|
|
"performance_hint" = unf_experi.performance_hint
|
|
)
|
|
return data
|
|
|
|
/datum/computer_file/program/science/ui_act(action, list/params)
|
|
// Check if the console is locked to block any actions occuring
|
|
if (locked && action != "toggleLock")
|
|
computer.say("Console is locked, cannot perform further actions.")
|
|
return TRUE
|
|
|
|
switch (action)
|
|
if ("toggleLock")
|
|
if(computer.obj_flags & EMAGGED)
|
|
to_chat(usr, span_boldwarning("Security protocol error: Unable to access locking protocols."))
|
|
return TRUE
|
|
if(lock_access in computer?.computer_id_slot?.access)
|
|
locked = !locked
|
|
else
|
|
to_chat(usr, span_boldwarning("Unauthorized Access. Please insert research ID card."))
|
|
return TRUE
|
|
if ("researchNode")
|
|
research_node(params["node_id"], usr)
|
|
return TRUE
|
|
|
|
/datum/computer_file/program/science/ui_static_data(mob/user)
|
|
. = list(
|
|
"static_data" = list()
|
|
)
|
|
|
|
// 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/research_designs/spritesheet = get_asset_datum(/datum/asset/spritesheet/research_designs)
|
|
var/size32x32 = "[spritesheet.name]32x32"
|
|
for (var/design_id in SSresearch.techweb_designs)
|
|
var/datum/design/design = SSresearch.techweb_designs[design_id] || SSresearch.error_design
|
|
var/compressed_id = "[compress_id(design.id)]"
|
|
var/size = spritesheet.icon_size_id(design.id)
|
|
design_cache[compressed_id] = list(
|
|
design.name,
|
|
"[size == size32x32 ? "" : "[size] "][design.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
|
|
)
|
|
|
|
/**
|
|
* Compresses an ID to an integer representation using the id_cache, used for deduplication
|
|
* in sent JSON payloads
|
|
*
|
|
* Arguments:
|
|
* * id - the ID to compress
|
|
*/
|
|
/datum/computer_file/program/science/proc/compress_id(id)
|
|
if (!id_cache[id])
|
|
id_cache[id] = id_cache_seq
|
|
id_cache_seq += 1
|
|
return id_cache[id]
|
|
|
|
/datum/computer_file/program/science/proc/research_node(id, mob/user)
|
|
if(!stored_research || !stored_research.available_nodes[id] || stored_research.researched_nodes[id])
|
|
computer.say("Node unlock failed: Either no techweb is found, node is already researched or is not available!")
|
|
return FALSE
|
|
var/datum/techweb_node/tech_node = SSresearch.techweb_node_by_id(id)
|
|
if(!istype(tech_node))
|
|
computer.say("Node unlock failed: Unknown error.")
|
|
return FALSE
|
|
var/list/price = tech_node.get_price(stored_research)
|
|
if(stored_research.can_afford(price))
|
|
user.investigate_log("researched [id]([json_encode(price)]) on techweb id [stored_research.id] via [computer].", INVESTIGATE_RESEARCH)
|
|
if(istype(stored_research, /datum/techweb/science))
|
|
SSblackbox.record_feedback("associative", "science_techweb_unlock", 1, list("id" = "[id]", "name" = tech_node.display_name, "price" = "[json_encode(price)]", "time" = SQLtime()))
|
|
if(stored_research.research_node_id(id))
|
|
computer.say("Successfully researched [tech_node.display_name].")
|
|
var/logname = "Unknown"
|
|
if(isAI(user))
|
|
logname = "AI [user.name]"
|
|
if(iscyborg(user))
|
|
logname = "CYBORG [user.name]"
|
|
if(iscarbon(user))
|
|
var/obj/item/card/id/idcard = user.get_active_held_item()
|
|
if(istype(idcard))
|
|
logname = "[idcard.registered_name]"
|
|
if(ishuman(user))
|
|
var/mob/living/carbon/human/human_user = user
|
|
var/obj/item/worn = human_user.wear_id
|
|
if(istype(worn))
|
|
var/obj/item/card/id/id_card_of_human_user = worn.GetID()
|
|
if(istype(id_card_of_human_user))
|
|
logname = "[id_card_of_human_user.registered_name]"
|
|
stored_research.research_logs += list(list(
|
|
"node_name" = tech_node.display_name,
|
|
"node_cost" = price["General Research"],
|
|
"node_researcher" = logname,
|
|
"node_research_location" = "[get_area(computer)] ([user.x],[user.y],[user.z])",
|
|
))
|
|
return TRUE
|
|
else
|
|
computer.say("Failed to research node: Internal database error!")
|
|
return FALSE
|
|
computer.say("Not enough research points...")
|
|
return FALSE
|