[MIRROR] TGUI Destructive Analyzer [MDB IGNORE] (#25005)

* TGUI Destructive Analyzer (#79572)

## About The Pull Request

I made this to help me move more towards my goals [laid out
here](https://hackmd.io/XLt5MoRvRxuhFbwtk4VAUA) which currently doesn't
have much interest.

This makes the Destructive Analyzer use a little neat TGUI menu instead
of its old HTML one. I also touch a lot of science stuff and a little
experimentor stuff, so let me explain a bit:
Old iterations of Science had different items that you can use to boost
nodes through deconstruction. This has been removed, and its only
feature is the auto-unlocking of nodes (that is; making them visible to
the R&D console). I thought that instead of keeping this deprecated code
around, I would rework it a little to make it clear what we actually use
it for (unhiding nodes).
All vars and procs that mentioned this have been renamed or reworked to
make more sense now.

Experimentor stuff shares a lot with the destructive analyzer, so I had
to mess with that a bit to keep its decayed corpse of deprecated code,
functional.

I also added context tips to the destructive analyzer, and added the
ability to AltClick to remove the inserted item. Removing items now also
plays a little sound because it was kinda lame.
Also, balloon alerts.

## Why It's Good For The Game

Moves a shitty machine to TGUI so it is slightly less shitty, now it's
more direct and compact with more player-feedback.
Helps me with a personal project and yea

### Video demonstration

I show off connecting the machine to R&D Servers, but I haven't changed
the behavior of that and the roundstart analyzers are connected to
servers by default.

https://github.com/tgstation/tgstation/assets/53777086/65295600-4fae-42d1-9bae-eccefe337a2b

## Changelog

🆑
refactor: Destructive Analyzers now have a TGUI menu.
/🆑

* TGUI Destructive Analyzer

* Modular

---------

Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com>
Co-authored-by: Giz <13398309+vinylspiders@users.noreply.github.com>
This commit is contained in:
SkyratBot
2023-11-14 15:19:08 +01:00
committed by GitHub
parent 64dd06c716
commit 73bdd7341a
17 changed files with 375 additions and 324 deletions

View File

@@ -1,10 +1,6 @@
#define RDSCREEN_NOBREAK "<NO_HTML_BREAK>"
/// For instances where we don't want a design showing up due to it being for debug/sanity purposes /// For instances where we don't want a design showing up due to it being for debug/sanity purposes
#define DESIGN_ID_IGNORE "IGNORE_THIS_DESIGN" #define DESIGN_ID_IGNORE "IGNORE_THIS_DESIGN"
#define RESEARCH_MATERIAL_DESTROY_ID "__destroy"
//! Techweb names for new point types. Can be used to define specific point values for specific types of research (science, security, engineering, etc.) //! Techweb names for new point types. Can be used to define specific point values for specific types of research (science, security, engineering, etc.)
#define TECHWEB_POINT_TYPE_GENERIC "General Research" #define TECHWEB_POINT_TYPE_GENERIC "General Research"

View File

@@ -27,8 +27,8 @@ SUBSYSTEM_DEF(research)
var/list/techweb_nodes_starting = list() var/list/techweb_nodes_starting = list()
///category name = list(node.id = TRUE) ///category name = list(node.id = TRUE)
var/list/techweb_categories = list() var/list/techweb_categories = list()
///associative double-layer path = list(id = list(point_type = point_discount)) ///List of all items that can unlock a node. (node.id = list(items))
var/list/techweb_boost_items = list() var/list/techweb_unlock_items = list()
///Node ids that should be hidden by default. ///Node ids that should be hidden by default.
var/list/techweb_nodes_hidden = list() var/list/techweb_nodes_hidden = list()
///Node ids that are exclusive to the BEPIS. ///Node ids that are exclusive to the BEPIS.
@@ -64,7 +64,7 @@ SUBSYSTEM_DEF(research)
/// Lookup list for ordnance briefers. /// Lookup list for ordnance briefers.
var/list/ordnance_experiments = list() var/list/ordnance_experiments = list()
/// Lookup list for scipaper partners. /// Lookup list for scipaper partners.
var/list/scientific_partners = list() var/list/datum/scientific_partner/scientific_partners = list()
/datum/controller/subsystem/research/Initialize() /datum/controller/subsystem/research/Initialize()
point_types = TECHWEB_POINT_TYPE_LIST_ASSOCIATIVE_NAMES point_types = TECHWEB_POINT_TYPE_LIST_ASSOCIATIVE_NAMES
@@ -153,7 +153,7 @@ SUBSYSTEM_DEF(research)
if (!verify_techweb_nodes()) //Verify all nodes have ids and such. if (!verify_techweb_nodes()) //Verify all nodes have ids and such.
stack_trace("Invalid techweb nodes detected") stack_trace("Invalid techweb nodes detected")
calculate_techweb_nodes() calculate_techweb_nodes()
calculate_techweb_boost_list() calculate_techweb_item_unlocking_requirements()
if (!verify_techweb_nodes()) //Verify nodes and designs have been crosslinked properly. if (!verify_techweb_nodes()) //Verify nodes and designs have been crosslinked properly.
CRASH("Invalid techweb nodes detected") CRASH("Invalid techweb nodes detected")
@@ -209,25 +209,15 @@ SUBSYSTEM_DEF(research)
N.unlock_ids -= u N.unlock_ids -= u
research_node_id_error(u) research_node_id_error(u)
. = FALSE . = FALSE
for(var/p in N.boost_item_paths) for(var/p in N.required_items_to_unlock)
if(!ispath(p)) if(!ispath(p))
N.boost_item_paths -= p N.required_items_to_unlock -= p
WARNING("[p] is not a valid path.") WARNING("[p] is not a valid path.")
node_boost_error(N.id, "[p] is not a valid path.") node_boost_error(N.id, "[p] is not a valid path.")
. = FALSE . = FALSE
var/list/points = N.boost_item_paths[p] var/list/points = N.required_items_to_unlock[p]
if(islist(points)) if(!isnull(points))
for(var/i in points) N.required_items_to_unlock -= p
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.") node_boost_error(N.id, "No valid list.")
WARNING("No valid list.") WARNING("No valid list.")
. = FALSE . = FALSE
@@ -281,18 +271,16 @@ SUBSYSTEM_DEF(research)
var/datum/techweb_node/prereq_node = techweb_node_by_id(prereq_id) var/datum/techweb_node/prereq_node = techweb_node_by_id(prereq_id)
prereq_node.unlock_ids[node.id] = node prereq_node.unlock_ids[node.id] = node
/datum/controller/subsystem/research/proc/calculate_techweb_boost_list(clearall = FALSE) /datum/controller/subsystem/research/proc/calculate_techweb_item_unlocking_requirements()
if(clearall)
techweb_boost_items = list()
for(var/node_id in techweb_nodes) for(var/node_id in techweb_nodes)
var/datum/techweb_node/node = techweb_nodes[node_id] var/datum/techweb_node/node = techweb_nodes[node_id]
for(var/path in node.boost_item_paths) for(var/path in node.required_items_to_unlock)
if(!ispath(path)) if(!ispath(path))
continue continue
if(length(techweb_boost_items[path])) if(length(techweb_unlock_items[path]))
techweb_boost_items[path][node.id] = node.boost_item_paths[path] techweb_unlock_items[path][node.id] = node.required_items_to_unlock[path]
else else
techweb_boost_items[path] = list(node.id = node.boost_item_paths[path]) techweb_unlock_items[path] = list(node.id = node.required_items_to_unlock[path])
CHECK_TICK CHECK_TICK
/datum/controller/subsystem/research/proc/populate_ordnance_experiments() /datum/controller/subsystem/research/proc/populate_ordnance_experiments()

View File

@@ -443,7 +443,7 @@
///Separator between the items on the list ///Separator between the items on the list
var/sep = "" var/sep = ""
///Nodes that can be boosted ///Nodes that can be boosted
var/list/boostable_nodes = techweb_item_boost_check(src) var/list/boostable_nodes = techweb_item_unlock_check(src)
if (boostable_nodes) if (boostable_nodes)
for(var/id in boostable_nodes) for(var/id in boostable_nodes)
var/datum/techweb_node/node = SSresearch.techweb_node_by_id(id) var/datum/techweb_node/node = SSresearch.techweb_node_by_id(id)
@@ -618,9 +618,6 @@
R.activate_module(src) R.activate_module(src)
R.hud_used.update_robot_modules_display() R.hud_used.update_robot_modules_display()
/obj/item/proc/GetDeconstructableContents()
return get_all_contents() - src
// afterattack() and attack() prototypes moved to _onclick/item_attack.dm for consistency // afterattack() and attack() prototypes moved to _onclick/item_attack.dm for consistency
/obj/item/proc/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) /obj/item/proc/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE)

View File

@@ -968,7 +968,8 @@
req_components = list( req_components = list(
/datum/stock_part/scanning_module = 1, /datum/stock_part/scanning_module = 1,
/datum/stock_part/servo = 1, /datum/stock_part/servo = 1,
/datum/stock_part/micro_laser = 1) /datum/stock_part/micro_laser = 1,
)
/obj/item/circuitboard/machine/experimentor /obj/item/circuitboard/machine/experimentor
name = "E.X.P.E.R.I-MENTOR" name = "E.X.P.E.R.I-MENTOR"

View File

@@ -65,9 +65,9 @@
singular_partner["path"] = partner.type singular_partner["path"] = partner.type
singular_partner["boostedNodes"] = list() singular_partner["boostedNodes"] = list()
singular_partner["acceptedExperiments"] = list() singular_partner["acceptedExperiments"] = list()
for (var/node_id in partner.boosted_nodes) for (var/node_id in partner.boostable_nodes)
var/datum/techweb_node/node = SSresearch.techweb_node_by_id(node_id) var/datum/techweb_node/node = SSresearch.techweb_node_by_id(node_id)
singular_partner["boostedNodes"] += list(list("name" = node.display_name, "discount" = partner.boosted_nodes[node_id], "id"=node_id)) singular_partner["boostedNodes"] += list(list("name" = node.display_name, "discount" = partner.boostable_nodes[node_id], "id" = node_id))
for (var/datum/experiment/ordnance/ordnance_experiment as anything in partner.accepted_experiments) for (var/datum/experiment/ordnance/ordnance_experiment as anything in partner.accepted_experiments)
singular_partner["acceptedExperiments"] += initial(ordnance_experiment.name) singular_partner["acceptedExperiments"] += initial(ordnance_experiment.name)
parsed_partners += list(singular_partner) parsed_partners += list(singular_partner)
@@ -154,7 +154,7 @@
data["purchaseableBoosts"][partner.type] = list() data["purchaseableBoosts"][partner.type] = list()
for(var/node_id in linked_techweb.get_available_nodes()) for(var/node_id in linked_techweb.get_available_nodes())
// Not from our partner // Not from our partner
if(!(node_id in partner.boosted_nodes)) if(!(node_id in partner.boostable_nodes))
continue continue
if(!partner.allowed_to_boost(linked_techweb, node_id)) if(!partner.allowed_to_boost(linked_techweb, node_id))
continue continue

View File

@@ -1,9 +1,11 @@
/* ///How much power it costs to deconstruct an item.
Destructive Analyzer #define DESTRUCTIVE_ANALYZER_POWER_USAGE (BASE_MACHINE_IDLE_CONSUMPTION * 2.5)
///The 'ID' for deconstructing items for Research points instead of nodes.
#define DESTRUCTIVE_ANALYZER_DESTROY_POINTS "research_points"
It is used to destroy hand-held objects and advance technological research. Controls are in the linked R&D console. /**
* ## Destructive Analyzer
Note: Must be placed within 3 tiles of the R&D Console * It is used to destroy hand-held objects and advance technological research.
*/ */
/obj/machinery/rnd/destructive_analyzer /obj/machinery/rnd/destructive_analyzer
name = "destructive analyzer" name = "destructive analyzer"
@@ -11,215 +13,177 @@ Note: Must be placed within 3 tiles of the R&D Console
icon_state = "d_analyzer" icon_state = "d_analyzer"
base_icon_state = "d_analyzer" base_icon_state = "d_analyzer"
circuit = /obj/item/circuitboard/machine/destructive_analyzer circuit = /obj/item/circuitboard/machine/destructive_analyzer
var/decon_mod = 0
/obj/machinery/rnd/destructive_analyzer/RefreshParts() /obj/machinery/rnd/destructive_analyzer/Initialize(mapload)
. = ..() . = ..()
var/T = 0 register_context()
for(var/datum/stock_part/stock_part in component_parts)
T += stock_part.tier
decon_mod = T
/obj/machinery/rnd/destructive_analyzer/proc/ConvertReqString2List(list/source_list) /obj/machinery/rnd/destructive_analyzer/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
var/list/temp_list = params2list(source_list) if(loaded_item)
for(var/O in temp_list) context[SCREENTIP_CONTEXT_ALT_LMB] = "Remove Item"
temp_list[O] = text2num(temp_list[O]) else if(!isnull(held_item))
return temp_list context[SCREENTIP_CONTEXT_LMB] = "Insert Item"
return CONTEXTUAL_SCREENTIP_SET
/obj/machinery/rnd/destructive_analyzer/Insert_Item(obj/item/O, mob/living/user) /obj/machinery/rnd/destructive_analyzer/attackby(obj/item/weapon, mob/living/user, params)
if(!user.combat_mode) if(user.combat_mode)
. = 1 return ..()
if(!is_insertion_ready(user)) if(!is_insertion_ready(user))
return return ..()
if(!user.transferItemToLoc(O, src)) if(!user.transferItemToLoc(weapon, src))
to_chat(user, span_warning("\The [O] is stuck to your hand, you cannot put it in the [src.name]!")) to_chat(user, span_warning("\The [weapon] is stuck to your hand, you cannot put it in the [name]!"))
return return TRUE
busy = TRUE busy = TRUE
loaded_item = O loaded_item = weapon
to_chat(user, span_notice("You add the [O.name] to the [src.name]!")) to_chat(user, span_notice("You add the [weapon.name] to the [name]!"))
flick("d_analyzer_la", src) flick("[base_icon_state]_la", src)
addtimer(CALLBACK(src, PROC_REF(finish_loading)), 10) addtimer(CALLBACK(src, PROC_REF(finish_loading)), 1 SECONDS)
updateUsrDialog() return TRUE
/obj/machinery/rnd/destructive_analyzer/proc/finish_loading() /obj/machinery/rnd/destructive_analyzer/AltClick(mob/user)
update_appearance() . = ..()
reset_busy() unload_item()
/obj/machinery/rnd/destructive_analyzer/update_icon_state() /obj/machinery/rnd/destructive_analyzer/update_icon_state()
icon_state = "[base_icon_state][loaded_item ? "_l" : null]" icon_state = "[base_icon_state][loaded_item ? "_l" : null]"
return ..() return ..()
/obj/machinery/rnd/destructive_analyzer/proc/destroy_item(obj/item/thing, innermode = FALSE) /obj/machinery/rnd/destructive_analyzer/ui_interact(mob/user, datum/tgui/ui)
if(QDELETED(thing) || QDELETED(src)) ui = SStgui.try_update_ui(user, src, ui)
return FALSE if(!ui)
if(!innermode) ui = new(user, src, "DestructiveAnalyzer")
flick("d_analyzer_process", src) ui.open()
busy = TRUE
addtimer(CALLBACK(src, PROC_REF(reset_busy)), 24)
use_power(250)
if(thing == loaded_item)
loaded_item = null
var/list/food = thing.GetDeconstructableContents()
for(var/obj/item/innerthing in food)
destroy_item(innerthing, TRUE)
for(var/mob/living/victim in thing)
if(victim.stat != DEAD)
victim.investigate_log("has been killed by a destructive analyzer.", INVESTIGATE_DEATHS)
victim.death()
qdel(thing) /obj/machinery/rnd/destructive_analyzer/ui_data(mob/user)
loaded_item = null var/list/data = list()
if (!innermode) data["server_connected"] = !!stored_research
update_appearance() data["node_data"] = list()
return TRUE if(loaded_item)
data["item_icon"] = icon2base64(getFlatIcon(image(icon = loaded_item.icon, icon_state = loaded_item.icon_state), no_anim = TRUE))
data["indestructible"] = !(loaded_item.resistance_flags & INDESTRUCTIBLE)
data["loaded_item"] = loaded_item
data["already_deconstructed"] = !!stored_research.deconstructed_items[loaded_item.type]
var/list/points = techweb_item_point_check(loaded_item)
data["recoverable_points"] = techweb_point_display_generic(points)
/obj/machinery/rnd/destructive_analyzer/proc/user_try_decon_id(id, mob/user) var/list/boostable_nodes = techweb_item_unlock_check(loaded_item)
if(!istype(loaded_item))
return FALSE
if (id && id != RESEARCH_MATERIAL_DESTROY_ID)
var/datum/techweb_node/TN = SSresearch.techweb_node_by_id(id)
if(!istype(TN))
return FALSE
var/dpath = loaded_item.type
var/list/worths = TN.boost_item_paths[dpath]
var/list/differences = list()
var/list/already_boosted = stored_research.boosted_nodes[TN.id]
for(var/i in worths)
var/used = already_boosted? already_boosted[i] : 0
var/value = min(worths[i], TN.research_costs[i]) - used
if(value > 0)
differences[i] = value
if(length(worths) && !length(differences))
return FALSE
var/choice = tgui_alert(user, "Are you sure you want to destroy [loaded_item] to [!length(worths) ? "reveal [TN.display_name]" : "boost [TN.display_name] by [json_encode(differences)] point\s"]?", "Destructive Analyzer", list("Proceed", "Cancel"))
if(choice != "Proceed")
return FALSE
if(QDELETED(loaded_item) || QDELETED(src))
return FALSE
SSblackbox.record_feedback("nested tally", "item_deconstructed", 1, list("[TN.id]", "[loaded_item.type]"))
if(destroy_item(loaded_item))
stored_research.boost_with_item(SSresearch.techweb_node_by_id(TN.id), dpath)
else
var/list/point_value = techweb_item_point_check(loaded_item)
if(stored_research.deconstructed_items[loaded_item.type])
point_value = list()
var/user_mode_string = ""
if(length(point_value))
user_mode_string = " for [json_encode(point_value)] points"
var/choice = tgui_alert(usr, "Are you sure you want to destroy [loaded_item][user_mode_string]?",, list("Proceed", "Cancel"))
if(choice == "Cancel")
return FALSE
if(QDELETED(loaded_item) || QDELETED(src))
return FALSE
destroy_item(loaded_item)
return TRUE
/obj/machinery/rnd/destructive_analyzer/proc/unload_item()
if(!loaded_item)
return FALSE
loaded_item.forceMove(get_turf(src))
loaded_item = null
update_appearance()
return TRUE
/obj/machinery/rnd/destructive_analyzer/ui_interact(mob/user)
. = ..()
var/datum/browser/popup = new(user, "destructive_analyzer", name, 900, 600)
popup.set_content(ui_deconstruct())
popup.open()
/obj/machinery/rnd/destructive_analyzer/proc/ui_deconstruct() //Legacy code
var/list/l = list()
if(!loaded_item)
l += "<div class='statusDisplay'>No item loaded. Standing-by...</div>"
else
l += "<div class='statusDisplay'>[RDSCREEN_NOBREAK]"
l += "<table><tr><td>[icon2html(loaded_item, usr)]</td><td><b>[loaded_item.name]</b> <A href='?src=[REF(src)];eject_item=1'>Eject</A></td></tr></table>[RDSCREEN_NOBREAK]"
l += "Select a node to boost by deconstructing this item. This item can boost:"
var/anything = FALSE
var/list/boostable_nodes = techweb_item_boost_check(loaded_item)
for(var/id in boostable_nodes) for(var/id in boostable_nodes)
anything = TRUE var/datum/techweb_node/unlockable_node = SSresearch.techweb_node_by_id(id)
var/list/worth = boostable_nodes[id] var/list/node_data = list()
var/datum/techweb_node/N = SSresearch.techweb_node_by_id(id) node_data["node_name"] = unlockable_node.display_name
node_data["node_id"] = unlockable_node.id
l += "<div class='statusDisplay'>[RDSCREEN_NOBREAK]" node_data["node_hidden"] = !!stored_research.hidden_nodes[unlockable_node.id]
if (stored_research.researched_nodes[N.id]) // already researched data["node_data"] += list(node_data)
l += "<span class='linkOff'>[N.display_name]</span>"
l += "This node has already been researched."
else if(!length(worth)) // reveal only
if (stored_research.hidden_nodes[N.id])
l += "<A href='?src=[REF(src)];deconstruct=[N.id]'>[N.display_name]</A>"
l += "This node will be revealed."
else else
l += "<span class='linkOff'>[N.display_name]</span>" data["loaded_item"] = null
l += "This node has already been revealed." return data
else // boost by the difference
var/list/differences = list()
var/list/already_boosted = stored_research.boosted_nodes[N.id]
for(var/i in worth)
var/already_boosted_amount = already_boosted? stored_research.boosted_nodes[N.id][i] : 0
var/amt = min(worth[i], N.research_costs[i]) - already_boosted_amount
if(amt > 0)
differences[i] = amt
if (length(differences))
l += "<A href='?src=[REF(src)];deconstruct=[N.id]'>[N.display_name]</A>"
l += "This node will be boosted with the following:<BR>[techweb_point_display_generic(differences)]"
else
l += "<span class='linkOff'>[N.display_name]</span>"
l += "This node has already been boosted.</span>"
l += "</div>[RDSCREEN_NOBREAK]"
var/list/point_values = techweb_item_point_check(loaded_item) /obj/machinery/rnd/destructive_analyzer/ui_static_data(mob/user)
if(point_values) var/list/data = list()
anything = TRUE data["research_point_id"] = DESTRUCTIVE_ANALYZER_DESTROY_POINTS
l += "<div class='statusDisplay'>[RDSCREEN_NOBREAK]" return data
if (stored_research.deconstructed_items[loaded_item.type])
l += "<span class='linkOff'>Point Deconstruction</span>"
l += "This item's points have already been claimed."
else
l += "<A href='?src=[REF(src)];deconstruct=[RESEARCH_MATERIAL_DESTROY_ID]'>Point Deconstruction</A>"
l += "This item is worth: <BR>[techweb_point_display_generic(point_values)]!"
l += "</div>[RDSCREEN_NOBREAK]"
if(!(loaded_item.resistance_flags & INDESTRUCTIBLE)) /obj/machinery/rnd/destructive_analyzer/ui_act(action, params, datum/tgui/ui)
l += "<div class='statusDisplay'><A href='?src=[REF(src)];deconstruct=[RESEARCH_MATERIAL_DESTROY_ID]'>Destroy Item</A>"
l += "</div>[RDSCREEN_NOBREAK]"
anything = TRUE
if (!anything)
l += "Nothing!"
l += "</div>"
for(var/i in 1 to length(l))
if(!findtextEx(l[i], RDSCREEN_NOBREAK))
l[i] += "<br>"
. = l.Join("")
return replacetextEx(., RDSCREEN_NOBREAK, "")
/obj/machinery/rnd/destructive_analyzer/Topic(raw, ls)
. = ..() . = ..()
if(.) if(.)
return return
add_fingerprint(usr) var/mob/user = usr
usr.set_machine(src) switch(action)
if("eject_item")
if(ls["eject_item"]) //Eject the item inside the destructive analyzer.
if(busy) if(busy)
to_chat(usr, span_danger("The destructive analyzer is busy at the moment.")) balloon_alert(user, "already busy!")
return return TRUE
if(loaded_item) if(loaded_item)
unload_item() unload_item()
if(ls["deconstruct"]) return TRUE
if(!user_try_decon_id(ls["deconstruct"], usr)) if("deconstruct")
if(!user_try_decon_id(params["deconstruct_id"]))
say("Destructive analysis failed!") say("Destructive analysis failed!")
return TRUE
updateUsrDialog() //This allows people to put syndicate screwdrivers in the machine. Secondary act still passes.
/obj/machinery/rnd/destructive_analyzer/screwdriver_act(mob/living/user, obj/item/tool) /obj/machinery/rnd/destructive_analyzer/screwdriver_act(mob/living/user, obj/item/tool)
return FALSE return FALSE
///Drops the loaded item where it can and nulls it.
/obj/machinery/rnd/destructive_analyzer/proc/unload_item()
if(!loaded_item)
return FALSE
playsound(loc, 'sound/machines/terminal_insert_disc.ogg', 30, FALSE)
loaded_item.forceMove(drop_location())
loaded_item = null
update_appearance(UPDATE_ICON)
return TRUE
///Called in a timer callback after loading something into it, this handles resetting the 'busy' state back to its initial state
///So the machine can be used.
/obj/machinery/rnd/destructive_analyzer/proc/finish_loading()
update_appearance(UPDATE_ICON)
reset_busy()
/**
* Destroys an item by going through all its contents (including itself) and calling destroy_item_individual
* Args:
* gain_research_points - Whether deconstructing each individual item should check for research points to boost.
*/
/obj/machinery/rnd/destructive_analyzer/proc/destroy_item(gain_research_points = FALSE)
if(QDELETED(loaded_item) || QDELETED(src))
return FALSE
flick("[base_icon_state]_process", src)
busy = TRUE
addtimer(CALLBACK(src, PROC_REF(reset_busy)), 2.4 SECONDS)
use_power(DESTRUCTIVE_ANALYZER_POWER_USAGE)
var/list/all_contents = loaded_item.get_all_contents()
for(var/innerthing in all_contents)
destroy_item_individual(innerthing, gain_research_points)
loaded_item = null
update_appearance(UPDATE_ICON)
return TRUE
/**
* Destroys the individual provided item
* Args:
* thing - The thing being destroyed. Generally an object, but it can be a mob too, such as intellicards and pAIs.
* gain_research_points - Whether deconstructing this should give research points to the stored techweb, if applicable.
*/
/obj/machinery/rnd/destructive_analyzer/proc/destroy_item_individual(obj/item/thing, gain_research_points = FALSE)
if(isliving(thing))
var/mob/living/mob_thing = thing
if(mob_thing.stat != DEAD)
mob_thing.investigate_log("has been killed by a destructive analyzer.", INVESTIGATE_DEATHS)
mob_thing.death()
var/list/point_value = techweb_item_point_check(thing)
if(point_value && !stored_research.deconstructed_items[thing.type])
stored_research.deconstructed_items[thing.type] = TRUE
stored_research.add_point_list(list(TECHWEB_POINT_TYPE_GENERIC = point_value))
qdel(thing)
/**
* Attempts to destroy the loaded item using a provided research id.
* Args:
* id - The techweb ID node that we're meant to unlock if applicable.
*/
/obj/machinery/rnd/destructive_analyzer/proc/user_try_decon_id(id)
if(!istype(loaded_item))
return FALSE
if(isnull(id))
return FALSE
if(id == DESTRUCTIVE_ANALYZER_DESTROY_POINTS)
if(!destroy_item(gain_research_points = TRUE))
return FALSE
return TRUE
var/datum/techweb_node/node_to_discover = SSresearch.techweb_node_by_id(id)
if(!istype(node_to_discover))
return FALSE
SSblackbox.record_feedback("nested tally", "item_deconstructed", 1, list("[node_to_discover.id]", "[loaded_item.type]"))
if(!destroy_item())
return FALSE
stored_research.unhide_node(SSresearch.techweb_node_by_id(node_to_discover.id))
return TRUE
#undef DESTRUCTIVE_ANALYZER_DESTROY_POINTS
#undef DESTRUCTIVE_ANALYZER_POWER_USAGE

View File

@@ -42,12 +42,6 @@
var/static/list/valid_items //valid items for special reactions like transforming var/static/list/valid_items //valid items for special reactions like transforming
var/list/critical_items_typecache //items that can cause critical reactions var/list/critical_items_typecache //items that can cause critical reactions
/obj/machinery/rnd/experimentor/proc/ConvertReqString2List(list/source_list)
var/list/temp_list = params2list(source_list)
for(var/O in temp_list)
temp_list[O] = text2num(temp_list[O])
return temp_list
/obj/machinery/rnd/experimentor/proc/valid_items() /obj/machinery/rnd/experimentor/proc/valid_items()
RETURN_TYPE(/list) RETURN_TYPE(/list)
@@ -131,20 +125,22 @@
return FALSE return FALSE
return TRUE return TRUE
/obj/machinery/rnd/experimentor/Insert_Item(obj/item/O, mob/living/user) /obj/machinery/rnd/experimentor/attackby(obj/item/weapon, mob/living/user, params)
if(!user.combat_mode) if(user.combat_mode)
. = 1 return ..()
if(!is_insertion_ready(user)) if(!is_insertion_ready(user))
return return ..()
if(!user.transferItemToLoc(O, src)) if(!user.transferItemToLoc(weapon, src))
return to_chat(user, span_warning("\The [weapon] is stuck to your hand, you cannot put it in the [name]!"))
loaded_item = O return TRUE
to_chat(user, span_notice("You add [O] to the machine.")) loaded_item = weapon
to_chat(user, span_notice("You add [weapon] to the machine."))
flick("h_lathe_load", src) flick("h_lathe_load", src)
return TRUE
/obj/machinery/rnd/experimentor/default_deconstruction_crowbar(obj/item/O) /obj/machinery/rnd/experimentor/default_deconstruction_crowbar(obj/item/O)
ejectItem() ejectItem()
. = ..(O) return ..(O)
/obj/machinery/rnd/experimentor/ui_interact(mob/user) /obj/machinery/rnd/experimentor/ui_interact(mob/user)
var/list/dat = list("<center>") var/list/dat = list("<center>")
@@ -161,22 +157,19 @@
if(istype(loaded_item,/obj/item/relic)) if(istype(loaded_item,/obj/item/relic))
dat += "<b><a href='byond://?src=[REF(src)];item=[REF(loaded_item)];function=[SCANTYPE_DISCOVER]'>Discover</A></b>" dat += "<b><a href='byond://?src=[REF(src)];item=[REF(loaded_item)];function=[SCANTYPE_DISCOVER]'>Discover</A></b>"
dat += "<b><a href='byond://?src=[REF(src)];function=eject'>Eject</A>" dat += "<b><a href='byond://?src=[REF(src)];function=eject'>Eject</A>"
var/list/listin = techweb_item_boost_check(src) var/list/listin = techweb_item_unlock_check(src)
if(listin) if(listin)
var/list/output = list("<b><font color='purple'>Research Boost Data:</font></b>") var/list/output = list("<b><font color='purple'>Research Boost Data:</font></b>")
var/list/res = list("<b><font color='blue'>Already researched:</font></b>") var/list/res = list("<b><font color='blue'>Already researched:</font></b>")
var/list/boosted = list("<b><font color='red'>Already boosted:</font></b>")
for(var/node_id in listin) for(var/node_id in listin)
var/datum/techweb_node/N = SSresearch.techweb_node_by_id(node_id) var/datum/techweb_node/N = SSresearch.techweb_node_by_id(node_id)
var/str = "<b>[N.display_name]</b>: [listin[N]] points.</b>" var/str = "<b>[N.display_name]</b>: [listin[N]] points.</b>"
var/datum/techweb/science_web = locate(/datum/techweb/science) in SSresearch.techwebs var/datum/techweb/science_web = locate(/datum/techweb/science) in SSresearch.techwebs
if(science_web.researched_nodes[N.id]) if(science_web.researched_nodes[N.id])
res += str res += str
else if(science_web.boosted_nodes[N.id])
boosted += str
if(science_web.visible_nodes[N.id]) //JOY OF DISCOVERY! if(science_web.visible_nodes[N.id]) //JOY OF DISCOVERY!
output += str output += str
output += boosted + res output += res
dat += output dat += output
else else
dat += "<b>Nothing loaded.</b>" dat += "<b>Nothing loaded.</b>"
@@ -218,9 +211,9 @@
experiment(dotype,process) experiment(dotype,process)
use_power(750) use_power(750)
if(dotype != FAIL) if(dotype != FAIL)
var/list/nodes = techweb_item_boost_check(process) var/list/nodes = techweb_item_unlock_check(process)
var/picked = pick_weight(nodes) //This should work. var/picked = pick_weight(nodes) //This should work.
stored_research.boost_with_item(SSresearch.techweb_node_by_id(picked), process.type) stored_research.unhide_node(SSresearch.techweb_node_by_id(picked))
updateUsrDialog() updateUsrDialog()
/obj/machinery/rnd/experimentor/proc/matchReaction(matching,reaction) /obj/machinery/rnd/experimentor/proc/matchReaction(matching,reaction)

View File

@@ -287,21 +287,20 @@
/// List of ordnance experiments that our partner is willing to accept. If this list is not filled it means the partner will accept everything. /// List of ordnance experiments that our partner is willing to accept. If this list is not filled it means the partner will accept everything.
var/list/accepted_experiments = list() var/list/accepted_experiments = list()
/// Associative list of which technology the partner might be able to boost and by how much. /// Associative list of which technology the partner might be able to boost and by how much.
var/list/boosted_nodes = list() var/list/boostable_nodes = list()
/datum/scientific_partner/proc/purchase_boost(datum/techweb/purchasing_techweb, datum/techweb_node/node) /datum/scientific_partner/proc/purchase_boost(datum/techweb/purchasing_techweb, datum/techweb_node/node)
if(!allowed_to_boost(purchasing_techweb, node.id)) if(!allowed_to_boost(purchasing_techweb, node.id))
return FALSE return FALSE
purchasing_techweb.boost_techweb_node(node, list(TECHWEB_POINT_TYPE_GENERIC=boosted_nodes[node.id])) purchasing_techweb.boost_techweb_node(node, list(TECHWEB_POINT_TYPE_GENERIC = boostable_nodes[node.id]))
purchasing_techweb.scientific_cooperation[type] -= boosted_nodes[node.id] * SCIENTIFIC_COOPERATION_PURCHASE_MULTIPLIER purchasing_techweb.scientific_cooperation[type] -= boostable_nodes[node.id] * SCIENTIFIC_COOPERATION_PURCHASE_MULTIPLIER
return TRUE return TRUE
/datum/scientific_partner/proc/allowed_to_boost(datum/techweb/purchasing_techweb, node_id) /datum/scientific_partner/proc/allowed_to_boost(datum/techweb/purchasing_techweb, node_id)
if(purchasing_techweb.scientific_cooperation[type] < (boosted_nodes[node_id] * SCIENTIFIC_COOPERATION_PURCHASE_MULTIPLIER)) // Too expensive if(purchasing_techweb.scientific_cooperation[type] < (boostable_nodes[node_id] * SCIENTIFIC_COOPERATION_PURCHASE_MULTIPLIER)) // Too expensive
return FALSE return FALSE
if(!(node_id in purchasing_techweb.get_available_nodes())) // Not currently available if(!(node_id in purchasing_techweb.get_available_nodes())) // Not currently available
return FALSE return FALSE
if((TECHWEB_POINT_TYPE_GENERIC in purchasing_techweb.boosted_nodes[node_id]) && (purchasing_techweb.boosted_nodes[node_id][TECHWEB_POINT_TYPE_GENERIC] >= boosted_nodes[node_id])) // Already bought or we have a bigger discount if((TECHWEB_POINT_TYPE_GENERIC in purchasing_techweb.boosted_nodes[node_id]) && (purchasing_techweb.boosted_nodes[node_id][TECHWEB_POINT_TYPE_GENERIC] >= boostable_nodes[node_id])) // Already bought or we have a bigger discount
return FALSE return FALSE
return TRUE return TRUE

View File

@@ -3,7 +3,7 @@
flufftext = "A local group of miners are looking for ways to improve their mining output. They are interested in smaller scale explosives." flufftext = "A local group of miners are looking for ways to improve their mining output. They are interested in smaller scale explosives."
accepted_experiments = list(/datum/experiment/ordnance/explosive/lowyieldbomb) accepted_experiments = list(/datum/experiment/ordnance/explosive/lowyieldbomb)
multipliers = list(SCIPAPER_COOPERATION_INDEX = 0.75, SCIPAPER_FUNDING_INDEX = 0.75) multipliers = list(SCIPAPER_COOPERATION_INDEX = 0.75, SCIPAPER_FUNDING_INDEX = 0.75)
boosted_nodes = list( boostable_nodes = list(
"bluespace_basic" = 2000, "bluespace_basic" = 2000,
"NVGtech" = 1500, "NVGtech" = 1500,
"practical_bluespace" = 2500, "practical_bluespace" = 2500,
@@ -16,7 +16,7 @@
name = "Ghost Writing" name = "Ghost Writing"
flufftext = "A nearby research station ran by a very wealthy captain seems to be struggling with their scientific output. They might reward us handsomely if we ghostwrite for them." flufftext = "A nearby research station ran by a very wealthy captain seems to be struggling with their scientific output. They might reward us handsomely if we ghostwrite for them."
multipliers = list(SCIPAPER_COOPERATION_INDEX = 0.25, SCIPAPER_FUNDING_INDEX = 2) multipliers = list(SCIPAPER_COOPERATION_INDEX = 0.25, SCIPAPER_FUNDING_INDEX = 2)
boosted_nodes = list( boostable_nodes = list(
"comp_recordkeeping" = 500, "comp_recordkeeping" = 500,
"computer_data_disks" = 500, "computer_data_disks" = 500,
) )
@@ -29,7 +29,7 @@
/datum/experiment/ordnance/explosive/pressurebomb, /datum/experiment/ordnance/explosive/pressurebomb,
/datum/experiment/ordnance/explosive/hydrogenbomb, /datum/experiment/ordnance/explosive/hydrogenbomb,
) )
boosted_nodes = list( boostable_nodes = list(
"adv_weaponry" = 5000, "adv_weaponry" = 5000,
"weaponry" = 2500, "weaponry" = 2500,
"sec_basic" = 1250, "sec_basic" = 1250,
@@ -47,7 +47,7 @@
/datum/experiment/ordnance/gaseous/nitrous_oxide, /datum/experiment/ordnance/gaseous/nitrous_oxide,
/datum/experiment/ordnance/gaseous/bz, /datum/experiment/ordnance/gaseous/bz,
) )
boosted_nodes = list( boostable_nodes = list(
"cyber_organs" = 750, "cyber_organs" = 750,
"cyber_organs_upgraded" = 1000, "cyber_organs_upgraded" = 1000,
"genetics" = 500, "genetics" = 500,
@@ -63,7 +63,7 @@
/datum/experiment/ordnance/gaseous/noblium, /datum/experiment/ordnance/gaseous/noblium,
/datum/experiment/ordnance/explosive/nobliumbomb, /datum/experiment/ordnance/explosive/nobliumbomb,
) )
boosted_nodes = list( boostable_nodes = list(
"engineering" = 5000, "engineering" = 5000,
"adv_engi" = 5000, "adv_engi" = 5000,
"emp_super" = 3000, "emp_super" = 3000,

View File

@@ -11,9 +11,10 @@
var/hacked = FALSE var/hacked = FALSE
var/console_link = TRUE //allow console link. var/console_link = TRUE //allow console link.
var/disabled = FALSE var/disabled = FALSE
var/obj/item/loaded_item = null //the item loaded inside the machine (currently only used by experimentor and destructive analyzer)
/// Ref to global science techweb. /// Ref to global science techweb.
var/datum/techweb/stored_research var/datum/techweb/stored_research
///The item loaded inside the machine, used by experimentors and destructive analyzers only.
var/obj/item/loaded_item
/obj/machinery/rnd/proc/reset_busy() /obj/machinery/rnd/proc/reset_busy()
busy = FALSE busy = FALSE
@@ -59,14 +60,6 @@
else else
return FALSE return FALSE
/obj/machinery/rnd/attackby(obj/item/O, mob/user, params)
if(is_refillable() && O.is_drainable())
return FALSE //inserting reagents into the machine
if(Insert_Item(O, user))
return TRUE
return ..()
/obj/machinery/rnd/crowbar_act(mob/living/user, obj/item/tool) /obj/machinery/rnd/crowbar_act(mob/living/user, obj/item/tool)
return default_deconstruction_crowbar(tool) return default_deconstruction_crowbar(tool)
@@ -103,36 +96,32 @@
wires.interact(user) wires.interact(user)
return TRUE return TRUE
//proc used to handle inserting items or reagents into rnd machines
/obj/machinery/rnd/proc/Insert_Item(obj/item/I, mob/user)
return
//whether the machine can have an item inserted in its current state. //whether the machine can have an item inserted in its current state.
/obj/machinery/rnd/proc/is_insertion_ready(mob/user) /obj/machinery/rnd/proc/is_insertion_ready(mob/user)
if(panel_open) if(panel_open)
to_chat(user, span_warning("You can't load [src] while it's opened!")) balloon_alert(user, "panel open!")
return FALSE return FALSE
if(disabled) if(disabled)
to_chat(user, span_warning("The insertion belts of [src] won't engage!")) balloon_alert(user, "belts disabled!")
return FALSE return FALSE
if(busy) if(busy)
to_chat(user, span_warning("[src] is busy right now.")) balloon_alert(user, "still busy!")
return FALSE return FALSE
if(machine_stat & BROKEN) if(machine_stat & BROKEN)
to_chat(user, span_warning("[src] is broken.")) balloon_alert(user, "machine broken!")
return FALSE return FALSE
if(machine_stat & NOPOWER) if(machine_stat & NOPOWER)
to_chat(user, span_warning("[src] has no power.")) balloon_alert(user, "no power!")
return FALSE return FALSE
if(loaded_item) if(loaded_item)
to_chat(user, span_warning("[src] is already loaded.")) balloon_alert(user, "item already loaded!")
return FALSE return FALSE
return TRUE return TRUE
//we eject the loaded item when deconstructing the machine //we eject the loaded item when deconstructing the machine
/obj/machinery/rnd/on_deconstruction() /obj/machinery/rnd/on_deconstruction()
if(loaded_item) if(loaded_item)
loaded_item.forceMove(loc) loaded_item.forceMove(drop_location())
..() ..()
/obj/machinery/rnd/proc/AfterMaterialInsert(item_inserted, id_inserted, amount_inserted) /obj/machinery/rnd/proc/AfterMaterialInsert(item_inserted, id_inserted, amount_inserted)

View File

@@ -10,14 +10,15 @@
WARNING("Invalid boost information for node \[[id]\]: [message]") WARNING("Invalid boost information for node \[[id]\]: [message]")
SSresearch.invalid_node_boost[id] = message SSresearch.invalid_node_boost[id] = message
///Returns an associative list of techweb node datums with values of the boost it gives. var/list/returned = list() ///Returns an associative list of techweb node datums with values of the nodes it unlocks.
/proc/techweb_item_boost_check(obj/item/I) /proc/techweb_item_unlock_check(obj/item/I)
if(SSresearch.techweb_boost_items[I.type]) if(SSresearch.techweb_unlock_items[I.type])
return SSresearch.techweb_boost_items[I.type] //It should already be formatted in node datum = list(point type = value) return SSresearch.techweb_unlock_items[I.type] //It should already be formatted in node datum = list(point type = value)
/proc/techweb_item_point_check(obj/item/I) /proc/techweb_item_point_check(obj/item/I)
if(SSresearch.techweb_point_items[I.type]) if(SSresearch.techweb_point_items[I.type])
return SSresearch.techweb_point_items[I.type] return SSresearch.techweb_point_items[I.type]
return FALSE
/proc/techweb_point_display_generic(pointlist) /proc/techweb_point_display_generic(pointlist)
var/list/ret = list() var/list/ret = list()

View File

@@ -26,7 +26,7 @@
var/list/boosted_nodes = list() var/list/boosted_nodes = list()
/// Hidden nodes. id = TRUE. Used for unhiding nodes when requirements are met by removing the entry of the node. /// Hidden nodes. id = TRUE. Used for unhiding nodes when requirements are met by removing the entry of the node.
var/list/hidden_nodes = list() var/list/hidden_nodes = list()
/// Items already deconstructed for a generic point boost, path = list(point_type = points) /// List of items already deconstructed for research points, preventing infinite research point generation.
var/list/deconstructed_items = list() var/list/deconstructed_items = list()
/// Available research points, type = number /// Available research points, type = number
var/list/research_points = list() var/list/research_points = list()
@@ -400,17 +400,15 @@
LAZYINITLIST(boosted_nodes[node.id]) LAZYINITLIST(boosted_nodes[node.id])
for(var/point_type in pointlist) for(var/point_type in pointlist)
boosted_nodes[node.id][point_type] = max(boosted_nodes[node.id][point_type], pointlist[point_type]) boosted_nodes[node.id][point_type] = max(boosted_nodes[node.id][point_type], pointlist[point_type])
if(node.autounlock_by_boost) unhide_node(node)
hidden_nodes -= node.id
update_node_status(node) update_node_status(node)
return TRUE return TRUE
/// Boosts a techweb node by using items. ///Removes a node from the hidden_nodes list, making it viewable and researchable (if no experiments are required).
/datum/techweb/proc/boost_with_item(datum/techweb_node/node, itempath) /datum/techweb/proc/unhide_node(datum/techweb_node/node)
if(!istype(node) || !ispath(itempath)) if(!istype(node))
return FALSE return FALSE
var/list/boost_amount = node.boost_item_paths[itempath] hidden_nodes -= node.id
boost_techweb_node(node, boost_amount)
return TRUE return TRUE
/datum/techweb/proc/update_tiers(datum/techweb_node/base) /datum/techweb/proc/update_tiers(datum/techweb_node/base)

View File

@@ -24,8 +24,8 @@
var/list/design_ids = list() var/list/design_ids = list()
/// CALCULATED FROM OTHER NODE'S PREREQUISITIES. Associated list id = TRUE /// CALCULATED FROM OTHER NODE'S PREREQUISITIES. Associated list id = TRUE
var/list/unlock_ids = list() var/list/unlock_ids = list()
/// Associative list, path = list(point type = point_value) /// List of items you need to deconstruct to unlock this node.
var/list/boost_item_paths = list() var/list/required_items_to_unlock = list()
/// Boosting this will autounlock this node /// Boosting this will autounlock this node
var/autounlock_by_boost = TRUE var/autounlock_by_boost = TRUE
/// The points cost to research the node, type = amount /// The points cost to research the node, type = amount

View File

@@ -2161,7 +2161,7 @@
display_name = "Alien Technology" display_name = "Alien Technology"
description = "Things used by the greys." description = "Things used by the greys."
prereq_ids = list("biotech","engineering") prereq_ids = list("biotech","engineering")
boost_item_paths = list( required_items_to_unlock = list(
/obj/item/stack/sheet/mineral/abductor, /obj/item/stack/sheet/mineral/abductor,
/obj/item/abductor, /obj/item/abductor,
/obj/item/cautery/alien, /obj/item/cautery/alien,
@@ -2204,7 +2204,7 @@
"alien_scalpel", "alien_scalpel",
) )
boost_item_paths = list( required_items_to_unlock = list(
/obj/item/abductor, /obj/item/abductor,
/obj/item/cautery/alien, /obj/item/cautery/alien,
/obj/item/circuitboard/machine/abductor, /obj/item/circuitboard/machine/abductor,
@@ -2243,7 +2243,7 @@
"alien_wrench", "alien_wrench",
) )
boost_item_paths = list( required_items_to_unlock = list(
/obj/item/abductor, /obj/item/abductor,
/obj/item/circuitboard/machine/abductor, /obj/item/circuitboard/machine/abductor,
/obj/item/crowbar/abductor, /obj/item/crowbar/abductor,
@@ -2296,12 +2296,12 @@
/datum/techweb_node/syndicate_basic/proc/register_uplink_items() /datum/techweb_node/syndicate_basic/proc/register_uplink_items()
SIGNAL_HANDLER SIGNAL_HANDLER
UnregisterSignal(SSearly_assets, COMSIG_SUBSYSTEM_POST_INITIALIZE) UnregisterSignal(SSearly_assets, COMSIG_SUBSYSTEM_POST_INITIALIZE)
boost_item_paths = list() required_items_to_unlock = list()
for(var/datum/uplink_item/item_path as anything in SStraitor.uplink_items_by_type) for(var/datum/uplink_item/item_path as anything in SStraitor.uplink_items_by_type)
var/datum/uplink_item/item = SStraitor.uplink_items_by_type[item_path] var/datum/uplink_item/item = SStraitor.uplink_items_by_type[item_path]
if(!item.item || !item.illegal_tech) if(!item.item || !item.illegal_tech)
continue continue
boost_item_paths |= item.item //allows deconning to unlock. required_items_to_unlock |= item.item //allows deconning to unlock.
////////////////////////B.E.P.I.S. Locked Techs//////////////////////// ////////////////////////B.E.P.I.S. Locked Techs////////////////////////

View File

@@ -496,15 +496,5 @@
return FALSE return FALSE
return default_deconstruction_crowbar(I) return default_deconstruction_crowbar(I)
/obj/machinery/mecha_part_fabricator/proc/is_insertion_ready(mob/user)
if(panel_open)
to_chat(user, span_warning("You can't load [src] while it's opened!"))
return FALSE
if(being_built)
to_chat(user, span_warning("\The [src] is currently processing! Please wait until completion."))
return FALSE
return TRUE
/obj/machinery/mecha_part_fabricator/maint /obj/machinery/mecha_part_fabricator/maint
link_on_init = FALSE link_on_init = FALSE

View File

@@ -163,17 +163,17 @@
timer_id = null timer_id = null
. = ..() . = ..()
/obj/machinery/rnd/rna_recombinator/Insert_Item(obj/item/O, mob/living/user) /obj/machinery/rnd/rna_recombinator/attackby(obj/item/weapon, mob/living/user, params)
if(user.combat_mode) if(user.combat_mode)
return FALSE return FALSE
if(!is_insertion_ready(user)) if(!is_insertion_ready(user))
return FALSE return FALSE
if(!istype(O, /obj/item/rna_vial)) if(!istype(weapon, /obj/item/rna_vial))
return FALSE return FALSE
if(!user.transferItemToLoc(O, src)) if(!user.transferItemToLoc(weapon, src))
return FALSE return FALSE
loaded_item = O loaded_item = weapon
to_chat(user, span_notice("You insert [O] to into [src] reciprocal.")) to_chat(user, span_notice("You insert [weapon] to into [src] reciprocal."))
flick("h_lathe_load", src) flick("h_lathe_load", src)
update_appearance() update_appearance()
playsound(loc, 'sound/weapons/autoguninsert.ogg', 35, 1) playsound(loc, 'sound/weapons/autoguninsert.ogg', 35, 1)

View File

@@ -0,0 +1,135 @@
import { BooleanLike } from 'common/react';
import { useBackend } from '../backend';
import { Button, Box, Section, NoticeBox } from '../components';
import { Window } from '../layouts';
type Data = {
server_connected: BooleanLike;
loaded_item: string;
item_icon: string;
indestructible: BooleanLike;
already_deconstructed: BooleanLike;
recoverable_points: string;
node_data: NodeData[];
research_point_id: string;
};
type NodeData = {
node_name: string;
node_id: string;
node_hidden: BooleanLike;
};
export const DestructiveAnalyzer = (props, context) => {
const { act, data } = useBackend<Data>(context);
const {
server_connected,
indestructible,
loaded_item,
item_icon,
already_deconstructed,
recoverable_points,
research_point_id,
node_data = [],
} = data;
if (!server_connected) {
return (
<Window width={400} height={260} title="Destructive Analyzer">
<Window.Content>
<NoticeBox textAlign="center" danger>
Not connected to a server. Please sync one using a multitool.
</NoticeBox>
</Window.Content>
</Window>
);
}
if (!loaded_item) {
return (
<Window width={400} height={260} title="Destructive Analyzer">
<Window.Content>
<NoticeBox textAlign="center" danger>
No item loaded! <br />
Put any item inside to see what it&apos;s capable of!
</NoticeBox>
</Window.Content>
</Window>
);
}
return (
<Window
width={400}
height={260}
scrollable
fill
title="Destructive Analyzer">
<Window.Content scrollable>
<Section
title={loaded_item}
buttons={
<Button
icon="eject"
tooltip="Ejects the item currently inside the machine."
onClick={() => act('eject_item')}
/>
}>
<Box
as="img"
src={`data:image/jpeg;base64,${item_icon}`}
height="64px"
width="64px"
style={{
'-ms-interpolation-mode': 'nearest-neighbor',
'vertical-align': 'middle',
}}
/>
</Section>
<Section title="Deconstruction Methods">
{!indestructible && (
<NoticeBox textAlign="center" danger>
This item can&apos;t be deconstructed!
</NoticeBox>
)}
{!!indestructible && (
<>
{!!recoverable_points && (
<>
<Box fontSize="14px">Research points from deconstruction</Box>
<Box>{recoverable_points}</Box>
</>
)}
<Button.Confirm
content="Deconstruct"
icon="hammer"
tooltip={
already_deconstructed
? 'This item item has already been deconstructed, and will not give any additional information.'
: 'Destroys the object currently residing in the machine.'
}
onClick={() =>
act('deconstruct', { deconstruct_id: research_point_id })
}
/>
</>
)}
{node_data.map((node) => (
<Button.Confirm
content={node.node_name}
icon="cash-register"
mt={1}
disabled={!node.node_hidden}
key={node.node_id}
tooltip={
node.node_hidden
? 'Deconstructing this will allow you to research the node in question by making it visible to R&D consoles.'
: 'This node has already been researched, and does not need to be deconstructed.'
}
onClick={() =>
act('deconstruct', { deconstruct_id: node.node_id })
}
/>
))}
</Section>
</Window.Content>
</Window>
);
};